make
继承特定于目标的变量,只要它们具有唯一的路径,因此以下内容将起作用:
.PHONY: all
all: all-customer1 all-customer2
.PHONY: all-customer1
all-customer1: CFLAGS=-ggdb3 -O0
all-customer1: $(outdir)customer1/bin/prog
.PHONY: all-customer2
all-customer2: CFLAGS=-O3
all-customer2: $(outdir)customer2/bin/prog
$(outdir)customer1/bin/prog: $(outdir)customer1/src/main.o
$(CC) -o $@ $(CFLAGS) $<
$(outdir)customer2/bin/prog: $(outdir)customer2/src/main.o
$(CC) -o $@ $(CFLAGS) $<
$(outdir)customer1/src/main.o: src/main.c
$(CC) -c -o $@ $(CFLAGS) $^
$(outdir)customer2/src/main.o: src/main.c
$(CC) -c -o $@ $(CFLAGS) $^
意思是每个顶级目标都使用一致的标志进行构建,而下面的代码则不是这样:
.PHONY: all
all: all-customer1 all-customer2
.PHONY: all-customer1
all-customer1: CFLAGS=-ggdb3 -O0
all-customer1: $(outdir)customer1/bin/prog
.PHONY: all-customer2
all-customer2: CFLAGS=-O3
all-customer2: $(outdir)customer2/bin/prog
$(outdir)customer1/bin/prog: src/main.o
$(CC) -o $@ $(CFLAGS) $<
$(outdir)customer2/bin/prog: src/main.o
$(CC) -o $@ $(CFLAGS) $<
src/main.o: src/main.c
$(CC) -c -o $@ $(CFLAGS) $^
因为你只编译了一次
src/main.o
,这不是你想要的。所以,从根本上讲,你
必须为每个工具链/选项集创建不同的物理目标。
关于解析方面,它需要多长时间与实际构建运行时间相比?我的经验是,即使是非常大的代码库也可以在极短的时间内将其解析为单个层次结构的构建树(即使使用庞大的构建服务器...)。
因此,考虑到这两个事实,你确定这是你想做的吗?鉴于你基本上正在运行几个构建,肯定有意义去这样做,特别是因为这样会更容易扩展;如果你有一个新的客户,你只需添加一个新的构建作业,如果运行所有这些构建需要很长时间,你可以在不同的服务器上运行它们,例如 Jenkins 的从节点。
当然,这并不是很“干净”,也可能导致它“适用于某些人,但对另一些人则不适用”,但无论如何,这总是一个管理问题...
如果你想以一种好的方式支持多个客户配置文件,你可以使用设置如下的配置文件:
customer1_cflags=-O0 -ggdb3
.PHONY: all-customer1
all-customer1: CFLAGS=$(customer1_cflags)
all-customer1: all
然后,在你的Makefile
中:
-include $(wildcard configs/*.mk)
这将使您能够运行
make all
以获取您的标准/参考构建,或者运行
make all-customer1
以运行针对您的客户定制的构建 - 如果您使用变量来指定工作/输出目录,则可以在命令行上覆盖该变量:
make outdir=customer1_build/ all-customer1
注意:变量覆盖是基于“最近”的覆盖 - 如果有人直接在规则上覆盖了一个变量,那么这种方法将会失效。
要超越这个限制,创建“一棵构建树来统治它们所有”的方式,你需要建立一个源列表,然后使用eval动态地创建目标。下面的示例演示了原理,但需要进行一些额外的工作才能使其达到生产就绪状态。
编辑:我已经在define...endef之间包含了注释,但在代码正确运行之前,这些注释需要被删除。
主要的Makefile:
BUILD=build/
outdir=$(or $(filter %/,$(strip $(BUILD))),$(strip $(BUILD))/)
program=bin/hello
include platforms/all.mk
include configs/all.mk
define CreateBuildConfig =
all: all-$(1)-$(2)
.PHONY: all-$(1)-$(2)
all-$(1)-$(2): $(outdir)$(1)/$(2)/$(3)
$(outdir)$(1)/$(2)/%.o: %.c
$$(CC) -c -o $$@ $$(CFLAGS) $$(DEFINES) $$<
$(outdir)$(1)/$(2)/$(3): DEFINES=$$(foreach def,$$(platform_$(2)_defines),-D$$(def))
$(outdir)$(1)/$(2)/$(3): CFLAGS=$$(config_$(1)_cflags)
$(outdir)$(1)/$(2)/$(3): $(foreach obj,$(4:.c=.o),$(outdir)$(1)/$(2)/$(obj))
$$(CC) -o $$@ $$(CFLAGS) $$(DEFINES) $$<
clean: clean-$(1)-$(2)
clean-$(1)-$(2):
-rm -f $(outdir)$(1)/$(2)/$(3)
find $(outdir)$(1)/$(2) -name '*.o' -delete
endef
.PHONY: all
all:
.PHONY: clean
clean:
sources+=src/main.c
$(foreach platform,$(all_platforms),$(foreach config,$(all_configs),$(eval $(call CreateBuildConfig,$(config),$(platform),$(program),$(sources)))))
平台配置文件
platforms/all.mk
:
all_platforms:=model1 model2
include platforms/model1.mk
include platforms/model2.mk
示例平台文件 platforms/model1.mk
:
_platform=model2
platform_$(_platform)_defines:=NAME=$(_platform)
platform_$(_platform)_defines+=MAX_FOO=16
_platform=
配置文件是
config/all.mk
:
all_configs:=gcc48 customer1 customer2
include configs/gcc48.mk
include configs/customer1.mk
include configs/customer2.mk
示例配置文件customer1.mk
:
_config=customer1
config_$(_config)_cflags=-O3
_config=
variant/hw1_customer1/all
目标,您可以在那里设置特定的变量,然后在构建该目标的依赖项时,它们将覆盖默认变量值等。 - Etan Reisner