Makefile, when is $(shell cat file) evaluated? - shell

Makefile
a:
echo "123" > afile
cat afile
# expect output 123, but it's empty
echo $(shell cat afile)
Why does echo output nothing?

In make, the entire recipe (all commands in the recipe) are expanded before the first command in the recipe is invoked.
Since $(shell ...) is a make function, not a shell function, make will expand it before it starts running any commands in the recipe.
You can change it to $$(cat afile) and make it a shell operation, and make won't run the cat.

thank you all. $(shell ...) expanded before recipe executing. make sense.
look following code
a:
ln -s hello.txt qq
cat qq
echo $(shell cat qq)
a1: a
cat qq
echo $(shell cat qq)
make a1.
why a1 could echo string right? hello.txt contains hello.

after google, find the answer. like MadScientist said avoid $(shell ...).
a:
echo "123" > afile
cat afile
echo `cat afile`

a:
echo "123" > afile
cat afile
echo $$(cat afile)

Related

addprefix in MAKE doesn't work correctly when -e is used as prefix

I encounter a strange behavior of addprefix with -e. The expected -e for the first word is not added. Here's the makefile to reproduce it.
a := file1 file2 file3
b := $(addprefix -s , $(a))
c := $(addprefix -e , $(a))
all:
#echo $(b)
#echo $(c)
Result:
-s file1 -s file2 -s file3
file1 -e file2 -e file3
Is it a bug or something is missing?
You should never use echo to print any string that is not just simple text that you know contains no starting dash characters. The echo command is not really standardized and there are a lot of versions, some of which accept options and some of which don't.
On your system, the echo command accepts a -e option so when you use echo -e file -e file2 -e file3 echo eats the first -e.
You can use:
all:
#printf '%s\n' '$(b)'
#printf '%s\n' '$(c)'
to be portable. Or you can use make's built-in functions:
all:
$(info $(b))
$(info $(c))

Makefile: Reuse environment variables

In my Makefile deploy target I create environment variables and I want to reuse those in the following lines:
SHELL=/bin/sh
deploy:
export $(shell sh this-script-generate-key-values.sh | xargs)
echo ${VAR1} #there is no variable here
echo ${VAR2} #there is no variable here
Where:
this-script-generate-key-values.sh generates this output:
VAR1="somevalue"
VAR2="somevalue"
Why the variables are not set in subsequent lines? How can I make it work?
Notes:
This line works: sh this-script-generate-key-values.sh | xargs
The shell must be /bin/sh (no bash)
All lines in a Makefile recipe run in a separate shell. You need to run the lines in a single shell. Also you need to escape the dollar sign ($) so that variable substitution is not done by make but by the shell.
SHELL=/bin/sh
deploy:
export $$(this-script-generate-key-values.sh | xargs) ;\
echo $${VAR1} ;\
echo $${VAR2}
Just to expand on my comment -- you could output to a file, and use the file to generate your output as so:
vars.txt:
this-script-generate-key-values.sh > $#
deploy : vars.txt
echo VAR1=$$(sed -n 's|VAR1=\(.*\)|\1|p' vars.txt)
echo VAR2=$$(sed -n 's|VAR2=\(.*\)|\1|p' vars.txt)
note: you may have to generate dependencies for vars.txt or declare it .PHONY, otherwise, this will not run on every invocation of make.
If the .ONESHELL special target appears anywhere in the makefile then all recipe lines for each target will be provided to a single invocation of the shell. Newlines between recipe lines will be preserved.
.ONESHELL:
deploy:
export $$(this-script-generate-key-values.sh)
echo $${VAR1}
echo $${VAR2}

Makefile executes $(shell command) unordered [duplicate]

This question already has answers here:
How to run sub Shell script in Makefile?
(2 answers)
Closed 2 years ago.
I am having an issue with my Makefile:
At the begining of the Makefile, a file is created.
I wanna retrieve the content of that created file by using the "cat command".
I need to use the $(shell cat) execution because I have to retrive it inside another command.
It's like if the sub-shell does not reallize of the creation of that file and does not find it.
If you take a look into the output message order... its like first of all, it is executing the $(shell cat) command, because the first line is saying: "cat: test.txt: The file or directory does not exist".
Why it seems to be executing the commands unordered... executing first the $(shell cat)...
It could be shown in the following silly example:
Take a look into the following Makefile with a test routine:
test:
rm -rf test.txt
echo "Hello World" > test.txt
echo "$(shell cat test.txt)"
Executing make test the Output is as follows:
cat: test.txt: The file or directory does not exist.
rm -rf test.txt
echo "Hello World" > test.txt
echo ""
If you execute "make test" twice, you can realize that the second execution of the Makefile is echoing the text "Hello World"... because it is executing $(shell cat test.txt) at the begining and the file exists from the frist "make test" execution...
Any suggestion about what is happening and how I have to proceed to accomplish my goal?
Many thanks in advance!
Double up your $s to escape them, so they're still seen by the shell in literal form (and thus executed by the shell, not by make prior to the shell's invocation):
test:
rm -rf test.txt
echo "Hello World" > test.txt
echo "$$(cat test.txt)"

How to prevent make to run $(shell command) too early

I want to create a file during a rule of a makefile,
At the end of the rule, I want to run a shell command for the file by using $(shell xxx) function of make.
This is simplified example makefile for showing the problem:
test:
#echo 123 >> file
#cat file
#echo "File: $(shell cat file) "
I expected that the last line of the makefile would print: File: 123
Instead it looks like that make runs $(shell cat file) before other lines of the makefile:
> rm file
> make test
cat: file: No such file or directory
123
File:
Is there some simple way to prevent the unwanted behavior, and get the following kind of result?
> rm file
> make test
123
File: 123
There's no need to use make's $(shell ... syntax here. Just use a backtick `...` sequence to invoke cat directly...
test:
#echo 123 >> file
#cat file
#echo "File: "`cat file`
Or, if your version of echo supports the option to suppress the trailing newline...
test:
#echo 123 >> file
#cat file
#echo -n "File: "; cat file
Is there some simple way to prevent the unwanted behavior
This is expected and documented behaviour. If that's not what you want, you mustn't use $(shell) at all.
test:
#echo 123 >> file
#cat file
#printf "File: "
#cat file
Escape your dollars in Makefile and use shell's own subprocess facility, $(command).
test:
echo "zzz" > z
echo "$$(cat z)"

Rules of executing shell command in Makefile

When I executed command make, I got an error message
Makefile:4: *** missing separator. Stop.
The command in Makefile is:
$(shell ./makejce common/jce jce)
What's wrong with it?
-------makejce---------
#!/bin/bash
FLAGS=""
local_protoc=""
dir0=`pwd`
dir=`pwd`
......
if [ $# -gt 1 ]
then
mkdir -p $2
cd $2
dir=`pwd`
cd $dir0
fi
cd $1
jce_dir=`pwd`
#sub dir
for d in `ls -d */`
do
if [ -d $d ]
then
cd $d
for f in `find . -name '*.jce'`
do
${local_protoc} ${FLAGS} --dir=${dir} $f
done
cd $jce_dir
fi
done
#current dir
for f in `ls *.jce`
do
${local_protoc} ${FLAGS} --dir=${dir} $f
done
cd $dir0
-----makefile------
......
$(shell ./makejce common/jce jce)
......
With so little info it looks extremely bizarre (why are you running all the build steps in a shell script then invoking that script with a shell makefile function? The entire point of a makefile is to manage the build steps...) but without more information I'll just answer your specific question:
The make shell function works like backticks or $(...) in shell scripts: that is it runs the command in a shell and expands to the stdout of the command.
In your makefile if you have:
$(shell echo hi)
then it runs the shell command echo hi and expands to the stdout (i.e., hi). Then make will attempt to interpret that as some makefile text, because that's where you have put the function invocation (on a line all by itself). That's a syntax error because make doesn't know what to do with the string hi.
If you want to run a shell function then either (a) redirect its output so it doesn't output anything:
$(shell ...command... >/dev/null 2>&1)
or (b) capture the output somewhere that it won't bother make, such as in a variable like this:
_dummy := $(shell ...command...)
(by using := here we ensure the shell function is evaluated when the makefile is parsed).

Resources