Fix and document rules to not touch the tree with `make -n'.
authorRalf Wildenhues <Ralf.Wildenhues@gmx.de>
Sun, 3 Oct 2010 10:29:40 +0000 (12:29 +0200)
committerRalf Wildenhues <Ralf.Wildenhues@gmx.de>
Thu, 4 Nov 2010 20:04:20 +0000 (21:04 +0100)
* 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.

Signed-off-by: Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
ChangeLog
NEWS
automake.in
doc/automake.texi
lib/am/lisp.am
lib/am/remake-hdr.am
tests/Makefile.am
tests/Makefile.in
tests/autohdrdry.test [new file with mode: 0755]
tests/lispdry.test [new file with mode: 0755]
tests/yaccdry.test [new file with mode: 0755]

index 18c5ff6..6c17cd3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
 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.
diff --git a/NEWS b/NEWS
index b3d4131..6bc0d6f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -45,6 +45,10 @@ Bugs fixed in 1.11.0a:
   - 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:
 
index 6b36ae2..c1b0396 100755 (executable)
@@ -5954,11 +5954,11 @@ sub lang_vala_finish_target ($$)
     {
       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/;
         }
     }
@@ -5974,11 +5974,11 @@ sub lang_vala_finish_target ($$)
                                  '--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
@@ -6073,10 +6073,8 @@ sub lang_yacc_target_hook
            $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
index b75c7a5..79f3f5a 100644 (file)
@@ -11372,11 +11372,15 @@ data.h data.w data.x: data.c
         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
@@ -11484,8 +11488,8 @@ elc-stamp: $(ELFILES)
         @@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.
@@ -11498,10 +11502,58 @@ $(ELCFILES): elc-stamp
             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
index 3449d18..ab45b30 100644 (file)
@@ -1,6 +1,6 @@
 ## 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
@@ -49,7 +49,16 @@ $(am__ELCFILES): elc-stamp
 ##
 ## 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
@@ -58,18 +67,18 @@ $(am__ELCFILES): elc-stamp
 ## 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
 
index 5077be2..35f4a46 100644 (file)
@@ -1,6 +1,6 @@
 ## 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
index acc7640..9c81564 100644 (file)
@@ -98,6 +98,7 @@ autohdr.test \
 autohdr2.test \
 autohdr3.test \
 autohdr4.test \
+autohdrdry.test \
 automake.test \
 auxdir.test \
 auxdir2.test \
@@ -456,6 +457,7 @@ lisp5.test \
 lisp6.test \
 lisp7.test \
 lisp8.test \
+lispdry.test \
 listval.test \
 location.test \
 longline.test \
@@ -788,6 +790,7 @@ yacc5.test \
 yacc6.test \
 yacc7.test \
 yacc8.test \
+yaccdry.test \
 yaccpp.test \
 yaccvpath.test \
 yflags.test \
index 117f2ba..b568a09 100644 (file)
@@ -365,6 +365,7 @@ autohdr.test \
 autohdr2.test \
 autohdr3.test \
 autohdr4.test \
+autohdrdry.test \
 automake.test \
 auxdir.test \
 auxdir2.test \
@@ -723,6 +724,7 @@ lisp5.test \
 lisp6.test \
 lisp7.test \
 lisp8.test \
+lispdry.test \
 listval.test \
 location.test \
 longline.test \
@@ -1055,6 +1057,7 @@ yacc5.test \
 yacc6.test \
 yacc7.test \
 yacc8.test \
+yaccdry.test \
 yaccpp.test \
 yaccvpath.test \
 yflags.test \
diff --git a/tests/autohdrdry.test b/tests/autohdrdry.test
new file mode 100755 (executable)
index 0000000..b501e76
--- /dev/null
@@ -0,0 +1,45 @@
+#!/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
+
+:
diff --git a/tests/lispdry.test b/tests/lispdry.test
new file mode 100755 (executable)
index 0000000..3bca21a
--- /dev/null
@@ -0,0 +1,60 @@
+#! /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
+
+:
diff --git a/tests/yaccdry.test b/tests/yaccdry.test
new file mode 100755 (executable)
index 0000000..d11d3fe
--- /dev/null
@@ -0,0 +1,59 @@
+#! /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
+
+: