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,这不是你想要的。因此,从根本上说,您必须为每个工具链/选项集创建不同的物理目标。
关于解析,与运行实际构建相比需要多长时间?我的经验是,即使是非常大的代码库也可以在 实际 执行构建所需的一小部分时间中解析为单个层次构建树(即使使用庞大的构建服务器......) .
那么,考虑到这两个事实,您确定这是您想要做的吗?鉴于您基本上正在运行多个构建,这样做肯定是有意义的,特别是因为它可以更容易扩展;如果您有一个新客户,您只需添加一个新的构建作业,如果运行所有这些构建需要很长时间,您可以在不同的服务器上运行它们,例如詹金斯奴隶。
当然,它不是那么“干净”,并且确实存在“为某些人工作,但对其他人无效”的可能性,但无论如何这总是是一个管理问题...
如果您想以一种很好的方式支持多个客户配置文件,您可以使用如下设置的配置文件来实现:
# Build config for "Customer X"
# Costomer identifier: customer1
customer1_cflags=-O0 -ggdb3
.PHONY: all-customer1
all-customer1: CFLAGS=$(customer1_cflags)
all-customer1: all
然后,在你的Makefile:
# While convenient, Wildcards are harmful - they make your builds more fragile.
# This, for example, will fail fi there are no configs...
-include $(wildcard configs/*.mk)
这将使您能够运行 make all 以获取您的库存/参考构建或 make all-customer1 为您的客户运行自定义构建 - 如果您使用变量来指定您的工作/输出目录,那么您可以在命令行上覆盖它:
make outdir=customer1_build/ all-customer1
注意:变量覆盖基于“最近”覆盖 - 如果有人直接覆盖了规则上的变量,那么这种方法将失效。
要超越这一点,要创建“一个构建树来统治它们”,您需要构建一个源列表,然后使用 eval 动态创建目标。下面的示例演示了该原理,但需要一些额外的工作才能为生产做好准备。
编辑:我已经在 define ... endef 之间包含了 cmets,但在代码正常运行之前需要删除这些。
主Makefile:
BUILD=build/
outdir=$(or $(filter %/,$(strip $(BUILD))),$(strip $(BUILD))/)
program=bin/hello
include platforms/all.mk
include configs/all.mk
# This creates all the necessary constructs for building a
# config/platform combination.
define CreateBuildConfig =
all: all-$(1)-$(2)
.PHONY: all-$(1)-$(2)
all-$(1)-$(2): $(outdir)$(1)/$(2)/$(3)
# Create implicit rule for building sources to objects
$(outdir)$(1)/$(2)/%.o: %.c
$$(CC) -c -o $$@ $$(CFLAGS) $$(DEFINES) $$<
# Set the variables for this config/platform combo...
$(outdir)$(1)/$(2)/$(3): DEFINES=$$(foreach def,$$(platform_$(2)_defines),-D$$(def))
$(outdir)$(1)/$(2)/$(3): CFLAGS=$$(config_$(1)_cflags)
# The rule for creating the executable.
$(outdir)$(1)/$(2)/$(3): $(foreach obj,$(4:.c=.o),$(outdir)$(1)/$(2)/$(obj))
$$(CC) -o $$@ $$(CFLAGS) $$(DEFINES) $$<
# Cleanup after ourselves.
clean: clean-$(1)-$(2)
clean-$(1)-$(2):
-rm -f $(outdir)$(1)/$(2)/$(3)
find $(outdir)$(1)/$(2) -name '*.o' -delete
endef
# Some top-level targets, for documentation purposes.
.PHONY: all
all:
.PHONY: clean
clean:
# Build the list of sources.
sources+=src/main.c
# Create the actual build targets, for each platform/config pair.
$(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=