diff mbox series

[v2,2/5] Makefile: rename scripts in-place, don't clobber

Message ID patch-2.6-49dbd54d0ff-20210329T161723Z-avarab@gmail.com (mailing list archive)
State New, archived
Headers show
Series Makefile: don't die on AIX with open ./git | expand

Commit Message

Ævar Arnfjörð Bjarmason March 29, 2021, 4:20 p.m. UTC
Change the rules that generate auxiliary files such as
GIT-BUILD-OPTIONS, *.s and other non-object files to use the same
in-place move pattern as we use for object files as of the preceding
commit.

Unlike the change in the preceding commit, this one isn't isn't needed
for AIX portability. I think it makes sense to do this for
consistency, we'll now use the same pattern for object and non-object
generation.

It'll also guard against any weird issues with e.g. a command that
wants to read GIT-PERL-DEFINES to generate one of the %.perl scripts
racing with either a concurrent instance of "make" that has partially
updated the file, or test-lib.sh dying with some particularly weird
error because GIT-BUILD-OPTIONS was partway through being updated when
it ran.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Makefile | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)

Comments

Junio C Hamano March 29, 2021, 6:40 p.m. UTC | #1
Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> It'll also guard against any weird issues with e.g. a command that
> wants to read GIT-PERL-DEFINES to generate one of the %.perl scripts
> racing with either a concurrent instance of "make" that has partially
> updated the file, or test-lib.sh dying with some particularly weird
> error because GIT-BUILD-OPTIONS was partway through being updated when
> it ran.

If something like that happens, doesn't that indicate a bug in the
dependency graph in the Makefile or the implementation of "make"?
The generated file is depended on for the consumer to be able to use
a non-stale version---so the consumer should not start before the
generator finishes.

You may be able to hide the breakage coming from "partially written
file is easily recognizable and the consumer would barf".  But I am
afraid that you are introducing a problem harder to diagnose, i.e.
"the consumer reads from a complete file so there is no syntax error
or other things that easily tells you there is a breakage, but what
is used is stale and not up to date".

The same comment applies to this step.  I do not think it would
break (other than adding intermediate crufts) the result if you
generate into temporary and rename to final, but it is not clear
to me what the point is in doing so.
Ævar Arnfjörð Bjarmason March 29, 2021, 11:28 p.m. UTC | #2
On Mon, Mar 29 2021, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> It'll also guard against any weird issues with e.g. a command that
>> wants to read GIT-PERL-DEFINES to generate one of the %.perl scripts
>> racing with either a concurrent instance of "make" that has partially
>> updated the file, or test-lib.sh dying with some particularly weird
>> error because GIT-BUILD-OPTIONS was partway through being updated when
>> it ran.
>
> If something like that happens, doesn't that indicate a bug in the
> dependency graph in the Makefile or the implementation of "make"?
> The generated file is depended on for the consumer to be able to use
> a non-stale version---so the consumer should not start before the
> generator finishes.

If everything we were building in the Makefile would be invoked via
other Makefile rules, yes. But if I run say (cd t && ./t0000-basic.sh)
I'm having test-lib.sh pick up one of those (possibly partial) files,
this guarantees they'll all be atomically updated.

> You may be able to hide the breakage coming from "partially written
> file is easily recognizable and the consumer would barf".  But I am
> afraid that you are introducing a problem harder to diagnose, i.e.
> "the consumer reads from a complete file so there is no syntax error
> or other things that easily tells you there is a breakage, but what
> is used is stale and not up to date".
>
> The same comment applies to this step.  I do not think it would
> break (other than adding intermediate crufts) the result if you
> generate into temporary and rename to final, but it is not clear
> to me what the point is in doing so.

I just think it makes sense to do this for consistency. So on "master"
we've got 65 hits for $@+ in the Makefile, at the end of this saga we've
got 100.

I think being consistent across the board makes things easier to read,
and in combination with the later ".DELETE_ON_ERROR" we can also drop
other copy/paste cruft.
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index b0dbf5888b7..ef2d4a9973b 100644
--- a/Makefile
+++ b/Makefile
@@ -2246,7 +2246,8 @@  git.res: git.rc GIT-VERSION-FILE GIT-PREFIX
 	$(QUIET_RC)$(RC) \
 	  $(join -DMAJOR= -DMINOR= -DMICRO= -DPATCHLEVEL=, $(wordlist 1, 4, \
 	    $(shell echo $(GIT_VERSION) 0 0 0 0 | tr '.a-zA-Z-' ' '))) \
-	  -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" -i $< -o $@
+	  -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" -i $< -o $@+ && \
+	mv $@+ $@
 
 # This makes sure we depend on the NO_PERL setting itself.
 $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
@@ -2293,7 +2294,8 @@  GIT-PERL-DEFINES: FORCE
 	@FLAGS='$(PERL_DEFINES)'; \
 	    if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \
 		echo >&2 "    * new perl-specific parameters"; \
-		echo "$$FLAGS" >$@; \
+		echo "$$FLAGS" >$@+; \
+		mv $@+ $@; \
 	    fi
 
 GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile
@@ -2455,7 +2457,8 @@  $(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)
 	$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
 
 %.s: %.c GIT-CFLAGS FORCE
-	$(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
+	$(QUIET_CC)$(CC) -o $@+ -S $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $< && \
+	mv $@+ $@
 
 ifdef USE_COMPUTED_HEADER_DEPENDENCIES
 # Take advantage of gcc's on-the-fly dependency generation
@@ -2478,9 +2481,8 @@  endif
 ifeq ($(GENERATE_COMPILATION_DATABASE),yes)
 all:: compile_commands.json
 compile_commands.json:
-	@$(RM) $@
-	$(QUIET_GEN)sed -e '1s/^/[/' -e '$$s/,$$/]/' $(compdb_dir)/*.o.json > $@+
-	@if test -s $@+; then mv $@+ $@; else $(RM) $@+; fi
+	$(QUIET_GEN)sed -e '1s/^/[/' -e '$$s/,$$/]/' $(compdb_dir)/*.o.json >$@+ && \
+	mv $@+ $@
 endif
 
 exec-cmd.sp exec-cmd.s exec-cmd.o: GIT-PREFIX
@@ -2673,11 +2675,13 @@  perl/build/lib/%.pm: perl/%.pm
 	$(QUIET_GEN)mkdir -p $(dir $@) && \
 	sed -e 's|@@LOCALEDIR@@|$(perl_localedir_SQ)|g' \
 	    -e 's|@@NO_PERL_CPAN_FALLBACKS@@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
-	< $< > $@
+	< $< >$@+ && \
+	mv $@+ $@
 
 perl/build/man/man3/Git.3pm: perl/Git.pm
 	$(QUIET_GEN)mkdir -p $(dir $@) && \
-	pod2man $< $@
+	pod2man $< $@+ && \
+	mv $@+ $@
 
 FIND_SOURCE_FILES = ( \
 	git ls-files \
@@ -2805,7 +2809,8 @@  GIT-PYTHON-VARS: FORCE
 	@VARS='$(TRACK_PYTHON)'; \
 	    if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
 		echo >&2 "    * new Python interpreter location"; \
-		echo "$$VARS" >$@; \
+		echo "$$VARS" >$@+; \
+		mv $@+ $@; \
             fi
 endif
 
@@ -2817,8 +2822,9 @@  bin-wrappers/%: wrap-for-bin.sh
 	@mkdir -p bin-wrappers
 	$(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 	     -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
-	     -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%$(X),$(@F))$(patsubst git%,$(X),$(filter $(@F),$(BINDIR_PROGRAMS_NEED_X)))|' < $< > $@ && \
-	chmod +x $@
+	     -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%$(X),$(@F))$(patsubst git%,$(X),$(filter $(@F),$(BINDIR_PROGRAMS_NEED_X)))|' < $< >$@+ && \
+	chmod +x $@+ && \
+	mv $@+ $@
 
 # GNU make supports exporting all variables by "export" without parameters.
 # However, the environment gets quite big, and some programs have problems
@@ -2866,8 +2872,9 @@  HCO = $(patsubst %.h,%.hco,$(CHK_HDRS))
 HCC = $(HCO:hco=hcc)
 
 %.hcc: %.h
-	@echo '#include "git-compat-util.h"' >$@
-	@echo '#include "$<"' >>$@
+	@echo '#include "git-compat-util.h"' >$@+ && \
+	@echo '#include "$<"' >>$@+ && \
+	mv $@+ $@
 
 $(HCO): %.hco: %.hcc FORCE
 	$(QUIET_HDR)$(CC) $(ALL_CFLAGS) -o /dev/null -c -xc $<