If comparison in make file - makefile

I have a makefile. I need to check the exit status of a command and the performing comparison, if exit status is 0 then perform some display action. But i am getting the same message if its success or failure for both the scenario.
Please find the below code and Help me what is the right way to do this:-
FILES = test1.sh test2.sh
manoj: $(FILES)
ls $(FILES)
$(eval exitstatus="$(shell echo $$?)")
#echo $(exitstatus)
ifeq (0,$(exitstatus))
$(error something going wrong..........)
endif
clean: pwd
Getting same output as :
testmake.mk:4: *** something going wrong........... Stop.
for both ifeq ifeq (0,$(exitstatus)) and ifneq ifeq (0,$(exitstatus))
I want to perform some action if the condition is success otherwise nothing want to do.

In the recipe shell commands are used, not make directives.
And when target's recipe is executed that means all its prerequisites do exist:
manoj: $(FILES)
#echo "$(FILES) do exist at this point."

Related

Makefile variable value not available during ifeq

I have the following Makefile
SHELL=/bin/bash
.SHELLFLAGS=-O extglob -o errexit -o pipefail -o nounset -c
.PHONY: testing
define getFileContents
$(shell cat ./test.txt)
endef
TEST_STATIC=dummy
deploy:
$(eval TEST=$(getFileContents))
#echo "$(TEST)"
ifeq ($(TEST),dummy)
#echo "$(TEST) is FAILED"
else
#echo "$(TEST) is PASS"
endif
ifneq (,$(findstring dummy,$(TEST)))
#echo "$(TEST) is FAILED"
else
#echo "$(TEST) is PASS"
endif
ifeq ($(TEST_STATIC),dummy)
#echo "$(TEST) is FAILED"
else
#echo "$(TEST) is PASS"
endif
ifneq (,$(findstring dummy,$(TEST_STATIC)))
#echo "$(TEST) is FAILED"
else
#echo "$(TEST) is PASS"
endif
No matter what value I put in ./test.txt, I always go into PASS in both the ifeq & the findstring conditions but the variable's values show up properly in the echo statements. So the value is not available during the evaluation of ifeq
However, the if-else behaves properly for the TEST_STATIC variable.
Any help would be appreciated. Thanks.
ifeq is parsed while the makefile is read in. Even if it looks like it's part of a recipe, it isn't. You can tell that it isn't, because it isn't indented with a TAB character. Anything not indented by TAB, is part of the makefile not part of the recipe, and is parsed when the makefile is read in, not when the rule is run.
So by the time your rule is running and it gets to your eval, all the ifeq statements have long been expanded and dealt with.
In general, it's virtually never a good idea to use eval inside a recipe. It will almost never do what you're hoping for.
If you need to test some value inside a recipe then you have to write shell code to do it, not makefile code, and indent the shell code with a TAB so it's passed to the shell.

Stop executing makefile

I implement a recipe in order to pass all the remaining string to the command, as example in this script:
Makefile
run:
# ./bin/run.sh $(filter-out $#,$(MAKECMDGOALS))
#echo $(filter-out $#,$(MAKECMDGOALS))
But when I run as example:
>make run my custom input params
my custom input params
make: *** No rule to make target `my'. Stop.
makefile try to execute also the remaining string so the error:
make: *** No rule to make target `my'. Stop.
How can I prevent this?
NB: As workaround I define a dummy recipe:
%:
#echo
So this will print an empty string instead of the error.
I want to avoid to do something like:
make run-example param="my custom param"
You can probably achieve what you want with a match-anything rule. Example (using a dummy printf recipe instead of a real one):
PARAMS := $(filter-out run,$(MAKECMDGOALS))
run:
#printf './bin/run.sh $(PARAMS)\n'
%:;
Demo:
$ make run my custom input params
./bin/run.sh my custom input params
make: 'my' is up to date.
make: 'custom' is up to date.
make: 'input' is up to date.
make: 'params' is up to date.
You can ignore the make: 'target' is up to date. messages or use the --quiet option (or --silent or -s):
$ make --quiet run my custom input params
./bin/run.sh my custom input params
If your Makefile is more complex than this, the match-anything rule could be a problem because it could catch other targets that you do not want to be caught. In this case make conditionals are an option:
ifeq ($(SECONDPASS),)
PARAMS := $(filter-out run,$(MAKECMDGOALS))
run:
#$(MAKE) --quiet $# PARAMS='$(PARAMS)' SECONDPASS=true
%:;
else
run:
#printf './bin/run.sh $(PARAMS)\n'
# other rules if any
endif
Finally, if the name of the first goal is not always the same, you can adapt this with:
GOAL := $(firstword $(MAKECMDGOALS))
PARAMS := $(filter-out $(GOAL),$(MAKECMDGOALS))
$(GOAL):
#printf './bin/$(GOAL).sh $(PARAMS)\n'
%:;
Or:
GOAL := $(firstword $(MAKECMDGOALS))
ifeq ($(SECONDPASS),)
PARAMS := $(filter-out $(GOAL),$(MAKECMDGOALS))
$(GOAL):
#$(MAKE) --quiet $# PARAMS='$(PARAMS)' SECONDPASS=true
%:;
else
$(GOAL):
#printf './bin/$(GOAL).sh $(PARAMS)\n'
# other rules if any
endif
Demo:
$ make --quiet nur foo bar
./bin/nur.sh foo bar
I don't think you should use a Makefile. You want to do your own parsing of the options, and that's more trouble to do in make.
If you're dead set on it, you could do this:
%:
#true
...which will avoid printing an empty line.
It would be better to do this in Bash, though. Here's one way you could do it:
#!/usr/bin/env bash
if [ $# -lt 1 ]; then
echo Not enough args
exit 1
fi
case "$1" in
"run")
shift
./bin/run.sh $#
;;
*)
echo "Command $1 not recognized"
exit 1
;;
esac
This seems easier and more extensible.
You can always pass / set ENV variables before executing make if you only want to pass variables to make or to a shell script.
MYPARAM1=123 MYPARAM2=456 make
OR using a subshell
(MYPARAM1=123; MYPARAM2=456; echo A=$MYPARAM1 B=$MYPARAM2; make)
(export MYPARAM1=123; export MYPARAM2=456; A=$MYPARAM1 B=$MYPARAM2; make) // exporting
You might also look at the bash-shell-special-parameters

Change directory before running any targets

I have the following use case where I read a variant at the top of a Makefile and then according this variable I must change directory to execute all the targets. I'd avoid to repeat in each target something like
my_target:
cd $(MY_DIR) &...
Any approach I could use to achieve that?
It's not 100% clear to me what you need but something like the following should work
ifndef submake
export submake=1
variant := somedir
$(MAKECMDGOALS):
$(MAKE) -C $(variant) -f $(realpath $(MAKEFILE_LIST)) $#
else
#actual targets defined here
foo:
#echo $#
bar:
#echo $#
endif
Go to the directory (cd) where you want Make to operate, and then
make -f /path/to/Makefile

Unexpected token in ifeq makefile

I'm picking up some old code that is out of my norm and running into some issues over something that is hopefully very simple to explain.
I'm working on a makefile that runs a number of SQL files then generates .done files to track the progress. After one specific SQL file, I need to be able to run a shell script to do some additional processing.
# this is the rule the describes how to execute a SQL script
%.done: %.sql
#echo "+==============================================================================+"
#echo "building $#"
#echo "...from $<"
#echo "building $<"
#$(PSQL_WRAPPER) -f $<
#ifeq ($(#),mysqlfile.done)
#echo "Executing Shell Script"
#../path/to/script/myscript.sh
endif
I added the ifeq portion, everything else was there before and works as expected.
Here's the output I'm getting. I'm really stuck and have been trying different little syntax tweaks, but there's clearly something else I'm just not understanding.
+==============================================================================+
building mysqlfile.done
...from ../..//path/to/file/mysqlfile.sql
building ../..//path/to/file/mysqlfile.sql
/bin/bash: -c: line 0: syntax error near unexpected token `mysqlfile.done,mysqlfile.done'
/bin/bash: -c: line 0: `ifeq (mysqlfile.done,mysqlfile.done)'
make: *** [mysqlfile.done] Error 1
Command exited with non-zero status 2
From the error message, it looks like I have two equal values. I'm really not sure what the unexpected token would be.
ifeq is a GNU Make directive,
for conditionally including or excluding a part of a parsed makefile. It is not a shell command.
You are using it in one of the lines of the %.done: %.sql recipe.
All lines of a recipe must be shell commands. Just use shell:
%.done: %.sql
#echo "+==============================================================================+"
#echo "building $#"
#echo "...from $<"
#echo "building $<"
#$(PSQL_WRAPPER) -f $<
#if [ "$#" = "mysqlfile.done" ]; then \
echo "Executing Shell Script"; \
../path/to/script/myscript.sh; \
fi
You cannot conditionally exclude the special-case processing from the recipe itself,
like:
%.done: %.sql
#echo "+==============================================================================+"
#echo "building $#"
#echo "...from $<"
#echo "building $<"
#$(PSQL_WRAPPER) -f $<
ifeq ($(#),mysqlfile.done)
#echo "Executing Shell Script"
#../path/to/script/myscript.sh
endif
because the target $(#) of the rule is only defined within the recipe.

Makefile ifeq: when are they evaluated?

The following is a very simple makefile that does not seem to work properly.
TEST=ON
buildbegin:
ifeq ($(TEST),ON)
#echo TEST PASSED
else
#echo TEST FAILED
endif
No matter what I set the TEST variable to, my ifeq statement passes. I always see TEST PASSED. Anyone see what I am doing wrong here?
EDIT:
ok. my example was not exactly accurate. What I actually have is this:
SHELL = /bin/sh
DEFAULT_TARGS:= all all_debug
DEBUG_TARGS:= all_debug
ALL_TARGS:= $(DEFAULT_TARGS) $(DEBUG_TARGS)
.PHONY: $(ALL_TARGS)
.PHONY: buildbegin
$(ALL_TARGS): buildbegin
TEST=ON
$(DEBUG_TARGS): TEST=OFF
buildbegin:
#echo $(TEST)
ifeq ($(TEST),ON)
#echo PASSED
else
#echo FAILED
endif
Running either make all or make all_debug will result in "PASSED" being printed. If I echo $(TEST) before the condition, it looks as if my rules are changing the variable, but the ifeq only ever sees whatever the default value is.
make evaluates conditionals when it reads a makefile (as you know it uses 2 passes), see: Conditional Parts of Makefiles. You can simply check this by using warning (which is good thing to debug makefiles):
buildbegin:
#echo $(TEST)
$(warning now we reached ifeq TEST=$(TEST))
ifeq ($(TEST),ON)
#echo PASSED
else
#echo FAILED
endif
You should use shell commands instead and include them in rule:
buildbegin:
#if [ "$(TEST)" = "ON" ]; then echo "PASSED"; else echo "FAILED"; fi
Here's a cleaner (I think, anyway) way to do what you want:
all:
$(MAKE) TEST=ON buildbegin
all_debug:
$(MAKE) TEST=OFF buildbegin
buildbegin:
#echo $(TEST)
ifeq ($(TEST),ON)
#echo PASSED
else
#echo FAILED
endif
Parameterise the shell command using make variables. This avoids recursive make and even the shell (make will fork the command directly and not go via a shell if the command contains no shell metacharacters (<>"&; and the like)).
Something like:
result<ON> := PASSED
result<OFF> := FAILED
buildbegin:
#echo ${result-<${TEST}>}
The <...> is simply a convention to indicate some sort of indirection.
Although the question is old, I want to share a simpler way.
It is to use MAKECMDGOALSvariable that represents the list of targets.
In your case, a solution maybe:
buildbegin:
#echo $(MAKECMDGOALS)
ifeq ($(MAKECMDGOALS),all)
#echo PASSED
else ifeq ($(MAKECMDGOALS),debug_all)
#echo FAILED
endif
debug_all: buildbegin
all: buildbegin
Usage: make debug_all or make all

Resources