How To test the exit status, and do something in Makefile - makefile

I'm trying to do this ..... and this is how my Makefile look like
.PHONY: run
SHELL := /bin/tcsh
run:
md5sum -c md; \
if ($$?==0) then \
echo "PASS" \
else \
echo "FAIL" \
endif
But i got this error.
if: Badly formed number.
make: *** [run] Error 1
Is what I'm doing correct? Or is there a better way of doing that in a Makefile?

Basically, you should simply not, ever, use csh (or tcsh) for writing makefile rules. Write the rule using POSIX shell:
.PHONY: run
run:
md5sum -c md; \
if [ $$? -eq 0 ]; then \
echo "PASS"; \
else \
echo "FAIL"; \
fi

Related

Behavior of parenthesis in the shell invoked from make

I'm building a Linux from scratch on my old Android phone in a chrooted environment (which can be part of the problem), on a mounted ext4 partition on the sd card. I managed to cross compile install most of the stuff I need to build locally there. I installed the latest release of everything.
And now time to make something in place without cross compilation.
I decided to make and install inetutils-1.9.4.
The configure runs just fine, but when I try to make, it just fails without saying a thing:
# make
make all-recursive
make[1]: Entering directory '/tmp/inetutils_build'
Making all in lib
make[1]: *** [Makefile:1491: all-recursive] Error 1
make[1]: Leaving directory '/tmp/inetutils_build'
make: *** [Makefile:1428: all] Error 2
So I looked at the generated makefile and it contains this rule:
$(am__recursive_targets):
#fail=; \
if $(am__make_keepgoing); then \
failcom='fail=yes'; \
else \
failcom='exit 1'; \
fi; \
dot_seen=no; \
target=`echo $# | sed s/-recursive//`; \
case "$#" in \
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
*) list='$(SUBDIRS)' ;; \
esac; \
for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| eval $$failcom; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
After inserting some echos I have found that the line with the $(am__cd)... fails, and the ored expression runs which exits the script with failure. So I attempted to find out which command fails there, and inserted some echos within the parenthesis, like this:
(echo "1" && $(am__cd) $$subdir && echo "2" && $(MAKE) $(AM_MAKEFLAGS) $$local_target && echo "3") \
|| eval $$failcom; \
Now it prints:
make all-recursive
make[1]: Entering directory '/tmp/inetutils_build'
Making all in lib
1
make[1]: *** [Makefile:1491: all-recursive] Error 1
make[1]: Leaving directory '/tmp/inetutils_build'
make: *** [Makefile:1428: all] Error 2
However I wanted to make sure the echo doesn't fail, so I added another one.
(echo "1" && echo "1b" && $(am__cd) $$subdir && echo "2" && $(MAKE) $(AM_MAKEFLAGS) $$local_target && echo "3") \
|| eval $$failcom; \
When I tried it. It again prints just 1.
I didn't give it up just yet.
I wanted to make sure echo really fails within the script.
So I tried adding new statements into the script, before the problematic command:
(echo "A" && echo "B"); \
(echo "X" || echo "Y"); \
echo "N" && echo "M"; \
(echo "C" && echo "D"); \
It prints:
A
X
Y
N
M
C
D
Then it prints
1
1b
2
and starts building!
I'm totally puzzled here. Why does the first two parenthesized expressions fail, but not the last one?
In fact even writing (true) || echo "ooops" fails at first, but not when I paste it below the ored X Y parenthesis. Apparently the ored parenthesis "fixes" it, whatever state it got stuck on.
What is going on?
It should be noted I cannot reproduce this at the shell prompt but only the statements inserted into the makefile does this.
EDIT:
By removing the # from the beginning it echoes back the command but nothing else, no errors:
fail=; \
if (target_option=k; case ${target_option-} in ?) ;; *) echo "am__make_running_with_option: internal error: invalid" "target option '${target_option-}' specified" >&2; exit 1;; esac; has_opt=no; sane_makeflags=$MAKEFLAGS; if { if test -z '1'; then false; elif test -n 'arm-unknown-linux-gnueabi'; then true; elif test -n '4.2' && test -n '/tmp/inetutils_build'; then true; else false; fi; }; then sane_makeflags=$MFLAGS; else case $MAKEFLAGS in *\\[\ \ ]*) bs=\\; sane_makeflags=`printf '%s\n' "$MAKEFLAGS" | sed "s/$bs$bs[$bs $bs ]*//g"`;; esac; fi; skip_next=no; strip_trailopt () { flg=`printf '%s\n' "$flg" | sed "s/$1.*$//"`; }; for flg in $sane_makeflags; do test $skip_next = yes && { skip_next=no; continue; }; case $flg in *=*|--*) continue;; -*I) strip_trailopt 'I'; skip_next=yes;; -*I?*) strip_trailopt 'I';; -*O) strip_trailopt 'O'; skip_next=yes;; -*O?*) strip_trailopt 'O';; -*l) strip_trailopt 'l'; skip_next=yes;; -*l?*) strip_trailopt 'l';; -[dEDm]) skip_next=yes;; -[JT]) skip_next=yes;; esac; case $flg in *$target_option*) has_opt=yes; break;; esac; done; test $has_opt = yes); then \
failcom='fail=yes'; \
else \
failcom='exit 1'; \
fi; \
dot_seen=no; \
target=`echo all-recursive | sed s/-recursive//`; \
case "all-recursive" in \
distclean-* | maintainer-clean-*) list='lib libinetutils libtelnet libicmp libls src telnet telnetd ftp ftpd talk talkd whois ping ifconfig doc man tests' ;; \
*) list='lib libinetutils libtelnet libicmp libls src telnet telnetd ftp ftpd talk talkd whois ping ifconfig doc man tests' ;; \
esac; \
for subdir in $list; do \
echo "Making $target in $subdir"; \
if test "$subdir" = "."; then \
dot_seen=yes; \
local_target="$target-am"; \
else \
local_target="$target"; \
fi; \
(CDPATH="${ZSH_VERSION+.}:" && cd $subdir && make $local_target) \
|| eval $failcom; \
done; \
if test "$dot_seen" = "no"; then \
make "$target-am" || exit 1; \
fi; test -z "$fail"
EDIT 2:
Thanks for the tips, I added set -x to the beginning of the rule. These are the commands:
+ fail=
+ target_option=k
+ case ${target_option-} in
+ has_opt=no
+ sane_makeflags=w
+ test -z 1
+ test -n arm-unknown-linux-gnueabi
+ true
+ sane_makeflags=-w
+ skip_next=no
+ for flg in $sane_makeflags
+ test no = yes
+ case $flg in
+ case $flg in
+ test no = yes
+ failcom='exit 1'
+ dot_seen=no
++ echo all-recursive
++ sed s/-recursive//
+ target=all
+ case "all-recursive" in
+ list='lib libinetutils libtelnet libicmp libls src telnet telnetd ftp ftpd talk talkd whois ping ifconfig doc man tests'
+ for subdir in $list
+ echo 'Making all in lib'
Making all in lib
+ test lib = .
+ local_target=all
+ CDPATH=:
+ eval exit 1
++ exit 1
Did you put SHELL := /bin/bash at the top of your makefile? Otherwise, make(1) tends to invoke /bin/sh as the shell, which has very different properties.

How to use eval Makefile variable from recipe to conditionally execute some commands in recipe

Goal is to apply patch ONLY if patch is not present. If patch is present don't do anything.
I used below makefile rule.
C_FILE_PATCH_SIG=###MAGIC_CODE;
C_FILE_CODE=~/code/file.c
C_PATCH_FILE=~/test.patch
.tmp/patch_c:
cp ${C_PATCH_FILE} ${SDK}
ifneq ($(PATCH_DONE), 1)
$(MAKE) applypatch || $(MAKE) helppatch
endif
#echo DONE > .tmp/patch_c
applypatch:
#echo "Patching ${C_FILE_CODE}"
if grep -Fq '${C_FILE_PATCH_SIG}' ${C_FILE_CODE} ; \
then \
echo 1 > .tmp/PATCH_PRESENT_file; \
else \
echo 0 > .tmp/PATCH_PRESENT_file;\
fi
cat .tmp/PATCH_PRESENT_file
# $(eval PATCH_PRESENT := `cat .tmp/PATCH_PRESENT_file`)
$(eval PATCH_PRESENT := $(shell cat .tmp/PATCH_PRESENT_file))
#echo "WWWWWW PATCH_PRESENT=[$(PATCH_PRESENT)] WWWWWWW"
ifeq ($(PATCH_PRESENT), 0)
#echo "Applying the patch $(PATCH_PRESENT)"
cd ~/code && git apply -v ${C_PATCH_FILE}
else
#echo "NOT Applying the patch $(PATCH_PRESENT)"
endif
helppatch:
#echo -e "\n\n\n"
#echo -e "++++++++++ Apply below patch manually then run 'make build PATCH_DONE=1' ++++++++++\n\n"
#echo -e "+++++++++++++++++++++++++++++++++++++"
#cat ${C_PATCH_FILE}
#echo -e "+++++++++++++++++++++++++++++++++++++"
#echo -e "\n\n\n"
false
But it always evaluates to the else part of ifeq.
Where am I doing wrong?
If I use the patch command of git withing the shell multiline I loose the error code returned by the git patch.
Thanks in advance...
Your ifeq will be evaluated when the makefile is first read (as opposed to when the recipe is run). The eval, on the other hand, will not be executed until the recipe is run (afterwards). Thus, PATCH_PRESENT is not equal to 0 at parse time, and make will expand the else portion of the clause. By the time the eval is run, the if statement is already evaluated and gone from memory.
BTW, a cleaner way to do this is to do everything in bash:
applypatch:
#echo "Patching ${C_FILE_CODE}"
#if grep -Fq '${C_FILE_PATCH_SIG}' ${C_FILE_CODE}; then \
echo "NOT Applying the patch"; \
else \
echo "Applying the patch"; \
cd ~/code && git apply -v ${C_PATCH_FILE}; \
fi

Makefile: redefining variable in runtime using #if,eval: variable always takes value of last eval

So I'm trying to hack one of my makefiles to be simpler (simpler, as if, not defining a lot of rules how to transform subdirectory into .deb).
build-if-need-status-vars:
#if [ ! -f debs/1.deb ]; then \
$(eval STATUS_REBUILD=1) \
echo "component: file not found: 1"; exit;\
else \
if [ $(shell find sources/ -newer debs/1.deb 2>/dev/null | wc -l) -gt 0 ]; then \
$(eval STATUS_REBUILD=1) echo "component: newer files exists: 1"; exit;\
else \
$(eval STATUS_REBUILD=0) echo "component: no newer files: 0"; \
fi;\
fi
#echo "status $(STATUS_REBUILD)"
actual-target: build-if-need-status-vars
ifeq ($(STATUS_REBUILD), 1)
#echo first status: 1
else
#echo second status: 0
#echo different action
endif
all: actual-target
.PHONY: actual-target
Test with:
mkdir -p test/{sources,debs}; touch test/debs/1.deb; sleep 2; touch test/sources/1.src; (create makefile there and run)
Result:
component: file not found: 1
status 0
second status: 0
Regardless of what conditional block is executed, STATUS_REBUILD will always be 0 (last evaluated value), try it: touch test/debs/1.deb
So it seems that last $(eval) is always used.. How to avoid this behaviour and keep the correct assigned value (from first match in build-if-need-status-var)?
$(eval) is a make-level function. It is expanded in your recipe during recipe the recipe expansion stage.
The contents of a recipe are expanded in the second phase of makefile parsing (discussed briefly in the manual here).
I believe, but cannot say for sure (without testing), that recipes are not expanded until they are about to be run (but for the purposes here that doesn't change anything either way).
So your problem here is that all the $(eval) calls are expanded by the time make goes to run your shell script so you always see the last value in effect when the last line is run.
That all being said you don't actually need a make-level variable here. Your recipe is already only two shell executions.
You can simply include the last line in the same execution as the first (split) line and use a shell variable.
build-if-need-status-vars:
#if [ ! -f debs/1.deb ]; then \
STATUS_REBUILD=1; \
echo "component: file not found"; \
else \
if [ $(shell find sources/ -newer debs/1.deb 2>/dev/null | wc -l) -gt 0 ]; then \
STATUS_REBUILD=1; echo "component: newer files exists"; \
else \
STATUS_REBUILD=0; echo "component: no newer files"; \
fi;\
fi; \
echo "status $$STATUS_REBUILD"
Note that I needed to remove the exit pieces to make this work. If those are necessary in the real makefile (because this is a stripped down sample) then you can keep them by wrapping the if in a sub-shell and/or by rewriting the recipe.

How can I propagate exit status through a shell 'if' command in a Makefile?

%/all:
if [ -f $(#D)/src/Makefile ]; then \
$(MAKE) -C $(#D); \
fi
If the inner make fails, the outer make continues, presumably because the implicit exit status of the 'if' command is 0. Is there a way around this?
This cannot be a real example. The shell will exit with the result of the last command executed, which if the if-statement succeeds will be the exit code of make, which is what you want. So obviously in your real code, you must be doing some other command between the make and the end. You can keep a copy of the result in a variable and use that as the exit:
if [ -f $(#D)/src/Makefile ]; then \
$(MAKE) -C $(#D); \
r=$$?; \
...do other stuff...; \
exit $$r; \
fi
Somehow I couldn't reproduce your problem but I suppose the following should work for you :
%/all:
if [ -f $(#D)/src/Makefile ]; then \
$(MAKE) -C $(#D) || (echo "make failure: $$?"; exit 1) \
fi

makefile setting a variable in if statement in a target

I have a Makefile with the following:
AVAR=""
all :
if [ -d ../old ]; then \
(echo "$# Ping!"; AVAR="../old"; echo $(AVAR)) \
fi
#echo $(AVAR)
The idea is that depending on the presence of directory "../old" i will or not
have information in AVAR (available for use later), however, the if is evaluating to true, and the Ping! is echoed, but nothing is assigned to AVAR, either inside or outside the if statement.
Output is as follows:
$ make all
if [ -d ../old ]; then \
(echo "all Ping!"; AVAR="../old"; echo "") \
fi
all Ping!
Any Insight appreciated.
1) Each command in a makefile recipe runs in its own subshell. You define AVAR in the first command (the "if" statement), so it's not available to the second command (#echo $(AVAR)).
2) Within the first command, you define AVAR, but you don't call it right. The term $(AVAR) is Make syntax; Make expands it before executing the command, and since Make doesn't know anything about such a variable, it expands to nothing. You have to use the shell to expand it, using shell syntax: $AVAR. But if you try that, Make will expand the $A to nothing, and you'll get "VAR". So you "escape" the $ with another $:
all :
if [ -d ../old ]; then \
(echo "$# Ping!"; AVAR="../old"; echo $$AVAR) \
fi
Try
(echo "$# Ping!"; AVAR="../old"; echo $$AVAR) \
instead of
(echo "$# Ping!"; AVAR="../old"; echo $(AVAR)) \
That may be a bit late, but in case someone else has the same question.
The way I handle it is by evaluating the if in a shell function and extract the stdout to a variable that's usable in the next command.
Method 1: appending the echo to the AVAR setting
if [ -d ../old ]; then \
(echo "$# Ping!") \
fi
AVAR=$(shell if [ -d ../old ]; then \
(AVAR="../old"; echo $${AVAR}) \
fi;) && \
echo $${AVAR}
or Method 2: evaluating the variable so that it could be used throughout the target without appending the echo ${AVAR} to the same shell.
if [ -d ../old ]; then \
(echo "$# Ping!") \
fi
$(eval AVAR=$(shell if [ -d ../old ]; then \
(AVAR="../old"; echo $${AVAR}) \
fi;))
#echo ${AVAR}
instead of
if [ -d ../old ]; then \
(echo "$# Ping!"; AVAR="../old"; echo $(AVAR)) \
fi
#echo $(AVAR)
Notes:
- AVAR being set by the $(shell ... ) output is a different variable from the one inside the if condition
- If you want to use a single if, you'll have to remove the stdout of "$# ping" from the beginning of AVAR content
Problem solved, Did the very bad recursive make option to redefine stuff and reinterpret.

Resources