summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org>2016-07-30 07:14:19 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2016-07-30 07:19:04 (GMT)
commit71e51308e7fe0136b76799f5e6dbbec94e279f46 (patch) (side-by-side diff)
tree820ee704751fd7a498464ecfb5c42ea6da530ead
parentbde49d719c1894d0e502a73d42ab546d905492f2 (diff)
downloaddico-modinc.tar.gz
dico-modinc.tar.bz2
Rename modinc to bootstrap.modinc
* modinc: Rename to bootstrap. * bootstrap: Add generic bootstrapping code. * stub.ac: Rename to configure.boot. * Makefile.am: Fix EXTRA_DIST * README-hacking: Document bootstrap.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile.am4
-rw-r--r--README-hacking36
-rwxr-xr-xbootstrap807
-rw-r--r--configure.boot (renamed from stub.ac)10
-rwxr-xr-xmodinc641
5 files changed, 813 insertions, 685 deletions
diff --git a/Makefile.am b/Makefile.am
index a157141..fa15921 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,3 @@
# This file is part of GNU Dico
-# Copyright (C) 1998-2000, 2008-2010, 2012 Sergey Poznyakoff
+# Copyright (C) 1998-2000, 2008-2010, 2012, 2016 Sergey Poznyakoff
#
@@ -36,3 +36,3 @@ SUBDIRS=\
-EXTRA_DIST = ChangeLog.2008 stub.ac modinc
+EXTRA_DIST = ChangeLog.2008 configure.boot bootstrap
diff --git a/README-hacking b/README-hacking
index 74cb866..7037357 100644
--- a/README-hacking
+++ b/README-hacking
@@ -44,2 +44,4 @@ which are extracted from other source packages:
+Use one or more --verbose options to see details about its progress.
+
Once done, proceed as described in the file README (section
@@ -47,14 +49,2 @@ INSTALLATION).
-Normally you will have to run bootstrap only once. However, if you
-intend to hack on Dico, you might need to run it again later. In
-this case, you will probably want to save some time and bandwidth by
-avoiding downloading the same files again. If so, create in the project's
-root directory a file named `.bootstrap' with the following
-contents:
-
- --gnulib-srcdir=$HOME/gnulib
-
-Replace `$HOME/gnulib' with the actual directory where the Gnulib
-sources reside.
-
For more information about `bootstrap', run `bootstrap --help'.
@@ -63,5 +53,5 @@ For more information about `bootstrap', run `bootstrap --help'.
-To debug dictd, use the following command:
+To debug dicod, use the following command:
- libtool --mode execute gdb dictd
+ libtool --mode execute gdb dicod
@@ -69,3 +59,3 @@ For debugging from Emacs run:
- M-x gdb RET gud-wrapper dictd RET
+ M-x gdb RET gud-wrapper dicod RET
@@ -73,2 +63,16 @@ For debugging from Emacs run:
+* Adding new modules
+
+If you want to add a new module to Dico (e.g. named `foo'), run the
+following command from the Dico source tree topmost directory:
+
+ ./bootstrap --add foo
+
+This will create the directory modules/foo, populate it with the
+necessary files, and add the appropriate statements to configure.ac
+and modules/Makefile.am files.
+
+After running this command, change to the modules/foo directory and
+start hacking on your new module.
+
@@ -76,3 +80,3 @@ For debugging from Emacs run:
-Copyright (C) 2008, 2010, 2012 Sergey Poznyakoff
+Copyright (C) 2008, 2010, 2012, 2016 Sergey Poznyakoff
diff --git a/bootstrap b/bootstrap
index 7589553..5a3ce01 100755
--- a/bootstrap
+++ b/bootstrap
@@ -1,23 +1,784 @@
-#! /bin/sh
-
-chksrc() {
- test -f modinc || return 1
- test -f stub.ac || return 1
- src=`sed -n 's/AC_CONFIG_SRCDIR(\[\(.*\)\]).*/\1/p' stub.ac`
- test -n "$src" || return 1
- test -f $src
-}
-
-if ! chksrc; then
- echo "$0: must be run from the Dico source tree root directory"
- exit 1
-fi
-
-set -e
-git submodule init
-git submodule update
-./modinc
-set +e
-gnulib=gnulibinit
-test -L $gnulib || ln -sf ./gnulib/build-aux/bootstrap $gnulib
-./$gnulib
+#! /usr/bin/perl
+# This file is part of GNU Dico
+# Copyright (C) 2014, 2016 Sergey Poznyakoff
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+=head1 NAME
+
+bootstrap - Bootstrap the Dico project
+
+=head1 SYNOPSIS
+
+B<bootstrap>
+[B<-ahmnv>]
+[B<--add>]
+[B<--help>]
+[B<--new>]
+[B<--modules>]
+[B<--dry-run>]
+[B<--verbose>]
+[I<NAME>...]
+
+=head1 DESCRIPTION
+
+Bootstrap the Dico project. This is the first command that should be run
+after cloning the project from the repository in order to be able to built
+it.
+
+When run without arguments performs the following actions:
+
+=over 4
+
+=item 1. Pulls in git submodules.
+
+=item 2. Creates autoconf/automake sources for building Dico modules.
+
+Recreates F<configure.ac> and F<modules/Makefile.am> from stub files
+F<configure.boot> and F<modules/stub.am>. The list of modules is obtained
+from the contents of the F<modules> directory -- each subdirectory
+is supposed to contain one module.
+
+Additional F<configure.ac> code for each module is obtained from
+file F<module.ac> in each module directory. See the description of
+this file in section B<FILES>.
+
+=item 3. Bootstraps the B<gnulib> submodule.
+
+=back
+
+The program must be run from the Dico source tree topmost directory.
+
+When given the B<-m> (B<--modules>) option, only the second step is
+performed.
+
+When run with B<-a> (B<--add>, B<--new>) option, the program creates
+basic infrastructure for modules named in the command line. For each
+I<NAME> it creates directory F<modules/I<NAME>>. Withih that directory,
+it creates two files: F<Makefile.am> from F<modules/template.am>, and
+F<I<NAME>.c>, which contains a stub code for the module. The new module
+is then integrated into F<configure.ac> and F<modules/Makefile.am> files.
+
+By default, the program prints only error diagnostics. That means that
+upon success, it will terminate quietly, without producing a single line
+on output. This can be changed using the B<-v> (or B<--verbose>) option.
+When the option is given once, the program prints general description before
+running each of the three steps described above. Two B<-v> options instruct
+it to also print whatever output that was generated when running auxiliary
+commands. Finally, three B<-v>'s produce additional debugging information.
+
+The B<-n> (B<--dry-run>) option instructs the tool to print what would have
+been done without actually doing it.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-a>, B<--add>, B<--new>
+
+Create basic infrastructure for modules named in the command line and
+add them to F<configure.ac> and F<modules/Makefile.am>
+
+=item B<-n>, B<--dry-run>
+
+Don't do anything, just print what would have been done.
+
+=item B<-m>, B<--modules>
+
+Run only the second step: creation of autoconf/automake sources for
+Dico modules.
+
+=item B<-v>, B<--verbose>
+
+Increase output verbosity. Multiple options accumulate.
+
+=back
+
+The following options are passed to the B<gnulib> bootstrap script
+verbatim:
+
+=over 4
+
+=item B<--gnulib-srcdir=>I<DIRNAME>
+
+Specifies the local directory where B<gnulib> sources reside. Use this if
+you already have gnulib sources on your machine, and do not want to waste
+your bandwidth downloading them again.
+
+=item B<--no-git>
+
+Do not use git to update B<gnulib>. Requires that B<--gnulib-srcdir> point
+to a correct gnulib snapshot.
+
+=item B<--skip-po>
+
+Do not download po files.
+
+=back
+
+The following options cause the program to display informative text and
+exit:
+
+=over 4
+
+=item B<-h>
+
+Show a short usage summary.
+
+=item B<--help>
+
+Show man page.
+
+=item B<--usage>
+
+Show a concise command line syntax reminder.
+
+=back
+
+=head1 FILES
+
+The following files are used as templates to create output files. When
+creating output file, each line from the corresponding template is read,
+I<macro expansion> is performed, and the resulting string is written to the
+output file.
+
+During macro expansion, each occurrence of B<< <I<NAME>> >> is replaced with
+the contents of the macro variable I<NAME>, if it is defined. Expansion
+of undefined variables leaves the text as is.
+
+The construct B<< <I<NAME>#I<TEXT>> >> is expanded if it appears on a line
+alone, possibly preceded by any string of characters. It works similarly
+to B<< <I<NAME>> >>, except that if I<NAME> expands to multiple lines, the
+second and subsequent lines of expansion are prefixed with I<TEXT> on output.
+If I<TEXT> is empty, the content of the source line immediately preceding the
+construct is used instead. This makes it possible to use expansions after a
+comment character. E.g. if the variable B<HEADING> contains:
+
+ This file is generated automatically.
+ Please, do not edit.
+ See the docs for more info.
+
+and the input file contains:
+
+ dnl <HEADING#>
+
+Then, the resulting expansion will be:
+
+ dnl This file is generated automatically.
+ dnl Please, do not edit.
+ dnl See the docs for more info.
+
+The macro variables are specific for each file, and are described below.
+
+For each file, a special comment sequence is defined. Lines beginning
+with that sequence are omitted from the output.
+
+=over 4
+
+=item F<configure.boot>
+
+Produces the F<configure.ac> file. Comment marker is B<##>.
+The following variables are defined:
+
+=over 8
+
+=item B<MODULES>
+
+B<Autoconf> code for each module, obtained from the F<module.ac>
+files.
+
+=item B<HEADING>
+
+Generates an initial header text, warning the reader that the file is
+generated automatically and informing him about the ways to recreate
+that file.
+
+=item B<STATUS_OUTPUT>
+
+A list of printable description for modules that can be disabled. Each
+module is represented with a line
+
+ MODNAME ................ STATUS
+
+where MODNAME is the module name and STATUS is the module status variable
+(which produces B<yes> or B<no> at configure time, depending on whether
+the module is enabled or not).
+
+This is intended for use in B<AC_CONFIG_COMMANDS>.
+
+=item B<STATUS_DEFN>
+
+A list of assignments for module status variables, intended
+for use in B<AC_CONFIG_COMMANDS>.
+
+=back
+
+The following code illustrates the use of the latter two variables:
+
+ AC_CONFIG_COMMANDS([status],[
+echo<STATUS_OUTPUT>
+],
+ [<STATUS_DEFN>])
+
+=item F<modules/*/module.ac>
+
+This file is optional. If present, it contains the B<autoconf> code for
+that module, which is appended to the contents of the B<MODULES> variable
+used for creating F<configure.ac>.
+
+No macro substitutions are performed for that file.
+
+Comment marker is B<##>.
+
+The following comments starting with B<## module> are I<pragmatic> ones,
+and are treated specially:
+
+=over 8
+
+=item B<## module name: I<NAME>>
+
+Use I<NAME> as module name when creating variable names for that module. This
+is useful when the module name contains symbols that cannot be used in variable
+names (as in, e.g. B<dict.org>).
+
+=item B<## module description: I<TEXT>>
+
+Use I<TEXT> as module description in B<STATUS_OUTPUT> (see F<configure.boot>).
+
+=item B<## module category:>
+
+Reserved for future use.
+
+=back
+
+As well as other B<##> comments, these comments do not appear in the output.
+
+=item F<modules/stub.am>
+
+Produces the file F<modules/Makefile.am>. Comment marker is B<##>.
+
+Macro variables:
+
+=over 8
+
+=item B<HEADING>
+
+Same as in F<configure.boot>.
+
+=item B<MODULES>
+
+Produces B<Makefille.am> code for the B<SUBDIRS> variable.
+
+=back
+
+=item F<modules/template.c>
+
+This template is used when B<-a> (B<--add>, B<--new>) option is given.
+It produces the file F<modules/I<NAME>/I<NAME>.c>. Comment marker is B<//>.
+
+Macro variables:
+
+=over 8
+
+=item B<MODNAME>
+
+Expands to the name of the module.
+
+=back
+
+=item F<modules/template.am>
+
+This template is used when B<-a> (B<--add>, B<--new>) option is given.
+It produces the file F<modules/I<NAME>/Makefile.am>.
+
+Macro variables:
+
+=over 8
+
+=item B<MODNAME>
+
+Expands to the name of the module.
+
+=back
+
+=back
+
+=head1 EXIT CODES
+
+=over 4
+
+=item B<0>
+
+Successful termination.
+
+=item B<64>
+
+Command line usage error.
+
+=item B<66>
+
+Can't open input file.
+
+=item B<73>
+
+Can't create output file.
+
+=back
+
+=head1 BUGS
+
+Error handling can be made better.
+
+=cut
+
+use strict;
+use Getopt::Long qw(:config gnu_getopt no_ignore_case);
+use Pod::Usage;
+use Pod::Man;
+use File::Temp qw(tempfile);
+use File::Basename;
+use Data::Dumper;
+use IPC::Open3;
+use Symbol 'gensym';
+
+use constant EX_OK => 0;
+use constant EX_USAGE => 64; # command line usage error
+use constant EX_DATAERR => 65; # data format error
+use constant EX_NOINPUT => 66; # cannot open input file
+use constant EX_SOFTWARE => 70; # internal software error (not used yet)
+use constant EX_CANTCREAT => 73; # can't create (user) output file
+
+my $progname = basename($0);
+my $progdescr = "Recreate Dico module autoconf structure";
+my $addmod;
+my $dry_run;
+my $verbose;
+my $modules;
+my @gnulib_options;
+my $autoconf = "module.ac";
+my $config_stub = "configure.boot";
+my $makefile_stub = "modules/stub.am";
+my $makefile = "modules/Makefile.am";
+my %outfile;
+
+my %categories; # not used now
+
+sub error {
+ my $msg = shift;
+ local %_ = @_;
+ my $fd = defined($_{fd}) ? $_{fd} : \*STDERR;
+ print $fd "$progname: " if defined($progname);
+ print $fd "$_{prefix}: " if defined($_{prefix});
+ print $fd "$msg\n"
+}
+
+sub abend {
+ my $code = shift;
+ print STDERR "$progname: " if defined($progname);
+ print STDERR "@_\n";
+ exit $code;
+}
+
+my $status = 0;
+
+sub rewrite_configure_ac {
+ my $ref = shift;
+
+ my %vartab = (
+ 'overwrite' => 1,
+ 'tempfile' => 1,
+ 'ignorecomm' => '##',
+ 'HEADING' => "Do not edit. -*- mode: autoconf; buffer-read-only: t; -*- vi: set ro:\
+This file is created automatically from $config_stub.\
+To recreate, run $progname from the top of Dico source tree.
+",
+ 'MODULES' => sub {
+ my $s;
+
+ foreach my $name (sort keys %{$ref}) {
+ my $h = $ref->{$name};
+ $s .= "# Module $name\n";
+ if (defined($h->{autoconf})) {
+ open(my $afd, "<", $h->{autoconf})
+ or abend(EX_NOINPUT,
+ "can't open $h->{autoconf} for reading: $!");
+ while (<$afd>) {
+ next if /^##/;
+ $s .= $_;
+ }
+ close $afd;
+ }
+ $s .= "\n";
+ $s .= "DICO_TESTS($h->{dir})\n" if ($h->{tests});
+ $s .= "AC_CONFIG_FILES([$h->{dir}/Makefile])\n";
+ $s .= "\n# End of module $name\n\n";
+ }
+ return $s;
+ },
+ 'STATUS_OUTPUT' => sub {
+ return join "\n",
+ map {
+ my $n;
+ if (defined($ref->{$_}{descr})) {
+ $n = $ref->{$_}{descr};
+ } else {
+ $n = $_;
+ $n =~ s/\b(\w)/\U$1/g;
+ }
+
+ my $l = 34 - length($n) - 2;
+
+ "$n " .
+ (($l > 0) ? '.' x $l : '') .
+ " \$status_$ref->{$_}{modname}";
+ }
+ grep { $ref->{$_}{status} }
+ sort keys %{$ref};
+ },
+ 'STATUS_DEFN' => sub {
+ return join "\n",
+ map { "status_$ref->{$_}{modname}=\$status_$ref->{$_}{modname}" }
+ grep { defined($ref->{$_}{status}) }
+ sort keys %{$ref};
+ }
+ );
+ make_file($config_stub, "configure.ac", \%vartab);
+}
+
+sub rewrite_makefile {
+ my $ref = shift;
+
+ my %vartab = (
+ 'overwrite' => 1,
+ 'tempfile' => 1,
+ 'ignorecomm' => '##',
+ 'HEADING' => "Do not edit. -*- buffer-read-only: t; -*- vi: set ro:\
+This file is created automatically from $makefile_stub.\
+To recreate, run $progname from the top of Dico source tree.
+",
+ 'MODULES' => sub {
+ my $s;
+ foreach my $am (map { $ref->{$_}{am} }
+ grep { defined($ref->{$_}{am}) }
+ sort keys %{$ref}) {
+ $s .= "$am\n";
+ }
+ $s .= "SUBDIRS=";
+ foreach my $dir (map { $ref->{$_}{SUBDIRS} } sort keys %{$ref}) {
+ $s .= "\\\n $dir";
+ }
+ $s .= "\n";
+ return $s;
+ }
+ );
+ make_file($makefile_stub, $makefile, \%vartab);
+}
+
+sub add_module {
+ my ($name,$ref) = @_;
+ my $dir = "modules/$name";
+
+ error("adding module $name", prefix=>'DEBUG') if ($verbose>1);
+ my $acname = uc($name);
+ $ref->{$name}{dir} = $dir;
+ $ref->{$name}{SUBDIRS} = $name;
+ $ref->{$name}{modname} = $name;
+ if (-r "$dir/$autoconf") {
+ $ref->{$name}{autoconf} = "$dir/$autoconf";
+ open(my $fd, "<", "$dir/$autoconf")
+ or abend(EX_NOINPUT,
+ "can't open $dir/$autoconf for reading: $!");
+ while (<$fd>) {
+ chomp;
+ s/\s+$//;
+ if (/^##\s*module\s+name\s*:\s*([^\s]+).*$/) {
+ $ref->{$name}{modname} = $1;
+ } elsif (/^##\s*module\s+description\s*:\s*(.+).*$/) {
+ $ref->{$name}{descr} = $1;
+ } elsif (/^##\s*module\s+category\s*:\s*(.+).*$/) {
+ $ref->{$name}{category} = $1;
+ $categories{$1}++;
+ }
+ s/#.*//;
+ next if ($_ eq "");
+ if (/AM_CONDITIONAL\s*\(\s*\[${acname}_COND\]/) {
+ $ref->{$name}{am} = <<EOT;
+if ${acname}_COND
+ ${acname}_DIR=$name
+endif
+EOT
+;
+ $ref->{$name}{SUBDIRS} = "\$(${acname}_DIR)";
+ }
+ $ref->{$name}{status} = 1
+ if (!$ref->{$name}{status} and /status_$ref->{$name}{modname}=/);
+ }
+ close $fd;
+ }
+ $ref->{$name}{tests} = (-d "$dir/tests"
+ and -f "$dir/tests/Makefile.am"
+ and -f "$dir/tests/testsuite.at");
+
+ if (-f "$dir/Makefile.am") {
+ open(my $fd, "<", "$dir/Makefile.am")
+ or abend(EX_NOINPUT,
+ "can't open $dir/$autoconf for reading: $!");
+ my %checks = ('mod' => "mod_LTLIBRARIES does not contain $ref->{$name}{modname}.la",
+ 'SOURCES' => "$ref->{$name}{modname}_la_SOURCES not defined");
+ while (<$fd>) {
+ chomp;
+ s/\s+$//;
+ s/#.*//;
+ next if ($_ eq "");
+ if (/mod_LTLIBRARIES\s*=\s*$ref->{$name}{modname}.la/) {
+ delete $checks{mod};
+ } elsif (/$ref->{$name}{modname}_la_SOURCES\s*=/) {
+ delete $checks{SOURCES};
+ }
+ }
+ close $fd;
+ if (keys(%checks)) {
+ while (my ($k,$v) = each %checks) {
+ error("$dir/Makefile.am: $v");
+ }
+ $status = EX_DATAERR;
+ return;
+ }
+ }
+
+ if ($verbose>2) {
+ print STDERR "module $name:\n";
+ print STDERR Dumper($ref->{$name});
+ }
+}
+
+sub replace_macro {
+ my $name = shift;
+ my $ref = shift;
+
+ my $s;
+
+ if (defined($ref->{$name})) {
+ if (ref($ref->{$name}) eq 'CODE') {
+ $s = &{$ref->{$name}};
+ } else {
+ $s = $ref->{$name};
+ }
+ } else {
+ $s = "<$name>";
+ }
+
+ return $s;
+}
+
+sub make_file {
+ my $infile = shift;
+ my $ofile = shift;
+ my $ref = shift;
+
+ error("creating $ofile from $infile", prefix => 'DEBUG') if ($verbose>1);
+ return if $dry_run;
+ if (!$ref->{overwrite} and -e $ofile) {
+ error("$ofile already exists", prefix => 'warning');
+ } else {
+ open(my $ifd, "<", $infile)
+ or abend(EX_DATAERR, "can't open $infile for reading: $!");
+
+ my $ofd;
+ if (defined($ref->{tempfile})) {
+ my $tempname;
+ ($ofd, $tempname) = tempfile(DIR => dirname($infile))
+ or abend(EX_CANTCREAT,
+ "can't open temporary file for creating $ofile: $!");
+ $outfile{$tempname} = $ofile;
+ } else {
+ open($ofd, ">", $ofile)
+ or abend(EX_CANTCREAT, "can't open $ofile for writing: $!");
+ }
+ while (<$ifd>) {
+ next if (defined($ref->{ignorecomm}) and /^$ref->{ignorecomm}/);
+ if (/^(.*)<([A-Z][A-Z0-9_]+)#([^>]*)>\s*$/) {
+ my $pfx = $3 || $1;
+ $_ = $1 . replace_macro($2, $ref);
+ chomp;
+ s/\n/\n$pfx/g;
+ $_ .= "\n";
+ } else {
+ s/<([A-Z][A-Z0-9_]+)>/replace_macro($1, $ref)/gex;
+ }
+ print $ofd $_;
+ }
+ close($ifd);
+ close($ofd);
+ }
+}
+
+sub create_module {
+ my $name = shift;
+ my $dir = "modules/$name";
+
+ if (-d $dir) {
+ error("$dir already exists", prefix => 'warning');
+ } else {
+ error("creating $dir", prefix => 'DEBUG') if ($verbose>1);
+ mkdir($dir) unless $dry_run;
+ }
+ make_file("modules/template.am", "$dir/Makefile.am",
+ { ignorecomm => '##',
+ MODNAME => $name });
+ make_file("modules/template.c", "$dir/${name}.c",
+ { ignorecomm => '//',
+ MODNAME => $name });
+}
+
+sub cleanup {
+ foreach my $f (keys %outfile) {
+ unlink $f if (-e $f);
+ }
+}
+
+sub checksrc {
+ return 0 unless -e $config_stub;
+ open(my $fd, '<', $config_stub)
+ or abend(EX_NOINPUT, "can't open $config_stub: $!");
+ my $anchor;
+ while (<$fd>) {
+ chomp;
+ s/^\s+//;
+ if (/^AC_CONFIG_SRCDIR\(\[(.*)\]\)/) {
+ $anchor = $1;
+ last;
+ }
+ }
+ close $fd;
+ abend(EX_DATAERR, "AC_CONFIG_SRCDIR not found in $config_stub")
+ unless defined $anchor;
+ return -e $anchor;
+}
+
+sub modproc {
+ my %modtab;
+ my @modnames = map { s#modules/##; $_; } grep { -d $_; } glob "modules/*";
+
+ error("Bootstrapping dico modules", fd => \*STDOUT) if $verbose;
+
+ foreach my $m (@modnames) {
+ add_module($m, \%modtab);
+ }
+
+ rewrite_configure_ac(\%modtab);
+ rewrite_makefile(\%modtab);
+
+ if ($status == 0 and !$dry_run) {
+ while (my ($from, $to) = each %outfile) {
+ rename $from, $to
+ or abend(EX_CANTCREAT, "can't rename $from to $to: $!");
+ }
+ }
+}
+
+sub run {
+ error("running @_", prefix=>'DEBUG') if ($verbose>2);
+ return if $dry_run;
+ my $out = gensym;
+ open(my $err, ">&", \*STDERR) or die "can't dup STDERR: $!";
+ open(my $in, "<&", \*STDIN) or die "can't dup STDIN: $!";
+ my $pid = open3($in, $out, $err, @_);
+ my $logpid;
+ if ($verbose > 1) {
+ $logpid = fork();
+ if ($logpid == 0) {
+ while (<$out>) {
+ chomp;
+ error($_, fd => \*STDOUT, prefix => "[$_[0]]");
+ }
+ exit;
+ }
+ }
+ waitpid($pid, 0);
+ my $ret = 0;
+ if ($? == -1) {
+ abend(EX_CANTCREAT, "failed to run @_");
+ } elsif ($? & 127) {
+ abend(EX_CANTCREAT, "$@ exited on signal " . ($? & 127));
+ } elsif (my $e = ($? >> 8)) {
+ abend(EX_CANTCREAT, "$@ exited with status $e");
+ }
+ waitpid($logpid, 0) if defined $logpid;
+}
+
+sub gnulib_init {
+ my $gnulib = 'gnulibinit';
+ unless (-l $gnulib) {
+ symlink('gnulib/build-aux/bootstrap', $gnulib)
+ or abend(EX_CANTCREAT, "can't symlink gnulib/build-aux/bootstrap: $!");
+ }
+ error("Bootstrapping gnulib", fd => \*STDOUT) if $verbose;
+ run('sh', $gnulib, @gnulib_options);
+}
+
+END {
+ cleanup;
+}
+
+$SIG{HUP} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = \&cleanup;
+
+#
+GetOptions("h" => sub {
+ pod2usage(-message => "$progname: $progdescr",
+ -exitstatus => 0);
+ },
+ "help" => sub {
+ pod2usage(-exitstatus => EX_OK, -verbose => 2);
+ },
+ "usage" => sub {
+ pod2usage(-exitstatus => EX_OK, -verbose => 0);
+ },
+ "add|new|a" => \$addmod,
+ "dry-run|n" => \$dry_run,
+ "verbose|v+" => \$verbose,
+ "modules|m" => \$modules,
+ "gnulib-srcdir=s" => sub { push @gnulib_options, "--$_[0]=$_[1]" },
+ "skip-po|no-git" => sub { push @gnulib_options, "--$_[0]" }
+) or exit(EX_USAGE);
+
+abend(EX_USAGE, "must run from the Dico source tree topmost directory")
+ unless checksrc();
+
+++$verbose if $dry_run;
+
+if ($addmod) {
+ abend(EX_USAGE, "not enough arguments") unless @ARGV;
+ foreach my $mod (@ARGV) {
+ create_module($mod);
+ }
+ modproc();
+} elsif ($modules) {
+ abend(EX_USAGE, "too many arguments") if @ARGV;
+ modproc();
+} elsif ($#ARGV >= 0) {
+ abend(EX_USAGE, "too many arguments; did you mean --new?");
+} else {
+ abend(EX_USAGE, "this does not look like a clone of the Dico repository; try $progname --help for more info")
+ unless -d '.git';
+
+ error("Initializing submodules", fd => \*STDOUT) if $verbose;
+ run('git submodule init');
+ run('git submodule update');
+ modproc();
+ gnulib_init();
+}
+
+exit($status);
+
diff --git a/stub.ac b/configure.boot
index 7e9b812..e518ece 100644
--- a/stub.ac
+++ b/configure.boot
@@ -1,4 +1,4 @@
## This file is a source for configure.ac. -*- autoconf -*-
-## Run ./modinc to create it.
-## Run ./modinc --help for a detailed description.
+## Run ./bootstrap to create it.
+## Run ./bootstrap --help for a detailed description.
## A short reminder:
@@ -6,3 +6,7 @@
## 2. A construct <NAME> is replaced with the value of the variable NAME.
-## 3. Everything else is reproduced verbatim.
+## 3. <NAME#TEXT> on a line alone (arbitrary leading characters allowed)
+## is replaced with (multiline) expansion of NAME. The second and
+## subsequent lines are prefixed with TEXT (or leading characters, if
+## TEXT is empty).
+## 4. Everything else is reproduced verbatim.
dnl <HEADING#>
diff --git a/modinc b/modinc
deleted file mode 100755
index 5b88051..0000000
--- a/modinc
+++ b/dev/null
@@ -1,641 +0,0 @@
-#! /usr/bin/perl
-# This file is part of GNU Dico
-# Copyright (C) 2014, 2016 Sergey Poznyakoff
-#
-# GNU Dico is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3, or (at your option)
-# any later version.
-#
-# GNU Dico is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
-
-=head1 NAME
-
-modinc - Recreate Dico module autoconf structure
-
-=head1 SYNOPSIS
-
-B<modinc>
-[B<-ahnv>]
-[B<--add>]
-[B<--help>]
-[B<--new>]
-[B<--dry-run>]
-[B<--verbose>]
-[I<NAME>...]
-
-=head1 DESCRIPTION
-
-Recreates F<configure.ac> and F<modules/Makefile.am> from stub files
-F<stub.ac> and F<modules/stub.am>. The list of modules is obtained
-from the contents of the F<modules> directory -- each subdirectory
-is supposed to contain one module.
-
-Additional F<configure.ac> code for each module is obtained from
-file F<module.ac> in each module directory. See the description of
-this file in section B<FILES>.
-
-When run with B<-a> (B<--add>, B<--new>) option, the program creates
-basic infrastructure for modules named in the command line. For each
-I<NAME> it creates directory F<modules/I<NAME>>. Withih that directory,
-it creates two files: F<Makefile.am> from F<modules/template.am>, and
-F<I<NAME>.c>, which contains a stub code for the module.
-
-The program must be run from the Dico source tree topmost directory.
-
-=head1 OPTIONS
-
-=over 4
-
-=item B<-a>, B<--add>, B<--new>
-
-Create basic infrastructure for modules named in the command line and
-add them to F<configure.ac> and F<modules/Makefile.am>
-
-=item B<-n>, B<--dry-run>
-
-Don't do anything, just print what would have been done.
-
-=item B<-v>, B<--verbose>
-
-Increase output verbosity. Multiple options accumulate.
-
-=back
-
-The following options cause the program to display informative text and
-exit:
-
-=over 4
-
-=item B<-h>
-
-Show a short usage summary.
-
-=item B<--help>
-
-Show man page.
-
-=item B<--usage>
-
-Show a concise command line syntax reminder.
-
-=back
-
-=head1 FILES
-
-The following files are used as templates to create output files. When
-creating output file, each line from the corresponding template is read,
-I<macro expansion> is performed, and the resulting string is written to the
-output file.
-
-During macro expansion, each occurrence of B<< <I<NAME>> >> is replaced with
-the contents of the macro variable I<NAME>, if it is defined. Expansion
-of undefined variables leaves the text as is.
-
-The construct B<< <I<NAME>#I<TEXT>> >> is expanded if it appears on a line
-alone, possibly preceded by any string of characters. It works similarly
-to B<< <I<NAME>> >>, except that if I<NAME> expands to multiple lines, the
-second and subsequent lines of expansion are prefixed with I<TEXT> on output.
-If I<TEXT> is empty, the content of the source line immediately preceding the
-construct is used instead. This makes it possible to use expansions after a
-comment character. E.g. if the variable B<HEADING> contains:
-
- This file is generated automatically.
- Please, do not edit.
- See the docs for more info.
-
-and the input file contains:
-
- dnl <HEADING#>
-
-Then, the resulting expansion will be:
-
- dnl This file is generated automatically.
- dnl Please, do not edit.
- dnl See the docs for more info.
-
-The macro variables are specific for each file, and are described below.
-
-For each file, a special comment sequence is defined. Lines beginning
-with that sequence are omitted from the output.
-
-=over 4
-
-=item F<stub.ac>
-
-Produces the F<configure.ac> file. Comment marker is B<##>.
-The following variables are defined:
-
-=over 8
-
-=item B<MODULES>
-
-B<Autoconf> code for each module, obtained from the F<module.ac>
-files.
-
-=item B<HEADING>
-
-Generates an initial header text, warning the reader that the file is
-generated automatically and informing him about the ways to recreate
-that file.
-
-=item B<STATUS_OUTPUT>
-
-A list of printable description for modules that can be disabled. Each
-module is represented with a line
-
- MODNAME ................ STATUS
-
-where MODNAME is the module name and STATUS is the module status variable
-(which produces B<yes> or B<no> at configure time, depending on whether
-the module is enabled or not).
-
-This is intended for use in B<AC_CONFIG_COMMANDS>.
-
-=item B<STATUS_DEFN>
-
-A list of assignments for module status variables, intended
-for use in B<AC_CONFIG_COMMANDS>.
-
-=back
-
-The following code illustrates the use of the latter two variables:
-
- AC_CONFIG_COMMANDS([status],[
-echo<STATUS_OUTPUT>
-],
- [<STATUS_DEFN>])
-
-=item F<modules/*/module.ac>
-
-This file is optional. If present, it contains the B<autoconf> code for
-that module, which is appended to the contents of the B<MODULES> variable
-used for creating F<configure.ac>.
-
-No macro substitutions are performed for that file.
-
-Comment marker is B<##>.
-
-The following comments starting with B<## module> are I<pragmatic> ones,
-and are treated specially:
-
-=over 8
-
-=item B<## module name: I<NAME>>
-
-Use I<NAME> as module name when creating variable names for that module. This
-is useful when the module name contains symbols that cannot be used in variable
-names (as in, e.g. B<dict.org>).
-
-=item B<## module description: I<TEXT>>
-
-Use I<TEXT> as module description in B<STATUS_OUTPUT> (see F<stub.ac>).
-
-=item B<## module category:>
-
-Reserved for future use.
-
-=back
-
-As well as other B<##> comments, these comments do not appear in the output.
-
-=item F<modules/stub.am>
-
-Produces the file F<modules/Makefile.am>. Comment marker is B<##>.
-
-Macro variables:
-
-=over 8
-
-=item B<HEADING>
-
-Same as in F<stub.ac>.
-
-=item B<MODULES>
-
-Produces B<Makefille.am> code for the B<SUBDIRS> variable.
-
-=back
-
-=item F<modules/template.c>
-
-This template is used when B<-a> (B<--add>, B<--new>) option is given.
-It produces the file F<modules/I<NAME>/I<NAME>.c>. Comment marker is B<//>.
-
-Macro variables:
-
-=over 8
-
-=item B<MODNAME>
-
-Expands to the name of the module.
-
-=back
-
-=item F<modules/template.am>
-
-This template is used when B<-a> (B<--add>, B<--new>) option is given.
-It produces the file F<modules/I<NAME>/Makefile.am>.
-
-Macro variables:
-
-=over 8
-
-=item B<MODNAME>
-
-Expands to the name of the module.
-
-=back
-
-=back
-
-=head1 EXIT CODES
-
-=over 4
-
-=item B<0>
-
-Successful termination.
-
-=item B<64>
-
-Command line usage error.
-
-=item B<66>
-
-Can't open input file.
-
-=item B<73>
-
-Can't create output file.
-
-=back
-
-=head1 BUGS
-
-Error handling can be made better.
-
-=cut
-
-use strict;
-use Getopt::Long qw(:config gnu_getopt no_ignore_case);
-use Pod::Usage;
-use Pod::Man;
-use File::Temp qw(tempfile);
-use File::Basename;
-use Data::Dumper;
-
-use constant EX_OK => 0;
-use constant EX_USAGE => 64; # command line usage error
-use constant EX_DATAERR => 65; # data format error
-use constant EX_NOINPUT => 66; # cannot open input file
-use constant EX_SOFTWARE => 70; # internal software error (not used yet)
-use constant EX_CANTCREAT => 73; # can't create (user) output file
-
-my $progname = basename($0);
-my $progdescr = "Recreate Dico module autoconf structure";
-my $addmod;
-my $dry_run;
-my $verbose;
-my $autoconf = "module.ac";
-my $config_stub = "stub.ac";
-my $makefile_stub = "modules/stub.am";
-my $makefile = "modules/Makefile.am";
-my %outfile;
-
-my %categories; # not used now
-
-sub error {
- my $msg = shift;
- local %_ = @_;
- print STDERR "$progname: " if defined($progname);
- print STDERR "$_{prefix}: " if defined($_{prefix});
- print STDERR "$msg\n"
-}
-
-sub abend {
- my $code = shift;
- print STDERR "$progname: " if defined($progname);
- print STDERR "@_\n";
- exit $code;
-}
-
-my $status = 0;
-
-sub rewrite_configure_ac {
- my $ref = shift;
-
- my %vartab = (
- 'overwrite' => 1,
- 'tempfile' => 1,
- 'ignorecomm' => '##',
- 'HEADING' => "Do not edit. -*- mode: autoconf; buffer-read-only: t; -*- vi: set ro:\
-This file is created automatically from $config_stub.\
-To recreate, run $progname from the top of Dico source tree.
-",
- 'MODULES' => sub {
- my $s;
-
- foreach my $name (sort keys %{$ref}) {
- my $h = $ref->{$name};
- $s .= "# Module $name\n";
- if (defined($h->{autoconf})) {
- open(my $afd, "<", $h->{autoconf})
- or abend(EX_NOINPUT,
- "can't open $h->{autoconf} for reading: $!");
- while (<$afd>) {
- next if /^##/;
- $s .= $_;
- }
- close $afd;
- }
- $s .= "\n";
- $s .= "DICO_TESTS($h->{dir})\n" if ($h->{tests});
- $s .= "AC_CONFIG_FILES([$h->{dir}/Makefile])\n";
- $s .= "\n# End of module $name\n\n";
- }
- return $s;
- },
- 'STATUS_OUTPUT' => sub {
- return join "\n",
- map {
- my $n;
- if (defined($ref->{$_}{descr})) {
- $n = $ref->{$_}{descr};
- } else {
- $n = $_;
- $n =~ s/\b(\w)/\U$1/g;
- }
-
- my $l = 34 - length($n) - 2;
-
- "$n " .
- (($l > 0) ? '.' x $l : '') .
- " \$status_$ref->{$_}{modname}";
- }
- grep { $ref->{$_}{status} }
- sort keys %{$ref};
- },
- 'STATUS_DEFN' => sub {
- return join "\n",
- map { "status_$ref->{$_}{modname}=\$status_$ref->{$_}{modname}" }
- grep { defined($ref->{$_}{status}) }
- sort keys %{$ref};
- }
- );
- make_file($config_stub, "configure.ac", \%vartab);
-}
-
-sub rewrite_makefile {
- my $ref = shift;
-
- my %vartab = (
- 'overwrite' => 1,
- 'tempfile' => 1,
- 'ignorecomm' => '##',
- 'HEADING' => "Do not edit. -*- buffer-read-only: t; -*- vi: set ro:\
-This file is created automatically from $makefile_stub.\
-To recreate, run $progname from the top of Dico source tree.
-",
- 'MODULES' => sub {
- my $s;
- foreach my $am (map { $ref->{$_}{am} }
- grep { defined($ref->{$_}{am}) }
- sort keys %{$ref}) {
- $s .= "$am\n";
- }
- $s .= "SUBDIRS=";
- foreach my $dir (map { $ref->{$_}{SUBDIRS} } sort keys %{$ref}) {
- $s .= "\\\n $dir";
- }
- $s .= "\n";
- return $s;
- }
- );
- make_file($makefile_stub, $makefile, \%vartab);
-}
-
-sub add_module {
- my ($name,$ref) = @_;
- my $dir = "modules/$name";
-
- error("adding module $name", prefix=>'DEBUG') if ($verbose);
- my $acname = uc($name);
- $ref->{$name}{dir} = $dir;
- $ref->{$name}{SUBDIRS} = $name;
- $ref->{$name}{modname} = $name;
- if (-r "$dir/$autoconf") {
- $ref->{$name}{autoconf} = "$dir/$autoconf";
- open(my $fd, "<", "$dir/$autoconf")
- or abend(EX_NOINPUT,
- "can't open $dir/$autoconf for reading: $!");
- while (<$fd>) {
- chomp;
- s/\s+$//;
- if (/^##\s*module\s+name\s*:\s*([^\s]+).*$/) {
- $ref->{$name}{modname} = $1;
- } elsif (/^##\s*module\s+description\s*:\s*(.+).*$/) {
- $ref->{$name}{descr} = $1;
- } elsif (/^##\s*module\s+category\s*:\s*(.+).*$/) {
- $ref->{$name}{category} = $1;
- $categories{$1}++;
- }
- s/#.*//;
- next if ($_ eq "");
- if (/AM_CONDITIONAL\s*\(\s*\[${acname}_COND\]/) {
- $ref->{$name}{am} = <<EOT;
-if ${acname}_COND
- ${acname}_DIR=$name
-endif
-EOT
-;
- $ref->{$name}{SUBDIRS} = "\$(${acname}_DIR)";
- }
- $ref->{$name}{status} = 1
- if (!$ref->{$name}{status} and /status_$ref->{$name}{modname}=/);
- }
- close $fd;
- }
- $ref->{$name}{tests} = (-d "$dir/tests"
- and -f "$dir/tests/Makefile.am"
- and -f "$dir/tests/testsuite.at");
-
- if (-f "$dir/Makefile.am") {
- open(my $fd, "<", "$dir/Makefile.am")
- or abend(EX_NOINPUT,
- "can't open $dir/$autoconf for reading: $!");
- my %checks = ('mod' => "mod_LTLIBRARIES does not contain $ref->{$name}{modname}.la",
- 'SOURCES' => "$ref->{$name}{modname}_la_SOURCES not defined");
- while (<$fd>) {
- chomp;
- s/\s+$//;
- s/#.*//;
- next if ($_ eq "");
- if (/mod_LTLIBRARIES\s*=\s*$ref->{$name}{modname}.la/) {
- delete $checks{mod};
- } elsif (/$ref->{$name}{modname}_la_SOURCES\s*=/) {
- delete $checks{SOURCES};
- }
- }
- close $fd;
- if (keys(%checks)) {
- while (my ($k,$v) = each %checks) {
- error("$dir/Makefile.am: $v");
- }
- $status = EX_DATAERR;
- return;
- }
- }
-
- if ($verbose>1) {
- print STDERR "module $name:\n";
- print STDERR Dumper($ref->{$name});
- }
-}
-
-sub replace_macro {
- my $name = shift;
- my $ref = shift;
-
- my $s;
-
- if (defined($ref->{$name})) {
- if (ref($ref->{$name}) eq 'CODE') {
- $s = &{$ref->{$name}};
- } else {
- $s = $ref->{$name};
- }
- } else {
- $s = "<$name>";
- }
-
- return $s;
-}
-
-sub make_file {
- my $infile = shift;
- my $ofile = shift;
- my $ref = shift;
-
- error("creating $ofile from $infile", prefix => 'DEBUG') if ($verbose);
- return if $dry_run;
- if (!$ref->{overwrite} and -e $ofile) {
- error("$ofile already exists", prefix => 'warning');
- } else {
- open(my $ifd, "<", $infile)
- or abend(EX_DATAERR, "can't open $infile for reading: $!");
-
- my $ofd;
- if (defined($ref->{tempfile})) {
- my $tempname;
- ($ofd, $tempname) = tempfile(DIR => dirname($infile))
- or abend(EX_CANTCREAT,
- "can't open temporary file for creating $ofile: $!");
- $outfile{$tempname} = $ofile;
- } else {
- open($ofd, ">", $ofile)
- or abend(EX_CANTCREAT, "can't open $ofile for writing: $!");
- }
- while (<$ifd>) {
- next if (defined($ref->{ignorecomm}) and /^$ref->{ignorecomm}/);
- if (/^(.*)<([A-Z][A-Z0-9_]+)#([^>]*)>\s*$/) {
- my $pfx = $3 || $1;
- $_ = $1 . replace_macro($2, $ref);
- chomp;
- s/\n/\n$pfx/g;
- $_ .= "\n";
- } else {
- s/<([A-Z][A-Z0-9_]+)>/replace_macro($1, $ref)/gex;
- }
- print $ofd $_;
- }
- close($ifd);
- close($ofd);
- }
-}
-
-sub create_module {
- my $name = shift;
- my $dir = "modules/$name";
-
- if (-d $dir) {
- error("$dir already exists", prefix => 'warning');
- } else {
- error("creating $dir", prefix => 'DEBUG') if ($verbose);
- mkdir($dir) unless $dry_run;
- }
- make_file("modules/template.am", "$dir/Makefile.am",
- { ignorecomm => '##',
- MODNAME => $name });
- make_file("modules/template.c", "$dir/${name}.c",
- { ignorecomm => '//',
- MODNAME => $name });
-}
-
-sub cleanup {
- foreach my $f (keys %outfile) {
- unlink $f if (-e $f);
- }
-}
-
-END {
- cleanup;
-}
-
-$SIG{HUP} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = \&cleanup;
-
-#
-GetOptions("h" => sub {
- pod2usage(-message => "$progname: $progdescr",
- -exitstatus => 0);
- },
- "help" => sub {
- pod2usage(-exitstatus => EX_OK, -verbose => 2);
- },
- "usage" => sub {
- pod2usage(-exitstatus => EX_OK, -verbose => 0);
- },
- "add|new|a" => \$addmod,
- "dry-run|n" => \$dry_run,
- "verbose|v+" => \$verbose,
-) or exit(EX_USAGE);
-
-abend(EX_USAGE, "must run from the Dico source tree topmost directory")
- unless (-d "modules");
-
-++$verbose if $dry_run;
-
-if ($addmod) {
- abend(EX_USAGE, "not enough arguments") unless ($#ARGV >= 0);
- foreach my $mod (@ARGV) {
- create_module($mod);
- }
-} elsif ($#ARGV >= 0) {
- abend(EX_USAGE, "too many arguments; did you mean --new?");
-}
-
-my %modtab;
-my @modnames = map { s#modules/##; $_; } grep { -d $_; } glob "modules/*";
-
-foreach my $m (@modnames) {
- add_module($m, \%modtab);
-}
-
-rewrite_configure_ac(\%modtab);
-rewrite_makefile(\%modtab);
-
-if ($status == 0 and !$dry_run) {
- while (my ($from, $to) = each %outfile) {
- rename $from, $to
- or abend(EX_CANTCREAT, "can't rename $from to $to: $!");
- }
-}
-
-exit($status);
-

Return to:

Send suggestions and report system problems to the System administrator.