【问题标题】:GNU make: Override variable based on targetGNU make:基于目标覆盖变量
【发布时间】:2013-12-19 10:18:36
【问题描述】:

我有一个非递归构建系统,其中解析发生一次。在解析时,我为二进制 创建了一个依赖链

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

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

由于解析会构建其余的依赖项,所以一个简单的 make all 为我完成了这项工作。

现在的要求是: 我们有不止一个客户,他们的工具链对于相同的硬件变体是不同的。我怎样才能解析一次,然后为所有客户同时运行构建,将客户特定的二进制文件、库等放入他们自己的目录中。

以下是我想到的选项。

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

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

我们的系统可能比我解释的要复杂。但是,从我听到的 cmets 中,我希望能得到更多帮助。感谢您的时间。

【问题讨论】:

  • 当你说目标特定变量不起作用时,你能更清楚地解释你的意思吗?为什么您认为拆分客户目标需要多次解析并多次调用 make?如果您拆分目标,那么它们应该能够每个都有自己的变量,这些变量仍然应该让您运行一次,不是吗?
  • 因为二进制
  • 此外,正如我已经想到的那样,影响依赖项的目标特定变量看起来非常困难。 stackoverflow.com/questions/1340060/… 但是,似乎还有其他一些方法可以解决这个问题。我正在阅读更多内容..
  • 为什么需要一个目标特定变量作为先决条件?你也在构建你的工具链吗?我假设您只需要覆盖代表现有工具的 $(CC)、$(AR) 等内容。
  • 如果您可以将它们设置在依赖链中二进制文件“上方”的虚假目标上,则不需要二进制文件上的目标特定变量。因此,如果您有一个 variant/hw1_customer1/all 目标,您可以在那里设置特定变量,然后在构建该目标的依赖项时,它们将覆盖默认变量值等。

标签: makefile gnu-make


【解决方案1】:

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=

【讨论】:

    猜你喜欢
    • 2011-12-23
    • 2012-12-13
    • 2022-06-11
    • 1970-01-01
    • 1970-01-01
    • 2014-04-11
    • 2011-03-05
    • 1970-01-01
    • 2014-07-08
    相关资源
    最近更新 更多