考虑以下makefile:
C_COMPILER = gcc
C_COMPILER_OPTS = -DFOOBAR
OBJECTS=obj/dir1/foo.o obj/dir2/bar.o obj/dir3/test.o
obj/dir1/%.o: src/dir1/%.c
$(C_COMPILER) -o $@ $(C_COMPILER_OPTS) src/dir1/$*.c
obj/dir2/%.o: src/dir2/%.c
$(C_COMPILER) -o $@ $(C_COMPILER_OPTS) src/dir2/$*.c
obj/dir3/%.o: src/dir3/%.c
$(C_COMPILER) -o $@ $(C_COMPILER_OPTS) -DANOTHEROPTION src/dir3/$*.c
libfoobar: $(OBJECTS)
ar rs $@.a $(OBJECTS)
obj/dir1/foo.o: src/dir1/foo.c
obj/dir2/bar.o: src/dir2/bar.c
obj/dir3/test.o: src/dir3/test.c
如您所见,src/dir1
和src/dir2
中文件的构建规则是相同的。但是,src/dir3
中的文件的构建方式不同。
那么有没有办法将src/dir1
和src/dir2
的两个构建规则简化为一个规则,从而消除冗余?
答案 0 :(得分:1)
我会尝试类似的东西(仅限GNU make):
C_COMPILER := gcc
C_COMPILER_OPTS := -DFOOBAR
DIRS := dir1 dir2 dir3
TOP := libfoobar.a
.PHONY: all clean
all: $(TOP)
dir1_C_COMPILER_OPTS := $(C_COMPILER_OPTS)
dir2_C_COMPILER_OPTS := $(C_COMPILER_OPTS)
dir3_C_COMPILER_OPTS := $(C_COMPILER_OPTS) -DANOTHEROPTION
# $(1): dir, $(2): file basename
define COMPILE_rule
OBJECTS += obj/$(1)/$(2).o
obj/$(1)/$(2).o: src/$(1)/$(2).c
$$(C_COMPILER) -o $$@ $$($(1)_C_COMPILER_OPTS) $$<
endef
$(foreach d,$(DIRS),$(foreach f,$(patsubst src/$(d)/%.c,%,$(wildcard src/$(d)/*.c)),$(eval $(call COMPILE_rule,$(d),$(f)))))
$(TOP): $(OBJECTS)
ar rs $@ $(OBJECTS)
clean:
rm -f $(OBJECTS) $(TOP)
foreach var,$(VAR)
循环播放$(VAR)中的字词,并将当前字词分配给$(var)
。call var,$(v1),$(v2)
执行var
扩展,将$(1)
替换为当前值$(v1)
,$(2)
,其当前值为$(v2)
} ...和$$
与$
。eval
将call
扩展的结果实例化为常规规则。如果您不想使用foreach-eval-call
构造,您还可以尝试使用这个稍微更详细的解决方案(它将依赖项与配方分开):
C_COMPILER := gcc
C_COMPILER_OPTS := -DFOOBAR
TOP := libfoobar.a
.PHONY: all clean
all: $(TOP)
OBJ1 := $(patsubst src/dir1/%.c,obj/dir1/%.o,$(wildcard src/dir1/*.c))
OBJ2 := $(patsubst src/dir2/%.c,obj/dir2/%.o,$(wildcard src/dir2/*.c))
OBJ3 := $(patsubst src/dir3/%.c,obj/dir3/%.o,$(wildcard src/dir3/*.c))
obj/dir1/%.o: src/dir1/%.c
obj/dir2/%.o: src/dir2/%.c
obj/dir3/%.o: src/dir3/%.c
$(OBJ3): C_COMPILER_OPTS += -DANOTHEROPTION
$(OBJ1) $(OBJ2) $(OBJ3):
$(C_COMPILER) -o $@ $(C_COMPILER_OPTS) $(patsubst obj%.o,src%.c,$@)
$(TOP): $(OBJ1) $(OBJ2) $(OBJ3)
ar rs $@ $^
clean:
rm -f $(OBJ1) $(OBJ2) $(OBJ3) $(TOP)
答案 1 :(得分:0)
使用=
定义的变量是递归计算的,直到没有替换为止:
所以你可以使用它:
compile-command = $(C_COMPILER) -o $@ $(C_COMPILER_OPTS) src/dir1/$*.c
obj/dir1/%.o: src/dir1/%.c
$(compile-command)
(虽然我个人认为这种行为是一种黑客行为并且不喜欢它 - 但是替代方案是$(eval …)
和脚本生成的makefile,而且那些也不理想。)