How to silent variable attributions (assignments)? - makefile

My makefile starts with something as
myVar := $$( etcExecute )
And it prints 10 times the $$ line if I use $(myVar) 10 times in an event. For example this test prints twice:
test:
ls $(myVar)/1
ls $(myVar)/2
How to supress these ugly prints?
There are a specific directive to silent only variable assigments?

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.

how using eval in makfile command change macros value with bash variable

I have a bash function inside the makefile command and want to change macros value.
Is it possible?
C_DFLAGS :=
gui :
parse_flags () { echo $$1; for word in $$1; do if [ $${word::2} = -D ] ; then $(eval C_D_FLAGS+=$${word}); fi ; done ; } ; parse_flags "-D/test -D/TEST"
#echo "C_D_FLAGS :$(C_D_FLAGS)"
$(eval) will be interpreted before your actual bash function call. You cannot update make variables from bash - it's a downstream process.
However, the code you try to run is fairly simple to replace with a native syntax, i.e.:
$ cat Makefile
C_D_FLAGS :=
gui: C_D_FLAGS += -D/test -D/TEST
gui:
#echo "C_D_FLAGS: $(C_D_FLAGS)"
$ make gui
C_D_FLAGS: -D/test -D/TEST
If the flags are provided from elsewhere, they can also be filtered, i.e.:
$ cat Makefile
C_D_FLAGS :=
gui: C_D_FLAGS += $(filter -D%,$(EXTRA_FLAGS))
gui:
#echo "C_D_FLAGS: $(C_D_FLAGS)"
$ make gui
C_D_FLAGS:
$ make gui EXTRA_FLAGS="-Isomething -DFOO -m32"
C_D_FLAGS: -DFOO

Syntax error: end of file unexpected

I define a function in Makefile
define write_file
for i in $( seq 1 10 )
do
echo "1234567" >> "tmp/test.txt"
done
endef
And
pre:
mkdir -p exe tmp
${call write_file}
But when I make pre,I got error:
mkdir -p exe tmp
for i in
/bin/sh: 1: Syntax error: end of file unexpected
Each line of a make-recipe is a single command that make runs in a single
new shell. So you need to make the body of the macro write_file into a single
shell command.
In addition, make expands anything of the unescaped form $(....),
treating .... as a defined or undefined make expression. So in your
case, make expands $( seq 1 10 ) to nothing. To stop make doing
that and let the shell expand $( seq 1 10 ), you need to escape $ for make,
which you do by writing $$ instead. The same goes for any $ in a make-recipe
that you intend to be expanded by the shell.
Putting these points together, you want:
define write_file
for i in $$( seq 1 10 ); \
do \
echo "1234567" >> "tmp/test.txt"; \
done
endef

Write a script to put a series of files in sequence

I am beginning in scripting and I am trying to write a script in bash. I need a script to write a sequence of several file names that are numbered from 1 to 50 inside one file. These are trajectory files from MD simulations. My idea was to write something like:
for valor in {1..50}
do
echo "
#!/bin/bash
catdcd -o Traj-all.dcd -stride 10 -dcd traj-$valor.dcd" > Traj.bash
exit
However, I just got one file with the following line:
#!/bin/bash
catdcd -o Traj-all.dcd -stride 10 -dcd traj-50.dcd
exit
But what I really want is something like:
#!/bin/bash
catdcd -o Traj-all.dcd -stride 10 -dcd traj-1.dcd -dcd traj-2.dcd -dcd traj-3.dcd ... -dcd traj-50.dcd
exit
How can I solve this problem?
You need to read a bit more about bash brace expansion. You can do this:
{
echo "#!/bin/bash"
echo "catdcd -o Traj-all.dcd -stride 10" "-dec traj-"{1..50}".dcd"
# ^^^^^^^^^^^^^^^^^^^^^^^^^
} > Traj.bash
The underlined part is where the brace expansion will get expanded by the shell into
-dec traj-1.dcd -dec traj-2.dcd ... -dec traj-50.dcd
You don't need to explicitly end your script with exit -- the shell will exit by itself when it runs out of commands.
> truncates the file on open. Either only use it once before the loop to create the file and then append (>>) within the loop, or redirect the entire loop.
> foo
for ...
do ...
echo ... >> foo
done
...
{
for ...
do ...
echo ...
done
} > foo

Increment a global variable in Bash

Here's a shell script:
globvar=0
function myfunc {
let globvar=globvar+1
echo "myfunc: $globvar"
}
myfunc
echo "something" | myfunc
echo "Global: $globvar"
When called, it prints out the following:
$ sh zzz.sh
myfunc: 1
myfunc: 2
Global: 1
$ bash zzz.sh
myfunc: 1
myfunc: 2
Global: 1
$ zsh zzz.sh
myfunc: 1
myfunc: 2
Global: 2
The question is: why this happens and what behavior is correct?
P.S. I have a strange feeling that function behind the pipe is called in a forked shell... So, can there be a simple workaround?
P.P.S. This function is a simple test wrapper. It runs test application and analyzes its output. Then it increments $PASSED or $FAILED variables. Finally, you get a number of passed/failed tests in global variables. The usage is like:
test-util << EOF | myfunc
input for test #1
EOF
test-util << EOF | myfunc
input for test #2
EOF
echo "Passed: $PASSED, failed: $FAILED"
Korn shell gives the same results as zsh, by the way.
Please see BashFAQ/024. Pipes create subshells in Bash and variables are lost when subshells exit.
Based on your example, I would restructure it something like this:
globvar=0
function myfunc {
echo $(($1 + 1))
}
myfunc "$globvar"
globalvar=$(echo "something" | myfunc "$globalvar")
Piping something into myfunc in sh or bash causes a new shell to spawn. You can confirm this by adding a long sleep in myfunc. While it's sleeping call ps and you'll see a subprocess. When the function returns, that sub shell exits without changing the value in the parent process.
If you really need that value to be changed, you'll need to return a value from the function and check $PIPESTATUS after, I guess, like this:
globvar=0
function myfunc {
let globvar=globvar+1
echo "myfunc: $globvar"
return $globvar
}
myfunc
echo "something" | myfunc
globvar=${PIPESTATUS[1]}
echo "Global: $globvar"
The problem is 'which end of a pipeline using built-ins is executed by the original process?'
In zsh, it looks like the last command in the pipeline is executed by the main shell script when the command is a function or built-in.
In Bash (and sh is likely to be a link to Bash if you're on Linux), then either both commands are run in a sub-shell or the first command is run by the main process and the others are run by sub-shells.
Clearly, when the function is run in a sub-shell, it does not affect the variable in the parent shell (only the global in the sub-shell).
Consider adding an extra test:
echo Something | { myfunc; echo $globvar; }
echo $globvar

Resources