[ moving to automake-patches; this is <http://thread.gmane.org/gmane.comp.sysutils.automake.general/9824> ]
* Ralf Wildenhues wrote on Sun, Oct 12, 2008 at 10:46:06PM CEST: > > I'll follow up on automake-patches with a patch to test. Here we go. WDYT? Cheers, Ralf Recursive `include' and helper macros. * automake.in (am_subdir, am_prefix, am_canon, am_reverse): New globals. (initialize_per_input): Initialize them. (simplify, relativize): New functions; relativize taken from likewise named function in gnulib-tool written by Bruno Haible, rewritten for perl. (read_am_file): Substitute the literal strings `$(AM_SUBDIR)', `$(AM_PREFIX)', `$(AM_CANON)', and `$(AM_REVERSE)' in the input, using the new globals, before any other transformation. Adjust values upon `include' statements. Change semantics of `include local-path' to not be relative to $(srcdir) but to $(AM_PREFIX). * doc/automake.texi (Alternative): Index `non-recursive'. Mention include fragments and special variables. (Include): Document changed `include fragment' semantics and special variables. * tests/include3.test: New test. * tests/Makefile.am: Update. * NEWS: Update. Idea and suggestion from Akim Demaille. diff --git a/NEWS b/NEWS index 6171e2e..cf8832e 100644 --- a/NEWS +++ b/NEWS @@ -119,6 +119,15 @@ New in 1.10a: - AM_MAINTAINER_MODE now allows for an optional argument specifying the default setting. + - The semantics of the `include fragment.am' statement (without a prefix + of `$(srcdir)/' or `$(top_srcdir)/' to the file name) is now documented, + and was changed to search the file relative to the location of the current + include fragment. + Furthermore, Automake now provides special variables that allow to + refer to the location of the currently included fragment relative to + the Makefile.am file that includes it. This helps to write location- + independent fragments. + Bugs fixed in 1.10a: * Long standing bugs: diff --git a/automake.in b/automake.in index decb35a..1cad013 100755 --- a/automake.in +++ b/automake.in @@ -503,6 +503,14 @@ my @cond_stack; # This holds the set of included files. my @include_stack; +# subdir path from Makefile.am to current include file, prefix (usable +# without appending a slash, and empty if subdir is '.'), canonicalized +# prefix, and reverse path. +my $am_subdir; +my $am_prefix; +my $am_canon; +my $am_reverse; + # List of dependencies for the obvious targets. my @all; my @check; @@ -657,6 +665,11 @@ sub initialize_per_input () @include_stack = (); + $am_subdir = '.'; + $am_prefix = ''; + $am_canon = ''; + $am_reverse = '.'; + @all = (); @check = (); @check_tests = (); @@ -1091,6 +1104,68 @@ sub backname ($) return join ('/', @res) || '.'; } + +# simplify ($DIR) +# --------------- +# Kill instances of `/.' and `sub/..' from a path. +sub simplify ($) +{ + my ($dir) = @_; + my @res = (); + foreach my $d (grep (!/^\.$/, split (/\//, $dir))) + { + if ($d eq '..') + { + my $p = pop @res; + if (!defined ($p) || $p eq '..') + { + push @res, $p + if defined ($p); + push @res, $d; + } + } + else + { + push @res, $d; + } + } + return join ('/', @res) || '.'; +} + +# &relativize ($CUR-DIR, $DIR1, $DIR2) +# ------------------------------------ +# Compute relative pathname $REL-DIR such that $DIR1/$REL-DIR = $DIR2. +sub relativize ($$$) +{ + my ($curdir, $dir1, $dir2) = @_; + my @dir0 = grep (!/^\.$/, split (/\//, $curdir)); + my @dir2 = grep (!/^\.$/, split (/\//, $dir2)); + + foreach my $first (grep (!/^\.$/, split (/\//, $dir1))) + { + if ($first eq '..') + { + my $d = pop @dir0 + or prog_error ("moving outside of tree in relativize"); + unshift @dir2, $d; + } + else + { + if (defined ($dir2[0]) && $first eq $dir2[0]) + { + shift @dir2; + } + else + { + unshift @dir2, '..'; + } + push @dir0, $first; + } + } + return simplify (join ('/', @dir2) || '.'); +} + + ################################################################ @@ -6468,6 +6543,11 @@ sub read_am_file ($$) chomp; $_ .= "\n"; + s/\$\(AM_SUBDIR\)/$am_subdir/go; + s/\$\(AM_PREFIX\)/$am_prefix/go; + s/\$\(AM_CANON\)/$am_canon/go; + s/\$\(AM_REVERSE\)/$am_reverse/go; + # Don't look at MAINTAINER_MODE_TRUE here. That shouldn't be # used by users. @MAINT@ is an anachronism now. $_ =~ s/[EMAIL PROTECTED]@//g @@ -6610,35 +6690,71 @@ sub read_am_file ($$) } elsif (/$INCLUDE_PATTERN/o) { - my $path = $1; - - if ($path =~ s/^\$\(top_srcdir\)\///) + # A note on the naming convention here: + # - $am_foo is relative to the .am file + # (as opposed to: relative to an .ac file) + # - $am_inc_foo is relative to the enclosing include file + # (incremental) + # - $inc_ is what the user gave us (include) + # - $prefix* has $(*srcdir) strings prepended + + my ($old_subdir, $old_prefix, $old_canon, $old_reverse) = + ($am_subdir, $am_prefix, $am_canon, $am_reverse); + + my $inc_name = $1; + my $prefix; + my $prefixed_name; + my $prefixed_lname; + my $path; + my $lpath; + + if ($inc_name =~ s/^\$\(top_srcdir\)\///) { - push (@include_stack, "\$\(top_srcdir\)/$path"); - # Distribute any included file. - - # Always use the $(top_srcdir) prefix in DIST_COMMON, - # otherwise OSF make will implicitly copy the included - # file in the build tree during `make distdir' to satisfy - # the dependency. - # (subdircond2.test and subdircond3.test will fail.) - push_dist_common ("\$\(top_srcdir\)/$path"); + $prefix = '$(top_srcdir)/'; + $path = $lpath = $inc_name; + my $d = dirname ($inc_name); + $am_subdir = relativize ($relative_dir, $relative_dir, $d); + $am_reverse = relativize ($relative_dir, $d, $relative_dir); } - else + elsif ($inc_name =~ s/\$\(srcdir\)\///) { - $path =~ s/\$\(srcdir\)\///; - push (@include_stack, "\$\(srcdir\)/$path"); - # Always use the $(srcdir) prefix in DIST_COMMON, - # otherwise OSF make will implicitly copy the included - # file in the build tree during `make distdir' to satisfy - # the dependency. - # (subdircond2.test and subdircond3.test will fail.) - push_dist_common ("\$\(srcdir\)/$path"); + $prefix = '$(srcdir)/'; + $path = $lpath = $inc_name; $path = $relative_dir . "/" . $path if $relative_dir ne '.'; + $am_subdir = simplify (dirname ($inc_name)); + $am_reverse = relativize ($relative_dir, $am_subdir, '.'); + } + else + { + $prefix = '$(srcdir)/'; + $path = ($relative_dir eq '.') ? "" : $relative_dir . "/"; + $lpath = $old_prefix . $inc_name; + $path .= $lpath; + $am_subdir = simplify ($am_subdir . '/' . dirname ($inc_name)); + $am_reverse = relativize ($relative_dir, $am_subdir, '.'); } + $prefixed_name = $prefix . $path; + $prefixed_lname = $prefix . $lpath; + + $am_prefix = ($am_subdir ne '.') ? $am_subdir . "/" : ""; + $am_canon = canonicalize ($am_prefix); + + push (@include_stack, $prefixed_lname); + # Distribute any included file. + + # Always use the corresponding srcdir prefix in DIST_COMMON, + # otherwise OSF make will implicitly copy the included + # file in the build tree during `make distdir' to satisfy + # the dependency. + # (subdircond2.test and subdircond3.test will fail.) + push_dist_common ($prefixed_lname); + $where->push_context ("`$path' included from here"); &read_am_file ($path, $where); $where->pop_context; + + ($am_subdir, $am_prefix, $am_canon, $am_reverse) = + ($old_subdir, $old_prefix, $old_canon, $old_reverse); } else { diff --git a/doc/automake.texi b/doc/automake.texi index fa4ceec..d64591a 100644 --- a/doc/automake.texi +++ b/doc/automake.texi @@ -4258,6 +4258,8 @@ variables it cannot ensure the corresponding directory exist. @node Alternative @section An Alternative Approach to Subdirectories [EMAIL PROTECTED] non-recursive + If you've ever read Peter Miller's excellent paper, @uref{http://www.pcug.org.au/~millerp/rmch/recu-make-cons-harm.html, Recursive Make Considered Harmful}, the preceding sections on the use of @@ -4269,7 +4271,8 @@ Automake provides sufficient cross-directory support @footnote{We believe. This work is new and there are probably warts. @xref{Introduction}, for information on reporting bugs.} to enable you to write a single @file{Makefile.am} for a complex multi-directory -package. +package, possibly amended with a set of included @file{Makefile} +fragments. By default an installable file specified in a subdirectory will have its @@ -4331,6 +4334,13 @@ Currently, @samp{nobase_*_LTLIBRARIES} are the only exception to this rule, in that there is no particular installation order guarantee for an otherwise equivalent set of variables without @samp{nobase_} prefix. +With a non-recursive approach, the @file{Makefile.am} file can end up +being large and using many relative directory prefixes. You can use +included @file{Makefile} fragments together with per-fragment special +variables to keep your tree modular; see @ref{Include}, for more +information and an example. + + @node Subpackages @section Nesting Packages @cindex Nesting packages @@ -9137,6 +9147,14 @@ not by @command{make}. As with conditionals, @command{make} has no idea that There are two forms of @code{include}: @table @code [EMAIL PROTECTED] include file +Include a fragment that is found relative to the current file. + +This previously undocumented statement has changed semantics +in Automake 1.11; earlier versions interpreted this as [EMAIL PROTECTED] $(srcdir)/file}; the difference matters for fragments +included by other fragments. + @item include $(srcdir)/file Include a fragment that is found relative to the current source directory. @@ -9151,6 +9169,70 @@ condition applies to the entire contents of that fragment. Makefile fragments included this way are always distributed because they are needed to rebuild @file{Makefile.in}. [EMAIL PROTECTED] $(AM_SUBDIR) [EMAIL PROTECTED] $(AM_PREFIX) [EMAIL PROTECTED] $(AM_CANON) [EMAIL PROTECTED] $(AM_REVERSE) + +Automake provides a set of special variables to refer to the location of +the currently included fragment relative to its including [EMAIL PROTECTED] file. These variables are special in that they are +not real @command{make} variables: their value is dependent on the +inclusion path, and they are textually replaced before any other +processing is done by @command{automake}:@footnote{In fact, the only +reason these ``variables'' look like @command{make} variables, enclosed +in @code{$(@dots{})}, is for readability.} + [EMAIL PROTECTED] @code [EMAIL PROTECTED] $(AM_SUBDIR) +The relative path from the @file{Makefile.am} file to the fragment. +This variable always specifies a directory path and is thus never empty. +For modularity, you should not append a @samp{/} to this variable: use [EMAIL PROTECTED](AM_PREFIX)} in this case. + [EMAIL PROTECTED] $(AM_PREFIX) +The same as @code{$(AM_SUBDIR)}, but usable as a prefix: If [EMAIL PROTECTED](AM_SUBDIR)} is @code{.}, then the prefix is empty, otherwise the +prefix is the subdir with a directory separator @code{/} appended. This +allows to use @code{$(AM_PREFIX)} in all places where [EMAIL PROTECTED](AM_SUBDIR)} is followed by more path components, but has the +added advantage that the file fragment can be included within the +current directory of the @file{Makefile.am} file [EMAIL PROTECTED] +non-GNU Make implementations do not identify @file{file} with [EMAIL PROTECTED]/file}.} + [EMAIL PROTECTED] $(AM_CANON) +Same as @code{$(AM_PREFIX)}, but canonicalized, so it can be used to +name a derived variable (@pxref{Canonicalization}). + [EMAIL PROTECTED] $(AM_REVERSE) +The reverse path to @code{$(AM_SUBDIR)}. You should hardly ever need +to use this variable. [EMAIL PROTECTED] table + +For example, if the following fragment + [EMAIL PROTECTED] +bin_PROGRAMS += $(AM_PREFIX)foo +$(AM_CANON)foo_SOURCES = $(AM_PREFIX)foo.c $(AM_PREFIX)foo.h +$(AM_CANON)foo_CPPFLAGS = -I$(AM_SUBDIR) -I$(AM_PREFIX)include [EMAIL PROTECTED] example + [EMAIL PROTECTED] +is included through @samp{include sub/foo.mk}, then @command{automake} +parses it as if you had written this instead: + [EMAIL PROTECTED] +bin_PROGRAMS += sub/foo +sub_foo_SOURCES = sub/foo.c sub/foo.h +sub_foo_CPPFLAGS = -Isub -Isub/include [EMAIL PROTECTED] example + +With recursive inclusion of fragments through fragments these special +variables always remain relative to the enclosing @file{Makefile.am} +file. + + @node Conditionals @chapter Conditionals diff --git a/tests/Makefile.am b/tests/Makefile.am index b9561ab..f753352 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -313,6 +313,7 @@ hosts.test \ implicit.test \ include.test \ include2.test \ +include3.test \ info.test \ init.test \ insh2.test \ diff --git a/tests/include3.test b/tests/include3.test new file mode 100755 index 0000000..145d01d --- /dev/null +++ b/tests/include3.test @@ -0,0 +1,198 @@ +#! /bin/sh +# Copyright (C) 2008 Free Software Foundation, Inc. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# Make sure we properly expand AM_PREFIX and friends in included snippets. + +. ./defs || Exit 1 + +set -e + +cat >> configure.in << 'END' +AC_CONFIG_FILES([sub/Makefile]) +AC_PROG_CC +AM_PROG_CC_C_O +AC_OUTPUT +END + +cat > Makefile.am << 'END' +SUBDIRS = sub +END + +mkdir sub +cd sub + +cat > Makefile.am << 'END' +AUTOMAKE_OPTIONS = subdir-objects +nobase_dist_data_DATA = $(data) +bin_PROGRAMS = $(progs) +data = +progs = + +include inc.am +include foo/rules.am + +all-local: top-test inc-test foo-test foo-bar-test baz-test abs-test rel-test dotdot-test + +top-test: + test $(AM_SUBDIR) = . + test '$(AM_PREFIX)' = '' + test '$(AM_CANON)' = '' + test $(AM_REVERSE) = . +END + +mkdir foo foo/bar baz abs foo/rel +: >inc.dat +: >foo/foo.dat +: >foo/bar/bar.dat +: >baz/baz.dat +: >abs/abs.dat +: >foo/rel/rel.dat +: >dotdot.dat +echo 'int main() { return 0; }' >inc.c +echo 'int main() { return 0; }' >foo/foo.c +echo 'int main() { return 0; }' >foo/bar/bar.c +echo 'int main() { return 0; }' >baz/baz.c +echo 'int main() { return 0; }' >abs/abs.c +echo 'int main() { return 0; }' >foo/rel/rel.c +echo 'int main() { return 0; }' >dotdot.c + +cat >inc.am << 'END' +data += $(AM_PREFIX)inc.dat +progs += $(AM_PREFIX)inc +$(AM_CANON)inc_SOURCES = $(AM_PREFIX)inc.c + +inc-test: + test $(AM_SUBDIR) = . + test '$(AM_PREFIX)' = '' + test '$(AM_CANON)' = '' + test $(AM_REVERSE) = . + +END + +cat >foo/rules.am << 'END' +include bar/more-rules.am +include ../baz/even-more.am +include $(top_srcdir)/sub/abs/abs.am +include $(srcdir)/foo/rel/rel.am +include ../dotdot.am + +data += $(AM_PREFIX)foo.dat +progs += $(AM_PREFIX)foo +$(AM_CANON)foo_SOURCES = $(AM_PREFIX)foo.c + +foo-test: + test $(AM_SUBDIR) = foo + test $(AM_PREFIX) = foo/ + test $(AM_CANON) = foo_ + test $(AM_REVERSE) = .. +END + +cat >foo/bar/more-rules.am << 'END' +data += $(AM_PREFIX)bar.dat +progs += $(AM_PREFIX)bar +$(AM_CANON)bar_SOURCES = $(AM_PREFIX)bar.c + +foo-bar-test: + test $(AM_SUBDIR) = foo/bar + test $(AM_PREFIX) = foo/bar/ + test $(AM_CANON) = foo_bar_ + test $(AM_REVERSE) = ../.. +END + +cat >baz/even-more.am << 'END' +data += $(AM_PREFIX)baz.dat +progs += $(AM_PREFIX)baz +$(AM_CANON)baz_SOURCES = $(AM_PREFIX)baz.c + +baz-test: + test $(AM_SUBDIR) = baz + test $(AM_PREFIX) = baz/ + test $(AM_CANON) = baz_ + test $(AM_REVERSE) = .. +END + +cat >abs/abs.am << 'END' +data += $(AM_PREFIX)abs.dat +progs += $(AM_PREFIX)abs +$(AM_CANON)abs_SOURCES = $(AM_PREFIX)abs.c + +abs-test: + test $(AM_SUBDIR) = abs + test $(AM_PREFIX) = abs/ + test $(AM_CANON) = abs_ + test $(AM_REVERSE) = .. +END + +cat >foo/rel/rel.am << 'END' +data += $(AM_PREFIX)rel.dat +progs += $(AM_PREFIX)rel +$(AM_CANON)rel_SOURCES = $(AM_PREFIX)rel.c + +rel-test: + test $(AM_SUBDIR) = foo/rel + test $(AM_PREFIX) = foo/rel/ + test $(AM_CANON) = foo_rel_ + test $(AM_REVERSE) = ../.. +END + +cat >dotdot.am <<'END' +data += $(AM_PREFIX)dotdot.dat +progs += $(AM_PREFIX)dotdot +$(AM_CANON)dotdot_SOURCES = $(AM_PREFIX)dotdot.c + +dotdot-test: + test $(AM_SUBDIR) = . + test '$(AM_PREFIX)' = '' + test '$(AM_CANON)' = '' + test $(AM_REVERSE) = . +END + +cd .. + +$ACLOCAL +$AUTOCONF +$AUTOMAKE --add-missing + +for subst in AM_SUBDIR AM_PREFIX AM_CANON AM_REVERSE +do + grep $subst Makefile.in sub/Makefile.in && Exit 1 +done + +./configure +$MAKE +$MAKE distcheck + +$MAKE distclean + +# Now, test the whole thing if the including Makefile.am +# is the toplevel one. +rm -f Makefile.am +mv sub/* . +rmdir sub +sed '/sub/d' configure.in >configure.int +mv -f configure.int configure.in +sed 's,sub/,,g' foo/rules.am > foo/rules.amt +mv -f foo/rules.amt foo/rules.am +sed 's,sub/,,g' abs/abs.am > abs/abs.amt +mv -f abs/abs.amt abs/abs.am + +$ACLOCAL +$AUTOCONF +$AUTOMAKE --add-missing +./configure +$MAKE +$MAKE distcheck +: