From owner-freebsd-hackers Mon May 28 3:18:37 2001 Delivered-To: freebsd-hackers@freebsd.org Received: from mail.rpi.edu (mail.rpi.edu [128.113.22.40]) by hub.freebsd.org (Postfix) with ESMTP id 1686237B42C for ; Mon, 28 May 2001 03:18:31 -0700 (PDT) (envelope-from drosih@rpi.edu) Received: from [128.113.24.47] (gilead.acs.rpi.edu [128.113.24.47]) by mail.rpi.edu (8.11.3/8.11.3) with ESMTP id f4SAIUH83008 for ; Mon, 28 May 2001 06:18:30 -0400 Mime-Version: 1.0 X-Sender: drosih@mail.rpi.edu Message-Id: Date: Mon, 28 May 2001 06:00:38 -0400 To: hackers@FreeBSD.org From: Garance A Drosihn Subject: 'make clean' vs automake vs /bin/sh, which to fix? Content-Type: text/plain; charset="us-ascii" ; format="flowed" Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG I went to do a 'make clean' on a project of mine, and it failed with '*** Error 1'. There is no message about what command has failed, 'make' just exits sideways. The rule for 'make clean' is generated by 'automake', and in looking around (a little...) I did find some freebsd ports which USE_AUTOMAKE and which also have this same problem with 'make clean', and a few other targets which are generated by automake. I now have a pretty good idea why this happens, but I'm not completely sure what the "most-strictly-correct" fix would be. Here is the situation: For a target called 'clean-recursive', automake wants to execute the following commands: + + + + + @set fnord $(MAKEFLAGS); amf=$$2; \ dot_seen=no; \ rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ rev="$$subdir $$rev"; \ test "$$subdir" = "." && dot_seen=yes; \ done; \ test "$$dot_seen" = "no" && rev=". $$rev"; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ done && test -z "$$fail" + + + + + Due to all the '\'s there, that entire section is meant to be executed as a single command by 'make'. The problem comes up in that first 'for ... ; do ... ; done' loop. That loop will successfully loop over however many subdirectories are in the 'list', but the error will occur (and abort the 'make') when the loop is done. Whatever command is after that 'done' line will never get executed. If you add the command 'true;' BEFORE the 'done' line, then everything will work fine. In the make/compat.c file, make sees the meta-characters in this long command, and decides that it wants to exec: /bin/sh -ec '......' The '-e' flag indicates that the shell should exit if it hits any error while executing the command. If we then look at src/bin/sh/eval.c, we see how 'make' tries to handle the 'eflag' setting. It is smart enough to know that: cmd1 && cmd2 should not trigger the error exit when 'cmd1' is false. That is exactly what is happening in the 'for' loop. The check for test "$$subdir" = "." && dot_seen=yes is always failing, and never does set 'dot_seen=yes'. So far so good. The problem is that error status is still sitting there, and as soon as the 'for' loop is done, *it* thinks some error has occurred so *it* triggers the error exit. If I change the processing of the evaltree routine like so: case NFOR: evalfor(n); if (exitstatus != 0) { flags |= EV_TESTED; } break; (I add that 'if' statement) then EV_TESTED indicates to the later eflag processing that this exit-status value is not a reason to abort. With that change in place, 'make clean' will work. This proves my basic analysis is right, but I am not sure if the specific fix is right. This problem could also be fixed by changing 'make' so it does NOT include '-e' when executing the multi-line command. It may also be that I'm turning on "EV_TESTED" at the wrong place, or maybe it should also be turned on in some other cases in the same routine. I looked in the PR database, but I didn't seem to find any report of this particular issue. It seems to me that if 'make' is executing a single command line which is made up of multiple unix commands, it should only care about the FINAL command status, and not the status of each of the interim commands. That is just my gut feeling though, I can't tell if the documentation really spells out what is to happen in this case. So, we could fix this by: 1) changing /bin/sh 2) changing make not to call /bin/sh with -e 3) changing 'automake' to include a "true;" statement in that 'for' loop (or some other trick) when spitting out the target for things like clean-recursive Me, I'm inclined to go with the some fix to the evaltree routine in /bin/sh, such as the one listed above. I could commit that fix if people think it's right. While I do wonder about 'make' calling '/bin/sh -e', I am less comfortable with committing that change myself, as there probably are makefiles which do assume the e-style behavior when handling multiple commands in a command-line. Perhaps we should update the documentation for 'make' to explicitly mention how 'make' handles such command-lines. Changing what 'automake' spits out is the least disruptive fix, but it also strikes me as the least desirable. That does not really solve anything, it just makes it less likely to run into. -- Garance Alistair Drosehn = gad@eclipse.acs.rpi.edu Senior Systems Programmer or gad@freebsd.org Rensselaer Polytechnic Institute or drosih@rpi.edu To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message