Passing command line argument in Makefile (tcsh syntax) - makefile

HUB ?= HUBHA
if (${HUB}, HUBHA) then
profile = example_profile_ha.txt
elif (${HUB}, HUBC) then
profile = example_profile_c.txt
else
profile = example_profile.txt
endif
hubname ?= ${HUB}
I want to pass value of HUB through command line, how to do that?

Related

How to compare strings in a conditional assignment?

Is difficult to understand this official guide... How to check string condition?
This assign is working fine, but is ugly:
x := $$( if [ $(HOME) = "/root" ]; then echo "IS ROOT"; else echo "IS OTHER"; fi )
This is not working:
ifeq ($(HOME),"/root")
x = "IS ROOT"
else
x = "IS OTHER"
endif
make --version says:
GNU Make 4.2.1
Built for x86_64-pc-linux-gnu
When you write:
ifeq ($(HOME),"/root")
you're using the form (from the doc you cite):
ifeq (arg1, arg2)
and arg1 is set to $(HOME) and arg2 is set to "/root". Suppose the HOME variable is set to the path /root.
So, make will compare /root to "/root". Are these the same string? No: one has quotes around it so they are not the same string.
You want to use:
ifeq ($(HOME),/root)

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

How does the makefile write user-defined variables to the yaml file to support vagrantfile fetch parameters

I have a project which use makefile to control vagrant, I want to put the vagrant parameter into the makefile, such as cpu, memory, ip, hostname, forwarded_port and the like. I find a way that vagrantfile read yaml file to parameterize vagrantfile. So makefile needs a target to read all the user option variables and write them to config.yaml as key-value pairs.
The sample is as follows
# === BEGIN USER OPTIONS ===
BOX_OS ?= fedora
# Box setup
#BOX_IMAGE
# Disk setup
DISK_COUNT ?= 1
DISK_SIZE_GB ?= 25
# VM Resources
MASTER_CPUS ?= 2
MASTER_MEMORY_SIZE_GB ?= 2
NODE_CPUS ?= 2
NODE_MEMORY_SIZE_GB ?= 2
NODE_COUNT ?= 2
# Network
MASTER_IP ?= 192.168.26.10
NODE_IP_NW ?= 192.168.26.
POD_NW_CIDR ?= 10.244.0.0/16
...
...
# === END USER OPTIONS ===
The echo command does achieve it
# Makefile
envInit:
#echo "POD_NW_CIDR : \"$(POD_NW_CIDR)\"" > ${FILECWD}/configs.yaml
But too many variables can be too complex.
Is there a way to bulk read variables and their values and write them to a yml file
I would very appreciate it if you guys can tell me how to achieve it that bulk read variables and their values and write them to a yml file.
Define all user options (along with the default values) as a list, so that they are iterable:
# list of user options with default values
userOptions = \
BOX_OS=2 \
DISK_COUNT=1 \
MASTER_IP=192.168.26.10
# replace each default value with the env value, if any
userOptionValues = $(foreach i, $(userOptions), \
$(word 1, $(subst =, ,$i))=$(or \
$($(word 1, $(subst =, ,$i))), $($(word 1, $(subst =, ,$i))), $(word 2, $(subst =, ,$i))))
# write the yaml file
envInit:
# empty the file
#printf "" > configs.yaml
# write a line for each option
#for i in $(userOptionValues); do \
printf "%s : %s\n" "$$(printf $$i | cut -d= -f1)" "$$(printf $$i | cut -d= -f2)" >> configs.yaml; \
done
#flyx Thank you for you answer, your code does work great. But I seem to have found a more convenient way, and I've partially modified it.
printvars:
#echo$(foreach V,$(sort $(.VARIABLES)), \
$(if $(filter-out environment% default automatic,$(origin $V)),$(info $V: $($V))))
But there is still a gap between achieving the goal.
# the Makefile test file
FILECWD = $(shell pwd)
# === BEGIN USER OPTIONS ===
CLOUD_IP ?= 192.168.79.222
CLOUD_NAME ?= cloud
CLOUD_CPU ?= 6
CLOUD_MEMORY ?= 8
# === END USER OPTIONS ===
printvars:
#echo$(foreach V,$(sort $(.VARIABLES)), \
$(if $(filter-out environment% default automatic,$(origin $V)),$(info $V: $($V))))
make printvars's output contains a number of other variables
$ make printvars
.DEFAULT_GOAL: printvars
CLOUD_IP: 192.168.79.222
CLOUD_MEMORY: 8
CLOUD_NAME: cloud
CURDIR: /testmakecreateyml0930
FILECWD: /testmakecreateyml0930
GNUMAKEFLAGS:
MAKEFILE_LIST: Makefile
MAKEFLAGS:
SHELL: /bin/sh
And it can only be printed and not exported to the yaml file.This is only one step away from success.
I would appreciate it if you could help me modify it to achieve my goal
You can write directly to a file with GNUmakes $(file) function:
define newline :=
$(strip)
$(strip)
endef
space := $(strip) $(strip)#
-never-matching := ¥# character 165, this is used as a list element that should never appear as a real element
option-names = $(subst $(-never-matching),,$(filter $(-never-matching)%,$(subst $(-never-matching)$(space),$(-never-matching),$(-never-matching)$(strip $(subst $(newline), $(-never-matching),$1)))))
# define your user options in as many separate parts as you like, spaces and empty lines included:
define USER_OPTIONS +=
a = spaces are no problem
b = "neither nearly all 'other' characters: 8&)("
endef
define USER_OPTIONS +=
c = baz baf
d = foobar
endef
# make all definition make variables verbatim
$(eval $(USER_OPTIONS))
YAML_FORMAT := $(foreach name,$(call option-names,$(USER_OPTIONS)),$(newline)$(name) : $($(name)))
# write the file. Warning: this happens before any rule is run!
$(file >test.yaml,$(YAML_FORMAT))
$(info $(foreach name,$(call option-names,$(USER_OPTIONS)),<$(name) : $($(name))> ))
The trick lies in the clustering of all relevant user option variables in one multi-line make variable. The function option-names pulls all identifiers from that variable into a separate list.
I took the newline etc. character definitions from the GNUmake table toolkit which has many functions for "programmatic" make.

Make: syntax error near unexpected token `--initialization'

I am trying to write 'if' check in define directive in one of our make file.
Actually I am trying to check platform and proceed with environment setting.
define templ_32
mkdir -p $(#D)
if [ "$(PLAT)" = "x86_64" ]; then env PERLLIB=$(EXTLIBS)/$(PLAT32)/lib/perl5/site_perl/5.10.0 --template $<; fi
if [ "$(PLAT)" = "aarch64" ]; then env PERLLIB=$(EXTLIBS)/$(PLAT32)/lib/perl5/site_perl/5.8.0 --template $<; fi
endef
I am using above define directive in one of my target recipe as follows.
some/%.c: test/tmpl-%.c $(NEW_DATA32)
$(templ_32) --initialization $(NEW_DATA32)
When I run the build with above changes. I am getting error :
/bin/sh: -c: line 0: syntax error near unexpected token `--initialization'
And also from the log I am seeing whole 'if' condition as follows.
if [ "aarch64" = "x86_64" ]; then env PERLLIB=$(EXTLIBS)/$(PLAT32)/lib/perl5/site_perl/5.8.0 --template /test/deploy/tmpl-kt.c ; fi --initialization /work/deploy/test.pl
From my success log which has no conational 'if' statements I can see only
env PERLLIB=$(EXTLIBS)/$(PLAT32)/lib/perl5/site_perl/5.8.0 --template /test/deploy/tmpl-kt.c --initialization /work/deploy/test.pl
I don't want to get the 'if' checks along with 'env' command.. I am only interested in
env PERLLIB=$(EXTLIBS)/$(PLAT32)/lib/perl5/site_perl/5.8.0 --template
How can I fix this issue?
You could use make conditionals instead of shell if statements:
ifeq ($(PLAT),x86_64)
PERLVERSION = 5.10.0
else ifeq ($(PLAT),aarch64)
PERLVERSION = 5.8.0
else
$(error "Unknown arhictecture: $(PLAT)")
endif
And then:
PERLLIB = $(EXTLIBS)/$(PLAT32)/lib/perl5/site_perl/$(PERLVERSION)
some/%.c: test/tmpl-%.c $(NEW_DATA32)
mkdir -p $(#D); \
env PERLLIB=$(PERLLIB) --template $< --initialization $(NEW_DATA32)
You should just use constructed macro names for this. For example:
PLATFORMS := x86_64 aarch64
$(or $(filter $(PLAT),$(PLATFORMS)),$(error Unknown architecture: $(PLAT)))
x86_64_PERLVER := 5.10.0
aarch64_PERLVER := 5.8.0
PERLLIB = $(EXTLIBS)/$(PLAT32)/lib/perl5/site_perl/$($(PLAT)_PERLVER)
More on this here.

:= and ?= in Makefile [duplicate]

Can anybody give a clear explanation of how variable assignment really works in Makefiles.
What is the difference between :
VARIABLE = value
VARIABLE ?= value
VARIABLE := value
VARIABLE += value
I have read the section in GNU Make's manual, but it still doesn't make sense to me.
Lazy Set
VARIABLE = value
Normal setting of a variable, but any other variables mentioned with the value field are recursively expanded with their value at the point at which the variable is used, not the one it had when it was declared
Immediate Set
VARIABLE := value
Setting of a variable with simple expansion of the values inside - values within it are expanded at declaration time.
Lazy Set If Absent
VARIABLE ?= value
Setting of a variable only if it doesn't have a value. value is always evaluated when VARIABLE is accessed. It is equivalent to
ifeq ($(origin VARIABLE), undefined)
VARIABLE = value
endif
See the documentation for more details.
Append
VARIABLE += value
Appending the supplied value to the existing value (or setting to that value if the variable didn't exist)
Using = causes the variable to be assigned a value. If the variable already had a value, it is replaced. This value will be expanded when it is used. For example:
HELLO = world
HELLO_WORLD = $(HELLO) world!
# This echoes "world world!"
echo $(HELLO_WORLD)
HELLO = hello
# This echoes "hello world!"
echo $(HELLO_WORLD)
Using := is similar to using =. However, instead of the value being expanded when it is used, it is expanded during the assignment. For example:
HELLO = world
HELLO_WORLD := $(HELLO) world!
# This echoes "world world!"
echo $(HELLO_WORLD)
HELLO = hello
# Still echoes "world world!"
echo $(HELLO_WORLD)
HELLO_WORLD := $(HELLO) world!
# This echoes "hello world!"
echo $(HELLO_WORLD)
Using ?= assigns the variable a value iff the variable was not previously assigned. If the variable was previously assigned a blank value (VAR=), it is still considered set I think. Otherwise, functions exactly like =.
Using += is like using =, but instead of replacing the value, the value is appended to the current one, with a space in between. If the variable was previously set with :=, it is expanded I think. The resulting value is expanded when it is used I think. For example:
HELLO_WORLD = hello
HELLO_WORLD += world!
# This echoes "hello world!"
echo $(HELLO_WORLD)
If something like HELLO_WORLD = $(HELLO_WORLD) world! were used, recursion would result, which would most likely end the execution of your Makefile. If A := $(A) $(B) were used, the result would not be the exact same as using += because B is expanded with := whereas += would not cause B to be expanded.
I suggest you do some experiments using "make". Here is a simple demo, showing the difference between = and :=.
/* Filename: Makefile*/
x := foo
y := $(x) bar
x := later
a = foo
b = $(a) bar
a = later
test:
#echo x - $(x)
#echo y - $(y)
#echo a - $(a)
#echo b - $(b)
make test prints:
x - later
y - foo bar
a - later
b - later bar
Check more elaborate explanation here
When you use VARIABLE = value, if value is actually a reference to another variable, then the value is only determined when VARIABLE is used. This is best illustrated with an example:
VAL = foo
VARIABLE = $(VAL)
VAL = bar
# VARIABLE and VAL will both evaluate to "bar"
When you use VARIABLE := value, you get the value of value as it is now. For example:
VAL = foo
VARIABLE := $(VAL)
VAL = bar
# VAL will evaluate to "bar", but VARIABLE will evaluate to "foo"
Using VARIABLE ?= val means that you only set the value of VARIABLE if VARIABLE is not set already. If it's not set already, the setting of the value is deferred until VARIABLE is used (as in example 1).
VARIABLE += value just appends value to VARIABLE. The actual value of value is determined as it was when it was initially set, using either = or :=.
In the above answers, it is important to understand what is meant by "values are expanded at declaration/use time". Giving a value like *.c does not entail any expansion. It is only when this string is used by a command that it will maybe trigger some globbing. Similarly, a value like $(wildcard *.c) or $(shell ls *.c) does not entail any expansion and is completely evaluated at definition time even if we used := in the variable definition.
Try the following Makefile in directory where you have some C files:
VAR1 = *.c
VAR2 := *.c
VAR3 = $(wildcard *.c)
VAR4 := $(wildcard *.c)
VAR5 = $(shell ls *.c)
VAR6 := $(shell ls *.c)
all :
touch foo.c
#echo "now VAR1 = \"$(VAR1)\"" ; ls $(VAR1)
#echo "now VAR2 = \"$(VAR2)\"" ; ls $(VAR2)
#echo "now VAR3 = \"$(VAR3)\"" ; ls $(VAR3)
#echo "now VAR4 = \"$(VAR4)\"" ; ls $(VAR4)
#echo "now VAR5 = \"$(VAR5)\"" ; ls $(VAR5)
#echo "now VAR6 = \"$(VAR6)\"" ; ls $(VAR6)
rm -v foo.c
Running make will trigger a rule that creates an extra (empty) C file, called foo.c but none of the 6 variables has foo.c in its value.
The most upvoted answer can be improved.
Let me refer to GNU Make manual "Setting variables" and "Flavors", and add some comments.
Recursively expanded variables
The value you specify is installed verbatim; if it contains references to other variables, these references are expanded whenever this variable is substituted (in the course of expanding some other string). When this happens, it is called recursive expansion.
foo = $(bar)
The catch: foo will be expanded to the value of $(bar) each time foo is evaluated, possibly resulting in different values. Surely you cannot call it "lazy"! This can surprise you if executed on midnight:
# This variable is haunted!
WHEN = $(shell date -I)
something:
touch $(WHEN).flag
# If this is executed on 00:00:00:000, $(WHEN) will have a different value!
something-else-later: something
test -f $(WHEN).flag || echo "Boo!"
Simply expanded variable
VARIABLE := value
VARIABLE ::= value
Variables defined with ‘:=’ or ‘::=’ are simply expanded variables.
Simply expanded variables are defined by lines using ‘:=’ or ‘::=’ [...]. Both forms are equivalent in GNU make; however only the ‘::=’ form is described by the POSIX standard [...] 2012.
The value of a simply expanded variable is scanned once and for all, expanding any references to other variables and functions, when the variable is defined.
Not much to add. It's evaluated immediately, including recursive expansion of, well, recursively expanded variables.
The catch: If VARIABLE refers to ANOTHER_VARIABLE:
VARIABLE := $(ANOTHER_VARIABLE)-yohoho
and ANOTHER_VARIABLE is not defined before this assignment, ANOTHER_VARIABLE will expand to an empty value.
Assign if not set
FOO ?= bar
is equivalent to
ifeq ($(origin FOO), undefined)
FOO = bar
endif
where $(origin FOO) equals to undefined only if the variable was not set at all.
The catch: if FOO was set to an empty string, either in makefiles, shell environment, or command line overrides, it will not be assigned bar.
Appending
VAR += bar
Appending:
When the variable in question has not been defined before, ‘+=’ acts just like normal ‘=’: it defines a recursively-expanded variable. However, when there is a previous definition, exactly what ‘+=’ does depends on what flavor of variable you defined originally.
So, this will print foo bar:
VAR = foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))
but this will print foo:
VAR := foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))
The catch is that += behaves differently depending on what type of variable VAR was assigned before.
Multiline values
The syntax to assign multiline value to a variable is:
define VAR_NAME :=
line
line
endef
or
define VAR_NAME =
line
line
endef
Assignment operator can be omitted, then it creates a recursively-expanded variable.
define VAR_NAME
line
line
endef
The last newline before endef is removed.
Bonus: the shell assignment operator ‘!=’
HASH != printf '\043'
is the same as
HASH := $(shell printf '\043')
Don't use it. $(shell) call is more readable, and the usage of both in a makefiles is highly discouraged. At least, $(shell) follows Joel's advice and makes wrong code look obviously wrong.

Resources