Can anyone suggest how to achieve below in one line -
ifeq ($(findstring city, $(TARGET_PRODUCT)), city)
..
else ifeq ($(findstring man, $(TARGET_PRODUCT)), man)
..
else ifeq ($(findstring zone, $(TARGET_PRODUCT)), zone)
..
endif
Here city, man, zone are not complete words but partial pattern ,
for ex, velocity, Hitman, xzoneplace etc.
I find word match with filter option -
ifneq (,$(filter $(OS),Darwin FreeBSD NetBSD))
bar
endif
But it seems not fit for partial match
Thanks
Not easily. You can write a recursive function that will do it, something like:
findany = $(if $1,$(or $(findstring $(firstword $1),$2),$(call findany,$(wordlist 2,$(words $1),$1),$2)))
ifneq (,$(call findany,city man zone,$(TARGET_PRODUCT)))
The findany function returns the empty string if nothing matched (or else the first match, if that matters).
ETA
Actually this can be done much more simply: I guess I was feeling too recursive the other day. You just need:
findany = $(foreach W,$1,$(findstring $W,$2))
ifneq (,$(call findany,city man zone,$(TARGET_PRODUCT)))
The first option above uses recursion to do the same thing.
The GNUmake library gmtt has a function glob-match which may or may not be what you are looking for:
include ../gmtt/gmtt.mk
TARGET_PRODUCT := velocity
pattern := *city*
test_result = $(call glob-match,$(TARGET_PRODUCT),$(pattern))
$(info For pattern "$(pattern)" in string "$(TARGET_PRODUCT)" there was $(if $(test_result), a match: $(test_result), no match))
TARGET_PRODUCT := Hitman
pattern := *city*
test_result = $(call glob-match,$(TARGET_PRODUCT),$(pattern))
$(info For pattern "$(pattern)" in string "$(TARGET_PRODUCT)" there was $(if $(test_result), a match: $(test_result), no match))
pattern := *man*
test_result = $(call glob-match,$(TARGET_PRODUCT),$(pattern))
$(info For pattern "$(pattern)" in string "$(TARGET_PRODUCT)" there was $(if $(test_result), a match: $(test_result), no match))
TARGET_PRODUCT := xzoneplace
pattern := *city*
test_result = $(call glob-match,$(TARGET_PRODUCT),$(pattern))
$(info For pattern "$(pattern)" in string "$(TARGET_PRODUCT)" there was $(if $(test_result), a match: $(test_result), no match))
pattern := *zone*
test_result = $(call glob-match,$(TARGET_PRODUCT),$(pattern))
$(info For pattern "$(pattern)" in string "$(TARGET_PRODUCT)" there was $(if $(test_result), a match: $(test_result), no match))
pattern := *place*
test_result = $(call glob-match,$(TARGET_PRODUCT),$(pattern))
$(info For pattern "$(pattern)" in string "$(TARGET_PRODUCT)" there was $(if $(test_result), a match: $(test_result), no match))
pattern := *zone*place*
test_result = $(call glob-match,$(TARGET_PRODUCT),$(pattern))
$(info For pattern "$(pattern)" in string "$(TARGET_PRODUCT)" there was $(if $(test_result), a match: $(test_result), no match))
Output:
For pattern "*city*" in string "velocity" there was a match: velo city
For pattern "*city*" in string "Hitman" there was no match
For pattern "*man*" in string "Hitman" there was a match: Hit man
For pattern "*city*" in string "xzoneplace" there was no match
For pattern "*zone*" in string "xzoneplace" there was a match: x zone place
For pattern "*place*" in string "xzoneplace" there was a match: xzone place
For pattern "*zone*place*" in string "xzoneplace" there was a match: x zone place
Of course you don't need to use variables: $(call glob-match,$(TARGET_PRODUCT),*some*funny*glob*) works as well.
Related
I have a makefile that gives me the source files in a hierarchy.
SRCS := $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/*/*.c)
gives me
./src/main.c ./src/add/add.c ./src/sub/sub.c
I want to flatten the object files into a single "obj" directory.
Of course
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.obj)
gives me
./obj/main.obj ./obj/add/add.obj ./obj/sub/sub.obj
instead of desired
./obj/main.obj ./obj/add.obj ./obj/sub.obj
Question: How do I get rid of additional source directory levels?
Never had to use complex substitution so far. My intuitive try with additional "/%" in pattern
# won't work as expected:
OBJS := $(SRCS:$(SRC_DIR)/%/%.c=$(OBJ_DIR)/%.obj)
produces no meaningful result (${OBJS} becomes same as ${SRCS}).
All examples I found so far only have single occurance of "%" in match pattern.
MAK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
SRC_DIR = $(MAK_DIR)./src
OBJ_DIR = $(MAK_DIR)./obj
# gives: ./src/main.c ./src/add/add.c ./src/sub/sub.c
SRCS := $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/*/*.c)
# gives: ./obj/main.obj ./obj/add/add.obj ./obj/sub/sub.obj
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.obj)
.PHONY : all
all :
#echo $(SRCS)
#echo $(OBJS)
Just use the notdir function, and also patsubst, more powerful (and that I find easier to understand than the shorthand):
OBJS := $(patsubst %.c,$(OBJ_DIR)/%.obj,$(notdir $(SRCS)))
But it is not the whole story because later on you will probably want to do something like:
$(OBJ_DIR)/%.obj: $(SRC_DIR)/%.c
which will not work any more. Not mentioning the fact that you could have several source files with the same name in different directories. Assuming you do not have such names conflicts you can generate all your dependencies using foreach-eval-call:
MAK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
SRC_DIR := $(MAK_DIR)./src
OBJ_DIR := $(MAK_DIR)./obj
SRCS := $(shell find $(MAK_DIR) -type f -name '*.c')
OBJS := $(patsubst %.c,$(OBJ_DIR)/%.obj,$(notdir $(SRCS)))
.PHONY: objs
objs: $(OBJS)
# $(1): source file
define DEP_rule
$(1)-obj := $$(patsubst %.c,$$(OBJ_DIR)/%.obj,$$(notdir $(1)))
$(1)-dep := $$(patsubst %.c,$$(OBJ_DIR)/%.d,$$(notdir $(1)))
$$($(1)-obj): $(1) $$($(1)-dep)
endef
$(foreach src,$(SRCS),$(eval $(call DEP_rule,$(src))))
Just remember that the DEP_rule macro gets expanded twice, thus the $$.
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.
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)
build_debug:=dltvr
define debug
$$(findstring $(2),$$(if $$(DEBUG_SCOPE_DIR),$$(if $$(filter $$(DEBUG_SCOPE_DIR)%,$(1)),$(build_debug)),$(build_debug)))
endef
define warn
$$(if $(call debug,$(1),$(2)),$$(warning $(3)))
endef
$(call warn,jianxi,d,D jianxi)
In my optinion, $(call warn,...) will be expand to
$(if $(findstring d,$(if $(DEBUG_SCOPE_DIR),$(if $(filter $(DEBUG_SCOPE_DIR)%,jianxi),dltvr),dltvr)),$(warning D jianxi))
And will output Makefile:13: D jianxi
You are correct; $(call warn,...) will be expanded to $(if $(findstring ...)...). But it will not be expanded any further.
You have escaped the '$' symbols too far. Try this:
define warn
$(if $(call debug,$(1),$(2)),$(warning $(3)))
endef