Could not read .config parameters in Makefile - makefile

I have a simple question about how can makefile read in a varibale is set in .config file.
for example i have CONFIG_a=y CONFIG_b and CONFIG_c as three variables in .config of my linux configuration . I have a Makefile which should define a variable depending upon which CONFIG_X is set
if CONFIG_A
DFLAGS=-DABC
if CONFIG_B
DFLAGS=-DBCD
if CONFIG_C
DFLAGS=-DCDE
How could i achieve this in make file.
I tried
ifeq ($(TARGET_A),y)
DFLAGS=-DABC
else
ifeq ($(TARGET_B),y)
DFLAGS=-DBCD
else
DFLAGS=-DCDE
endif
endif
THIS could not help each time -DCDE is set.

You seem to be using GNU Make.
Let's assume a that exactly one of CONFIG_a, CONFIG_b, CONFIG_c must be set
in foo.config, by a line of the form:
CONFIG_(a|b|c)\s*=\s*y
where \s* means 0 or more spaces.
Then you can parse the setting and conditionally set DFLAGS as shown:-
Makefile
CONFIG := $(filter CONFIG%=y,$(shell tr -d ' ' < foo.config))
ifneq ($(words $(CONFIG)),1)
$(error Exactly one CONFIG_? must be set)
endif
ifeq ($(CONFIG),CONFIG_a=y)
DFLAGS:=-DABC
endif
ifeq ($(CONFIG),CONFIG_b=y)
DFLAGS=-DBCD
endif
ifeq ($(CONFIG),CONFIG_c=y)
DFLAGS=-DCDE
endif
$(info CONFIG=$(CONFIG))
.PHONY: all
all:
#echo DFLAGS=$(DFLAGS)
With:
foo.config (1)
CONFIG_a = y
CONFIG_b
CONFIG_c
you'll get:
$ make
CONFIG=CONFIG_a=y
DFLAGS=-DABC
With:
foo.config (2)
CONFIG_a = n
CONFIG_b=y
CONFIG_c
you'll get:
$ make
CONFIG=CONFIG_b=y
DFLAGS=-DBCD
With:
foo.config (3)
CONFIG_a = y
CONFIG_b
CONFIG_c=y
you'll get:
$ make
Makefile:3: *** Exactly one CONFIG_? must be set. Stop.
And with:
foo.config (4)
CONFIG_a = n
CONFIG_b
CONFIG_c
again:
$ make
Makefile:3: *** Exactly one CONFIG_? must be set. Stop.
To understand how it works see:-
man tr
8.13 The shell Function in the GNU Make manual
8.2 Functions for String Substitution and Analysis in the GNU Make manual.

Related

Dollar in conditional variable assignment operator in Makefile

Is it possible to pass value with single dollar from shell to Makefile, or I it is only way to put double dollar in bash and then to call make?
Makefile is:
HASH ?= $$6$$salt$$val
.PHONY: tst
tst:
echo '$(HASH)'
Command to run:
> make HASH='$6$salt$val'
echo 'altal'
altal
If I use double quotes, all is fine:
> make HASH='$$6$$salt$$val'
echo '$6$salt$val'
$6$salt$val
But is it possible do not make substitution $ to $$ in bash?
How about writing the initialisation within the file identical to the one coming from the command line? The below script demonstrates how to rewrite a variable with the override directive:
quote-one-level = $(eval override $1=$(subst $,$$$$,$(value $1)))
var-info = $(info $1=$(value $1) flavour=$(flavor $1) origin=$(origin $1))
A ?= $abc
$(call var-info,A)
$(call quote-one-level,A)
$(call var-info,A)
$(call var-info,B)
$(call quote-one-level,B)
$(call var-info,B)
export A
export B
all:
#echo A = '$(A)'
#echo B = '$(B)'
ifeq ($(MAKELEVEL),0)
$(MAKE)
endif
Inflating one $ to $$$$ (and not just $$) is necessary because the eval command literally generates make code, thereby obviously reducing the quoting level by one. Resulting output:
$ make B='$abc'
A=$abc flavour=recursive origin=file
A=$$abc flavour=recursive origin=override
B=$abc flavour=recursive origin=command line
B=$$abc flavour=recursive origin=override
A = $abc
B = $abc
make
make[1]: Entering directory
A=$abc flavour=recursive origin=environment
A=$$abc flavour=recursive origin=override
B=$abc flavour=recursive origin=command line
B=$$abc flavour=recursive origin=override
A = $abc
B = $abc
make[1]: Leaving directory
Try this:
In console:
export HASH='$6$salt$val'; make
in Makefile:
.PHONY: tst
tst:
#echo "$$HASH"
Result:
$6$salt$val

How to debug GNU make's variable assignment

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))

Dynamically generating a list of targets

If a file exists, I want to add a target to build. If the file does not exist, I want the target to be skipped.
an example:
FILENAME = f
TARGETS := normal
ifneq($(shell stat test_$(FILENAME).c), "")
TARGETS += test
endif
all: $(TARGETS)
normal:
#echo normal
test:
#echo test
I'm not sure the $(shell stat ...) part even works, but the bigger problem is that make with any file test_f.c in the current folder gives:
Makefile:4: *** multiple target patterns. Stop.
Removing the ifneq ... endif block makes the target normal. How can I only run the target test if test_f.c exists?
What you can do is generate a string variable (let's call it OPTIONAL) such that when 'test_f.c' exists, OPTIONAL=test; otherwise, OPTIONAL=_nothing_. And then add OPTIONAL as a prerequisite of all. e.g.:
FILENAME = f
TARGETS = normal
OPTIONAL = $(if $(wildcard test_f.c), test, )
all: $(TARGETS) $(OPTIONAL)
normal:
#echo normal
test:
#echo test
You can also iterate over targets with for loop
.PHONY: all
RECIPES = one
all: RECIPES += $(if $(wildcard test_f.c), two, )
all:
for RECIPE in ${RECIPES} ; do \
$(MAKE) $${RECIPE} ; \
done
one:
$(warning "One")
two:
$(warning "Two")
> make
for RECIPE in one ; do \
/Applications/Xcode.app/Contents/Developer/usr/bin/make ${RECIPE} ; \
done
makefile:11: "One"
make[1]: `one' is up to date.
> touch test_f.c
> make
for RECIPE in one two ; do \
/Applications/Xcode.app/Contents/Developer/usr/bin/make ${RECIPE} ; \
done
makefile:11: "One"
make[1]: `one' is up to date.
makefile:14: "Two"
make[1]: `two' is up to date.

conditional execution in makefile

I have the following makefile:
C_FILE=""
cfg:
## C to CFG ####
# echo $(C_FILE)
ifndef C_FILE
$(error variable C_FILE not set)
endif
$(eval CFG_FILE := ./outputs/temp/$(shell basename $(C_FILE) .c).cfg)
gcc -fdump-tree-cfg=$(CFG_FILE) $(C_FILE)
When I run the command make cfg C_FILE="./inputs/Fib.c" it always
terminates saying variable C_FILE not set.
Lines beginning with a tab character (by default) aren't parsed by make (other than for variable expansion), they're sent directly to the shell, get rid of the indents on the lines with the make conditionals
C_FILE=""
cfg:
## C to CFG ####
# echo $(C_FILE)
ifndef C_FILE
$(error variable C_FILE not set)
endif
$(eval CFG_FILE := ./outputs/temp/$(shell basename $(C_FILE) .c).cfg)
gcc -fdump-tree-cfg=$(CFG_FILE) $(C_FILE)
I'd like to add some comments to user657267's answer.
ifndef C_FILE is always false. C_FILE is defined on the first line, or from command line. Consider using ifeq "" "$(C_FILE)".
Quote (") is normal character in makefile. Define empty variable this way:
C_FILE=
instead of using $(shell ) function, use makefile built-ins:
$(basename $(notdir $(C_FILE)))
avoid using $(eval ) if not really needed. Extract relevant code outside recipe.
My proposal is:
C_FILE=
ifeq "" "$(C_FILE)"
$(error variable C_FILE not set)
endif
CFG_FILE=./outputs/temp/$(basename $(notdir $(C_FILE))).cfg
cfg:
## C to CFG ####
# echo $(C_FILE)
gcc -fdump-tree-cfg=$(CFG_FILE) $(C_FILE)

In Kernel makefile $(call cmd, tags) what is the cmd here refers to?

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.

Resources