2010-11-01 Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+ Fix and document rules to not touch the tree with `make -n'.
+ * doc/automake.texi (Multiple Outputs): Document the problem of
+ modifications during dry-run execution, propose solution.
+ * NEWS: Update.
+ * automake.in (lang_vala_finish_target): Split recipe so the
+ stamp file is not removed with GNU `make -n'.
+ (lang_yacc_target_hook): Separate removal of parser output file
+ and header remaking.
+ * lib/am/lisp.am ($(am__ELCFILES)): Determine whether -n was
+ passed to make, take care not to remove any files in that case.
+ * lib/am/remake-hdr.am (%CONFIG_H%): Separate removal of
+ %STAMP% file from induced remaking of config header.
+ * tests/autohdrdry.test, tests/lispdry.test, tests/yaccdry.test:
+ New tests.
+ * tests/Makefile.am (TESTS): Update.
+
+2010-11-01 Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+
Add FAQ entry for bug reporting instructions.
* doc/automake.texi (Reporting Bugs): New section.
(Introduction): Refer to it.
- The order of Yacc and Lex flags is fixed to be consistent with other
languages: $(AM_YFLAGS) comes before $(YFLAGS), and $(AM_LFLAGS) before
$(LFLAGS), so that the user variables override the developer variables.
+
+ - Rules generated by Automake now try harder to not change any files when
+ `make -n' is invoked. Fixes include compilation of Emacs Lisp, Vala, or
+ Yacc source files and the rule to update config.h.
\f
New in 1.11:
{
foreach my $file ($var->value_as_list_recursive)
{
- $output_rules .= "\$(srcdir)/$file: \$(srcdir)/${derived}_vala.stamp\n".
- "\t\@if test -f \$@; then :; else \\\n".
- "\t rm -f \$(srcdir)/${derived}_vala.stamp; \\\n".
- "\t \$(am__cd) \$(srcdir) && \$(MAKE) \$(AM_MAKEFLAGS) ${derived}_vala.stamp; \\\n".
- "\tfi\n"
+ $output_rules .= "\$(srcdir)/$file: \$(srcdir)/${derived}_vala.stamp\n"
+ . "\t\@if test -f \$@; then :; else rm -f \$(srcdir)/${derived}_vala.stamp; fi\n"
+ . "\t\@if test -f \$@; then :; else \\\n"
+ . "\t \$(am__cd) \$(srcdir) && \$(MAKE) \$(AM_MAKEFLAGS) ${derived}_vala.stamp; \\\n"
+ . "\tfi\n"
if $file =~ s/(.*)\.vala$/$1.c/;
}
}
'--vapi', '--internal-vapi', '--gir')))
{
my $headerfile = $flag;
- $output_rules .= "\$(srcdir)/$headerfile: \$(srcdir)/${derived}_vala.stamp\n".
- "\t\@if test -f \$@; then :; else \\\n".
- "\t rm -f \$(srcdir)/${derived}_vala.stamp; \\\n".
- "\t \$(am__cd) \$(srcdir) && \$(MAKE) \$(AM_MAKEFLAGS) ${derived}_vala.stamp; \\\n".
- "\tfi\n";
+ $output_rules .= "\$(srcdir)/$headerfile: \$(srcdir)/${derived}_vala.stamp\n"
+ . "\t\@if test -f \$@; then :; else rm -f \$(srcdir)/${derived}_vala.stamp; \n"
+ . "\t\@if test -f \$@; then :; else \\\n"
+ . "\t \$(am__cd) \$(srcdir) && \$(MAKE) \$(AM_MAKEFLAGS) ${derived}_vala.stamp; \\\n"
+ . "\tfi\n";
# valac is not used when building from dist tarballs
# distribute the generated files
$output_rules .=
"$condstr${header}: $output\n"
# Recover from removal of $header
- . "$condstr\t\@if test ! -f \$@; then \\\n"
- . "$condstr\t rm -f $output; \\\n"
- . "$condstr\t \$(MAKE) \$(AM_MAKEFLAGS) $output; \\\n"
- . "$condstr\telse :; fi\n";
+ . "$condstr\t\@if test ! -f \$@; then rm -f $output; else :; fi\n"
+ . "$condstr\t\@if test ! -f \$@; then \$(MAKE) \$(AM_MAKEFLAGS) $output; else :; fi\n";
}
# Distribute the generated file, unless its .y source was
# listed in a nodist_ variable. (&handle_source_transform
fi
@end example
-However there are now two minor problems in this setup. One is related
+However there are now three minor problems in this setup. One is related
to the timestamp ordering of @file{data.h}, @file{data.w},
-@file{data.x}, and @file{data.c}. The other one is a race condition
+@file{data.x}, and @file{data.c}. Another one is a race condition
if a parallel @command{make} attempts to run multiple instances of the
-recover block at once.
+recover block at once. Finally, the recursive rule breaks @samp{make -n}
+when run with GNU @command{make} (as well as some other @command{make}
+implementations), as it may remove @file{data.h} even when it should not
+(@pxref{MAKE Variable, , How the @code{MAKE} Variable Works, make,
+The GNU Make Manual}).
Let us deal with the first problem. @command{foo} outputs four files,
but we do not know in which order these files are created. Suppose
@@mv -f elc-temp $@@
$(ELCFILES): elc-stamp
-## Recover from the removal of $@@
@@if test -f $@@; then :; else \
+## Recover from the removal of $@@
trap 'rm -rf elc-lock elc-stamp' 1 2 13 15; \
if mkdir elc-lock 2>/dev/null; then \
## This code is being executed by the first process.
while test -d elc-lock; do sleep 1; done; \
## Succeed if and only if the first process succeeded.
test -f elc-stamp; exit $$?; \
+@c $$
fi; \
fi
@end example
+
+These solutions all still suffer from the third problem, namely that
+they break the promise that @samp{make -n} should not cause any actual
+changes to the tree. For those solutions that do not create lock files,
+it is possible to split the recover rules into two separate recipe
+commands, one of which does all work but the recursion, and the
+other invokes the recursive @samp{$(MAKE)}. The solutions involving
+locking could act upon the contents of the @samp{MAKEFLAGS} variable,
+but parsing that portably is not easy (@pxref{The Make Macro MAKEFLAGS,,,
+autoconf, The Autoconf Manual}). Here is an example:
+
+@example
+ELFILES = one.el two.el three.el @dots{}
+ELCFILES = $(ELFILES:=c)
+
+elc-stamp: $(ELFILES)
+ @@rm -f elc-temp
+ @@touch elc-temp
+ $(elisp_comp) $(ELFILES)
+ @@mv -f elc-temp $@@
+
+$(ELCFILES): elc-stamp
+## Recover from the removal of $@@
+ @@dry=; for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=*|--*);; \
+ *n*) dry=:;; \
+ esac; \
+ done; \
+ if test -f $@@; then :; else \
+ $$dry trap 'rm -rf elc-lock elc-stamp' 1 2 13 15; \
+ if $$dry mkdir elc-lock 2>/dev/null; then \
+## This code is being executed by the first process.
+ $$dry rm -f elc-stamp; \
+ $(MAKE) $(AM_MAKEFLAGS) elc-stamp; \
+ $$dry rmdir elc-lock; \
+ else \
+## This code is being executed by the follower processes.
+## Wait until the first process is done.
+ while test -d elc-lock && test -z "$$dry"; do \
@c $$
+ sleep 1; \
+ done; \
+## Succeed if and only if the first process succeeded.
+ $$dry test -f elc-stamp; exit $$?; \
+ fi; \
+ fi
+@end example
For completeness it should be noted that GNU @command{make} is able to
express rules with multiple output files using pattern rules
## automake - create Makefile.in from Makefile.am
## Copyright (C) 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
-## 2007, 2008, 2009 Free Software Foundation, Inc.
+## 2007, 2008, 2009, 2010 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
##
## Do not call `make elc-stamp' if emacs is not available, because it would
## be useless.
- @if test "$(EMACS)" != no && test ! -f $@; then \
+##
+## If `make -n' is called, do not execute any command in the recipe that
+## changes the tree; however, invoke the recursive make for debuggability.
+ @dry=; for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=*|--*);; \
+ *n*) dry=:;; \
+ esac; \
+ done; \
+ if test "$(EMACS)" != no && test ! -f $@; then \
## If `make -j' is used and more than one file has been erased, several
## processes can execute this block. We have to make sure that only
## the first one will run `$(MAKE) $(AM_MAKEFLAGS) elc-stamp', and the
## There is a race here if only one child of make receive a signal.
## In that case the build may fail. We remove elc-stamp when we receive
## a signal so we are sure the build will succeed the next time.
- trap 'rm -rf elc-lock elc-stamp' 1 2 13 15; \
- if mkdir elc-lock 2>/dev/null; then \
+ $$dry trap 'rm -rf elc-lock elc-stamp' 1 2 13 15; \
+ if $$dry mkdir elc-lock 2>/dev/null; then \
## This code is being executed by the first process.
- rm -f elc-stamp; \
+ $$dry rm -f elc-stamp; \
$(MAKE) $(AM_MAKEFLAGS) elc-stamp; \
- rmdir elc-lock; \
+ $$dry rmdir elc-lock; \
else \
## This code is being executed by the follower processes.
## Wait until the first process is done.
- while test -d elc-lock; do sleep 1; done; \
+ while test -d elc-lock && test -z "$$dry"; do sleep 1; done; \
## Succeed if and only if the first process succeeded.
- test -f elc-stamp; exit $$?; \
+ $$dry test -f elc-stamp; exit $$?; \
fi; \
else : ; fi
## automake - create Makefile.in from Makefile.am
## Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2003, 2004, 2005,
-## 2008, 2009 Free Software Foundation, Inc.
+## 2008, 2009, 2010 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
%CONFIG_H%: %STAMP%
## Recover from removal of CONFIG_HEADER
- @if test ! -f $@; then \
- rm -f %STAMP%; \
- $(MAKE) $(AM_MAKEFLAGS) %STAMP%; \
- else :; fi
+ @if test ! -f $@; then rm -f %STAMP%; else :; fi
+ @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) %STAMP%; else :; fi
%STAMP%: %CONFIG_H_DEPS% $(top_builddir)/config.status
autohdr2.test \
autohdr3.test \
autohdr4.test \
+autohdrdry.test \
automake.test \
auxdir.test \
auxdir2.test \
lisp6.test \
lisp7.test \
lisp8.test \
+lispdry.test \
listval.test \
location.test \
longline.test \
yacc6.test \
yacc7.test \
yacc8.test \
+yaccdry.test \
yaccpp.test \
yaccvpath.test \
yflags.test \
autohdr2.test \
autohdr3.test \
autohdr4.test \
+autohdrdry.test \
automake.test \
auxdir.test \
auxdir2.test \
lisp6.test \
lisp7.test \
lisp8.test \
+lispdry.test \
listval.test \
location.test \
longline.test \
yacc6.test \
yacc7.test \
yacc8.test \
+yaccdry.test \
yaccpp.test \
yaccvpath.test \
yflags.test \
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2010 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/>.
+
+# Removal recovery rules for AC_CONFIG_HEADERS should not remove files
+# with `make -n'.
+
+. ./defs || Exit 1
+
+set -e
+
+cat >>configure.in <<'EOF'
+AC_PROG_CC
+AC_CONFIG_HEADERS([config.h])
+AC_OUTPUT
+EOF
+
+: >Makefile.am
+
+$ACLOCAL
+$AUTOCONF
+$AUTOHEADER
+$AUTOMAKE
+
+./configure
+$MAKE
+
+rm -f config.h
+$MAKE -n
+test -f stamp-h1
+test ! -f config.h
+
+:
--- /dev/null
+#! /bin/sh
+# Copyright (C) 2005, 2008, 2010 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 GNU Automake; see the file COPYING. If not, write to
+
+# Check that `make -n' works with the lisp_LISP recover rule.
+
+required='emacs non-root'
+. ./defs || Exit 1
+
+set -e
+
+cat > Makefile.am << 'EOF'
+dist_lisp_LISP = am-one.el am-two.el am-three.el
+EOF
+
+cat >> configure.in << 'EOF'
+AM_PATH_LISPDIR
+AC_OUTPUT
+EOF
+
+echo "(require 'am-two)" > am-one.el
+echo "(require 'am-three) (provide 'am-two)" > am-two.el
+echo "(provide 'am-three)" > am-three.el
+
+$ACLOCAL
+$AUTOCONF
+$AUTOMAKE --add-missing
+./configure
+
+$MAKE
+
+test -f am-one.elc
+test -f am-two.elc
+test -f am-three.elc
+test -f elc-stamp
+
+rm -f am-*.elc elc-stamp
+
+chmod a-w .
+
+$MAKE -n
+
+test ! -f am-one.elc
+test ! -f am-two.elc
+test ! -f am-three.elc
+test ! -f elc-stamp
+
+:
--- /dev/null
+#! /bin/sh
+# Copyright (C) 2010 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/>.
+
+# Removal recovery rules for headers should not remove files with `make -n'.
+
+. ./defs || Exit 1
+
+set -e
+
+cat >> configure.in << 'END'
+AC_PROG_CC
+AC_PROG_YACC
+AC_OUTPUT
+END
+
+cat > Makefile.am << 'END'
+AM_YFLAGS = -d
+bin_PROGRAMS = foo
+foo_SOURCES = foo.c parse.y
+END
+
+cat > foo.c << 'END'
+int main () { return 0; }
+END
+
+cat > parse.y << 'END'
+%{
+int yylex () {return 0;}
+void yyerror (char *s) {}
+%}
+%%
+foobar : 'f' 'o' 'o' 'b' 'a' 'r' {};
+END
+
+$ACLOCAL
+$AUTOMAKE --add-missing
+$AUTOCONF
+./configure
+$MAKE
+
+rm -f parse.h
+$MAKE -n parse.h
+test -f parse.c
+test ! -f parse.h
+
+: