This snippet of a makefile shall stop the execution if the file myFile does not exist:
test:
if [ -e myFile ] ; then \
echo "Error Message"; \
fi;
If I replace the echo-statement by $(error: Error Message); \ the make file is being stopped in both cases. But I need the makefile to be stopped if the file exists.
$(error ) is interpreted by make itself, so if it is hit during reading of the file, the error is produced. If you need an error during execution of a recipe, you have to run a command that returns an error exit code. Most straight-forward would be false for that, e.g.
test:
if [ -e myFile ] ; then \
echo "Error Message"; false; \
fi;
Of course, you could check for the file using $(shell ), without a recipe:
ifeq ($(shell test -e myFile && echo yes),)
$(error Error Message)
endif
Related
My Makefile includes follow target
run: $(BIN_FILE)
if [ -d $(BIN_FILE) ]; then $(error Sorry BIN_FILE is directory); else ./$(BIN_FILE) $(RUN_ARGS); fi
But it raises the error no matter whether test passes or not. What is wrong? Why it raises an error even BIN_FILE is not directory? Have directive $(error... any special meaning?
Many thanks.
You can't embed a Make expression like that inside your shell script. Make performs all expansions of $(...) expressions before the shell even starts, so it sees your $(error ...) command and exits. You would need to emit this error using shell logic instead, doing something like:
run: $(BIN_FILE)
if [ -d $(BIN_FILE) ]; then \
echo "Sorry BIN_FILE is directory"; \
exit 1; \
fi
$(BIN_FILE) $(RUN_ARGS)
Or with slightly more compact logic:
run: $(BIN_FILE)
[ -d "$(BIN_FILE)" ] && { echo "$(BIN_FILE) is a directory"; exit 1; } ||:
$(BIN_FILE) $(RUN_ARGS)
I used this post as a basis for how to pass arguments to a make target.
I would like to perform a string comparison on this command line arg, using this post as inspiration for doing string equality comparisons in a makefile.
Update using the suggested answer below:
%:
#:
test:
if [ '$(filter-out $#,$(MAKECMDGOALS))' = hi ]; \
echo "WON"; \
else \
echo "LOST"; \
fi
where I do 8 spaces instead of indents, nothing prints. When I do tabs instead of spaces, I get the following errors:
if [ 'hi' = hi ]; \
echo "WON"; \
else \
echo "LOST"; \
fi
/bin/sh: -c: line 0: syntax error near unexpected token `else'
/bin/sh: -c: line 0: `if [ 'hi' = hi ]; echo "WON"; else echo "LOST"; fi'
make: *** [test] Error 2
Original attempt:
%:
#:
test:
ifeq ($(filter-out $#,$(MAKECMDGOALS)),hi)
echo "WON"
else
echo "LOST"
endif
However, when I run, make test hi, I get
arg="hi"
ifeq (hi,hi)
/bin/sh: -c: line 0: syntax error near unexpected token `hi,hi'
/bin/sh: -c: line 0: `ifeq (hi,hi)'
make: *** [test] Error 2
What is the unexpected token ?
ifeq is a make directive. By indenting it with a TAB, you have put it into the recipe, and all commands in the recipe are passed to the shell. The shell has no idea what an ifeq command is, and can't understand all the parens here, so you get these errors.
If you want to conditionalize on an automatic variable like $#, which are only available in the recipe, you have to write a shell conditional statement you can't use a make conditional statement:
test:
if [ '$(filter-out $#,$(MAKECMDGOALS))' = hi ]; then \
echo "WON"; \
else \
echo "LOST"; \
fi
Leaving this answer as the one posted doesn't work as it is missing a then
test:
if [ '$(filter-out $#,$(MAKECMDGOALS))' = hi ]; then \
echo "WON"; \
else \
echo "LOST"; \
fi
shell conditionals go if [] then ... else ... fi
I have a simple Makefile:
git_repo := some_git_repo
repo:
if [ -v $(git_repo) ]; then \
echo "exists!" \
else \
echo "not exist!" \
fi;
clean: repo
Running make clean gives me an error:
/bin/sh: -c: line 4: syntax error: unexpected end of file
make: *** [repo] Error 2
I'm not quite sure what's the cause for this error. I've double checked the syntax countless times, checked many different StackOverflow questions and even tried running the bash code under the repo rule separately and it works fine. What am I doing wrong here?
You need semicolons. The "\" effect is to put everything on the same line.
repo:
if [ -v $(git_repo) ]; then \
echo "exists!"; \
else \
echo "not exist!"; \
fi;
See the echo ends with a semicolon now.
The backslashes cause all the shell lines to be concatenated into one long line, which means you need semi-colons at the end of each line to separate the statements.
if [ -v $(git_repo) ]; then \
echo "exists!"; \
else \
echo "not exist!"; \
fi
After the backslashes and newlines are removed (and $(git_repo) is substituted) the shell sees:
if [ -v some_git_repo ]; then echo "exists!"; else echo "not exist!"; fi
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.
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