GNU make:根据目标覆盖变量

时间:2013-12-03 11:23:26

标签: makefile gnu-make

我有一个非递归的构建系统,其中解析发生一次。在解析时,我为二进制< - library< - object

创建了一个依赖链

为多种硬件变体编译相同的源代码。

目标全部取决于变体/全部。 变体/ all所需的每个硬件都有变体/ variant_name / all。 variant / variant_name / all取决于所有二进制文件。

由于解析会构建其余的依赖关系,因此简单的make make会为我完成工作。

现在的要求是: 我们有多个客户,其工具链对于相同的硬件变体是不同的。我如何解析一次,并立即为所有客户运行构建,将客户特定的二进制文件,库等放入他们自己的目录中。

以下是我想到的选项。

  1. 多次解析(==客户数量),并多次调用make。拆分变量/全部分为两个(==客户数量)all_customer1,all_customer2变体/ all取决于all_customer1 all_customer2这几乎正常工作!但是,我有兴趣解析一次,并为此目的服务。

  2. Make的特定变量功能。但是,这不能在我的情况下使用,因为我不知道所有目标,因此依赖图未预定义,所以我不能真正使用此功能。

  3. 我们的系统可能比我解释的更复杂。但是,从我听到的评论中,我希望能得到更多的帮助。感谢你的时间。

1 个答案:

答案 0 :(得分:2)

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仅被编译一次,这不是你想要的。因此,从根本上说,必须为每个工具链/选项集创建不同的物理目标。

关于解析,运行实际构建需要多长时间?我的经验是,即使是非常大的代码库也可以在实际执行构建所花费的时间的一小部分时间内解析为单个的层次构建树(即使是使用庞大的构建服务器......)

因此,考虑到这两个事实,您确定这是您想要做的吗?鉴于您基本上运行多个构建,确实这样做是有意义的,特别是因为它会使它更容易扩展;如果你有一个新客户,你只需添加一个新的构建作业,如果运行所有这些构建需要很长时间,你可以在不同的服务器上运行它们,例如詹金斯奴隶。

当然,它并不像'干净',并且确实保留了它为某些人工作的可能性,但不会为其他人工作,但无论如何,总是是一个管理问题... < / p>

如果您希望以一种很好的方式支持多个客户配置文件,可以使用配置文件来实现,如下所示:

# 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之间添加了评论,但在代码正常运行之前需要删除这些评论。

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=