#! /usr/bin/perl ## $Id: buildconfig.in 6806 2004-05-18 01:18:57Z rra $ ## ## Generate linkage code and makefiles for storage and overview methods. ## ## Goes through all subdirectories of the current directory and finds ## directories that history methods or overview methods. Builds ## hismethods.[ch] as well as makefile stubs. require 5.003; use strict; use vars qw(@HISTORY); # History API functions. @HISTORY = qw(open close sync lookup check write replace expire walk remember ctl); # Used to make heredocs more readable. sub unquote { my ($string) = @_; $string =~ s/^:( {0,7}|\t)//gm; $string } # Parse a hismethod.config file for a history method, putting information # about that history method into the given hash ref. sub parse_config { my ($dir, $file, $config) = @_; local $_; $$config{sources} ||= []; $$config{extra} ||= []; $$config{programs} ||= []; $$config{makefiles} ||= []; open (CONFIG, "$dir/$file") or die "Can't open $dir/$file: $!\n"; while () { s/^\s+//; s/\s+$//; if (/^name\s*=\s*(\S+)$/) { my $method = $1; die "$dir/$file: $method has already been defined\n" if (defined $$config{method}{$method}); $$config{method}{$method} = $dir; } elsif (/^number\s*=\s*(\d+)$/) { my $number = $1; if (defined $$config{number}{$number}) { die "$dir/$file: method number $number was already " . "allocated in $$config{number}{$number}\n"; } $$config{number}{$dir} = $number; } elsif (/^sources\s*=\s*(.*)/) { my $sources = $1; my @sources = split (' ', $sources); push (@{ $$config{sources} }, map { "$dir/$_" } @sources); } elsif (/^extra-sources\s*=\s*(.*)/) { my $extra = $1; my @extra = split (' ', $extra); push (@{ $$config{extra} }, map { "$dir/$_" } @extra); } elsif (/^programs\s*=\s*(.*)/) { my $programs = $1; my @programs = split (' ', $programs); push (@{ $$config{programs} }, map { "$dir/$_" } @programs); } else { warn "$dir/$file: ignoring unknown line: $_\n"; } } # If there is a makefile fragment in the directory, note it. if (-f "$dir/hismethod.mk") { push (@{ $$config{makefiles} }, "$dir/hismethod.mk"); } } # Write out include directives for a list of files. sub write_includes { my ($fh, $config) = @_; my $method; for $method (sort keys %{ $$config{method} }) { my $path = $$config{method}{$method}; print $fh qq(\#include "$path/$method.h"\n); } } # Write out the method struct. sub write_methods { my ($fh, $config, $prefix, @funcs) = @_; my ($notfirst, $method); for $method (sort keys %{ $$config{method} }) { print $fh "\n},\n" if $notfirst; print $fh qq(\{\n "$method"); print $fh ', ', $prefix, '_', uc ($method) if $prefix; for (@funcs) { print $fh ",\n ${method}_$_"; } $notfirst++; } print $fh "\n}\n};\n\n"; } # Write out hismethods.c and hismethods.h for the interface to the history # methods. sub write_history { my $history = shift; open (DEF, '> hismethods.c.new') or die "Can't create hismethods.c.new: $!\n"; print DEF unquote (<<'EOE'); : /* This file is automatically generated by buildconfig. */ : : #include "hisinterface.h" : #include "hismethods.h" EOE my $method; write_includes (\*DEF, $history); print DEF "\nHIS_METHOD his_methods[", scalar (keys %{ $$history{method} }), "] = {\n"; write_methods (\*DEF, $history, undef, @HISTORY); close DEF; rename ('hismethods.c.new', 'hismethods.c'); open (H, '> hismethods.h.new') or die "Can't open hismethods.h.new: $!\n"; print H unquote (<<'EOE'); : /* This file is automatically generated by buildconfig */ : : #ifndef HISMETHODS_H : #define HISMETHODS_H 1 : : #include "hisinterface.h" : EOE print H '#define NUM_HIS_METHODS ', scalar (keys %{ $$history{method} }), "\n"; print H unquote (<<'EOE'); : : extern HIS_METHOD his_methods[NUM_HIS_METHODS]; : : #endif /* HISMETHODS_H */ EOE close H; rename ('hismethods.h.new', 'hismethods.h'); } # Return a string setting a makefile variable. Tab over the = properly and # wrap to fit our coding standards. sub makefile_var { my ($variable, @values) = @_; my $output; $output = sprintf ("%-15s =", $variable); my $column = 17; for (@values) { if ($column > 17 && 77 - $column < length ($_)) { $output .= " \\\n" . ' ' x 17; $column = 17; } $output .= " $_"; $column += 1 + length ($_); } $output .= "\n"; return $output; } # Write out the makefile fragment for history methods. sub write_makefile { my ($dirs, $sources, $extra, $programs, $makefiles) = @_; open (MAKE, '> Make.methods.new') or die "Can't create Make.methods.new: $!\n"; print MAKE "# This file is automatically generated by buildconfig\n\n"; print MAKE makefile_var ('METHOD_SOURCES', @$sources); print MAKE makefile_var ('EXTRA_SOURCES', @$extra); print MAKE makefile_var ('PROGRAMS', @$programs); for (@$makefiles) { print MAKE "\n\n## Included from $_\n\n"; open (FRAG, $_) or die "Can't open $_: $!\n"; print MAKE ; close FRAG; } rename ('Make.methods.new', 'Make.methods'); } my ($dir, %history); if (!-d 'hisv6') { if (-d 'history/cnfs') { chdir 'history' or die "Can't chdir to history: $!\n"; } else { die "Can't find history directory (looking for history/hisv6)\n"; } } opendir (D, ".") or die "Can't open current directory: $!\n"; my @dirs = sort readdir D; for $dir (@dirs) { if (-e "$dir/hismethod.config") { parse_config ($dir, 'hismethod.config', \%history); } } write_history (\%history); @dirs = sort values %{ $history{method} }; my @sources = sort @{ $history{sources} }; my @extra = sort @{ $history{extra} }; my @programs = sort @{ $history{programs} }; my @makefiles = sort @{ $history{makefiles} }; write_makefile (\@dirs, \@sources, \@extra, \@programs, \@makefiles);