My Makefile contains something like
BASE_VAR=$(cat path/to/value.txt)
DERIVED_VAR=$(if $(BASE_VAR),foo,bar)
This does not produce the results I expected. make --debug=a produces detailed trace but path/to/value.txt doesn't appear anywhere in it. How do I debug this kind of variable assignment in a makefile?
The problem comes from:
BASE_VAR=$(cat path/to/value.txt)
As there is not make variable or macro named cat path/to/value.txt the expansion
$(cat path/to/value.txt) produces the empty string. If you want to set variable BASE_VAR with the content of file path/to/value.txt you need the shell make function:
BASE_VAR = $(shell cat path/to/value.txt)
And about your question on debugging variables, the simplest in your case is probably to use the info make function:
$ cat Makefile
BASE_VAR = $(shell cat path/to/value.txt)
DERIVED_VAR = $(if $(BASE_VAR),foo,bar)
.PHONY: debug
debug:
$(info BASE_VAR = $(BASE_VAR))
$(info DERIVED_VAR = $(DERIVED_VAR))
$ make debug
BASE_VAR = blah
DERIVED_VAR = foo
make: Nothing to be done for 'debug'.
But that target-specific variables have different values in different targets. Use $(info ...) in the recipe of the target of interest:
target: VAR = value
target: ...
$(info VAR of $# = $(VAR))
I have a Fortran code that can be compiled by the following Makefile:
FC = mpif90
FFLAGS := ...
TARGET = run1
SRC = main.f90 param.f90 ...
OBJ = $(SRC:.f90=.o)
all: $(TARGET)
$(TARGET): $(OBJ) $(FC) $(FFLAGS)
...
In the above, the TARGET can be run1 or run2 or run3, depending on the value of a variable in my script. For example, if I have the following in param.f90:
character(len=1), parameter :: case_num = "1"
Then, I want the TARGET variable to be set as run1. Now, the question is how to do that automatically without manually changing the Makefile every time?
I have tried something like
num = $(grep 'case_num = "' param.f90 | sed -n -e 's/^.*case_num = "//p' | cut -c1-1)
TARGET = run$num
in the Makefile, which doesn't work. I have checked that the grep and sed sentence is correct in a regular bash script. How to do this in a Fortran Makefile? Any help is appreciated.
Your makefile appears to a GNU Make makefile. To assign the output of a
shell command to a make variable, you need to use the $(shell ...) function,
like:
num := $(shell grep 'case_num = "' param.f90 | sed -n -e 's/^.*case_num = "//p' | cut -c1-1)
TARGET := run$(num)
I need to write a pattern rule for the following case:
There are 2 folders: A and B
Running the command python gen.py --a=A/file1.foo --b=file2.bar --c=file3.bar generates B/file1.foo
file1, file2 and file3 are different strings
Is there a way to group those filenames in some kind of a multidimensional array, so that all files are written exactly once (I'll use python syntax):
files = [["a1.foo", "a2.bar", "a3.bar"],
#...200 other groups...
["b1.foo", "b2.bar", "b3.bar"]]
and then the rule looks like this:
$(files): B/{reference 1 elem}: A/{1 elem} {2 elem} {3 elem}
python gen.py --a=A/{1 elem} --b={2 elem} --c={3 elem}
Any ideas how to archive it?
You can use standard make syntax for that:
all :
targets :=
define add_target
B/${1}: A/${1} ${2} ${3}
targets += B/${1}
endef
# Build dependencies.
$(eval $(call add_target,a1.foo,a2.bar,a3.bar))
# ...
$(eval $(call add_target,b1.foo,b2.bar,b3.bar))
# One generic rule for all ${targets}
${targets} : % :
#echo Making $# from $^
all : ${targets}
.PHONY: all
Note that these $(eval $(call add_target,...) are white-space sensitive, do not insert spaces in there.
If you would like make to create the directory for outputs automatically do:
${targets} : % : | B
B :
mkdir $#
Sometimes a little repetition isn't so bad really
targets := B/a1.foo B/b1.foo
.PHONY: all
all: $(targets)
$(targets): B/%: A/%
python gen.py --a=$< --b=$(word 2,$^) --c=$(word 3,$^)
B/a1.foo: a2.bar a3.bar
B/b1.foo: b2.bar b3.bar
In Kernel Makefile i found the code like below:
ctags CTAGS CSCOPE: $(HEADERS) $(SOURCES)
$(ETAGS) $(ETAGSFALGS) $(HEADERS) $(SOURCES)
$(call cmd, ctags)
Also, where can i find the Macro or function ?
Using MadScientist's method on kernel v4.1:
make -p | grep -B1 -E '^cmd '
we find:
# makefile (from `scripts/Kbuild.include', line 211)
cmd = #$(echo-cmd) $(cmd_$(1))
scripts/Kbuild.include is included on the top level Makefile. It also contains:
echo-cmd = $(if $($(quiet)cmd_$(1)),\
echo ' $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';)
quiet: set at the top level makefile, depending on the value of V.
Will be either:
quiet_ to print CC file.c
empty to print the command on V=
silent_ to not print anything on make -s
escsq is defined as:
squote := '
escsq = $(subst $(squote),'\$(squote)',$1)
It escapes single quotes so that echo '$(call escsq,Letter 'a'.' will print properly in sh.
echo-why: defined further down at Kbuild.include.
It is used for make V=2, and says why a target is being remade.
The setup of make tags is done in the Makefile:
quiet_cmd_tags = GEN $#
cmd_tags = $(CONFIG_SHELL) $(srctree)/scripts/tags.sh $#
tags TAGS cscope gtags: FORCE
$(call cmd,tags)
Which shows the typical usage pattern for calling commands on kbuild:
quiet_cmd_XXX = NAME $#
cmd_XXX = actual-command $#
target: prerequisites
$(call cmd,tags)
A comment on the Makefile explains how all of this is done to make the make output prettier:
# Beautify output
# ---------------------------------------------------------------------------
#
# Normally, we echo the whole command before executing it. By making
# that echo $($(quiet)$(cmd)), we now have the possibility to set
# $(quiet) to choose other forms of output instead, e.g.
#
# quiet_cmd_cc_o_c = Compiling $(RELDIR)/$#
# cmd_cc_o_c = $(CC) $(c_flags) -c -o $# $<
If you run make -p it will print the entire database of all variables, rules, etc. with line numbers where they were last defined.
How do you perform a logical OR using make's ifeq operator?
e.g., I have (simplified):
ifeq ($(GCC_MINOR), 4)
CFLAGS += -fno-strict-overflow
endif
ifeq ($(GCC_MINOR), 5)
CFLAGS += -fno-strict-overflow
endif
but would like to consolidate these lines.
(yes, yes, autotools, configure, etc etc; too heavy-handed for the current situation, would like to keep everything within the Makefile here)
[logical opposite of this question: How to Use of Multiple condition in 'ifeq' statement ]
As found on the mailing list archive,
http://osdir.com/ml/gnu.make.windows/2004-03/msg00063.html
http://osdir.com/ml/gnu.make.general/2005-10/msg00064.html
one can use the filter function.
For example
ifeq ($(GCC_MINOR),$(filter $(GCC_MINOR),4 5))
filter X, A B will return those of A,B that are equal to X.
Note, while this is not relevant in the above example, this is a XOR operation. I.e. if you instead have something like:
ifeq (4, $(filter 4, $(VAR1) $(VAR2)))
And then do e.g. make VAR1=4 VAR2=4, the filter will return 4 4, which is not equal to 4.
A variation that performs an OR operation instead is:
ifneq (,$(filter $(GCC_MINOR),4 5))
where a negative comparison against an empty string is used instead (filter will return en empty string if GCC_MINOR doesn't match the arguments). Using the VAR1/VAR2 example it would look like this:
ifneq (, $(filter 4, $(VAR1) $(VAR2)))
The downside to those methods is that you have to be sure that these arguments will always be single words. For example, if VAR1 is 4 foo, the filter result is still 4, and the ifneq expression is still true. If VAR1 is 4 5, the filter result is 4 5 and the ifneq expression is true.
One easy alternative is to just put the same operation in both the ifeq and else ifeq branch, e.g. like this:
ifeq ($(GCC_MINOR),4)
#echo Supported version
else ifeq ($(GCC_MINOR),5)
#echo Supported version
else
#echo Unsupported version
endif
You can introduce another variable. It doesnt consolidate both checks, but it at least avoids having to put the body in twice:
do_it =
ifeq ($(GCC_MINOR), 4)
do_it = yes
endif
ifeq ($(GCC_MINOR), 5)
do_it = yes
endif
ifdef do_it
CFLAGS += -fno-strict-overflow
endif
I don't think there's a concise, sensible way to do that, but there are verbose, sensible ways (such as Foo Bah's) and concise, pathological ways, such as
ifneq (,$(findstring $(GCC_MINOR),4-5))
CFLAGS += -fno-strict-overflow
endif
(which will execute the command provided that the string $(GCC_MINOR) appears inside the string 4-5).
Here more flexible variant: it uses external shell, but allows to check for arbitrary conditions:
ifeq ($(shell test ".$(GCC_MINOR)" = .4 -o \
".$(GCC_MINOR)" = .5 -o \
".$(TODAY)" = .Friday && printf "true"), true)
CFLAGS += -fno-strict-overflow
endif
Note that ifeq ($(GCC_MINOR),$(filter $(GCC_MINOR),4 5)) will catch the case where GCC_MINOR is not defined at all.
If you want to catch GCC_MINOR==4 or GCC_MINOR==5 this will do trick:
ifneq ($(filter $(GCC_MINOR),4 5),)
echo "def"
endif
ifeq ($(GCC_MINOR), 4)
CFLAGS += -fno-strict-overflow
endif
ifeq ($(GCC_MINOR), 5)
CFLAGS += -fno-strict-overflow
endif
Another you can consider using in this case is:
GCC42_OR_LATER = $(shell $(CXX) -v 2>&1 | $(EGREP) -c "^gcc version (4.[2-9]|[5-9])")
# -Wstrict-overflow: http://www.airs.com/blog/archives/120
ifeq ($(GCC42_OR_LATER),1)
CFLAGS += -Wstrict-overflow
endif
I actually use the same in my code because I don't want to maintain a separate config or Configure.
But you have to use a portable, non-anemic make, like GNU make (gmake), and not Posix's make.
And it does not address the issue of logical AND and OR.
In the case that you are looking to logically "or" several boolean flags together, one practical hack can be to simply let strings concatenate: if the end result is an empty string, then none of the options were true, else non-empty then at least one of them was enabled:
# Use `docker build --pull` in case either `PULL=whatever` is set OR if the `CI` environment variable is present.
ifneq ($(PULL)$(CI),)
PULL_OR_NOT := --pull
endif
build:
docker build $(PULL_OR_NOT)