Have make command exit 1 - makefile

I'm trying to throw an error with Make command. This is my makefile:
exit:
val=1
exit $(val)
However, when I run make exit && echo $? it gave me exit code 0. I was expecting exit code 1.
Did I use it wrong? Appreciate it.

Makefile target "scripts" are not run like real scripts, instead each command is run in a separate shell.
You need to escape dollar signs in makefiles by using an extra dollar sign.
There is a fundamental difference between makefile variables, declared outside of targets and referenced using $(name), and shell variables, declared as part of a command inside a target and referenced using $${name}.
What you want is either a single command equivalent to val=1 && exit $val:
exit:
val=1 && exit $${val}
or a makefile variable used in a simple command:
val = 1
exit:
exit $(val)

Related

why ‘&&’ disables errexit(set -e)?

consider script here:
set -e
make && make install
echo "SHOULD NOT BE HERE"
I expect that if make fails, the script will be aborted, but it's not:
make: *** No targets specified and no makefile found. Stop.
SHOULD NOT BE HERE
But, if I changed it like this:
set -e
make
make install
echo "SHOULD NOT BE HERE"
It works as expected:
make: *** No targets specified and no makefile found. Stop.
Why this happens?
Due to make && make install is commonly used in my build script, how should I use it correctly?
And please DO NOT link this question to Using set -e / set +e in bash with functions, it's not the same question.
Quoting from the answer to the question you linked:
Quoting sh(1) from FreeBSD, which explains this better than bash's man page:
-e errexit
Exit immediately if any untested command fails in non-interactive
mode. The exit status of a command is considered to be explicitly
tested if the command is part of the list used to control an if,
elif, while, or until; if the command is the left hand operand of
an “&&” or “||” operator; or if the command is a pipeline preceded
by the ! operator. If a shell function is executed and its exit
status is explicitly tested, all commands of the function are con‐
sidered to be tested as well.
errexit exits only if an untested command fails. Using && (or ||) will means that bash considers the command to the left of && to be explicitly tested (which in turn means that it will not be handled by errexit).
See also here (specifically the part about list constructs).
As far a I know, there is no way to achieve what you would like by setting bash options.

How to have multiple (non-file) dependencies in a Makefile

I have a Makefile of the following form:
upload_image_local: build_image_local ; echo "This gets printed" ; upload_image
with
build_image_local: echo "This is build_image_local"
./my_shell_script_1.sh $(SOME_ENV_VAR_1)
upload_image: echo "This is upload_image"
./my_shell_script_2.sh $(SOME_ENV_VAR_2)
As you can tell, when I run make upload_image_local, I want build_image_local and upload_image to be run. Instead I get:
/bin/sh: upload_image: command not found
I know for file dependencies we put spaces between them, but here I'm not sure how to separate the two statements properly. I also tried putting them in the next line with semicolons and a tab character (I know it matters):
upload_image_local:
build_image_local ; echo "This gets printed" ; upload_image
In which case I get:
/bin/sh: build_image_local: command not found
make: *** [upload_image_local] Error 127
What is the correct way to run this target? Also, why do the echo commands get printed fine? If it matters I'm running this Makefile on a Mac and with the sh shell (as it says).
I would really advise you to take a tutorial on Make.
GNU make manual
Quickstart
To achieve the desired effect, I think the following should work:
all: upload_image
build_image_local:
echo "This is build_image_local"
./my_shell_script_1.sh $$SOME_ENV_VAR_1
upload_image_local: build_image_local
echo "This gets printed"
upload_image: upload_image_local
echo "This is upload_image"
./my_shell_script_2.sh $$SOME_ENV_VAR_2
Also, I am assuming SOME_ENV_VAR_1 and SOME_ENV_VAR_2 are shell variables which is why I wrote them as $$SOME_ENV_VAR_1 and $$SOME_ENV_VAR_2. If they are variables of your Makefile then turn them back to way they were.
Also remember that the recipe in Makefile would spawn a subshell and you need to make sure that your env vars are available to those subshells.

How do I write an if else statement in a makefile with the zsh shell?

I am writing a makefile, and my shell is zsh. All is operating well until I hit an if statement. I've tried using the traditional ifeq makefile statement, but that doesn't appear to work when using zsh. So, I've tried just using zsh if statements, like the following:
if [[ "$(BUILD_ENV)" == "DEV" ]]; then
#echo dev environment
else
#echo not dev environment
fi
Every time I run it, I get the following error:
...
if [[ "DEV" == "DEV" ]]; then
zsh:1: parse error near `then'
make: *** [build] Error 1
I've tried removing the semicolon (;), putting then on a new line (like in bash syntax), but to no avail.
How can I properly perform an if else statement in a makefile, using zsh as the shell?
It's irrelevant what shell you use (consider the insanity if make always used the shell of the user who invoked it... no makefile could ever be portable between two people!)
make always uses /bin/sh. If you want to use some different shell you must explicitly set it in your makefile by adding:
SHELL := /bin/zsh
However, I don't recommend this because not every system will have zsh installed.
You should simply write your recipes to use portable POSIX syntax rather than using zsh extensions. In this case, that means using [..] instead of [[...]] and using = for equality instead of ==.
Also, remember that each logical line of a makefile is invoked in a separate shell so if you want to write an if-statement you have to use backslashes to ensure all the physical lines are combined into a single logical line:
mytarget:
#if [ "$(BUILD_ENV)" = "DEV" ]; then \
echo dev environment; \
else \
echo not dev environment; \
fi

Abort issue when error/exception occurs?

I have a bash script which calls multiple scripts (bash & python) from some directories.
I would like to get it aborted when any of the script throws an error/exception.
#!/bin/bash
/usr/bin/test.sh /usr/1/sample.sh /usr/2/temp.py
exit 0
Any suggestion on how to achieve this ?
FYI : I'm a beginner in bash scripting.
You can put set -e at the top of the script:
-e errexit If not interactive, exit immediately if any
untested command fails. The exit status of a com‐
mand is considered to be explicitly tested if the
command is used to control an if, elif, while, or
until; or if the command is the left hand operand
of an “&&” or “||” operator.
This will only work if one of your commands exit with an exit code of non-zero on failure. Well-behaved programs should always exit with 0 only on success, and if yours don't, you probably want to fix that.
I'm not entirely sure what you expect this to do:
/usr/bin/test.sh /usr/1/sample.sh /usr/2/temp.py
Since this will run one command (/usr/bin/test.sh) with two arguments, you probably want to put them on separate lines.

Why doesn't this Bash script error out?

Here's my Bash script:
#!/bin/bash -e
if [ == "" ]; then
echo "BAD"
exit 1
fi
echo "OK"
And here's the output:
./test.sh: line 3: [: ==: unary operator expected
OK
The return code is 0.
There's an obvious syntax error at line 3. Rather than raise the syntax error and refuse to run the script, somehow the script just runs and reports the syntax error at run time. The -e flag hasn't protected me from this - apparently a syntax error in an if statement constitutes a false condition rather than a reason to immediately exit the program. BUT, somehow Bash has parsed that whole if ... fi block, so after ignoring the bad line, execution somehow resumes not at the next syntactically correct line but after the end of the block?
I have two questions:
What is going on?
How can I protect myself from this behaviour in future?
if runs the command [, and just examines its return code. Bash doesn't know nor care about the syntax for the [ command.
You can put some other command there, and Bash still won't know anything about its particular syntax.
Two things come to mind:
Using [[ instead of [: Bash does know and care about its syntax.
Using ShellCheck1; online, manually or within your favourite editor.
Both if and -e deal with exit codes: If it's non-zero if won't let you into the then block, and -e will exit. You can't really have both those behaviours at once. (Well, it seems [ exits with different codes for false results (1) and syntax errors (2), so it might be possible to ‘detect’ syntax errors.)
1Or some other tool, but that's the only one of which I know. Suggestions welcome.
You don't have a shell syntax error here.
You have an error in the arguments to the [ command/builtin.
The reason set -e doesn't help here is because that's explicitly not what it is supposed to do. set -e would become entirely useless if you couldn't have if statements in your code with it on. Just think about that.
If you look in the POSIX spec for what the -e/errexit flag does you see this description:
-e
When this option is on, when any command fails (for any of the reasons listed in Consequences of Shell Errors or by returning an exit status greater than zero), the shell immediately shall exit with the following exceptions:
The failure of any individual command in a multi-command pipeline shall not cause the shell to exit. Only the failure of the pipeline itself shall be considered.
The -e setting shall be ignored when executing the compound list following the while, until, if, or elif reserved word, a pipeline beginning with the ! reserved word, or any command of an AND-OR list other than the last.
If the exit status of a compound command other than a subshell command was the result of a failure while -e was being ignored, then -e shall not apply to this command.
This requirement applies to the shell environment and each subshell environment separately. For example, in:
set -e; (false; echo one) | cat; echo two
See point two there? That's your situation.
The reason the shell continues to execute is back to the "not a shell syntax error". You have a command error. The [ command/builtin attempted to parse its arguments and failed. It then returned an error return code. The if caught that, skipped its body and returned true (as per documented behavior of if when no conditions return true). So the shell script continued normally.
As I indicated in my comment, however, if you had used [[ (which is a bash-ism and a language construct) then your script would have had a syntax error and would have exited immediately on that line (at least in my tests).
From the bash man page
-e
Exit immediately if a pipeline (which may consist of a single simple command), a subshell command enclosed in parentheses, or one of the commands executed as part of a command list enclosed by braces (see SHELL GRAMMAR above) exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test following the if or elif reserved words, part of any command executed in a && or ││ list except the command following the final && or ││, any command in a pipeline but the last, or if the command's return value is being inverted with !. A trap on ERR, if set, is executed before the shell exits. This option applies to the shell environment and each subshell environment separately (see COMMAND EXECUTION ENVIRONMENT above), and may cause subshells to exit before executing all the commands in the subshell.
Emphasis mine.
First, that isn't a syntax error. You are simply providing bad arguments to the [ command.
Second, the exit status of a command in the list following the if keyword are ignored for the purpose of the -e option.

Resources