[comp.unix.programmer] Problems with tests in make

6sigma2@polari.UUCP (Brian Matthews) (06/07/91)

I'm trying to write some make rules that use test to conditionally
execute different shell commands.  For instance, if a certain
make variable has one value, I want to do one thing, otherwise something
else.

The obvious solution is to do something like this:

	if $(VAR) = val1; then command1; else command2; fi

If VAR happens to contain val1, or I'm using a System V-like make,
everything works fine.  However, if VAR doesn't contain val1, BSD-like
makes seem to bail out as soon as the test fails - the else command2
isn't executed, and the make fails.

For a concrete example, put:
SHELL=/bin/sh

a:	b c
	@echo a made

b:
	@if test x = x; then echo b made;fi

c:
	@if test x = y; then echo c not made;else echo c made;fi

In a file called Makefile and do a make.  I would expect to see:
b made
c made
a made
but when using BSD-like makes (on BSD 4.3 or Ultrix for example) make
fails with exit code 1 after outputting b made.

I've tried all sort of different things, including prefixing the if
with set +e (thinking the shell was bailing out when test exited with
a 1), running in a subshell and doing an exit 0, etc.  I can put a
- before the @if, but I'd rather not, because I would like the make
to stop if the command executed as a consequence of the test result
fails.

Any ideas?
-- 
Brian L. Matthews
blm@6sceng.UUCP

torek@elf.ee.lbl.gov (Chris Torek) (06/07/91)

In article <4378@polari.UUCP> 6sigma2@polari.UUCP (Brian Matthews) writes:
>I'm trying to write some make rules that use test to conditionally
>execute different shell commands. ... BSD-like makes seem to bail out
>as soon as the test fails ...
>c:
>	@if test x = y; then echo c not made;else echo c made;fi
>... make fails with exit code 1 after outputting b made.
>
>I've tried all sort of different things, including prefixing the if
>with set +e (thinking the shell was bailing out when test exited with
>a 1), running in a subshell and doing an exit 0, etc.

You were on the right track.  The problem is a bug in the BSD Bourne
shell; under -e, it improperly exits for *all* nonzero subprocess
exits, rather than only those which are tested.

The bug is partially fixed in 4.3BSD-tahoe: I fixed the

	if foo; then bar; else baz; fi

case but forgot to hit up the

	foo && bar || baz

cases (I got thoroughly sick of working on sh in just the few minutes it
took to fix if, while, and until: Bourne's pseudo-Algol is a nightmare).

The reason `set +e' did not help is that the BSD Bourne shell was
so old that it did not have `set +' for turning off options.  Once
-e is set in such a shell, you cannot get rid of it.

>I can put a - before the @if, but I'd rather not, because I would
>like the make to stop if the command executed as a consequence of
>the test result fails.

Unfortunately, unless you have source, you are stuck with that.

I had a version of the BSD Bourne shell which I had deBourned and then
fixed this and a number of other bugs, but it has fallen by the
wayside; BSD now uses a Bourne-shell clone based on Kenneth Almquist's
`ash' as /bin/sh.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

clewis@ferret.ocunix.on.ca (Chris Lewis) (06/08/91)

In article <4378@polari.UUCP> 6sigma2@polari.UUCP (Brian Matthews) writes:

Hi Brian, long time no see.

>I'm trying to write some make rules that use test to conditionally
>execute different shell commands.

>The obvious solution is to do something like this:

>	if $(VAR) = val1; then command1; else command2; fi

>If VAR happens to contain val1, or I'm using a System V-like make,
>everything works fine.  However, if VAR doesn't contain val1, BSD-like
>makes seem to bail out as soon as the test fails - the else command2
>isn't executed, and the make fails.

This is a bug in the BSD shell.  To-wit, make starts the shell script
with "-e" set, and the test's failure causes the shell script
to terminate immediately rather than processing thru the else clause.
You can test this yourself with the following shell script:

	set -e
	if test x = y
	then
	    echo wha?
	else
	    echo "worked properly"
	fi

On non-SYSV shells this won't print anything.  I think this botch was
even in V7.

What I usually do is this:

	#	comment out on System V.
	IGNORESH = set +e ;

	all:
		$(IGNORESH) if .... \
		    then
		    ...

Unfortunately, you have to check the returns of each subcommand
individually (as you would do in a normal script), and on error execute
"exit 1".

Psroff has this in its makefiles, and even a way of testing whether you
need it commented out or not.

BTW: go multi line on your shell scripts, it makes them a lot easier to
read even if you have to put backslashes on the ends of all of the lines...

If it wasn't for this stupid botch in BSD, set -e and trap 0's would make error
recovery in shell scripts easy and portable...  Grrr!
-- 
Chris Lewis, Phone: (613) 832-0541, Domain: clewis@ferret.ocunix.on.ca
UUCP: ...!cunews!latour!ecicrl!clewis; Ferret Mailing List:
ferret-request@eci386; Psroff (not Adobe Transcript) enquiries:
psroff-request@eci386 or Canada 416-832-0541.  Psroff 3.0 in c.s.u soon!