From: Nick Bowler Date: Thu, 10 May 2012 00:51:24 +0000 (-0400) Subject: Merge branch 'fix-gnulib' of /home/nbowler/projects/cdecl99 X-Git-Url: http://git.draconx.ca/gitweb/dxcommon.git/commitdiff_plain/83acbb95d2f181cd67f08f1bae4572de9768b63b?hp=8a326debcc4498494325cf392b5501cdcf199fc0 Merge branch 'fix-gnulib' of /home/nbowler/projects/cdecl99 Import the fix-gnulib history from cdecl99. * 'fix-gnulib' of /home/nbowler/projects/cdecl99: Better document fix-gnulib. Make fix-gnulib only need a single output file. Simplify glconfig include mechanism. Mangle spliced makefile lines in Gnulib correctly. Avoid gratuitous library dependencies linking gnulib. Rewrite Gnulib symbols to be in libcdecl's namespace. Split gnulib_headers into 3 variables. Move all Gnulib CLEANFILES variables to the end of the makefile. Handle gl_LIBOBJS/gl_LTLIBOBJS correctly. Eliminate use of BUILT_SOURCES from Gnulib. Integrate Gnulib non-recursively. --- diff --git a/scripts/fix-gnulib.pl b/scripts/fix-gnulib.pl new file mode 100755 index 0000000..85ae632 --- /dev/null +++ b/scripts/fix-gnulib.pl @@ -0,0 +1,251 @@ +#!/usr/bin/env perl +# +# Copyright © 2011-2012 Nick Bowler +# +# Prepare the Gnulib tree for inclusion into a non-recursive automake build. +# While the output of gnulib-tool is "include"-able if the --makefile-name +# option is used, it is still not suitable for non-recursive use for a couple +# reasons; chief among them is that filenames are not relative to the top +# source directory. +# +# This script postprocesses the gnulib-tool output to produce something that +# is intended to be suitable for inclusion into such non-recursive build +# environments. Since the integration involves both configure.ac and +# Makefile.am, the output must be included into _both_. Supposing the output +# is written to lib/gnulib.mk, you would add: +# +# m4_include([lib/gnulib.mk]) # to configure.ac, after any call to gl_INIT +# include $(top_srcdir)/lib/gnulib.mk # to Makefile.am +# +# You must also arrange for the Gnulib-generated header files to be built +# before the object files which depend on them; the most robust way to do this +# is by explicit prerequisites, for example: +# +# bin_PROGRAMS = foo +# $(foo_OBJECTS): $(gnulib_headers) +# +# The $(gnulib_headers) variable will expand to GNU-make order-only +# prerequisites when available, avoiding spurious incremental rebuilds when +# unused headers are changed. If this feature is not available, it will +# expand to ordinary prerequisites. It is therefore only appropriate for use +# in target prerequisites; the $(gnulib_raw_headers) variable may be used in +# other contexts when only the list of header files is required. +# +# This script also provides machinery for Gnulib symbol renaming via the +# glconfig.mk Makefile.am snippet; use of this feature is optional. +# +# Most of the specific transformations are documented below. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +use strict; +use List::Compare; +use Getopt::Long; + +my $output = undef; +my $input = undef; + +my $line = 0; + +Getopt::Long::Configure("gnu_getopt", "no_auto_abbrev"); +GetOptions( + "o|output=s" => \$output, + "i|input=s" => \$input, +); + +open STDOUT, ">", $output or die "$output: $!\n" if (defined $output); +open STDIN, "<", $input or die "$input: $!\n" if (defined $input); + +my $printed_header = 0; +my (%allvars, %sourcevars); +my @cleanfiles; + +sub drop { + undef $_; + next; +} + +sub basename { + my $file = shift; + $file =~ m|(?:.+/)?([^/]+)/?|; + return $1; +} + +sub mangle_file { + my $word = shift; + + if ($word =~ /^\$\(([[:word:].]+)\)$/) { + # Don't touch variables now, but record them for later. + $sourcevars{$1} = 1; + } elsif ($word =~ /^\$\((?:top_)?(?:srcdir|builddir)\)/) { + # Do nothing. Generic transformation will take care of + # $(srcdir) and $(builddir). + } elsif ($word =~ /^[[:word:].+\/-]+$/) { + # Fix up things that look like filenames. + $word = "lib/$word"; + } else { + print STDERR "$0:$line: warning: unrecognized source file: $word\n"; + } + + return "$word"; +} + +sub mangle_variable { + my $raw = shift; + + $raw =~ /([^=]+=)[[:space:]]*(.*)/s; + my ($left, @right) = ($1, split(/[[:space:]]+/, $2)); + + return join(" ", ($left, map(mangle_file($_), @right))) . "\n"; +} + +sub mangle_target { + my $raw = shift; + + $raw =~ /([^:]+):[[:space:]]*(.*)/s; + my @left = split(/[[:space:]]+/, $1); + my @right = split(/[[:space:]]+/, $2); + + @left = map(mangle_file($_), @left); + @right = map(mangle_file($_), @right); + + return join(" ", @left) . ": " . join(" ", @right) . "\n"; +} + +while () { + $line++; + + # Combine line splices. + while (s/\\$//) { + $line++; + $_ = $_ . + } + + next if (/^#/); + + if (!$printed_header) { + print "# Postprocessed by ", basename($0), "\n\n"; + print <<'EOF'; +# BEGIN AUTOMAKE/M4 POLYGLOT \ +m4_unquote(m4_argn([2], [ +.PHONY: # Automake code follows + +# This trick should define gnulib_orderonly to | iff we're using GNU make. +gnulib_have_orderonly = $(findstring order-only,$(.FEATURES)) +gnulib_orderonly = $(gnulib_have_orderonly:order-only=|) +gnulib_core_headers = +gnulib_raw_headers = $(gnulib_core_headers) +gnulib_headers = $(gnulib_orderonly) $(gnulib_raw_headers) +EOF + + $printed_header = 1; + drop; + } + + # For some reason, gnulib-tool adds core dumps to "make mostlyclean". + # Since these files are (hopefully!) not created by make, they should + # not be cleaned. + drop if (/^MOSTLYCLEANFILES.*core/); + + # Some modules set AM_CPPFLAGS/AM_CFLAGS/etc. in a manner that is not + # useful for non-recursive builds. Strip them out. + drop if (/^(AM_CPPFLAGS|AM_CFLAGS)/); + + # Library dependencies are added automatically to libgnu.la by + # gnulib-tool. Unfortunately, this means that everything linking + # against libgnu.la is forced to pull in the same deps, even if they're + # unneeded. Furthermore, a libtool linker flag reordering bug prevents + # --as-needed from stripping out the useless deps, so it's better to + # handle them all manually. + drop if (/LDFLAGS/); + + # Rewrite automake hook targets to be more generic. + if (s/^(.*)-local:/\1-gnulib:/) { + print ".PHONY: $1-gnulib\n"; + print "$1-local: $1-gnulib\n"; + s/$1-generic//; + + # Don't let these targets get confused with filenames below. + next; + } + + # We need to mangle filenames in make variables; prepending a lib/ on + # relative paths. The following should catch all variable assignments + # that need mangling. + if (/^([[:word:]]+)[[:space:]]*\+?=/) { + $allvars{$1} = 1; + + if (/_SOURCES|CLEANFILES|EXTRA_DIST|[[:upper:]]+_H/) { + $_ = mangle_variable($_); + } + } + + # BUILT_SOURCES has similar problems to recursive make: inadequate + # dependencies lead to incorrect builds. Collect them into an + # ordinary variable so we can deal with them later. + s/BUILT_SOURCES/gnulib_core_headers/; + + # Targets are similar to variables: the target and its dependencies + # need to be mangled. + if (/^[^\t].*:/) { + $_ = mangle_target($_); + } + + # When using conditional-dependencies, *CLEANFILES can end up + # depending on the configuration. This means that "make distclean" + # may not actually delete everything if the configuration changes + # after building the package. Stash all the variables for later so + # they can be moved outside of any conditional. + if (/CLEANFILES/) { + push(@cleanfiles, $_); + drop; + } + + # Finally, references to $(srcdir) and $(builddir) need to be fixed up. + s:\$\(srcdir\):\$\(top_srcdir\)/lib:g; + s:\$\(builddir\):\$\(top_builddir\)/lib:g; +} continue { s/(\n.)/\\\1/g; print; }; + +print <<'EOF'; +gnulib_lt_objects = $(libgnu_la_OBJECTS) $(gl_LTLIBOBJS) +$(gnulib_lt_objects): $(gnulib_headers) +EOF +print @cleanfiles; + +# Some filenames are AC_SUBSTed by the Gnulib macros, and thus we need to +# prepend lib/ if and only if they're not empty. Unfortunately, make is not +# powerful to do this, so we need to put this transformation into configure +# itself by defining a new autoconf macro. + +my $lc = List::Compare->new('-u', '-a', \%sourcevars, \%allvars); +my @vars = $lc->get_unique; + +print <<'EOF'; +if FALSE +], [dnl M4 code follows + +AC_SUBST([GLSRC], [lib]) +AC_DEFUN_ONCE([DX_GLSYM_PREFIX], [AC_SUBST([GLSYM_PREFIX], [$1])]) +AC_CONFIG_COMMANDS_PRE([DX_GLSYM_PREFIX([${PACKAGE}__])]) + +m4_foreach([gl_objvar], [[gl_LIBOBJS], [gl_LTLIBOBJS]], [dnl +set x $gl_objvar; shift +gl_objvar= +while test ${#} -gt 0; do + gl_objvar="$gl_objvar lib/${1}"; shift +done +]) +EOF + +foreach (@vars) { + print "$_=\${$_:+lib/\$$_}\n"; +} + +print <<'EOF'; +], [ +endif +# ]))dnl +# END AUTOMAKE/M4 POLYGLOT +EOF diff --git a/snippet/glconfig.mk b/snippet/glconfig.mk new file mode 100644 index 0000000..dd999af --- /dev/null +++ b/snippet/glconfig.mk @@ -0,0 +1,81 @@ +# Copyright © 2011 Nick Bowler +# +# Automake fragment to generate a Gnulib config header to rewrite exported +# symbols. This fragment relies on the Gnulib makefile postprocessing done by +# fix-gnulib.pl. Furthermore, the following additions to configure.ac may be +# required: +# +# * Add AM_PROC_CC_C_O to configure.ac +# * Pass the desired symbol prefix to DX_GLSYM_PREFIX in configure.ac, +# after including the fix-gnulib.pl output. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +GLCONFIG = @GLSRC@/glconfig.h +GLCAT = cat /dev/null + +GLSYM_V = $(GLSYM_V_$(V)) +GLSYM_V_ = $(GLSYM_V_$(AM_DEFAULT_VERBOSITY)) +GLSYM_V_0 = @echo " GLSYM " $<; + +gnulib_symfiles = $(gnulib_lt_objects:.lo=.glsym) +gnulib_headers += $(GLCONFIG) + +# This suffix rule triggers symbol generation only on demand. Dependencies are +# not tracked directly, so it must remain phony and thus not create the target. +.c.glsym: + $(GLSYM_V) $(MKDIR_P) $(@D)/.syms + $(AM_V_at) depfile=$(@D)/.syms/$(*F).deps \ + source=$< object=$(GLCONFIG) $(CCDEPMODE) \ + $(depcomp) $(COMPILE) -DNO_GLCONFIG \ + -c -o $(@D)/.syms/$(*F).o $< + $(AM_V_at) $(SHELL) $(top_builddir)/exported.sh \ + $(@D)/.syms/$(*F).o > $(@D)/.syms/$(*F).sym +$(gnulib_symfiles): $(gnulib_core_headers) + +clean-local: clean-glconfig +clean-glconfig: + @for sym in $(libgnu_la_SOURCES) $(EXTRA_libgnu_la_SOURCES); do \ + symdir=`expr "$$sym" : '\(.*/\)'`.syms; \ + if test -d "$$symdir"; then \ + echo "rm -rf $$symdir"; rm -rf "$$symdir"; \ + fi; \ + done +.PHONY: clean-glconfig + +# The config header requires compilation of all gnulib object files via the +# .glsym rule above. However, it cannot depend on those build products +# directly because they are phony, and would make this header never up-to-date. +# +# Thus, we use a recursive make call to regenerate the header, which avoids +# the need to list prerequisites. +# +# Since GNU make does not appear to allow the target of a suffix rule to be +# marked .PHONY, we also delete the .glsym files here just in case they were +# created for some reason (e.g., make -t). +$(GLCONFIG): $(gnulib_core_headers) + -$(AM_V_at) rm -f $(gnulib_symfiles) + $(AM_V_at) $(MAKE) $(AM_MAKEFLAGS) glconfig-gen + $(AM_V_GEN) mv -f $@.tmp $@ +CLEANFILES += $(GLCONFIG) + +# The glconfig-gen target is intended only for use in recursive make +# invocations. +glconfig-gen: $(gnulib_symfiles) + $(AM_V_at) depfiles=; symfiles=; \ + for sym in $(gnulib_symfiles); do \ + symdir=`expr "$$sym" : '\(.*/\)'`; \ + symfile=`expr "$$sym" : '.*/\(.*\)' || printf '%s\n' "$$sym"`; \ + symbase=$$symdir.syms/`expr "$$symfile" : '\(.*\)\..*'`; \ + test -f "$$symbase.deps" && \ + depfiles="$$depfiles $$symbase.deps"; \ + symfiles="$$symfiles $$symbase.sym"; \ + done; \ + $(GLCAT) $$depfiles > @GLSRC@/$(DEPDIR)/glconfig.Ph && \ + $(GLCAT) $$symfiles | sed 's/.*/#define & $(GLSYM_PREFIX)&/' \ + > $(GLCONFIG).tmp +.PHONY: glconfig-gen + +@AMDEP_TRUE@@am__include@ @am__quote@@GLSRC@/$(DEPDIR)/glconfig.Ph@am__quote@