1#! /usr/bin/perl
2
3##  $Id: buildconfig.in 8817 2009-11-17 18:57:19Z iulius $
4##
5##  Generate linkage code and makefiles for storage and overview methods.
6##
7##  Goes through all subdirectories of the current directory and finds
8##  directories that are either storage methods or overview methods.  Builds
9##  methods.[ch] and ovmethods.[ch] as well as makefile stubs.
10
11require 5.003;
12
13use strict;
14use vars qw(@OVERVIEW @STORAGE);
15
16# Storage API functions.
17@STORAGE = qw(init store retrieve next freearticle cancel ctl flushcacheddata
18              printfiles explaintoken shutdown);
19
20# Overview API functions.
21@OVERVIEW = qw(open groupstats groupadd groupdel add cancel opensearch search
22               closesearch getartinfo expiregroup ctl close);
23
24# Used to make heredocs more readable.
25sub unquote { my ($string) = @_; $string =~ s/^:( {0,7}|\t)//gm; $string }
26
27# Parse a method.config file for a storage method, putting information about
28# that storage method into the given hash ref.
29sub parse_config {
30    my ($dir, $file, $config) = @_;
31    local $_;
32    $$config{sources} ||= [];
33    $$config{extra} ||= [];
34    $$config{programs} ||= [];
35    $$config{makefiles} ||= [];
36    open (CONFIG, "$dir/$file") or die "Can't open $dir/$file: $!\n";
37    while (<CONFIG>) {
38        s/^\s+//;
39        s/\s+$//;
40        if (/^name\s*=\s*(\S+)$/) {
41            my $method = $1;
42            die "$dir/$file: $method has already been defined\n"
43                if (defined $$config{method}{$method});
44            $$config{method}{$method} = $dir;
45        } elsif (/^number\s*=\s*(\d+)$/) {
46            my $number = $1;
47            if (defined $$config{number}{$number}) {
48                die "$dir/$file: method number $number was already "
49                    . "allocated in $$config{number}{$number}\n";
50            }
51            $$config{number}{$dir} = $number;
52        } elsif (/^sources\s*=\s*(.*)/) {
53            my $sources = $1;
54            my @sources = split (' ', $sources);
55            push (@{ $$config{sources} }, map { "$dir/$_" } @sources);
56        } elsif (/^extra-sources\s*=\s*(.*)/) {
57            my $extra = $1;
58            my @extra = split (' ', $extra);
59            push (@{ $$config{extra} }, map { "$dir/$_" } @extra);
60        } elsif (/^programs\s*=\s*(.*)/) {
61            my $programs = $1;
62            my @programs = split (' ', $programs);
63            push (@{ $$config{programs} }, map { "$dir/$_" } @programs);
64        } else {
65            warn "$dir/$file: ignoring unknown line: $_\n";
66        }
67    }
68
69    # If there is a makefile fragment in the directory, note it.
70    if (-f "$dir/method.mk") {
71        push (@{ $$config{makefiles} }, "$dir/method.mk");
72    } elsif (-f "$dir/ovmethod.mk") {
73        push (@{ $$config{makefiles} }, "$dir/ovmethod.mk");
74    }
75}
76
77# Write out include directives for a list of files.
78sub write_includes {
79    my ($fh, $config) = @_;
80    my $method;
81    for $method (sort keys %{ $$config{method} }) {
82        my $path = $$config{method}{$method};
83        print $fh qq(\#include "$path/$method.h"\n);
84    }
85}
86
87# Write out the method struct.
88sub write_methods {
89    my ($fh, $config, $prefix, @funcs) = @_;
90    my ($notfirst, $method);
91    for $method (sort keys %{ $$config{method} }) {
92        print $fh "\n},\n" if $notfirst;
93        print $fh qq(\{\n    "$method");
94        print $fh ', ', $prefix, '_', uc ($method) if $prefix;
95        for (@funcs) {
96            print $fh ",\n    ${method}_$_";
97        }
98        $notfirst++;
99    }
100    print $fh "\n}\n};\n\n";
101}
102
103# Write out the constant defines for methods.
104sub write_constants {
105    my ($fh, $config, $prefix) = @_;
106    my $method;
107    for $method (sort keys %{ $$config{method} }) {
108        printf $fh "#define ${prefix}_%-30s%d\n", uc ($method),
109            $$config{number}{$$config{method}{$method}};
110    }
111}
112
113# Write out methods.c and methods.h for the interface to the storage
114# methods.
115sub write_storage {
116    my $storage = shift;
117    open (DEF, '> methods.c.new') or die "Can't create methods.c.new: $!\n";
118    print DEF unquote (<<'EOE');
119:       /* This file is automatically generated by buildconfig. */
120:
121:       #include "interface.h"
122:       #include "methods.h"
123EOE
124    my $method;
125    write_includes (\*DEF, $storage);
126    print DEF "\nSTORAGE_METHOD storage_methods[",
127        scalar (keys %{ $$storage{method} }), "] = {\n";
128    write_methods (\*DEF, $storage, 'TOKEN', @STORAGE);
129    close DEF;
130    rename ('methods.c.new', 'methods.c');
131
132    open (H, '> methods.h.new') or die "Can't open methods.h.new: $!\n";
133    print H unquote (<<'EOE');
134:       /* This file is automatically generated by buildconfig */
135:
136:       #ifndef METHODS_H
137:       #define METHODS_H 1
138:
139:       #include "interface.h"
140:
141EOE
142    print H '#define NUM_STORAGE_METHODS ',
143        scalar (keys %{ $$storage{method} }), "\n\n";
144    write_constants (\*H, $storage, 'TOKEN');
145    print H unquote (<<'EOE');
146:
147:       extern STORAGE_METHOD storage_methods[NUM_STORAGE_METHODS];
148:
149:       #endif /* METHODS_H */
150EOE
151    close H;
152    rename ('methods.h.new', 'methods.h');
153}
154
155# Write out ovmethods.c and ovmethods.h for the interface to the overview
156# methods.
157sub write_overview {
158    my $overview = shift;
159    open (DEF, '> ovmethods.c.new')
160        or die "Can't create ovmethods.c.new: $!\n";
161    print DEF unquote (<<'EOE');
162:       /* This file is automatically generated by buildconfig. */
163:
164:       #include "ovinterface.h"
165EOE
166    write_includes (\*DEF, $overview);
167    print DEF "\nOV_METHOD ov_methods[",
168        scalar (keys %{ $$overview{method} }), "] = {\n";
169    write_methods (\*DEF, $overview, undef, @OVERVIEW);
170    close DEF;
171    rename ('ovmethods.c.new', 'ovmethods.c');
172
173    open (H, '> ovmethods.h.new') or die "Can't open ovmethods.h.new: $!\n";
174    print H unquote (<<'EOE');
175:       /* This file is automatically generated by buildconfig */
176:
177:       #ifndef OVMETHODS_H
178:       #define OVMETHODS_H 1
179:
180:       #include "ovinterface.h"
181:
182EOE
183    print H '#define NUM_OV_METHODS ',
184        scalar (keys %{ $$overview{method} }), "\n";
185    print H unquote (<<'EOE');
186:
187:       extern OV_METHOD ov_methods[NUM_OV_METHODS];
188:
189:       #endif /* OVMETHODS_H */
190EOE
191    close H;
192    rename ('ovmethods.h.new', 'ovmethods.h');
193}
194
195# Return a string setting a makefile variable.  Tab over the = properly and
196# wrap to fit our coding standards.
197sub makefile_var {
198    my ($variable, @values) = @_;
199    my $output;
200    $output = sprintf ("%-15s =", $variable);
201    my $column = 17;
202    for (@values) {
203        if ($column > 17 && 77 - $column < length ($_)) {
204            $output .= " \\\n" . ' ' x 17;
205            $column = 17;
206        }
207        $output .= " $_";
208        $column += 1 + length ($_);
209    }
210    $output .= "\n";
211    return $output;
212}
213
214# Write out the makefile fragment for overview and storage methods.
215sub write_makefile {
216    my ($dirs, $sources, $extra, $programs, $makefiles) = @_;
217    open (MAKE, '> Make.methods.new')
218        or die "Can't create Make.methods.new: $!\n";
219    print MAKE "# This file is automatically generated by buildconfig\n\n";
220    print MAKE makefile_var ('METHOD_SOURCES', @$sources);
221    print MAKE makefile_var ('EXTRA_SOURCES', @$extra);
222    print MAKE makefile_var ('PROGRAMS', @$programs);
223    for (@$makefiles) {
224        print MAKE "\n\n##  Included from $_\n\n";
225        open (FRAG, $_) or die "Can't open $_: $!\n";
226        print MAKE <FRAG>;
227        close FRAG;
228    }
229    rename ('Make.methods.new', 'Make.methods');
230}
231
232my ($dir, %storage, %overview);
233if (!-d 'cnfs') {
234    if (-d 'storage/cnfs') {
235        chdir 'storage' or die "Can't chdir to storage: $!\n";
236    } else {
237        die "Can't find storage directory (looking for storage/cnfs)\n";
238    }
239}
240opendir (D, ".") or die "Can't open current directory: $!\n";
241my @dirs = sort readdir D;
242for $dir (@dirs) {
243    if (-e "$dir/method.config") {
244        parse_config ($dir, 'method.config', \%storage);
245    }
246    if (-e "$dir/ovmethod.config") {
247        parse_config ($dir, 'ovmethod.config', \%overview);
248    }
249}
250write_storage (\%storage);
251write_overview (\%overview);
252@dirs = (sort values %{ $storage{method} },
253         sort values %{ $overview{method} });
254my @sources = (sort @{ $storage{sources} }, sort @{ $overview{sources} });
255my @extra = (sort @{ $storage{extra} }, sort @{ $overview{extra} });
256my @programs = sort (@{ $storage{programs} }, @{ $overview{programs} });
257my @makefiles = sort (@{ $storage{makefiles} }, @{ $overview{makefiles} });
258write_makefile (\@dirs, \@sources, \@extra, \@programs, \@makefiles);
259