Well, I finally snuck in a little time to update my proof of concept for non recursive includes.
Still, I don't code perl - and it shows ;). How to use? Grab CVS automake, apply thepatch, drop the test files into tests subdir. Have a look at the test cases to see how to use it. What does it do? It transforms macros and paths in an included file (called Makefile.rules for now) , to make them suitable for a non-recursive build. As show by the test cases, this allows a couple of neat things: 1) A stub Makefile.am === include \$(srcdir)/Makefile.rules === is all that is needed in a given subdirectory to generate a full makefile. (Useful if you want to be able to cd to a given dir and perform builds just in that dir). 2) File paths, and canonical macro names are conveniently short - just what Bob F has been (rightfully IMO) complaining about. 3) You don't end up with a huge Makefile.am to support, rather each part of the project has a small rules file. Rob -- GPG key available at: <http://www.robertcollins.net/keys.txt>.
Index: automake.in =================================================================== RCS file: /cvs/automake/automake/automake.in,v retrieving revision 1.1523 diff -u -p -r1.1523 automake.in --- automake.in 30 Nov 2003 17:00:36 -0000 1.1523 +++ automake.in 1 Dec 2003 08:41:24 -0000 @@ -181,11 +181,27 @@ my $ELSE_PATTERN = my $ENDIF_PATTERN = '^endif(?:\s+(!?)\s*([A-Za-z][A-Za-z0-9_]*))?\s*(?:#.*)?' . "\$"; my $PATH_PATTERN = '(\w|[/.-])+'; +my $INCLUDE_KEYWORD = 'include'; +my $SUBDIR_INCLUDE_KEYWORD = 'subdir_include'; # This will pass through anything not of the prescribed form. -my $INCLUDE_PATTERN = ('^include\s+' +my $INCLUDE_PATTERN = ('^' . $INCLUDE_KEYWORD . '\s+' . '((\$\(top_srcdir\)/' . $PATH_PATTERN . ')' . '|(\$\(srcdir\)/' . $PATH_PATTERN . ')' . '|([^/\$]' . $PATH_PATTERN . '))\s*(#.*)?' . "\$"); +my $SUBDIR_INCLUDE_PATTERN = ('^' . $SUBDIR_INCLUDE_KEYWORD . '\s+' + . '((\$\(top_srcdir\)/' . $PATH_PATTERN . ')' + . '|(\$\(srcdir\)/' . $PATH_PATTERN . ')' + . '|([^/\$]' . $PATH_PATTERN . '))\s*(#.*)?' . "\$"); + +# Canonised variable suffixes +my @canonised_macro_names = + qw(SOURCES); +# Canonised variable contents (foo->path/foo) +my @canonised_macro_values = + qw(SOURCES); +# Canonised macro lists (foo ->path_foo) +my @canonised_macro_lists = + qw(PROGRAMS); # Match `-d' as a command-line argument in a string. my $DASH_D_PATTERN = "(^|\\s)-d(\\s|\$)"; @@ -216,7 +232,7 @@ my @common_files = ansi2knr.1 ansi2knr.c compile config.guess config.rpath config.sub configure configure.ac configure.in depcomp elisp-comp install-sh libversion.in mdate-sh missing mkinstalldirs - py-compile texinfo.tex ylwrap), + py-compile texinfo.tex ylwrap Makefile.rules), @libtool_files, @libtool_sometimes); # Commonly used files we auto-include, but only sometimes. @@ -1697,6 +1713,38 @@ sub handle_single_transform_list ($$$$@) return @result; } +# $VALUE +# transform_file_list ($PREPEND, @FILES) +# ---------------------------------------- +# insert $PREPEND before every file path that is not absolute +# +sub transform_file_list ($$) +{ + my ($prepend, $tmpfiles) = @_; + my $result = ""; + my @files = (); + @files = split(/ /, $tmpfiles); + while (scalar @files > 0) + { + $_ = shift @files; + + if ($_ =~ s/^\$\(top_srcdir\)\///) + { + $result .= " \$\(top_srcdir\)\/" . $_; + } + elsif ( $_ =~ s/^\$\(srcdir\)\///) + { + $result .= " \$\(srcdir\)\/$prepend" . $_; + } + else + { + $result .= " $prepend" . $_; + } + } + verb "transformed value: '$result'\n"; + return $result . "\n"; +} + # $LINKER # define_objects_from_sources ($VAR, $OBJVAR, $NODEFINE, $ONE_FILE, @@ -2145,7 +2193,7 @@ sub handle_programs # Canonicalize names and check for misspellings. my $xname = &check_canonical_spelling ($one_file, '_LDADD', '_LDFLAGS', '_SOURCES', '_OBJECTS', - '_DEPENDENCIES'); + '_DEPENDENCIES', '_CFLAGS'); $where->push_context ("while processing program `$one_file'"); $where->set (INTERNAL->get); @@ -2250,7 +2298,7 @@ sub handle_libraries # Canonicalize names and check for misspellings. my $xlib = &check_canonical_spelling ($onelib, '_LIBADD', '_SOURCES', '_OBJECTS', '_DEPENDENCIES', - '_AR'); + '_AR', '_CFLAGS'); if (! var ($xlib . '_AR')) { @@ -2371,7 +2419,20 @@ sub handle_ltlibraries # Canonicalize names and check for misspellings. my $xlib = &check_canonical_spelling ($onelib, '_LIBADD', '_LDFLAGS', '_SOURCES', '_OBJECTS', - '_DEPENDENCIES'); + '_DEPENDENCIES', '_CFLAGS'); + +# Tell the source code what library we are building +# my $tempvariable = ''; +# if ( &variable_defined ($xlib . '_CFLAGS')) +# { +# # Define the lib_CFLAGS variable. +# $tempvariable .= &variable_value ($xlib . '_CFLAGS'); +# &variable_delete ($xlib . '_CFLAGS'); +# } +# my $libname_short = $xlib; +# $libname_short =~ s/_la$// ; +# $libname_short = uc ($libname_short); +# &define_variable ($xlib . '_CFLAGS', ' -D' . $libname_short . '_COMPILATION ' . $tempvariable); # Check that the library fits the standard naming convention. my $libname_rx = "^lib.*\.la"; @@ -5413,19 +5474,62 @@ sub check_trailing_slash ($\$) return $$line =~ /\\$/; } +# include () +# worker routine to include a file. +# +sub include($$$$@) +{ + my ($path, $relative_dir, $where, $canonise, @include_stack) = @_; + my $prepend_path = ""; + + if ($path =~ s/^\$\(top_srcdir\)\///) + { + push (@include_stack, "\$\(top_srcdir\)/$path"); + am_error ("attempt to translate a top_srcdir include file: $path") if $canonise; + # 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"); + } + else + { + $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"); + $prepend_path = $path; + $prepend_path =~ s/[^\/]*$//; + $path = $relative_dir . "/" . $path if $relative_dir ne '.'; + } + $where->push_context ("`$path' included from here"); + &read_am_file ($path, $where, $prepend_path); + $where->pop_context; +} -# &read_am_file ($AMFILE, $WHERE) +# &read_am_file ($AMFILE, $WHERE, $PREPEND) # ------------------------------- # 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 ($$) +# Prepend $PREPEND to all file paths if ne "" +sub read_am_file ($$$) { - my ($amfile, $where) = @_; + my ($amfile, $where, $prepend_path) = @_; + my $prepend_macro = $prepend_path; + $prepend_macro =~ s/\//_/; my $am_file = new Automake::XFile ("< $amfile"); verb "reading $amfile"; + verb "prepending $prepend_path" if $prepend_path ne ""; # Keep track of the youngest output dependency. my $mtime = mtime $amfile; @@ -5533,6 +5637,7 @@ sub read_am_file ($$) my $cond = new Automake::Condition @cond_stack; $output_trailer .= $cond->subst_string; $output_trailer .= $_; + error ("can't translate saw-bk, was_rule, $_") if $prepend_path ne ""; } elsif ($prev_state == IN_COMMENT) { @@ -5560,6 +5665,7 @@ sub read_am_file ($$) $last_where, VAR_ASIS) if $cond != FALSE; $comment = $spacing = ''; + # error ("define $last_var_name, $last_var_value") if $prepend_path ne ""; } } } @@ -5611,8 +5717,59 @@ sub read_am_file ($$) $last_var_value = $3 . "\n"; } + if ($last_var_name =~ /_([^_]+)$/o) + { + my $var_suffix = $1; + if ($prepend_path ne "") + { + grep + { + if ($_ eq $var_suffix) + { + $last_var_name = $prepend_macro . $last_var_name; + } + } + @canonised_macro_names; + } + } + + if (!/\\$/) { + if ($last_var_name =~ /_([^_]+)$/o) + { + my $var_suffix = $1; + if ($prepend_path ne "") + { + grep + { + if ($_ eq $var_suffix) + { +# error ("prepending path '$prepend_path' to '$last_var_value' "); + $last_var_value = transform_file_list($prepend_path, $last_var_value); + } + } + @canonised_macro_values; + } + } + + if ($last_var_name =~ /_([^_]+)$/o) + { + my $var_suffix = $1; + if ($prepend_path ne "") + { + grep + { + if ($_ eq $var_suffix) + { +# error ("prepending literal '$prepend_macro' to '$last_var_value' "); + $last_var_value = transform_file_list($prepend_macro, $last_var_value); + } + } + @canonised_macro_lists; + } + } + Automake::Variable::define ($last_var_name, VAR_MAKEFILE, $last_var_type, $cond, $last_var_value, $comment, @@ -5623,35 +5780,11 @@ sub read_am_file ($$) } elsif (/$INCLUDE_PATTERN/o) { - my $path = $1; - - if ($path =~ 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"); - } - else - { - $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"); - $path = $relative_dir . "/" . $path if $relative_dir ne '.'; - } - $where->push_context ("`$path' included from here"); - &read_am_file ($path, $where); - $where->pop_context; + include $1, $relative_dir, $where, FALSE, @include_stack; + } + elsif (/$SUBDIR_INCLUDE_PATTERN/o) + { + include $1, $relative_dir, $where, TRUE, @include_stack; } else { @@ -5723,7 +5856,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, ""); } Index: tests/Makefile.am =================================================================== RCS file: /cvs/automake/automake/tests/Makefile.am,v retrieving revision 1.537 diff -u -p -r1.537 Makefile.am --- tests/Makefile.am 30 Nov 2003 13:35:29 -0000 1.537 +++ tests/Makefile.am 1 Dec 2003 08:41:27 -0000 @@ -431,6 +431,8 @@ subdirbuiltsources.test \ subcond.test \ subcond2.test \ subcond3.test \ +subdir_include.test \ +subdir_include_distcheck.test \ subobj.test \ subobj2.test \ subobj3.test \
#!/bin/sh # Test for subdir_include basic functionality. . defs || exit 1 subdirs="foo" for i in $subdirs; do mkdir $i cat >$i/$i-a.c <<EOF int maina() { return 0; } EOF cat >$i/$i-b.c <<EOF int mainb() { return 0; } EOF cat >$i/Makefile.rules <<EOF bin_PROGRAMS = ${i}bin ${i}bin_SOURCES = \$(top_srcdir)/main.c $i-a.c $i-b.c EOF cat >$i/Makefile.am <<EOF include \$(srcdir)/Makefile.rules EOF done cat >main.c <<EOF int main() { return 0; } EOF # echo "SUBDIRS = $subdirs" > Makefile.am cat >Makefile.am <<EOF #AUTOMAKE_OPTIONS = subdir-objects DIST_SUBDIRS = $subdirs subdir_include \$(srcdir)/foo/Makefile.rules EOF cat >configure.in <<EOF AC_INIT(foo/foo-a.c) AC_CONFIG_AUX_DIR(.) AM_INIT_AUTOMAKE(test_am, 1.0) AC_PROG_CC #AM_PROG_CC_C_O AC_OUTPUT(Makefile foo/Makefile) EOF # Fail gracefully if no autoconf. $needs_autoconf # Likewise for gcc. (gcc -v) > /dev/null 2>&1 || exit 77 touch README NEWS AUTHORS ChangeLog mkdir build # We use gcc and not gcc -traditional as the latter fails on some # Linux boxes (Red Hat 5.1 in particular). $ACLOCAL \ && $AUTOCONF \ && $AUTOMAKE -a || exit 1 cd build \ && CC='gcc' ../configure \ && $MAKE || exit 1
#!/bin/sh # Test for subdir_include distcheck. . defs || exit 1 subdirs="foo" for i in $subdirs; do mkdir $i cat >$i/$i-a.c <<EOF int maina() { return 0; } EOF cat >$i/$i-b.c <<EOF int mainb() { return 0; } EOF cat >$i/Makefile.rules <<EOF bin_PROGRAMS = ${i}bin ${i}bin_SOURCES = ../main.c $i-a.c $i-b.c EOF cat >$i/Makefile.am <<EOF #EXTRA_DIST=Makefile.rules include \$(srcdir)/Makefile.rules EOF done cat >main.c <<EOF int main() { return 0; } EOF # echo "SUBDIRS = $subdirs" > Makefile.am cat >Makefile.am <<EOF #AUTOMAKE_OPTIONS = subdir-objects DIST_SUBDIRS = $subdirs SUBDIRS = subdir_include \$(srcdir)/foo/Makefile.rules EOF cat >configure.in <<EOF AC_INIT(foo/foo-a.c) AC_CONFIG_AUX_DIR(.) AM_INIT_AUTOMAKE(test_am, 1.0) AC_PROG_CC #AM_PROG_CC_C_O AC_OUTPUT(Makefile foo/Makefile) EOF # Fail gracefully if no autoconf. $needs_autoconf # Likewise for gcc. (gcc -v) > /dev/null 2>&1 || exit 77 touch README NEWS AUTHORS ChangeLog mkdir build # We use gcc and not gcc -traditional as the latter fails on some # Linux boxes (Red Hat 5.1 in particular). $ACLOCAL \ && $AUTOCONF \ && $AUTOMAKE -a || exit 1 cd build \ && CC='gcc' ../configure \ && $MAKE distcheck || exit 1