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