避免makefile规则中的冗余

时间:2017-07-24 12:48:27

标签: makefile

考虑以下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/dir1src/dir2中文件的构建规则是相同的。但是,src/dir3中的文件的构建方式不同。

那么有没有办法将src/dir1src/dir2的两个构建规则简化为一个规则,从而消除冗余?

2 个答案:

答案 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) } ...和$$$
  • evalcall扩展的结果实例化为常规规则。

如果您不想使用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,而且那些也不理想。)