Generating GNU Makefile Rules

2019-07-31 16:39发布

问题:

So in my project I have a src directory and an obj directory. I'm recursively finding the .c and .cpp files in my src directory, and then its corresponding .o file gets put right in the obj directory. So for example if I have a .cpp file: src/dir1/dir2/file.cpp, its corresponding .o file would be obj/file.o. Then I'm generating the rule to get the .o file from the .cpp file using a make foreach function using this code:

rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)$(filter $(subst *,%,$2),$d))
src = $(call rwildcard,src/,*.cpp *.c)
obj = $(patsubst %,obj/%.o,$(basename $(notdir $(src))))

$(info src: [$(src)])
$(info obj: [$(obj)])

game.exe: $(obj)
    g++ $^ -o $@

define objFromSrc
$(1): $(2)
    $(info $(1) $(2))
    g++ -c $(2) -o $(1)
endef

$(foreach t,$(src),$(call objFromSrc,$(patsubst %,obj/%.o,$(basename $(notdir $(t)))),$(t)))

Here is the output for some example files:

src: [src/dir/main.cpp src/dir/dir2/other3.cpp src/dir/other2.cpp src/other.c]
obj: [obj/main.o obj/other3.o obj/other2.o obj/other.o]
obj/main.o src/dir/main.cpp
obj/other3.o src/dir/dir2/other3.cpp
obj/other2.o src/dir/other2.cpp
obj/other.o src/other.c
makefile:20: *** multiple target patterns.  Stop.

You can see the obj variable correctly holds the corresponding .o file names. And the objFromSrc function generates a rule where the target and dependency are correct, but yet I get a multiple target patterns error.

Why am I getting this error and how can I fix it?

回答1:

You are missing the $(eval) to parse the generated makefile code:

$(eval $(foreach t,$(src),...))

I would also suggest to add an empty line at the end of the multi-line define. Leaving this out is usually calling for trouble when $(eval)uating dynamically generated makefile code.

define objFromSrc
$(1): $(2)
    $(info $(1) $(2))
    g++ -c $(2) -o $(1)

endef

$(info eval $(foreach t,$(src),...))

BONUS CODE: your recipe is a constant so there is no need to re-generate it for every rule. Use a static pattern rule for $(obj) instead:

.DEFAULT_GOAL := game.exe

obj :=
define objFromSrc
$(1): $(2)
obj += $(1)

endef

$(eval $(foreach t,$(src),...))

$(info obj: [$(obj)])

$(obj): %.o:
    g++ -o $@ -c $<

game.exe: $(obj)
    g++ $^ -o $@


回答2:

Why am I getting this error and how can I fix it?

All these define and $(call ...) in make produce simple strings. You have to eval it to make the make do what you've ordered (i.e. to create the rule $1 : $2):

$(foreach t,$(src),$(eval $(call objFromSrc,$(patsubst %,obj/%.o,$(basename $(notdir $(t)))),$(t))))


标签: gnu-make