Succinctly adding verbose output to GNU make - makefile

I would like to add verbose output to a gnu makefile if V=1 is passed on the command line.
I can already do it in three lines as follows:
ifeq ($(V),1)
$(info SRC_FILES=$(SRC_FILES))
endif
Is there some more terse idiom, e.g., that appears all on one line? Ideally I'd like something like:
$(verbose SRC_FILES=$(SRC_FILES))
which may not be possible, or at least a one-liner like:
$(if $(V) $(info SRC_FILES=$(SRC_FILES)))

$(info $(if $(V),SRC_FILES=$(SRC_FILES))) almost does what you want. Its only drawback is that it outputs an empty line when V is undefined.
EDIT: from MadScientist's remark, and as initially suggested by BeeOnRope, $(if $(V),$(info SRC_FILES=$(SRC_FILES))) works exactly as expected, without the empty line when V is undefined.
You could also define a macro that prints an info message about a variable if and only if V is defined:
define verbose
$(if $(V),$(info $(1) = $($(1))))
endef
$(call verbose,SRC_FILES)
Of course, if you want a more generic macro, you can pass it the text to print:
define verbose
$(if $(V),$(info $(1)))
endef
$(call verbose,SRC_FILES = $(SRC_FILES))
Note that there are other types of information that you may want to control with a verbosity level variable. For the commands echoing, the commands outputs and the quiet command options I frequently use the following:
# Verbosity
ifeq ($(V),)
Q := #
MQ := --quiet
ECHO := echo
OUT := &> /dev/null
else ifeq ($(V),1)
Q :=
MQ :=
ECHO := echo
OUT :=
else
$(error V: invalid value ($(V)))
endif
foo:
$(Q)$(ECHO) 'making $#' && \
some-command $# $(OUT)
bar: cuz
$(Q)$(MAKE) $(MQ) $#
...

Maybe this will be practical:
verbose = $(if $(VERBOSITY),$(info $(-verbose)))
-verbose = $(if $(findstring 0,$(VERBOSITY)),$1 )$(if $(findstring 1,$(VERBOSITY)),$2 )$(if $(findstring 2,$(VERBOSITY)),$3 )$(if $(findstring 3,$(VERBOSITY)),$4 )$(if $(findstring 4,$(VERBOSITY)),$5 )$(if $(findstring 5,$(VERBOSITY)),$6 )$(if $(findstring 6,$(VERBOSITY)),$7 )$(if $(findstring 7,$(VERBOSITY)),$8 )$(if $(findstring 8,$(VERBOSITY)),$9 )$(if $(findstring 9,$(VERBOSITY)),$(10) )
VERBOSITY = 0
$(call verbose,warninglevel 1,warninglevel 2,warninglevel 3,info 1,info 2,info 3)
VERBOSITY = 2
$(call verbose,warninglevel 1,warninglevel 2,warninglevel 3,info 1,info 2,info 3)
VERBOSITY = 012345
$(call verbose,warninglevel 1,warninglevel 2,warninglevel 3,info 1,info 2,info 3)
Output:
warninglevel 1
warninglevel 3
warninglevel 1 warninglevel 2 warninglevel 3 info 1 info 2 info 3
I have added it to gmtt.

Related

Makefile expanding variable inside define

define func1
include $(shell pwd)/test/$(strip $1)/component.mk
$(info :::::::${NAME} ::::::::::::::: )
endef
INCLUDES := a b c
$(foreach dir, $(INCLUDES), $(eval $(call func1, $(dir)) ))
all : $(objs)
Contents of each makefile:
cat test/a/component.mk
NAME := AA
cat test/b/component.mk
NAME := BB
cat test/c/component.mk
NAME := CC
Output is
::::::: :::::::::::::::
:::::::AA :::::::::::::::
:::::::BB :::::::::::::::
It looks like first time NAME is empty.
Let's look at the expansion of $(foreach dir, ${INCLUDES}, $(eval $(call func1, ${dir}) )) in painful detail.
${INCLUDES} is expanded, giving $(foreach dir,a b c,$(eval $(call func1,${dir})))
Next dir is set to a
$(call func1,a) is expanded
1 is set to a
func1 is expanded:
include $(shell pwd)/test/$(strip $1)/component.mk
$(info :::::::${NAME} ::::::::::::::: )
$(shell pwd) becomes HERE, say (N.B. Use ${CURDIR} instead)
$(strip $1) becomes $(strip a) becomes a
${NAME} expands to nothing
$(info ::::::: ::::::::::::::: ) expands to nothing
As a side effect ::::::: ::::::::::::::: appears on stdout
$(eval $(call func1,a)) expands to $(eval include HERE/test/a/component.mk), expands to nothing
As a side effect, the include is processed by make
Presumably HERE/test/a/component.mk exists and contains valid make syntax,
and the variable NAME gets a value.
1 is set to b. Lather, rinse, repeat.
Tip
To get a hint of problems in code like this, always run make with --warn:
$ make --warn -Rr
Makefile:8: warning: undefined variable 'NAME'
::::::: :::::::::::::::
⋮
Fix
To get some insight, replace the $(eval stuff) with $(error [stuff])
$ make
::::::: :::::::::::::::
Makefile:8: *** [ include /cygdrive/c/Users/somewhere/a/component.mk
]. Stop.
Here we see the $(info …) has disappeared even before it has got to the eval.
The naive fix is pretty horrible.
define func1
include $(shell pwd)/test/$(strip $1)/component.mk
$$(info :::::::$${NAME} ::::::::::::::: )
endef
Running this with the $(error …) in place gives
$ make
Makefile:8: *** [ include /cygdrive/c/Users/somewhere/a/component.mk
$(info :::::::${NAME} ::::::::::::::: )]. Stop.
That stuff between the [ and ] is valid make syntax.
Tidied up it looks like:
include /cygdrive/c/Users/somewhere/a/component.mk
$(info :::::::${NAME} ::::::::::::::: )
Job done. There are cleaner ways, but you need to understand the pain first!

Missing separate error from makefile

With this Makefile, I'm not sure why I'm thrown a missing separator error.
define foo
$(eval a := $(1))
$(eval b := $(1))
endef
$(call foo,hello)
$(info $(a))
$(info $(b))
all: ;
If I however replaces the first eval with this,
$(eval a := $(1)) \
then the error goes away. eval expands to nothing, and it's happy with only one eval inside the define. But I'm not sure why it's complaining in this case, nor why the trailing back slash solves it.
define foo
$(eval a := $(1))
$(eval b := $(1))
endef
bar:=$(call foo,hello)
$(info $(a))
$(info $(b))
$(info bar=[$(bar)])
all: ;
Running this makefile outputs:
$ make -f Makefile.sample
hello
hello
bar=[
]
make: 'all' is up to date.
So $(foo) function outputs new line character. It should either output nothing or value should be captured in variable or trapped with $(eval) or $(strip).
$(eval a := $(1)) \ results in no new line output from $(foo) that is why it fixes the problem.
Alternatives to adding backslashes to your $(foo) are:
#1:
define fooBody
$(eval a := $(1))
$(eval b := $(1))
endef
foo = $(strip $(call fooBody,$1,$2))
#2
define fooBody
$(eval a := $(1))
$(eval b := $(1))
endef
foo = $(eval $(call fooBody,$1,$2))
#3
$(strip $(call foo,hello))
#4
$(eval $(call foo,hello))
#5
.:=$(call foo,hello)
My personal choice is #1.
The call expansion results in a single newline (because, as you say, both the eval's expand to the empty string. When make tries to parse that, it doesn't understand it and throws this error.
The important thing to understand is that make will break the input into logical lines before it tries to run expansion. So after the expansion is complete, make expects to see a single line of output and it doesn't understand newlines existing in that single line.
Probably this could be handled better, if you wanted to file a bug at https://savannah.gnu.org/bugs/?func=additem&group=make
ETA Actually I checked and this has already been fixed in GNU make 4.2:
$ make-4.1
Makefile:5: *** missing separator. Stop.
$ make-4.2
make: *** No targets. Stop.

Custom Make function doesn't get parameter

I want to add Modules to my build system.
To keep my makefile clean when adding new modules, they all follow the same pattern, so I tried to generalize it with a function:
uc = $(shell echo $1 | tr '[a-z]' '[A-Z]')
define driver-mod
$(eval CFLAGS += -DUSE_$(call uc, $1));
$(eval include $(DRIVERS_SRC)/$1/Makefile.include);
endef
ifneq (,$(filter led,$(USEMODULE)))
$(call driver-mod, led)
endif
ifneq (,$(filter uart,$(USEMODULE)))
$(call driver-mod, uart)
endif
ifneq (,$(filter button,$(USEMODULE)))
$(call driver-mod, button)
endif
(the ifneq is going to be replaced with a $(foreach x, $(USEMODULE), $(call driver-mod, $(x))
However, it seems like $1 in driver-mod is not evaluated, I get
make: *** $(DRIVERS_SRC): Is a directory. Stop.
(doesn't actually output $(DRIVERS_SRC) but it's value, edited for clarity)
When I replace the $1 with e.g. led, it works as expected.
What am I missing?
Turns out I have to escape the $ for eval:
define driver-mod
$(eval CFLAGS += -DUSE_$(call uc, $1));
$(eval include $(DRIVERS_SRC)/\$1/Makefile.include);
endef
works!
Can be simplified as follows:
uc = $(shell echo $1 | tr '[a-z]' '[A-Z]')
define __driver-mod
CFLAGS += -DUSE_$(uc)
include $(DRIVERS_SRC)/$1/Makefile.include
endef
driver-mod = $(eval $(call __driver-mod,$(strip $1)))
$(foreach 1,$(USEMODULE),$(driver-mod))

How do I define a variable which is a sequential list of integers in gmake?

Given a variable MAX, how do I create a variable LIST which contains the integers 1 to $(MAX)?
Using shell or similar is not possible for my context.
Looks good, though you don't need the $eval:
seq = $(if $(filter $1,$(words $2)),$2,$(call seq,$1,$2 $(words $2)))
$(error [$(call seq,10)])
or somesuch. Make will complain warning: undefined variable '2' under --warn, but you can avoid that by using $(value…).
[You probably want $(filter…) rather than $(findstring…)in your solution BTW.]
Here's a clumsy solution using eval:
UPTO = $(eval TEMP += $(words $(2))) \
$(if $(findstring $(1),$(words $(2))),$(TEMP),$(call UPTO,$(1),$(2) x))
SEQUENCE_TO = $(eval TEMP := )$(strip $(call UPTO,$(1),x))
MAX := 50
LIST := $(call SEQUENCE_TO,$(MAX))
Here is a simple recursive solution, I find it somewhat more understandable than the $(words ...) solution although I guess in the end they're not that different. For better or for worse, this is certainly more verbose.
The repeated call to $(wordlist 2,...) is a bit of a wart. Maybe it could be avoided.
count = $(call count0,$1,0 1 2 3 4 5 6 7 8 9)
count0 = $(if $(wordlist $1,$1,$(wordlist 2,1000000,$2)), \
$(wordlist 1,$1,$(wordlist 2,1000000,$2)), \
$(patsubst 0%,%,$(call count0,$1,$(patsubst %,0%,$2) \
$(patsubst %,1%,$2) $(patsubst %,2%,$2) $(patsubst %,3%,$2) \
$(patsubst %,4%,$2) $(patsubst %,5%,$2) $(patsubst %,6%,$2) \
$(patsubst %,7%,$2) $(patsubst %,8%,$2) $(patsubst %,9%,$2))))
.PHONY: nst
nst:
#echo 7: $(call count,7)
#echo 51: $(call count,51)
#echo 111: $(call count,111)

Using GNU-make functions to check if variables are defined

I'm writing a makefile that requires some enviroment variables to be defined. I am trying to use something like this to acheive this:
define check-var-defined
ifndef $(1)
$(error $(1) is not defined)
endif
endef
$(call check-var-defined,VAR1)
$(call check-var-defined,VAR2)
$(call check-var-defined,VAR3)
rule1:
#stuff
When I run make with no args I get this:
$ make
Makefile:7: *** VAR1 is not defined. Stop.
But when I run it with VAR1 specified I get the same error.
$ make VAR1=hello
Makefile:7: *** VAR1 is not defined. Stop.
Any ideas why this doesn't work? What can I do to make this work? Thanks in advance.
(Note that I need to check that the variables are actually defined when the makefile is run, as I need to include another makefle further down and the variables need to be set correctly by the time I do this).
The $(call ...) function does not evaluate the results of the function as if it were makefile code, so you can't things like ifdef there.
What happens is that the contents of check-var-defined are expanded and since it doesn't recognize the ifdef operation, it just proceeds to expand the $(error ...) function every time.
If you want to use ifdef you have to use $(eval ...) with $(call ...) which will evaluate the result as if it were a makefile.
Simpler is to use the $(if ...) function, like this:
check-var-defined = $(if $(1),,$(error $(1) is not defined))
Note that this will fail if the variable is empty, which is not quite the same thing as being undefined; it could have been defined to be empty (as VAR1=). But that's the way ifdef works, too, confusingly.
the macro in 1st answer is great but doesn't actually report the name of the 'empty' variable. here is a slight improvement with example/test:
# -*- mode: makefile -*-
check-var-defined = $(if $(strip $($1)),,$(error "$1" is not defined))
my_def1:=hello
my_def3:=bye
$(call check-var-defined,my_def1)
$(call check-var-defined,my_def2)
$(call check-var-defined,my_def3)
and the result:
Makefile:10: * "my_def2" is not defined. Stop.
defined = $(strip $(filter-out undefined,$(flavor $1)))
ensure-defined = \
$(eval .ensure-defined :=) \
$(foreach V,$(sort $1), \
$(if $(call defined,$V),,$(eval .ensure-defined += $V)) \
) \
$(if $(strip ${.ensure-defined}), \
$(foreach V,${.ensure-defined}, \
$(info NOT DEFINED: $$$V) \
) \
$(error Required variables not defined) \
)
ifFOO = $(if $(call defined,FOO), \
$(info FOO is defined: '${FOO}'), \
$(info FOO not defined) \
)
$(ifFOO)
FOO := foo
$(ifFOO)
$(call ensure-defined,FOO BAR)
all: ; #:
OUTPUT:
$ make -f foo.mk
FOO not defined
FOO is defined: 'foo'
NOT DEFINED: $BAR
foo.mk:25: *** Required variables not defined. Stop.

Resources