Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 28 May 2001 06:00:38 -0400
From:      Garance A Drosihn <drosih@rpi.edu>
To:        hackers@FreeBSD.org
Subject:   'make clean' vs automake vs /bin/sh, which to fix?
Message-ID:  <p05100e16b737c14ab4e6@[128.113.24.47]>

next in thread | raw e-mail | index | archive | help
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 '...<all of it>...'

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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?p05100e16b737c14ab4e6>