automake-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: magic variables for included fragments (was: Feature request)


From: Ralf Wildenhues
Subject: Re: magic variables for included fragments (was: Feature request)
Date: Sun, 12 Oct 2008 22:50:11 +0200
User-agent: Mutt/1.5.18 (2008-05-17)

[ 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/address@hidden@//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
 
address@hidden 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
address@hidden 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
address@hidden $(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}.
 
address@hidden $(AM_SUBDIR)
address@hidden $(AM_PREFIX)
address@hidden $(AM_CANON)
address@hidden $(AM_REVERSE)
+
+Automake provides a set of special variables to refer to the location of
+the currently included fragment relative to its including
address@hidden 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.}
+
address@hidden @code
address@hidden $(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
address@hidden(AM_PREFIX)} in this case.
+
address@hidden $(AM_PREFIX)
+The same as @code{$(AM_SUBDIR)}, but usable as a prefix: If
address@hidden(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
address@hidden(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 address@hidden
+non-GNU Make implementations do not identify @file{file} with
address@hidden/file}.}
+
address@hidden $(AM_CANON)
+Same as @code{$(AM_PREFIX)}, but canonicalized, so it can be used to
+name a derived variable (@pxref{Canonicalization}).
+
address@hidden $(AM_REVERSE)
+The reverse path to @code{$(AM_SUBDIR)}.  You should hardly ever need
+to use this variable.
address@hidden table
+
+For example, if the following fragment
+
address@hidden
+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
address@hidden example
+
address@hidden
+is included through @samp{include sub/foo.mk}, then @command{automake}
+parses it as if you had written this instead:
+
address@hidden
+bin_PROGRAMS += sub/foo
+sub_foo_SOURCES = sub/foo.c sub/foo.h
+sub_foo_CPPFLAGS = -Isub -Isub/include
address@hidden 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
+:




reply via email to

[Prev in Thread] Current Thread [Next in Thread]