I would like to know which solution is better to add two numbers in a Makefile. In my case I will would use the function add as shown below:
result = $(call add, 34, 56)
$(error $(result))
Solution 1:
add = $(shell echo $$(( $(1) + $(2) )))
Solution 2:
add = $(shell perl -e 'print $1 + $2')
Solution 3:
add = $(shell echo '$1 + $2' | bc | tr '\n' ' ')
Solution 4:
16 := x x x x x x x x x x x x x x x
_input_int := $(foreach a,$(16),$(foreach b,$(16),$(foreach c,$(16),$(16)))))
_decode = $(words $1)
_encode = $(wordlist 1,$1,$(_input_int))
_plus = $1 $2
_max = $(subst xx,x,$(join $1,$2))
_push = $(eval stack := $$1 $(stack))
_pop = $(word 1,$(stack))$(eval stack := $(wordlist 2,$(words $(stack)),$(stack)))
_pope = $(call _encode,$(call _pop))
_pushd = $(call _push,$(call _decode,$1))
calculate=$(eval stack:=)$(foreach t,$1,$(call handle,$t))$(stack)
handle =$(call _pushd, \
$(if $(filter +,$1), \
$(call _plus,$(call _pope),$(call _pope)), \
$(call _encode,$1)))
add = $(strip $(foreach v,$(2), $(call calculate, $v $(1) +)))
I admit the solution 4 is ridiculous but it is the only one that does not depend on an external tool such as bash, perl or bc.
Try GNU Make Standard Library, it provides integer arithmetic functions.
Related
Here's the code snippet from my Makefile:
%/foo: %/bar.yaml
$(BINARY) generate -g go \
--package-name {COOL_VALUE}
# COOL_VALUE should be the parent folder of a `foo`, e.g., `foo1/foo2/foo -> foo2`
the question is how can I split $# string by / to get the second last element:
E.g.,
make foo1/foo2/foo
> ./binary generate -g go \
--package-name foo2
make foo3/foo
> ./binary generate -g go \
--package-name foo3
My attempts:
I came up with
$(eval package_name := $(word 1,$(subst /, ,$#)))
% pick second last element somehow
If you are really talking about / as a delimiter, then your best bet is to use the filename functions like this:
PARENT = $(notdir $(patsubst %/,%,$(dir $#)))
$(eval package_folders := $(filter-out foo,$(subst /, ,$#)))
$(eval package_name := $(word $(words $(package_folders)), $(package_folders)))
#echo "$(package_name)"
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.
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))
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)
here is a snippet from my makefile:
main_DEPS = $(TARGETS_$(d)/classes/player) $(TARGETS_$(d)/classes/monster)
It sets main_DEPS to the expanded versions of the other two variables.
This works as it should.
How can I replace:
$(TARGETS_$(d)/classes/player) $(TARGETS_$(d)/classes/monster)
with a program that gives the same output?
I tried:
main_DEPS = $(shell program)
but it appeared to set main_DEPS equal to the string value $(TARGETS_$(d)/classes/player) $(TARGETS_$(d)/classes/monster), not the expanded versions.
I've also tried:
main_DEPS = $(eval $(shell program))
main_DEPS = $(value $(shell program))
main_DEPS = $(value $(eval $(shell program)))
main_DEPS = $(eval $(value $(shell program)))
Did you try this:
$(eval main_DEPS = $(shell program))
Here, the argument of eval (inner expression with shell) is expanded to get the statement to evaluate:
main_DEPS = $(TARGETS_$(d)/classes/player) $(TARGETS_$(d)/classes/monster)
If I properly understand, this should be exactly what you want.