Subject: Re: bug#13524: Improving user experience for non-recursive builds

From: Peter Rosin <[email-masked]>
Date: Sun, 27 Jan 2013 01:54:34 +0100

On 2013-01-25 17:03, Peter Rosin wrote:
> On 2013-01-24 13:22, Peter Rosin wrote:
>> On 2013-01-23 16:08, Stefano Lattarini wrote:
>>> On 01/23/2013 03:34 PM, Peter Rosin wrote:
>>>> On 2013-01-23 13:45, Stefano Lattarini wrote:
>> *snip*
>>>>> Too much automagic here IMO.  We'd better have two distinct subst, one for
>>>>> the "real" directory name, and one for the directory name "canonicalized"
>>>>> for use in Automake primaries.  I.e., from:
>>>>
>>>> The gain was that the '.' case needs to peak at, and perhaps eat, the
>>>> trailing separator anyway (or it will look bad, I mean, who wants __foo
>>>> after writing &{CANON_CURDIR}&_foo and curdir happens to be '.'?)
>>>>
>>> Good point.  We should allow the user to write something like
>>> "&{CANON_CURDIR}&foo_SOURCES" instead, so that it can work for the
>>> current directory too; not much important for human-written makefiles,
>>> but might be for autogenerated ones (I'm thinking ahead about a Gnulib
>>> integration here; the current support for non-recursive projects
>>> there is quite hacky, and could benefit from this new feature).
>>
>> Are you saying that &{CURDIR}& should be replaced with the empty
>> string when the current relative directory is "." and the current
>> relative directory followed by a slash otherwise? (And similar, but
>> with a trailing underscore for &{CANON_CURDIR}&) I don't fancy
>> that, as I think &{CURDIR}&gazonk.c is that much harder to read than
>> &{CURDIR}&/gazonk.c.
>>
>> So, I'd rather have the magic extend beyond the }& even if that is
>> ugly indeed. Maybe I'm just deluded to want that...
>>
>> Or, do you mean that &{CURDIR}& should peak ahead and only add a
>> trailing "/" if it is not followed by a slash (or whitespace?)
>> already, making "&{CURDIR}&foo.c" and "&{CURDIR}&/foo.c" equivalent
>> except when &{CURDIR}& is "." (which would come out as "foo.c" and
>> "./foo.c" respectively)?
>>
>> *snip*
>>> Thanks, I'll take a look at it tomorrow.
>>
>> Another day, another version. I hope you didn't wast too much time
>> on v2...
>>
>> I changed to &{CANON_CURDIR}& and &{CURDIR}&, added support for
>> $(top_srcdir) and improved the test. There's more code due to the
>> $(top_srcdir) support, and maybe there are some preexisting
>> functionality that strips common leading directory components that
>> could have been reused, but I'm ignorant. Besides, what's wrong
>> with NIH? :-)
> 
> Zapping the NIH part reduced the code size significantly (the patch
> is now short, sweet and unintrusive again) so I'm posting a new version.
> After all, it's a new day, right?
> 
> I hope it's ok to use File::Spec->abs2rel () in Automake?

This time with documentation and a NEWS entry. I also fixed the case
of including something above the current base Makefile.am with a
relative path, e.g.:

	include ../top.mk

That change shaved a couple of more lines. Neat.

I also rebased it, so now it is against master.

Cheers,
Peter

>From b32d5197ac8f6c97750a22990995c98240c4cadb Mon Sep 17 00:00:00 2001
From: Peter Rosin <[email-masked]>
Date: Sun, 27 Jan 2013 00:48:28 +0100
Subject: [PATCH] curdir: add support for relative names in included fragments

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.

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

Signed-off-by: Peter Rosin <[email-masked]>
---
 NEWS               |   10 ++++
 automake.in        |   24 ++++++++--
 doc/automake.texi  |   20 ++++++++
 t/curdir.sh        |  129 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 t/list-of-tests.mk |    1 +
 5 files changed, 179 insertions(+), 5 deletions(-)
 create mode 100755 t/curdir.sh

diff --git a/NEWS b/NEWS
index 7a25b80..3a2d8e7 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,16 @@ New in 1.14:
     over the same-named automake-provided macro, as defined in file
     '/usr/local/share/aclocal-1.14/vala.m4'.
 
+* Current directory in makefile fragments:
+
+  - Use the special makefile fragment substitutions &{CURDIR}& and
+    &{CANON_CURDIR}& (a.k.a. &{D}& and &{C}& respectively) to insert the
+    relative directory of the fragment (or its canonicalized version) in
+    the makefile:
+
+        bin_PROGRAMS += &{CURDIR}&/foo
+        &{CANON_CURDIR}&_foo_SOURCES = &{CURDIR}&/bar.c
+
 * Obsolescent features flagged:
 
   - Use of the special makefile variable 'ACLOCAL_AMFLAGS' is deprecated.
diff --git a/automake.in b/automake.in
index 4776581..f31a4ba 100644
--- a/automake.in
+++ b/automake.in
@@ -6302,15 +6302,15 @@ sub check_trailing_slash ($\$)
 }

-# &read_am_file ($AMFILE, $WHERE)
-# -------------------------------
+# &read_am_file ($AMFILE, $WHERE, $CURDIR)
+# ----------------------------------------
 # 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 ($$)
 {
-    my ($amfile, $where) = @_;
+    my ($amfile, $where, $curdir) = @_;
 
     my $am_file = new Automake::XFile ("< $amfile");
     verb "reading $amfile";
@@ -6395,6 +6395,18 @@ sub read_am_file ($$)
 
 	my $new_saw_bk = check_trailing_slash ($where, $_);
 
+	my $cur_dir = &canonicalize ($curdir);
+	if ($curdir eq '.')
+	  {
+	    # If present, eat the following '_' or '/', converting
+	    # "&{CURDIR}&/foo" and "&{CANON_CURDIR}&_foo" into plain "foo"
+	    # when $curdir is '.'.
+	    $_ =~ s,&\{(D|CURDIR)\}&/,,g;
+	    $_ =~ s,&\{(C|CANON_CURDIR)\}&_,,g;
+	  }
+	$_ =~ s/&\{(D|CURDIR)\}&/${curdir}/g;
+	$_ =~ s/&\{(C|CANON_CURDIR)\}&/${cur_dir}/g;
+
 	if (/$IGNORE_PATTERN/o)
 	{
 	    # Merely delete comments beginning with two hashes.
@@ -6556,8 +6568,10 @@ sub read_am_file ($$)
 		push_dist_common ("\$\(srcdir\)/$path");
 		$path = $relative_dir . "/" . $path if $relative_dir ne '.';
 	      }
+	    my $new_curdir = File::Spec->abs2rel ($path, $relative_dir);
+	    $new_curdir = '.' if $new_curdir !~ s,/[^/]*$,,;
 	    $where->push_context ("'$path' included from here");
-	    &read_am_file ($path, $where);
+	    &read_am_file ($path, $where, $new_curdir);
 	    $where->pop_context;
 	}
 	else
@@ -6630,7 +6644,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 263e0cc..43b3542 100644
--- a/doc/automake.texi
+++ b/doc/automake.texi
@@ -10500,6 +10500,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{&@{CURDIR@}&} is replaced with the
+directory of the fragment relative to the base @file{Makefile.am}.
+Similarly, @code{&@{CANON_CURDIR@}&} is replaced with the canonicalized
+(@pxref{Canonicalization}) form of @code{&@{CURDIR@}&}.  As a convenience,
+@code{&@{D@}&} is a synonym for @code{&@{CURDIR@}&}, and @code{&@{C@}&}
+is a synonym for @code{&@{CANON_CURDIR@}&}.
+
+A special feature is that if the fragment is in the same directory as
+the base @file{Makefile.am} (i.e., @code{&@{CURDIR@}&} is @code{.}), then
+@code{&@{CURDIR@}&} and @code{&@{CANON_CURDIR@}&} 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 += &@{CURDIR@}&/mumble
+&@{CANON_CURDIR@}&_mumble_SOURCES = &@{CURDIR@}&/one.c
+@end example
+
 @node Conditionals
 @chapter Conditionals
 
diff --git a/t/curdir.sh b/t/curdir.sh
new file mode 100755
index 0000000..d222a52
--- /dev/null
+++ b/t/curdir.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 &{CURDIR}& and &{CANON_CURDIR}&.
+
+. 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_CURDIR}&_whoami:
+	@echo "I am &{CURDIR}&/local.mk"
+
+bin_PROGRAMS += &{CURDIR}&/mumble
+&{CANON_CURDIR}&_mumble_SOURCES = &{CURDIR}&/one.c
+END
+
+cat > top.mk << 'END'
+&{CANON_CURDIR}&_top_whoami:
+	@echo "I am &{CURDIR}&/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 += &{CURDIR}&/sigh
+&{CANON_CURDIR}&_sigh_SOURCES = &{CURDIR}&/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 &{CURDIR}&/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
diff --git a/t/list-of-tests.mk b/t/list-of-tests.mk
index af78211..cc8d808 100644
--- a/t/list-of-tests.mk
+++ b/t/list-of-tests.mk
@@ -340,6 +340,7 @@ t/cscope.tap \
 t/cscope2.sh \
 t/cscope3.sh \
 t/c-demo.sh \
+t/curdir.sh \
 t/cxx.sh \
 t/cxx2.sh \
 t/cxxcpp.sh \
-- 
1.7.9