Makefile and command line different behaviour - bash

I am currently working on a bigger project where i want to test executable file with few different codes as input.
I call it like this ./test < code1
and after command echo $?, it shows last returned value [0, 1, 2, ..]
I wanted to automate is, so i created call in makefile like this :
#makefile
[...]
test :
./test < code1
#echo $$?
./test < code2
#echo $$?
[...]
[...]
So i can call make test.
When program returns 0 as success, everything works fine. But when program has to return something else than 0, it shows me this :
./test < code3
Makefile:19: recipe for target 'test' failed
make: *** [test[ Error 2
Weird thing is, when i try to call program with code which made it crash in command line like :
./test < code3; echo $?
It works perfectly and shows me last exit status ( for exapmle 3 ).
I am confused now, because i thought it should work the same. Can someone help me out?
Thank you!

See this answer: https://stackoverflow.com/a/41452754/939557
You need to put the echo into the same logical line as your test invocation:
test :
./test < code1; echo $$?
./test < code2; echo $$?

Related

log.Println not working with os.Exit(1) for makefile

I have a command line tool in Go, example:
err := doSomething()
if err != nil {
log.Println(err) //fmt.Println(err)
os.Exit(1)
}
In the makefile, I am doing:
V = 0
Q = $(if $(filter1, $V),,#)
.PHONY: dosomething
dosomething: ; $(info $(shell printf "running dosomething")) #
$Q cd $(BASE) && ret=0 \
test -z "$$($(dosomething))" || ret = 1 ; \
exit $$ret
make fails (if error occurs) when using fmt.Println(err), but doesn't print anything. When I use log.Println, it prints the error but make continues. How to fail make as well print the error? Also, what to do in the case of a panic() in golang code?
I don't understand this makefile at all. What is $(info $(shell printf "running dosomething")) supposed to do? Why not just $(info running dosomething)? In general you never want to use make's shell function inside a recipe, it just leads to confusion.
Second, it looks like your formatting is wrong because you've put the # at the end of the line.
Also you need a semicolon after ret=0.
Finally, what is $$($(dosomething)) supposed to do? You've not set the make variable dosomething to any value, so this is basically a no-op.
I can only assume that in your real makefile you set the make variable dosomething to the command you want to run. It's helpful when asking questions, that you provide the actual files that reproduce the issue (creating a small repro case if the real file is too large or complex).
In that case the reason for the confusion is easy; when you run $(dosomething) in the shell it runs the dosomething program and captures its stdout and expands to that string... so the output is not printed. It's captured. For example:
$ echo hi
hi
$ foo=$(echo hi)
$ echo $foo
hi
Note how hi was not printed in the second command because it was captured and stored in the foo variable.
On the other hand, $(...) does not capture stderr, only stdout:
$ echo hi 1>&2
hi
$ foo=$(echo hi 1>&2)
hi
$ echo $foo
Note here that hi was printed (to stdout) and not captured (so foo is now empty).
I don't know much about Go but I can only assume that one of the two functions you show prints to stdout, and the other prints to stderr.
You don't want to capture any output and compare that, instead you want to compare the exit code of the program. You can do it like this:
V = 0
Q = $(if $(filter1, $V),,#)
.PHONY: dosomething
dosomething = <command-to-run>
dosomething:
$(info running dosomething)
$Q cd $(BASE) && $(dosomething)
This allows the output of the dosomething command to run without capturing any output, and it will exit with the exit code of the command, which will be 0 on success and not-0 (1 in your case) on error.

Bash script, check numeric variable in in range

This is my script:
#!/bin/bash
JOB_NUM=4
function checkJobNumber() {
if (( $JOB_NUM < 1 || $JOB_NUM > 16 )); then
echo "pass"
fi
}
...
checkJobNumber
...
When I try to launch the script I get the message:
./script.sh line 49: ((: < 1 || > 16 : syntax error: operand expected (error token is "< 1 || > 16 ")
(Please notice the spaces in the error message)
I really don't understand what the problem is. If I do the evaluation manually at the command line, it works.
Also, I tried different evaluations like if [[ "$JOB_NUM" -lt 1 -o "$JOB_NUM" gt 16 ]];... still no success.
UPDATE: As suggested I made few more attempts in the rest of the code outside the function call, and I found the problem.
My variables declaration was actually indented this way:
JOB_NUM= 4
THREAD_NUM= 8
.....
VERY_LONG_VAR_NAME= 3
and apparently this hoses the evaulation. BAH! It always worked for me before, so why doesn’t it now?
If I delete the white spaces, the evaluation works:
JOB_NUM=4
THREAD_NUM=8
....
VERY_LONG_VAR_NAME=3
OK I officially hate bash . . . sigh :(
this will work fine
#!/bin/bash
JOB_NUM=7
function checkJobNumber() {
if [ $JOB_NUM -lt 0 ] || [ $JOB_NUM -gt 16 ] ; then
echo "true"
else
echo "false"
fi
}
checkJobNumber
And if you want to check variable during tests you can write in your bash :
set -u
this will generate a message like "JOB_NUM: unbound variable" if variable is not well set

Do calculation in the Makefile

I got confused with Makefile. I am trying to run a simple command in the Makefile but it gives me the error "/bin/bash: line 3: :=: command not found". I am using shell to run this makefile
This is my part of my Makefile:
all:
vlog Benchmarks/$(NAME)/Syn/*.v
$(eval tux_number := 1)
$(eval range := 1)
$(eval ssh_log := 255)
echo "Start Range: ${range}"
echo "tux-number: ${tux_number}"
while [[ $$range -le 50 ]] ; do \
ssh -l yazdanbakhsh tux-$(tux_number).cae.wisc.edu exit ; \
echo "range: ${range}" ; \
eval $$range := $$((${range}+1)) ; \
done
Thanks
all:
#range=1; \
while [ $$range -le 10 ] ; \
do echo Range: $$range; \
let range=range+1 ; \
done;
Note that the whitespace in front of #range... is the only TAB.
Just to fix your obvious problems with Makefile syntax, here is an attempt at refactoring your attempt into valid code.
tux_number := 1
ssh_log := 255 # not used anywhere
all:
vlog Benchmarks/$(NAME)/Syn/*.v
echo "Start Range: 1" # This is probably no longer very useful output
echo "tux-number: ${tux_number}"
range=1; while [ $$range -le 50 ] ; do \
ssh -l yazdanbakhsh tux-$(tux_number).cae.wisc.edu exit ; \
echo "range: $$range" ; \
range=$$(expr "$$range + 1); \
done
Notice how tux_number and ssh_log are Makefile variables, while range only exists in the shell which executes the while loop. I have avoided the Bashisms in order to make this portable. (If portability is not important, you might want to refactor it back to Bash syntax and use for ((range=1; range<=50; range++)); do... instead.)
Your use of eval is misguided. As you can see, I simply lifted out the Makefile variables outside the recipe where they don't belong. What you were doing was (1) have Make evaluate the expression range := 1 (which evaluates to itself) and (2) use the output as a shell command in a recipe. Since it's not a valid shell command, you got the syntax error from Bash. Without further ado, I'll just take the easy way out here and say that eval is a complex subject, and until you get more experience with Make, it's probably just best to forget that it exists.
In order to properly make use of Make's facilities, I would make this parallelizable, i.e. split it up into 50 individual targets. This is a bit clumsy (there's probably a better way to define range here), but at least it should illustrate a number of differences to your approach. (If you don't insist on having range count up from 1, making it zero-based would make this a little less clumsy. This exploits the fact that the empty string is harmless in a shell snippet, so we can use it instead of a zero prefix. Again, this could be simplifed if you don't care about the human readability of the range index.)
digits := 0 1 2 3 4 5 6 7 8 9
deca := "" 1 2 3 4
range := $(filter-out ""0,$(foreach d,$(deca),$(foreach i,$(digits),$d$i))) 50
# Or, at the expense of an external process,
# range := $(shell perl -le 'print $$_ for 1..50')
.PHONY: all
all: $(patsubst %,ssh-%,$(range))
.PHONY: ssh-%
ssh-%:
ssh -l yazdanbakhsh tux-$(tux_number).cae.wisc.edu exit
echo "range: $*"
This can be run with something like make -j 5 to execute these in parallel batches of five, for example.
Incidentally, the commented-out $(shell ...) call might be the actual answer to your question, if what you really wanted to do was to use Make to drive an external program to calculate something for you.

GNU override target?

I'm wondering if it's possible to override a target in a makefile! The environment I'm working in does not allow me to do this due to auto generation! I was wondering if I coded the same rule above or below the static target would this achieve an override?
%_emul.flist: $(if ${GEN_FLIST},%_synth.flist,) ${rdlh_file_deps}
${QUIET}if test ${SYN_DEBUG} -eq 1 ; then set -xv ; fi; \
$(if ${TOOL_VERILOG},rm -f $#; touch $#,$(if ${TOOL_BBOX_LIBS},echo ${TOOL_BBOX_LIBS} > $#,rm -f $#; touch $#))
/bin/sed -e '/\/libs\//d' -e '/\/place\//d' $(foreach mod,$(filter %.vhd,$^),-e 's%^\(.*\/\)\{0,1\}$(basename $(notdir ${mod}))\.v$$%${mod}%') $*_synth.flist >> $#
Yes , i think that would work .... but you need to be a bit more careful in the way you code things. You don't want to override something that might be useful!
GNU make would take the most recent of the target it encounters. So, the following works (but not as i would have liked it to work :( )
Output: I think you are looking for something like this --
Kaizen ~/make_prac $ make -nf mk.name
mk.name:20: warning: overriding recipe for target `name'
mk.name:17: warning: ignoring old recipe for target `name'
arg1="Kaizen" ;
echo "hello "" ;" ;
hello ;
Code: Here the target "name" appears twice and is overridden.
Kaizen ~/make_prac $ cat mk.name
##
## make to accept name and display hello name
##
arg1="" ;
.PHONY : name \
hello
#.DEFAULT :
# hello
hello : name
+ echo "hello $(arg1)" ;
name :
echo "name given is : $(arg1)" ;
name :
arg1="Kaizen" ;
PS: Take note of the use of : -- if you use :: then both rules get executed.
Explanation for the arg1 .... not showing in the output: The variable arg1, even though it gets assigned in the first parsing, it gets ignored, since its assignment is target dependent. If you would have had a variable declaration elsewhere -- e.g. like arg1 is defined at the start -- there would not be any dereferencing issues.

Meaning of $? (dollar question mark) in shell scripts

What does
echo $?
mean in shell programming?
This is the exit status of the last executed command.
For example the command true always returns a status of 0 and false always returns a status of 1:
true
echo $? # echoes 0
false
echo $? # echoes 1
From the manual: (acessible by calling man bash in your shell)
?       Expands to the exit status of the most recently executed foreground pipeline.
By convention an exit status of 0 means success, and non-zero return status means failure. Learn more about exit statuses on wikipedia.
There are other special variables like this, as you can see on this online manual: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters
$? returns the exit value of the last executed command. echo $? prints that value on console. zero implies a successful execution while non-zero values are mapped to various reason for failure.
Hence when scripting; I tend to use the following syntax
if [ $? -eq 0 ]; then
# do something
else
# do something else
fi
The comparison is to be done on equals to 0 or not equals 0.
** Update Based on the comment: Ideally, you should not use the above code block for comparison, refer to #tripleee comments and explanation.
echo $? - Gives the EXIT STATUS of the most recently executed command . This EXIT STATUS would most probably be a number with ZERO implying Success and any NON-ZERO value indicating Failure
? - This is one special parameter/variable in bash.
$? - It gives the value stored in the variable "?".
Some similar special parameters in BASH are 1,2,*,# ( Normally seen in echo command as $1 ,$2 , $* , $# , etc., ) .
It has the last status code (exit value) of a command.
Minimal POSIX C exit status example
To understand $?, you must first understand the concept of process exit status which is defined by POSIX. In Linux:
when a process calls the exit system call, the kernel stores the value passed to the system call (an int) even after the process dies.
The exit system call is called by the exit() ANSI C function, and indirectly when you do return from main.
the process that called the exiting child process (Bash), often with fork + exec, can retrieve the exit status of the child with the wait system call
Consider the Bash code:
$ false
$ echo $?
1
The C "equivalent" is:
false.c
#include <stdlib.h> /* exit */
int main(void) {
exit(1);
}
bash.c
#include <unistd.h> /* execl */
#include <stdlib.h> /* fork */
#include <sys/wait.h> /* wait, WEXITSTATUS */
#include <stdio.h> /* printf */
int main(void) {
if (fork() == 0) {
/* Call false. */
execl("./false", "./false", (char *)NULL);
}
int status;
/* Wait for a child to finish. */
wait(&status);
/* Status encodes multiple fields,
* we need WEXITSTATUS to get the exit status:
* http://stackoverflow.com/questions/3659616/returning-exit-code-from-child
**/
printf("$? = %d\n", WEXITSTATUS(status));
}
Compile and run:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o bash bash.c
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o false false.c
./bash
Output:
$? = 1
In Bash, when you hit enter, a fork + exec + wait happens like above, and bash then sets $? to the exit status of the forked process.
Note: for built-in commands like echo, a process need not be spawned, and Bash just sets $? to 0 to simulate an external process.
Standards and documentation
POSIX 7 2.5.2 "Special Parameters" http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 :
? Expands to the decimal exit status of the most recent pipeline (see Pipelines).
man bash "Special Parameters":
The shell treats several parameters specially. These parameters may only be referenced; assignment to them is not allowed. [...]
? Expands to the exit status of the most recently executed foreground pipeline.
ANSI C and POSIX then recommend that:
0 means the program was successful
other values: the program failed somehow.
The exact value could indicate the type of failure.
ANSI C does not define the meaning of any vaues, and POSIX specifies values larger than 125: What is the meaning of "POSIX"?
Bash uses exit status for if
In Bash, we often use the exit status $? implicitly to control if statements as in:
if true; then
:
fi
where true is a program that just returns 0.
The above is equivalent to:
true
result=$?
if [ $result = 0 ]; then
:
fi
And in:
if [ 1 = 1 ]; then
:
fi
[ is just an program with a weird name (and Bash built-in that behaves like it), and 1 = 1 ] its arguments, see also: Difference between single and double square brackets in Bash
From http://www.gnu.org/s/bash/manual/bash.html#Special-Parameters
?
Expands to the exit status of the most recently executed foreground pipeline.
See The Bash Manual under 3.4.2 Special Parameters:
? - Expands to the exit status of the most recently executed foreground pipeline.
It is a little hard to find because it is not listed as $? (the variable name is "just" ?). Also see the exit status section, of course ;-)
Happy coding.
Outputs the result of the last executed unix command
0 implies true
1 implies false

Resources