1#! /usr/bin/perl
2
3##  $Id: buildconfig.in 6803 2004-05-18 00:48:58Z rra $
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 history methods or overview methods.  Builds
9##  hismethods.[ch] as well as makefile stubs.
10
11require 5.003;
12
13use strict;
14use vars qw(@HISTORY);
15
16# History API functions.
17@HISTORY = qw(open close sync lookup check write replace expire walk remember
18              ctl);
19
20# Used to make heredocs more readable.
21sub unquote { my ($string) = @_; $string =~ s/^:( {0,7}|\t)//gm; $string }
22
23# Parse a hismethod.config file for a history method, putting information
24# about that history method into the given hash ref.
25sub parse_config {
26    my ($dir, $file, $config) = @_;
27    local $_;
28    $$config{sources} ||= [];
29    $$config{extra} ||= [];
30    $$config{programs} ||= [];
31    $$config{makefiles} ||= [];
32    open (CONFIG, "$dir/$file") or die "Can't open $dir/$file: $!\n";
33    while (<CONFIG>) {
34        s/^\s+//;
35        s/\s+$//;
36        if (/^name\s*=\s*(\S+)$/) {
37            my $method = $1;
38            die "$dir/$file: $method has already been defined\n"
39                if (defined $$config{method}{$method});
40            $$config{method}{$method} = $dir;
41        } elsif (/^number\s*=\s*(\d+)$/) {
42            my $number = $1;
43            if (defined $$config{number}{$number}) {
44                die "$dir/$file: method number $number was already "
45                    . "allocated in $$config{number}{$number}\n";
46            }
47            $$config{number}{$dir} = $number;
48        } elsif (/^sources\s*=\s*(.*)/) {
49            my $sources = $1;
50            my @sources = split (' ', $sources);
51            push (@{ $$config{sources} }, map { "$dir/$_" } @sources);
52        } elsif (/^extra-sources\s*=\s*(.*)/) {
53            my $extra = $1;
54            my @extra = split (' ', $extra);
55            push (@{ $$config{extra} }, map { "$dir/$_" } @extra);
56        } elsif (/^programs\s*=\s*(.*)/) {
57            my $programs = $1;
58            my @programs = split (' ', $programs);
59            push (@{ $$config{programs} }, map { "$dir/$_" } @programs);
60        } else {
61            warn "$dir/$file: ignoring unknown line: $_\n";
62        }
63    }
64
65    # If there is a makefile fragment in the directory, note it.
66    if (-f "$dir/hismethod.mk") {
67        push (@{ $$config{makefiles} }, "$dir/hismethod.mk");
68    }
69}
70
71# Write out include directives for a list of files.
72sub write_includes {
73    my ($fh, $config) = @_;
74    my $method;
75    for $method (sort keys %{ $$config{method} }) {
76        my $path = $$config{method}{$method};
77        print $fh qq(\#include "$path/$method.h"\n);
78    }
79}
80
81# Write out the method struct.
82sub write_methods {
83    my ($fh, $config, $prefix, @funcs) = @_;
84    my ($notfirst, $method);
85    for $method (sort keys %{ $$config{method} }) {
86        print $fh "\n},\n" if $notfirst;
87        print $fh qq(\{\n    "$method");
88        print $fh ', ', $prefix, '_', uc ($method) if $prefix;
89        for (@funcs) {
90            print $fh ",\n    ${method}_$_";
91        }
92        $notfirst++;
93    }
94    print $fh "\n}\n};\n\n";
95}
96
97# Write out hismethods.c and hismethods.h for the interface to the history
98# methods.
99sub write_history {
100    my $history = shift;
101    open (DEF, '> hismethods.c.new')
102        or die "Can't create hismethods.c.new: $!\n";
103    print DEF unquote (<<'EOE');
104:       /* This file is automatically generated by buildconfig. */
105:
106:       #include "hisinterface.h"
107:       #include "hismethods.h"
108EOE
109    my $method;
110    write_includes (\*DEF, $history);
111    print DEF "\nHIS_METHOD his_methods[",
112        scalar (keys %{ $$history{method} }), "] = {\n";
113    write_methods (\*DEF, $history, undef, @HISTORY);
114    close DEF;
115    rename ('hismethods.c.new', 'hismethods.c');
116
117    open (H, '> hismethods.h.new') or die "Can't open hismethods.h.new: $!\n";
118    print H unquote (<<'EOE');
119:       /* This file is automatically generated by buildconfig */
120:
121:       #ifndef HISMETHODS_H
122:       #define HISMETHODS_H 1
123:
124:       #include "hisinterface.h"
125:
126EOE
127    print H '#define NUM_HIS_METHODS ',
128        scalar (keys %{ $$history{method} }), "\n";
129    print H unquote (<<'EOE');
130:
131:       extern HIS_METHOD his_methods[NUM_HIS_METHODS];
132:
133:       #endif /* HISMETHODS_H */
134EOE
135    close H;
136    rename ('hismethods.h.new', 'hismethods.h');
137}
138
139# Return a string setting a makefile variable.  Tab over the = properly and
140# wrap to fit our coding standards.
141sub makefile_var {
142    my ($variable, @values) = @_;
143    my $output;
144    $output = sprintf ("%-15s =", $variable);
145    my $column = 17;
146    for (@values) {
147        if ($column > 17 && 77 - $column < length ($_)) {
148            $output .= " \\\n" . ' ' x 17;
149            $column = 17;
150        }
151        $output .= " $_";
152        $column += 1 + length ($_);
153    }
154    $output .= "\n";
155    return $output;
156}
157
158# Write out the makefile fragment for history methods.
159sub write_makefile {
160    my ($dirs, $sources, $extra, $programs, $makefiles) = @_;
161    open (MAKE, '> Make.methods.new')
162        or die "Can't create Make.methods.new: $!\n";
163    print MAKE "# This file is automatically generated by buildconfig\n\n";
164    print MAKE makefile_var ('METHOD_SOURCES', @$sources);
165    print MAKE makefile_var ('EXTRA_SOURCES', @$extra);
166    print MAKE makefile_var ('PROGRAMS', @$programs);
167    for (@$makefiles) {
168        print MAKE "\n\n##  Included from $_\n\n";
169        open (FRAG, $_) or die "Can't open $_: $!\n";
170        print MAKE <FRAG>;
171        close FRAG;
172    }
173    rename ('Make.methods.new', 'Make.methods');
174}
175
176my ($dir, %history);
177if (!-d 'hisv6') {
178    if (-d 'history/cnfs') {
179        chdir 'history' or die "Can't chdir to history: $!\n";
180    } else {
181        die "Can't find history directory (looking for history/hisv6)\n";
182    }
183}
184opendir (D, ".") or die "Can't open current directory: $!\n";
185my @dirs = sort readdir D;
186for $dir (@dirs) {
187    if (-e "$dir/hismethod.config") {
188        parse_config ($dir, 'hismethod.config', \%history);
189    }
190}
191write_history (\%history);
192@dirs = sort values %{ $history{method} };
193my @sources = sort @{ $history{sources} };
194my @extra = sort @{ $history{extra} };
195my @programs = sort @{ $history{programs} };
196my @makefiles = sort @{ $history{makefiles} };
197write_makefile (\@dirs, \@sources, \@extra, \@programs, \@makefiles);
198