0

How can I terminate Makefile with an error if shell command output an $$? -ne 0 ?

HELPER := $(shell dirname $(abspath $(lastword 
VERSION ?= $(shell . $(HELPER); getVersion) ||  (echo "getVersion failed '$$?' status"; exit 1)

test:
        @echo "What is going on?$(VERSION) ?"

helper.sh:

#!/bin/bash
printError() {
    echo "[MAKE] $1" 2>/dev/stderr
}
getVersion() {
    version=$(git describe --exact-match --tags $(git log -n1 --pretty='%h') 2>/dev/null)
    if [ $? -ne 0 ]; then
        printError "Getting git tag for version failed. Checkout existing tag or"
        printError "provide your own version 'make <action> VERSION=<your-version>'"
        exit 1
    fi
    echo "${version}"
    exit 0
}

output:

$ make test
What is going on?[MAKE] Getting git tag for version failed. Checkout existing tag or [MAKE] provide your own version 'make <action> VERSION=<your-version>' ||  (echo getVersion failed $? status; exit 1) ?

I cannot terminate it on error ( getVersion returns 1 ) but also error messages that are piped to /dev/stderr are getting to VERSION variable

sebastian_t
  • 2,241
  • 6
  • 23
  • 39
  • Makefile questions should be tagged make, not shell. Make syntax is not shell syntax -- it's used to *generate* shell commands, but it is itself its own very different thing. – Charles Duffy Jun 21 '18 at 01:32
  • See also https://stackoverflow.com/a/59392005/491884, you can use a hack (kill parent make process with `|| kill $$PPID`. – jmuc Dec 18 '19 at 12:26

1 Answers1

1

First, answers to extra questions. This line is clearly wrong; I assume you just had a cut/paste error:

HELPER := $(shell dirname $(abspath $(lastword 

I don't know why you're breaking out to the shell here and running its dirname but whatever.

Second, this from your helper script:

echo "[MAKE] $1" 2>/dev/stderr

does not do at all what you want: it says "take the output going to file descriptor 2 (stderr) and pipe it to /dev/stderr. Since echo doesn't print anything to stderr this is basically a no-op. You appear to want to redirect stdout (file descriptor 1) to stderr; that would be this:

echo "[MAKE] $1" >/dev/stderr

although the more correct and portable way to write this is:

echo "[MAKE] $1" 1>&2

which sets fd1 to write to wherever fd2 is writing.

Now on to your real question: make has no concept of shell operators like || or &&, so here:

VERSION ?= $(shell . $(HELPER); getVersion) ||  (echo "getVersion failed '$$?' status"; exit 1)

the || etc. is simply treated as text; it sets VERSION to the string:

 <whatever `shell`prints> ||  (echo "getVersion failed '$$?' status"; exit 1)

which is exactly what you see.

If you have a new-enough version of GNU make (4.2 or above) you can use the .SHELLSTATUS variable to find out the exit status of the last shell. Then you could do something like this:

.SHELLSTATUS = 0
VERSION ?= $(shell . $(HELPER); getVersion)
$(if $(filter-out 0,$(.SHELLSTATUS)),$(error getVersion failed '$(.SHELLSTATUS)' status))

(you have to set it first because you're using ?= here so sometimes no shell function will be invoked).

If you need to be portable to earlier versions of GNU make, or if you just prefer to use something a bit more clear, you can check the value of VARIABLE. If you change your script so it writes errors to stderr and writes nothing to stdout on failure you can use:

VERSION ?= $(shell . $(HELPER); getVersion)
$(if $(VERSION),,$(error getVersion failed))
MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • I've redirected my printError to 1>&2 but now VERSION variable contains error message. Every time I use $(VERSION) which I suppose to be empty I get error message printed out to my terminal. – sebastian_t Jun 22 '18 at 05:17
  • You must have done something else wrong or different. I ran this: `echo 'VER := $(shell echo hi 1>&2) | make -f-` and this printed `hi` which it would not have done if the output was captured in the `VER` variable. – MadScientist Jun 22 '18 at 15:25