Subject: bug#13524: [PATCH 1/2] preproc: add support for relative names in included fragments

From: Peter Rosin <[email-masked]>
Date: Fri, 8 Feb 2013 09:45:25 +0100

The rationale for this change is that it is annoying to have
to repeat the directory name when including a Makefile fragment.
For deep directory structures these repeats can generate a lot
of bloat.  It also hinders reuse and easy directory restructuring
if all Makefile fragments have to know exactly where they live.

Suggested by Bob Friesenhahn, and later discussed in bug#13524.

In the course of discussion, the following notations were rejected:
&{reldir}& - to hard to type, {reldir} - interferes with ${reldir},
{am_reldir} - short form {D} interferes with ${D}, @am_reldir@ - short
form @D@ interferes with AC_SUBST([D]) as well as invading the
config.status turf. Other notations were also suggested...

* automake.in (read_am_file): Add third argument specifying the
relative directory of this Makefile fragment compared to the
main Makefile.  Replace %reldir% and %canon_reldir% in the
fragment with this relative directory (with slashes etc, or
canonicalized).
(read_main_am_file): Adjust.
* t/preproc-reldir.sh: New test.
* t/list-of-tests.mk: Augment.
* doc/automake.texi (Include): Document the new feature.
NEWS: Add new feature.

Co-authored-by: Stefano Lattarini <[email-masked]>
Signed-off-by: Peter Rosin <[email-masked]>
---
 NEWS                |   12 +++++
 automake.in         |   26 ++++++++--
 doc/automake.texi   |   20 ++++++++
 t/list-of-tests.mk  |    1 +
 t/preproc-reldir.sh |  129 +++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 182 insertions(+), 6 deletions(-)
 create mode 100755 t/preproc-reldir.sh

diff --git a/NEWS b/NEWS
index 6dcce72..e27e0cf 100644
--- a/NEWS
+++ b/NEWS
@@ -100,6 +100,18 @@ New in 1.13.2:
     be longer necessary, so we deprecate it with runtime warnings.  It will
     likely be removed altogether in Automake 1.14.
 
+* Relative directory in Makefile fragments:
+
+  - The special Automake-time substitutions '%reldir%' and '%canon_reldir%'
+    (and their short versions, '%D%' and '%C%' respectively) can now be used
+    in an included Makefile fragment.  The former is substituted with the
+    relative directory of the included fragment (compared to the top level
+    including Makefile), and the latter with the canonicalized version of
+    the same relative directory:
+
+        bin_PROGRAMS += %reldir%/foo
+        %canon_reldir%_foo_SOURCES = {reldir}/bar.c
+
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 New in 1.13.2:
diff --git a/automake.in b/automake.in
index d6ed599..80e54ff 100644
--- a/automake.in
+++ b/automake.in
@@ -6330,15 +6330,16 @@ sub check_trailing_slash ($\$)
 }

-# &read_am_file ($AMFILE, $WHERE)
-# -------------------------------
+# &read_am_file ($AMFILE, $WHERE, $RELDIR)
+# ----------------------------------------
 # Read Makefile.am and set up %contents.  Simultaneously copy lines
 # from Makefile.am into $output_trailer, or define variables as
 # appropriate.  NOTE we put rules in the trailer section.  We want
 # user rules to come after our generated stuff.
-sub read_am_file ($$)
+sub read_am_file ($$$)
 {
-    my ($amfile, $where) = @_;
+    my ($amfile, $where, $reldir) = @_;
+    my $canon_reldir = &canonicalize ($reldir);
 
     my $am_file = new Automake::XFile ("< $amfile");
     verb "reading $amfile";
@@ -6423,6 +6424,17 @@ sub read_am_file ($$)
 
 	my $new_saw_bk = check_trailing_slash ($where, $_);
 
+	if ($reldir eq '.')
+	  {
+	    # If present, eat the following '_' or '/', converting
+	    # "%reldir%/foo" and "%canon_reldir%_foo" into plain "foo"
+	    # when $reldir is '.'.
+	    $_ =~ s,%(D|reldir)%/,,g;
+	    $_ =~ s,%(C|canon_reldir)%_,,g;
+	  }
+	$_ =~ s/%(D|reldir)%/${reldir}/g;
+	$_ =~ s/%(C|canon_reldir)%/${canon_reldir}/g;
+
 	if (/$IGNORE_PATTERN/o)
 	{
 	    # Merely delete comments beginning with two hashes.
@@ -6584,8 +6596,10 @@ sub read_am_file ($$)
 		push_dist_common ("\$\(srcdir\)/$path");
 		$path = $relative_dir . "/" . $path if $relative_dir ne '.';
 	      }
+	    my $new_reldir = File::Spec->abs2rel ($path, $relative_dir);
+	    $new_reldir = '.' if $new_reldir !~ s,/[^/]*$,,;
 	    $where->push_context ("'$path' included from here");
-	    &read_am_file ($path, $where);
+	    &read_am_file ($path, $where, $new_reldir);
 	    $where->pop_context;
 	}
 	else
@@ -6658,7 +6672,7 @@ sub read_main_am_file ($$)
     &define_standard_variables;
 
     # Read user file, which might override some of our values.
-    &read_am_file ($amfile, new Automake::Location);
+    &read_am_file ($amfile, new Automake::Location, '.');
 }

diff --git a/doc/automake.texi b/doc/automake.texi
index feae3ac..d420d28 100644
--- a/doc/automake.texi
+++ b/doc/automake.texi
@@ -10519,6 +10519,26 @@ 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}.
 
+Inside a fragment, the construct @code{%reldir%} is replaced with the
+directory of the fragment relative to the base @file{Makefile.am}.
+Similarly, @code{%canon_reldir%} is replaced with the canonicalized
+(@pxref{Canonicalization}) form of @code{%reldir%}.  As a convenience,
+@code{%D%} is a synonym for @code{%reldir%}, and @code{%C%}
+is a synonym for @code{%canon_reldir%}.
+
+A special feature is that if the fragment is in the same directory as
+the base @file{Makefile.am} (i.e., @code{%reldir%} is @code{.}), then
+@code{%reldir%} and @code{%canon_reldir%} will expand to the empty
+string as well as eat, if present, a following slash or underscore
+respectively.
+
+Thus, a makefile fragment might look like this:
+
+@example
+bin_PROGRAMS += %reldir%/mumble
+%canon_reldir%_mumble_SOURCES = %reldir%/one.c
+@end example
+
 @node Conditionals
 @chapter Conditionals
 
diff --git a/t/list-of-tests.mk b/t/list-of-tests.mk
index 0acbdcf..72c99ee 100644
--- a/t/list-of-tests.mk
+++ b/t/list-of-tests.mk
@@ -865,6 +865,7 @@ t/pr401.sh \
 t/pr401b.sh \
 t/pr401c.sh \
 t/prefix.sh \
+t/preproc-reldir.sh \
 t/primary.sh \
 t/primary2.sh \
 t/primary3.sh \
diff --git a/t/preproc-reldir.sh b/t/preproc-reldir.sh
new file mode 100755
index 0000000..ab443df
--- /dev/null
+++ b/t/preproc-reldir.sh
@@ -0,0 +1,129 @@
+#! /bin/sh
+# Copyright (C) 2013 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 2, 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/>.
+
+# Test %reldir% and %canon_reldir%.
+
+. test-init.sh
+
+cat >> configure.ac << 'END'
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_CONFIG_FILES([zot/Makefile])
+AC_OUTPUT
+END
+
+mkdir foo
+mkdir foo/bar
+mkdir foo/foobar
+mkdir zot
+
+cat > Makefile.am << 'END'
+AUTOMAKE_OPTIONS = subdir-objects
+bin_PROGRAMS =
+include $(top_srcdir)/foo/local.mk
+include $(srcdir)/foo/foobar/local.mk
+include local.mk
+END
+
+cat > zot/Makefile.am << 'END'
+AUTOMAKE_OPTIONS = subdir-objects
+bin_PROGRAMS =
+include $(top_srcdir)/zot/local.mk
+include $(top_srcdir)/top.mk
+include ../reltop.mk
+END
+
+cat > local.mk << 'END'
+%canon_reldir%_whoami:
+	@echo "I am %reldir%/local.mk"
+
+bin_PROGRAMS += %reldir%/mumble
+%canon_reldir%_mumble_SOURCES = %reldir%/one.c
+END
+
+cat > top.mk << 'END'
+%canon_reldir%_top_whoami:
+	@echo "I am %reldir%/top.mk"
+
+bin_PROGRAMS += %D%/scream
+%C%_scream_SOURCES = %D%/two.c
+END
+
+cat > reltop.mk << 'END'
+%C%_reltop_whoami:
+	@echo "I am %D%/reltop.mk"
+
+bin_PROGRAMS += %reldir%/sigh
+%canon_reldir%_sigh_SOURCES = %reldir%/three.c
+END
+
+cat > one.c << 'END'
+int main(void) { return 0; }
+END
+
+cp local.mk foo
+cp local.mk foo/bar
+cp local.mk foo/foobar
+cp local.mk zot
+echo "include %reldir%/bar/local.mk" >> foo/local.mk
+
+cp one.c foo
+cp one.c foo/bar
+cp one.c foo/foobar
+cp one.c zot
+cp one.c two.c
+cp one.c three.c
+
+$ACLOCAL
+$AUTOCONF
+$AUTOMAKE -a
+./configure
+
+$MAKE whoami >output 2>&1 || { cat output; exit 1; }
+cat output
+grep "I am local.mk" output
+$MAKE foo_whoami >output 2>&1 || { cat output; exit 1; }
+cat output
+grep "I am foo/local.mk" output
+$MAKE foo_bar_whoami >output 2>&1 || { cat output; exit 1; }
+cat output
+grep "I am foo/bar/local.mk" output
+$MAKE foo_foobar_whoami >output 2>&1 || { cat output; exit 1; }
+cat output
+grep "I am foo/foobar/local.mk" output
+
+$MAKE
+./mumble
+foo/mumble
+foo/bar/mumble
+foo/foobar/mumble
+
+cd zot
+
+$MAKE ___top_whoami >output 2>&1 || { cat output; exit 1; }
+cat output
+grep "I am ../top.mk" output
+$MAKE ___reltop_whoami >output 2>&1 || { cat output; exit 1; }
+cat output
+grep "I am ../reltop.mk" output
+$MAKE whoami >output 2>&1 || { cat output; exit 1; }
+cat output
+grep "I am local.mk" output
+
+$MAKE
+./mumble
+../scream
+../sigh
-- 
1.7.9