]> git.draconx.ca Git - cdecl99.git/blob - fix-gnulib.pl
Better document fix-gnulib.
[cdecl99.git] / fix-gnulib.pl
1 #!/usr/bin/env perl
2 #
3 # Copyright © 2011-2012 Nick Bowler
4 #
5 # Prepare the Gnulib tree for inclusion into a non-recursive automake build.
6 # While the output of gnulib-tool is "include"-able if the --makefile-name
7 # option is used, it is still not suitable for non-recursive use for a couple
8 # reasons; chief among them is that filenames are not relative to the top
9 # source directory.
10 #
11 # This script postprocesses the gnulib-tool output to produce something that
12 # is intended to be suitable for inclusion into such non-recursive build
13 # environments.  Since the integration involves both configure.ac and
14 # Makefile.am, the output must be included into _both_.  Supposing the output
15 # is written to lib/gnulib.mk, you would add:
16 #
17 #   m4_include([lib/gnulib.mk]) # to configure.ac, after any call to gl_INIT
18 #   include $(top_srcdir)/lib/gnulib.mk # to Makefile.am
19 #
20 # You must also arrange for the Gnulib-generated header files to be built
21 # before the object files which depend on them; the most robust way to do this
22 # is by explicit prerequisites, for example:
23 #
24 #   bin_PROGRAMS = foo
25 #   $(foo_OBJECTS): $(gnulib_headers)
26 #
27 # The $(gnulib_headers) variable will expand to GNU-make order-only
28 # prerequisites when available, avoiding spurious incremental rebuilds when
29 # unused headers are changed.  If this feature is not available, it will
30 # expand to ordinary prerequisites.  It is therefore only appropriate for use
31 # in target prerequisites; the $(gnulib_raw_headers) variable may be used in
32 # other contexts when only the list of header files is required.
33 #
34 # This script also provides machinery for Gnulib symbol renaming via the
35 # glconfig.mk Makefile.am snippet; use of this feature is optional.
36 #
37 # Most of the specific transformations are documented below.
38 #
39 # License WTFPL2: Do What The Fuck You Want To Public License, version 2.
40 # This is free software: you are free to do what the fuck you want to.
41 # There is NO WARRANTY, to the extent permitted by law.
42
43 use strict;
44 use List::Compare;
45 use Getopt::Long;
46
47 my $output   = undef;
48 my $input    = undef;
49
50 my $line     = 0;
51
52 Getopt::Long::Configure("gnu_getopt", "no_auto_abbrev");
53 GetOptions(
54         "o|output=s"   => \$output,
55         "i|input=s"    => \$input,
56 );
57
58 open STDOUT, ">", $output or die "$output: $!\n" if (defined $output);
59 open STDIN,  "<", $input  or die "$input: $!\n"  if (defined $input);
60
61 my $printed_header = 0;
62 my (%allvars, %sourcevars);
63 my @cleanfiles;
64
65 sub drop {
66         undef $_;
67         next;
68 }
69
70 sub basename {
71         my $file = shift;
72         $file =~ m|(?:.+/)?([^/]+)/?|;
73         return $1;
74 }
75
76 sub mangle_file {
77         my $word = shift;
78
79         if ($word =~ /^\$\(([[:word:].]+)\)$/) {
80                 # Don't touch variables now, but record them for later.
81                 $sourcevars{$1} = 1;
82         } elsif ($word =~ /^\$\((?:top_)?(?:srcdir|builddir)\)/) {
83                 # Do nothing.  Generic transformation will take care of
84                 # $(srcdir) and $(builddir).
85         } elsif ($word =~ /^[[:word:].+\/-]+$/) {
86                 # Fix up things that look like filenames.
87                 $word = "lib/$word";
88         } else {
89                 print STDERR "$0:$line: warning: unrecognized source file: $word\n";
90         }
91
92         return "$word";
93 }
94
95 sub mangle_variable {
96         my $raw = shift;
97
98         $raw =~ /([^=]+=)[[:space:]]*(.*)/s;
99         my ($left, @right) = ($1, split(/[[:space:]]+/, $2));
100
101         return join(" ", ($left, map(mangle_file($_), @right))) . "\n";
102 }
103
104 sub mangle_target {
105         my $raw = shift;
106
107         $raw =~ /([^:]+):[[:space:]]*(.*)/s;
108         my @left  = split(/[[:space:]]+/, $1);
109         my @right = split(/[[:space:]]+/, $2);
110
111         @left  = map(mangle_file($_), @left);
112         @right = map(mangle_file($_), @right);
113
114         return join(" ", @left) . ": " . join(" ", @right) . "\n";
115 }
116
117 while (<STDIN>) {
118         $line++;
119
120         # Combine line splices.
121         while (s/\\$//) {
122                 $line++;
123                 $_ = $_ . <STDIN>
124         }
125
126         next if (/^#/);
127
128         if (!$printed_header) {
129                 print "# Postprocessed by ", basename($0), "\n\n";
130                 print <<'EOF';
131 # BEGIN AUTOMAKE/M4 POLYGLOT \
132 m4_unquote(m4_argn([2], [
133 .PHONY: # Automake code follows
134
135 # This trick should define gnulib_orderonly to | iff we're using GNU make.
136 gnulib_have_orderonly = $(findstring order-only,$(.FEATURES))
137 gnulib_orderonly = $(gnulib_have_orderonly:order-only=|)
138 gnulib_core_headers =
139 gnulib_raw_headers = $(gnulib_core_headers)
140 gnulib_headers = $(gnulib_orderonly) $(gnulib_raw_headers)
141 EOF
142
143                 $printed_header = 1;
144                 drop;
145         }
146
147         # For some reason, gnulib-tool adds core dumps to "make mostlyclean".
148         # Since these files are (hopefully!) not created by make, they should
149         # not be cleaned.
150         drop if (/^MOSTLYCLEANFILES.*core/);
151
152         # Some modules set AM_CPPFLAGS/AM_CFLAGS/etc. in a manner that is not
153         # useful for non-recursive builds.  Strip them out.
154         drop if (/^(AM_CPPFLAGS|AM_CFLAGS)/);
155
156         # Library dependencies are added automatically to libgnu.la by
157         # gnulib-tool.  Unfortunately, this means that everything linking
158         # against libgnu.la is forced to pull in the same deps, even if they're
159         # unneeded.  Furthermore, a libtool linker flag reordering bug prevents
160         # --as-needed from stripping out the useless deps, so it's better to
161         # handle them all manually.
162         drop if (/LDFLAGS/);
163
164         # Rewrite automake hook targets to be more generic.
165         if (s/^(.*)-local:/\1-gnulib:/) {
166                 print ".PHONY: $1-gnulib\n";
167                 print "$1-local: $1-gnulib\n";
168                 s/$1-generic//;
169
170                 # Don't let these targets get confused with filenames below.
171                 next;
172         }
173
174         # We need to mangle filenames in make variables; prepending a lib/ on
175         # relative paths.  The following should catch all variable assignments
176         # that need mangling.
177         if (/^([[:word:]]+)[[:space:]]*\+?=/) {
178                 $allvars{$1} = 1;
179
180                 if (/_SOURCES|CLEANFILES|EXTRA_DIST|[[:upper:]]+_H/) {
181                         $_ = mangle_variable($_);
182                 }
183         }
184
185         # BUILT_SOURCES has similar problems to recursive make: inadequate
186         # dependencies lead to incorrect builds.  Collect them into an
187         # ordinary variable so we can deal with them later.
188         s/BUILT_SOURCES/gnulib_core_headers/;
189
190         # Targets are similar to variables: the target and its dependencies
191         # need to be mangled.
192         if (/^[^\t].*:/) {
193                 $_ = mangle_target($_);
194         }
195
196         # When using conditional-dependencies, *CLEANFILES can end up
197         # depending on the configuration.  This means that "make distclean"
198         # may not actually delete everything if the configuration changes
199         # after building the package.  Stash all the variables for later so
200         # they can be moved outside of any conditional.
201         if (/CLEANFILES/) {
202                 push(@cleanfiles, $_);
203                 drop;
204         }
205
206         # Finally, references to $(srcdir) and $(builddir) need to be fixed up.
207         s:\$\(srcdir\):\$\(top_srcdir\)/lib:g;
208         s:\$\(builddir\):\$\(top_builddir\)/lib:g;
209 } continue { s/(\n.)/\\\1/g; print; };
210
211 print <<'EOF';
212 gnulib_lt_objects = $(libgnu_la_OBJECTS) $(gl_LTLIBOBJS)
213 $(gnulib_lt_objects): $(gnulib_headers)
214 EOF
215 print @cleanfiles;
216
217 # Some filenames are AC_SUBSTed by the Gnulib macros, and thus we need to
218 # prepend lib/ if and only if they're not empty.  Unfortunately, make is not
219 # powerful to do this, so we need to put this transformation into configure
220 # itself by defining a new autoconf macro.
221
222 my $lc = List::Compare->new('-u', '-a', \%sourcevars, \%allvars);
223 my @vars = $lc->get_unique;
224
225 print <<'EOF';
226 if FALSE
227 ], [dnl M4 code follows
228
229 AC_SUBST([GLSRC], [lib])
230 AC_DEFUN_ONCE([DX_GLSYM_PREFIX], [AC_SUBST([GLSYM_PREFIX], [$1])])
231 AC_CONFIG_COMMANDS_PRE([DX_GLSYM_PREFIX([${PACKAGE}__])])
232
233 m4_foreach([gl_objvar], [[gl_LIBOBJS], [gl_LTLIBOBJS]], [dnl
234 set x $gl_objvar; shift
235 gl_objvar=
236 while test ${#} -gt 0; do
237         gl_objvar="$gl_objvar lib/${1}"; shift
238 done
239 ])
240 EOF
241
242 foreach (@vars) {
243         print "$_=\${$_:+lib/\$$_}\n";
244 }
245
246 print <<'EOF';
247 ], [
248 endif
249 # ]))dnl
250 # END AUTOMAKE/M4 POLYGLOT
251 EOF