From d7c1679b14c1ab691927f3243df1cb3cbb2360aa Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Sun, 3 Oct 2010 12:29:40 +0200 Subject: [PATCH] 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. Signed-off-by: Ralf Wildenhues --- ChangeLog | 18 ++++++++++++++++ NEWS | 4 ++++ automake.in | 26 +++++++++++----------- doc/automake.texi | 60 +++++++++++++++++++++++++++++++++++++++++++++++---- lib/am/lisp.am | 25 ++++++++++++++------- lib/am/remake-hdr.am | 8 +++---- tests/Makefile.am | 3 +++ tests/Makefile.in | 3 +++ tests/autohdrdry.test | 45 ++++++++++++++++++++++++++++++++++++++ tests/lispdry.test | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/yaccdry.test | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 280 insertions(+), 31 deletions(-) create mode 100755 tests/autohdrdry.test create mode 100755 tests/lispdry.test create mode 100755 tests/yaccdry.test diff --git a/ChangeLog b/ChangeLog index 18c5ff6..6c17cd3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,23 @@ 2010-11-01 Ralf Wildenhues + 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 + 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 --- 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. New in 1.11: diff --git a/automake.in b/automake.in index 6b36ae2..c1b0396 100755 --- a/automake.in +++ b/automake.in @@ -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 diff --git a/doc/automake.texi b/doc/automake.texi index b75c7a5..79f3f5a 100644 --- a/doc/automake.texi +++ b/doc/automake.texi @@ -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 diff --git a/lib/am/lisp.am b/lib/am/lisp.am index 3449d18..ab45b30 100644 --- a/lib/am/lisp.am +++ b/lib/am/lisp.am @@ -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 diff --git a/lib/am/remake-hdr.am b/lib/am/remake-hdr.am index 5077be2..35f4a46 100644 --- a/lib/am/remake-hdr.am +++ b/lib/am/remake-hdr.am @@ -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 @@ -20,10 +20,8 @@ %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 diff --git a/tests/Makefile.am b/tests/Makefile.am index acc7640..9c81564 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 \ diff --git a/tests/Makefile.in b/tests/Makefile.in index 117f2ba..b568a09 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -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 index 0000000..b501e76 --- /dev/null +++ b/tests/autohdrdry.test @@ -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 . + +# 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 index 0000000..3bca21a --- /dev/null +++ b/tests/lispdry.test @@ -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 index 0000000..d11d3fe --- /dev/null +++ b/tests/yaccdry.test @@ -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 . + +# 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 + +: -- 2.7.4