Issue with passing environment variable to make file - makefile

In my make file I have 2 parameters defined as below with default values.
PARAM1 ?= 2
PARAM2 ?= 11
I am using these parameters in my target cpp_run.
cpp_run:
./dig_vr_App $(PARAM1) $(PARAM2) |& tee run_Cpp.log
However if I pass the parameters from the command line for example
make cpp_run PARAM1=102 PARAM2=0
the make file still picks up the default values, i.e., 2 and 11 (from the log file run_cpp.log). I followed other posts which says use PARAM1=num1 and PARAM2=num2 in the command line as shown above but it doesn't work.

As I said in my comments, you are not telling us something. I created this test:
$ cat Makefile
PARAM1 ?= 2
PARAM2 ?= 11
cpp_run:
#echo ./dig_vr_App $(PARAM1) $(PARAM2)
and it works exactly how I'd expect:
$ make --version | head -n2
GNU Make 4.3
Built for x86_64-pc-linux-gnu
$ make cpp_run
./dig_vr_App 2 11
$ make cpp_run PARAM1=102 PARAM2=0
./dig_vr_App 102 0
So there's something about your environment or makefile that you haven't explained to us.

Related

Put Very Long Dependency List at the END of the Makefile

I'm trying to put a very long list of objects at the END of my Makefile. I cannot get this code to work. I can see $(LIST) getting assigned in the ASSIGN_LIST macro, however, it seems that $(LIST) is blank for stuff.txt. I'm a newbie at Make so maybe I'm way off the mark. (Using Red Hat Linux, GNU Make v3.81.)
> cat makefile
define ASSIGN_LIST
LIST = $1
endef
stuff.txt: $(LIST)
echo $(LIST)
# MUCH MORE CODE HERE
# THIS CODE CONTAINING THE LIST IS AT THE VERY END OF MAKEFILE (i.e. OUT OF THE WAY)
define VERY_LONG_LIST_OF_OBJECTS
aaa \
bbb \
ccc \
ddd \
eee \
fff \
etc
endef
$(info $(call ASSIGN_LIST, $(VERY_LONG_LIST_OF_OBJECTS)))
When I run the makefile:
> make
make: `stuff.txt' is up to date.
I use 'touch' to make the prereqs newer than stuff.txt. If I swapout "eval" with "info" (last line), I can see this
> make
LIST = aaa bbb ccc ddd eee fff etc
make: `stuff.txt' is up to date.
Is this even possible? Thanks for the help.
Variables that appear in targets or prerequisites are expanded immediately when read by make during the parsing of the makefile. So, this cannot work:
stuff.txt: $(LIST)
echo $(LIST)
...
LIST = ...
Here, the prerequisite variable is expanded when make reads that line but the variable inside the recipe is not expanded until later when make wants to build the target. So the variable is not set yet when parsing the prerequisite, then it gets set, then the recipe is run.
If you want to see what make sees you should use automatic variables instead:
stuff.txt: $(LIST)
echo $^
The best way to do this is delay the prerequisites until after the variable is set. If you really, really don't want to do that you can use secondary expansion:
.SECONDEXPANSION:
stuff.txt: $$(LIST)
echo $^
Using eval on ASSIGN_LIST will only set LIST to the 1st file (because of LIST = $1). As a result, stuff.txt only depends on aaa.
As an alternative, add the dependency after setting the variable:
define VERY_LONG_LIST_OF_OBJECTS
aaa \
... \
etc
endef
stuff: $(VERY_LONG_LIST_OF_OBJECTS)

Makefile does not call shell script when setting variable

I am trying to follow the meaty skeleton tutorial on osdev. The Makefile is not running one of the shell scripts. I have set all of the permissions on each of the files to be executable.
In lib/Makefile, I have the below few lines set:
$(info DEFAULT_HOST!=../default-host.sh)
$(info HOST?=DEFAULT_HOST)
$(info HOSTARCH!=../target-triplet-to-arch.sh $(HOST))
after these lines have executed, neither DEFAULT_HOST nor HOSTARCH get set.
default-host.sh:
#!/bin/sh
echo i686-elf
arget-triplet-to-arch.sh:
#!/bin/sh
if echo "$1" | grep -Eq 'i[[:digit:]]86-'; then
touch here.txt
echo i386
else
touch there.txt
echo "$1" | grep -Eo '^[[:alnum:]_]*'
fi
Note, I added the touch statements in arget-triplet-to-arch.sh. When run from the shell, one or other of those files is created, but not when the Makefile is run. This means that make seems to not be running the shell commands. How can I get make to run the shell commands?
As Beta says, info doesn't "allow you to see the value of that line being evaluated". info expands its argument then prints it to stdout. "Expands" means it resolves any variable references, it doesn't mean interpreting it as a makefile command. So if you run $(info hi) it prints "hi". If you run $(info foo = bar) if prints foo = bar but does not set the value of the variable foo to bar.
For using !=, note that this feature was added to GNU make 4.0. If your version is older than that then this assignment doesn't do what you expect. In particular, a line like FOO!=echo bar will be interpreted as if it were FOO! = echo bar... in other words it sets the make variable named FOO!.
Personally I always put whitespace around the assignment statements in my makefiles... this makes it clear that they are make assignments, not shell variable assignments (not that it shouldn't be clear anyway for anyone who knows makefile syntax, but...). In newer versions of GNU make, variable names cannot contain whitespace.

makefile use variable defined in target [duplicate]

This question already has answers here:
Define make variable at rule execution time
(4 answers)
Closed 4 years ago.
How can one use the variable defined inside a make target
.PHONY: foo
VAR_GLOBAL=$(shell cat /tmp/global)
foo:
echo "local" > /tmp/local
VAR_LOCAL=$(shell cat /tmp/local)
echo ${VAR_GLOBAL}
echo ${VAR_LOCAL}
here is the execution output:
$ echo global > /tmp/global
$ make foo
echo "local" > /tmp/local
VAR_LOCAL=local
echo global
global
echo
As #KelvinSherlock pointed out this is a duplicate of another question
here is the specific solution for my question:
.PHONY: foo
VAR_GLOBAL=$(shell cat /tmp/global)
foo:
echo "local" > /tmp/local
$(eval VAR_LOCAL := $(shell cat /tmp/local))
echo ${VAR_GLOBAL}
echo ${VAR_LOCAL}
You probably want to use the override directive in a target-specific variable assignment, so try:
foo: override LS_LOCAL=$(shell ls /var | tail -1)
echo ${LS_GLOBAL}
echo ${LS_LOCAL}
If LS_LOCAL is never defined (even by builtin-rules) you might not need the override keyword.
BTW, you might avoid $(shell ls /var | tail -1) by using the wildcard function combined with the lastword function (perhaps combined with notdir function), so you might code $(lastword $(wildcard /var/*)) or $(notdir $(lastword $(wildcard /var/*))) instead . However, beware of the order of expansion, and of filenames with spaces. At last the shell function probably uses your $PATH variable (so strange things could happen if some weird ls program appears there before /bin/ls). Perhaps using $(shell /bin/ls /var | /usr/bin/tail -1) might be better.
Look also into Guile-extended make; consider perhaps some other build-automation tool like ninja and/or generating your Makefile (or other build configuration) with something like a configure script generated via autoconf or cmake.
Notice also that a command in recipe can be made of several physical backslashed lines (hence executed in the same shell). Maybe you might consider something like
export MY_VAR=$$(ls /var | tail); \
dosomething; \
use $$MY_VAR
inside some recipe.

Generate and check download file checksum in Makefile

As part of my makefile I need to download and build ZLib. However I want to ensure that when I download ZLib, it is correct by comparing the sha256 of the downloaded .tar.gz against the known correct sha256 value. This need to work on multiple platforms.
I have so far something like the following, however the value of ZLIB_SHA256_ACTUAL always seems to be blank when I compare it with ZLIB_SHA256, so my makefile always exits with an error because the checksums are not the same. I am newish to Makefiles, can someone tell me what I am doing wrong please?
ZLIB_VER = 1.2.11
ZLIB_SHA256 = c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
SHA256_CMD = sha256sum
ifeq ($(PLATFORM), OS_MACOSX)
SHA256_CMD = openssl sha256 -r
endif
ifeq ($(PLATFORM), OS_SOLARIS)
SHA256_CMD = digest -a sha256
endif
libz.a:
-rm -rf zlib-$(ZLIB_VER)
curl -O -L http://zlib.net/zlib-$(ZLIB_VER).tar.gz
ZLIB_SHA256_ACTUAL = $(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz
ifneq ($(ZLIB_SHA256), $(ZLIB_SHA256_ACTUAL))
$(error zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected="$(ZLIB_SHA256)" actual="$(ZLIB_SHA256_ACTUAL)")
endif
tar xvzf zlib-$(ZLIB_VER).tar.gz
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC' ./configure --static && make
cp zlib-$(ZLIB_VER)/libz.a .
A makefile consists of two different programming languages in one file. Most of the file uses makefile syntax, that make understands and parses. But the recipes of the rules use shell syntax, which make doesn't try to interpret: it just passes the contents of the recipe to the shell to interpret.
The recipe is the part of the makefile indented with a TAB character, after a target definition. So in your example above, the target definition is libz.a: and all the lines after that which are indented with a TAB, are recipe lines. They are passed to the shell, not run by make.
The recipe is a single block of lines; you cannot intersperse recipe lines with makefile lines. Once make sees the first non-recipe line, that's the end of the recipe and make starts treating the remaining lines as if they were makefile lines.
Let's look at your rule:
libz.a:
-rm -rf zlib-$(ZLIB_VER)
curl -O -L http://zlib.net/zlib-$(ZLIB_VER).tar.gz
OK, this is fine: you've created a target libz.a and provided two command lines, which are valid shell commands, in your recipe.
ZLIB_SHA256_ACTUAL = $(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz
OK, now you have problems; this is a make variable assignment, not a shell command, but since you've indented it with a TAB make will not interpret it: make will just pass it to the shell. That's not a valid shell command (in the shell, variable assignments cannot have spaces around the equal sign); this is trying to run a program named literally ZLIB_SHA256_ACTUAL and pass it the arguments = and the expansion of the SHA256_CMD variable. Even if this was recognized as a make assignment it wouldn't do what you want since it would just set the value of the variable to the string openssl sha256 -r zlib-1.2.11.tar.gz: you want to run that command and set the variable to the output.
Then the next lines:
ifneq ($(ZLIB_SHA256), $(ZLIB_SHA256_ACTUAL))
$(error zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected="$(ZLIB_SHA256)" actual="$(ZLIB_SHA256_ACTUAL)")
endif
Again, this is wrong because these are make commands but you've put them into a recipe which means they'll be passed to the shell, but the shell doesn't know anything about them.
However, they never get the chance to be passed to the shell because the one thing make does with a recipe before it sends it off to the shell is expand all make variables and functions. So, when make expands this it runs the error function and that immediately fails and make never has a chance to try to run the recipe.
This is the tricky part of make. Maybe I've just confused you with all of the above stuff.
The short, simple answer is: you have to use shell commands to perform operations in a recipe. You cannot use make commands (like ifeq etc.), and if you want to set variables in a recipe they have to be shell variables, not make variables.
So, you want something like this, which uses shell syntax not make syntax for the variable assignment and test.
EDIT Note your SHA generation command doesn't print just the SHA it also prints the name of the file, so you can't compare them as strings: they'll never be the same. You need to do something fancier; there are many ways to go about it. Here I decided to use case to do the comparison:
libz.a:
-rm -rf zlib-$(ZLIB_VER)
curl -O -L http://zlib.net/zlib-$(ZLIB_VER).tar.gz
ZLIB_SHA256_ACTUAL=`$(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz`; \
case "$$ZLIB_SHA256_ACTUAL " in \
($(ZLIB_SHA256)\ *) : ok ;; \
(*) echo zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected=\"$(ZLIB_SHA256)\" actual=\"$$ZLIB_SHA256_ACTUAL\"; \
exit 1 ;; \
esac
tar xvzf zlib-$(ZLIB_VER).tar.gz
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC' ./configure --static && $(MAKE)
cp zlib-$(ZLIB_VER)/libz.a .
Note that each logical line in the recipe is passed to a new instance of the shell, so if you want to set a shell variable and test its value you have to combine physical lines into one logical line with the backslash/newline syntax.
Also, when running a sub-make in a recipe you should always use the variable $(MAKE) and never use just make.

What's causing this freaky time travel with make?

I have a file, tmp containing:
{
"VolumeId": "vol--22222222",
}
and a makefile, containing only:
MYFILE =latest
x:
\cp tmp $(MYFILE)
grep VolumeId $(MYFILE)
#echo $(shell grep VolumeId $(MYFILE))
If I run make x, I get:
\cp tmp latest
grep VolumeId latest
"VolumeId": "vol--22222222",
VolumeId: vol--22222222,
as expected. If I modify the file tmp, replacing 2's wit 4's, I get:
\cp tmp latest
grep VolumeId latest
"VolumeId": "vol--44444444444",
VolumeId: vol--22222222,
...huh? The greps return different results! The second contains data from the file before the copy.
I run
rm latest ; make x
I get:
\cp tmp latest
grep VolumeId latest
"VolumeId": "vol--4444444444",
What's going on here?
GNU Make 3.81. Ubuntu 12.04 on VMWare 5.
Update 1
Here's a more explicit example
CMD0 =$(shell date +"%s.%N")
CMD1 =date +"%s.%N"
CMD2 =date +"%s.%N"
y:
#sleep 2
#date +"%s.%N" # 2nd
#echo $(CMD0) # 1st A
#sleep 2
#date +"%s.%N" # 3rd
#sleep 2
#$(CMD1) # 4th
#sleep 2
#echo $(shell $(CMD2) ) # 1st B
Output:
1381596581.761093768
1381596579.743610973
1381596583.769058027
1381596585.774766561
1381596579.751625601
Its look like all the $(shell ... ) commands get evaluated together before any line of the y recipe.
This is a little surprising (and counter-intuitive - what's the rationale for this odd design?). It has important consequences if the shell commands have side effects. Also, I can't find any documentation in make manual that describes this order of evaluation.
Update 2
What's especially odd about the above is that its hard to come up with a mental model for the order of evaluation. Is it that any rule of the form, $(function ... ) is executed before each line of the recipe? If so, why is $(CMD0) evaluated before the recipe, but not $(CMD1). Its true that CMD0 contains a $(f ... ) - does make look at this, even though CMD0 is declared as a delayed evaluated variable (i.e.declared with = not :=)?
Update 3
Reducing it down to essential components:
notSafe:
backup-everything # Format executed even if backup fails
echo $(shell format-disk) | tee log.txt #
CMDX =$(shell format-disk)
alsoNotSafe:
backup-everything # Format executed even if backup fails
echo $(CMDX) | tee log.txt # Even though CMDX is a delayed evaluation variable
CMDZ =format-disk
safe:
backup-everything # Works as expected.
$(CMDZ) | tee log.txt # Evaluation of CMDZ is delayed until line is executed.
Mad. Who designed this?
You're not telling us everything: when you run it the 1st time, you echo an empty line. Instead you get a grep file not found error.
And that's the decisive hint: $-expressions are evaluated by any variant of make before running the commands of the rule. I.e. in each later run you see the file contents of the previous run.
Btw. your leading backslash is spurious and causes the command to be passed to the Shell, instead of being directly executed by make. With makepp's builtin commands (prefixed by &) and fixed dependencies that would be:
latest: tmp
&cp $(input) $(output)
&grep VolumeId $(output)
#&echo $(&grep VolumeId $(output))
That doesn't change the problem, but by using the proper rule variables, makes it obvious what's wrong.

Resources