Is conditional statement in Makefile valid syntax - bash

I have the following Makefile
~/w/i/craft-api git:develop ❯❯❯ cat Makefile ⏎ ✱ ◼
test:
echo "TODO: write tests"
generate-toc:
if ! [ -x "$(command -v doctoc)" ]; then
echo "Missing doctoc. Run 'npm install doctoc -g' first"
else
doctoc ./README.md
fi
I'm encountering this error
~/w/i/craft-api git:develop ❯❯❯ make generate-toc ✱ ◼
if ! [ -x "" ]; then
/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [generate-toc] Error 2
What is incorrect in my Makefile syntax / usage?
edit 1
Adding line-continuing backslashes doesn't appear to fix the issue:
~/w/i/craft-api git:develop ❯❯❯ cat Makefile ⏎ ✱ ◼
test:
echo "TODO: write tests"
generate-toc:
if ! [ -x "$(command -v doctoc)" ]; then \
echo "Missing doctoc. Run 'npm install doctoc -g' first" \
else \
doctoc ./README.md \
fi
~/w/i/craft-api git:develop ❯❯❯ make generate-toc ✱ ◼
if ! [ -x "" ]; then \
echo "Missing doctoc. Run 'npm install doctoc -g' first" \
else \
doctoc ./README.md \
fi
/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [generate-toc] Error 2

Every line is treated as a separate command and is passed to a different shell instance. You can use \ continuations to combine all of the lines so make knows to pass them as one long string to a single shell. This removes the newlines so you also need to add ; at the end of each command.
if ! [ -x "$$(command -v doctoc)" ]; then \
echo "Missing doctoc. Run 'npm install doctoc -g' first"; \
else \
doctoc ./README.md; \
fi
You'll also want to escape the $, otherwise make will interpret it rather than the shell.

Related

Makefile: write conditional statement for the shell

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

Makefile: Syntax error /bin/sh: -c: syntax error: unexpected end of file

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

Makefile multiline dash command run executable in a detached process

I have the following target in my makefile: (I'd like to run python http server in a detached process and when bash script is done kill the server)
TEST_PORT = 17777
test::
$(ENV_VARS) \
python -m SimpleHTTPServer $(TEST_PORT); \
PID=$$(lsof -t -i #localhost:$(TEST_PORT) -sTCP:listen); \
echo $(PID); \
if [ -n "$$PID" ]; \
then \
python test.py; \
fi; \
function finish { \
if [ -n "$$PID" ]; \
then \
kill -9 $$PID; \
fi \
} \
trap finish EXIT;
However when I put a & after the line python ... I get an error
/bin/dash: Syntax error: ";" unexpected
How can this be done in a proper way?
EDIT
I have changed my makefile to do the following:
test::
python -m SimpleHTTPServer $(TEST_PORT) &
PID=$$(lsof -t -i #localhost:$(TEST_PORT) -sTCP:listen); \
if [ -n "$$PID" ]; \
then \
$(ENV_VARS) python test.py; \
fi \
function finish { \
if [ -n "$$PID" ]; \
then \
kill -9 $$PID; \
fi \
} \
echo $$PID; \
trap finish EXIT;
However I am getting an error: (without the line number)
/bin/dash: Syntax error: word unexpected
The important thing to remember here is that your line breaks don't actually exist when the shell sees the command.
So your first command becomes:
$(ENV_VARS) python -m SimpleHTTPServer $(TEST_PORT); PID=$$(lsof -t -i #localhost:$(TEST_PORT) -sTCP:listen); echo $(PID); if [ -n "$$PID" ]; then python test.py; fi; function finish { if [ -n "$$PID" ]; then kill -9 $$PID; fi } trap finish EXIT;
And your second command becomes:
PID=$$(lsof -t -i #localhost:$(TEST_PORT) -sTCP:listen); if [ -n "$$PID" ]; then $(ENV_VARS) python test.py; fi function finish { if [ -n "$$PID" ]; then kill -9 $$PID; fi } echo $$PID; trap finish EXIT;
Now those are both very hard to read so I don't expect you to spot the problem but the problem is that you are missing statement terminators in a few places.
Specifically:
Braces ({}) are word elements and so need spaces around them (and a terminator before, and after, the closing brace). You are missing those terminators here fi } trap and here fi } echo.
fi is also not a statement terminator and so it needs one between it and the next statement. You are missing one here test.py; fi function (as well as the ones in the braces from the first point).

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

How To test the exit status, and do something in 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

Resources