【问题标题】:Variables in non-recursive Makefiles非递归 Makefile 中的变量
【发布时间】:2019-02-13 16:32:53
【问题描述】:

我正在使用 GNU Make 并尝试使用非递归方法设计 Makefile。我遇到的问题 - 似乎没有办法限制不同 Makefile 中变量的范围。

例如,如果我有两个模块 libA 和 libB 的 Makefile

libA Makefile.inc:
src_dir := libA/src
inc_dir := libA/inc

libB Makefile.inc:
src_dir := libB/src
inc_dir := libB/inc

然后当我将上述 Makefile 包含到主 Makefile 中时

include libA/Makefile.inc
include libB/Makefile.inc

变量src_dirinc_dir 中的值被包含的最后一个Makefile 覆盖。好的,这可能是预期的,因为变量在此处是全局范围的,这会弄乱使用这些变量的构建命令,即 libA 的构建命令会找到 libB 的变量值。

一种解决方法是为每个 Makefile 创建唯一的变量

libA Makefile.inc:
src_dir_libA := libA/src
inc_dir_libA := libA/inc

libB Makefile.inc:
src_dir_libB := libB/src
inc_dir_libB := libB/inc

这解决了问题,但使用起来有点尴尬,因为每个变量都必须重命名。有谁知道是否有更好的方法来解决这个问题,例如如果 GNU Make 有一些范围或命名空间的概念?我查看了文档,但似乎找不到任何类似的东西。有特定于目标的变量,但它们似乎有令人讨厌的副作用。

好的,具体一点,下面是一个Makefile示例

dep_all :=
all:

# Begin Makefile.inc for project1
# ------------------------------------------------------------------------------
$(foreach var,$(filter local_%,$(.VARIABLES)),$(eval $(var) := ))
local_src_dir := project1/src
local_obj_dir := project1/obj
local_src := $(local_src_dir)/fileA.c $(local_src_dir)/fileB.c
local_obj := $(patsubst $(local_src_dir)/%.c,$(local_obj_dir)/%.o,$(local_src))

dep_all += $(local_src)
dep_all += $(local_obj)

$(info local_obj="$(local_obj)")
$(local_obj): $(local_obj_dir)/%.o: $(local_src_dir)/%.c
    gcc -L$(local_obj_dir) -c -o $@ $<
# ------------------------------------------------------------------------------
# End Makefile.inc for project1


# Begin Makefile.inc for project2
# ------------------------------------------------------------------------------
$(foreach var,$(filter local_%,$(.VARIABLES)),$(eval $(var) := ))
local_src_dir := project2/src
local_obj_dir := project2/obj
local_src := $(local_src_dir)/fileX.c $(local_src_dir)/fileY.c
local_obj := $(patsubst $(local_src_dir)/%.c,$(local_obj_dir)/%.o,$(local_src))

dep_all += $(local_src)
dep_all += $(local_obj)

$(info local_obj="$(local_obj)")
$(local_obj): $(local_obj_dir)/%.o: $(local_src_dir)/%.c
    gcc -L$(local_obj_dir) -c -o $@ $<
# ------------------------------------------------------------------------------
# End Makefile.inc for project2

$(info dep_all="$(dep_all)")

.PHONY: all
all: $(dep_all)

.PHONY: clean
clean:
    rm -f project1/obj/* project2/obj/*

如果我运行它,那么传递给 gcc 的 -L&lt;object_path&gt; 选项包含来自最后包含的 Makefile 的值,即在构建 project1 时,它使用 -Lproject2/obj 运行 gcc,这不是该项目的正确对象路径。这就是我要解决的问题。

mkdir -p project1/{src,obj} project2/{src,obj}
touch project1/src/{fileA.c,fileB.c} project2/src/{fileX.c,fileY.c}

$ make
local_obj="project1/obj/fileA.o project1/obj/fileB.o"
local_obj="project2/obj/fileX.o project2/obj/fileY.o"
dep_all=" project1/src/fileA.c project1/src/fileB.c project1/obj/fileA.o project1/obj/fileB.o project2/src/fileX.c project2/src/fileY.c project2/obj/fileX.o project2/obj/fileY.o"
gcc -Lproject2/obj -c -o project1/obj/fileA.o project1/src/fileA.c
gcc -Lproject2/obj -c -o project1/obj/fileB.o project1/src/fileB.c
gcc -Lproject2/obj -c -o project2/obj/fileX.o project2/src/fileX.c
gcc -Lproject2/obj -c -o project2/obj/fileY.o project2/src/fileY.c

【问题讨论】:

  • 显而易见且合理的标准解决方案是递归make
  • 看看我的非递归 makefile 实现:github.com/igagis/prorab 方法是所有这些“本地”变量都以 this_ 前缀命名,并在每个 makefile 的开头全部带有this_前缀的变量被清除,看这里:github.com/igagis/prorab/blob/master/wiki/…
  • 我见过的另一种方法是在每个局部变量前面加上$(DIR),其中$(DIR) 派生自目录名称。 (顺便说一句,现在已经使用非递归 makefile 了一段时间,我不得不说我支持@tripleee 对此的建议......)
  • recursive make 遇到各种问题,为什么要使用它?

标签: makefile


【解决方案1】:

解决方案是将所有变量命名为具有某些前缀的特定文件的本地变量。 比如我用this_前缀。

然后,在每个子makefile的开头,那些有this_前缀的变量可以清除如下:

$(foreach var,$(filter this_%,$(.VARIABLES)),$(eval $(var) := ))

PS。我在实现非递归makefiles 时使用了这种方法,在此处清除WIKI 中描述的变量:https://github.com/cppfw/prorab

【讨论】:

  • 您好,感谢您的建议,但这似乎对我不起作用。我已经更新了原始问题,请查看示例以查看哪些变量的值不正确。
  • 好的,recipes 中变量的问题在于,这些变量在执行 recipe 之前就被评估,这是在首先评估所有 makefile 之后完成的。我按照此处所述处理此问题:github.com/igagis/prorab/blob/master/wiki/… in Defining Custom Rules 部分
  • 好的,谢谢你的信息,我刚刚测试了define,然后是eval,它似乎有效。它实际上比目标特定变量更灵活。但是,当我在具有 cmets 的东西上调用 eval 时,这些 cmets 会在标准输出上回显,这是否正常,有没有办法禁用它?
  • 注释可能很棘手,请尝试在每条注释行的上方和下方添加空白行,以确保它们与其他行分开。我认为这可能是因为在运行eval 时,结果可能会意外地与相邻行“合并”,这可能会导致各种不同的副作用
  • 看起来如果 cmets 内部有变量引用,例如 #bla bla $(var) bla bla,那么这些变量引用仍然会被 make 扩展...我有时也会观察到 cmets 的奇怪行为...
【解决方案2】:

我想我刚刚找到了解决方案。我正在阅读“The GNU Make Book”,它说明了目标特定变量的副作用

特定于目标的变量不仅适用于目标,还适用于所有 该目标的先决条件及其所有先决条件,以及 很快。目标特定变量的范围是整个树 目标,从定义变量的目标开始。

这不是我想要的行为,但是从 GNU Make 3.82 开始,支持私有目标特定变量

一个特定于目标的变量通常是为一个目标定义的,所有的 其先决条件。但是如果特定于目标的变量是前缀 使用关键字private,它只为那个目标定义,而不是它的 先决条件。

所以下面的 Makefile 似乎对这些私有变量正常工作

dep_all :=
all:

# Begin Makefile.inc for project1
# ------------------------------------------------------------------------------
inc_dir := project1/inc
src_dir := project1/src
obj_dir := project1/obj
src := $(src_dir)/fileA.c $(src_dir)/fileB.c
obj := $(patsubst $(src_dir)/%.c,$(obj_dir)/%.o,$(src))

# These flags will be overwritten by another Makefile
CFLAGS      := -I$(inc_dir)

# These flags will be private and not be overwritten by another Makefile
CFLAGS_priv := -I$(inc_dir) -L$(obj_dir)

dep_all += $(src)
dep_all += $(obj)

# Private target specific variables
$(obj): private CFLAGS_priv:=$(CFLAGS_priv)

$(obj): $(obj_dir)/%.o: $(src_dir)/%.c
    gcc $(CFLAGS) $(CFLAGS_priv) -c -o $@ $<
# ------------------------------------------------------------------------------
# End Makefile.inc for project1


# Begin Makefile.inc for project2
# ------------------------------------------------------------------------------
inc_dir := project2/inc
src_dir := project2/src
obj_dir := project2/obj
src := $(src_dir)/fileX.c $(src_dir)/fileY.c
obj := $(patsubst $(src_dir)/%.c,$(obj_dir)/%.o,$(src))

# These flags will be overwritten by another Makefile
CFLAGS      := -I$(inc_dir)

# These flags will be private and not be overwritten by another Makefile
CFLAGS_priv := -I$(inc_dir) -L$(obj_dir)

dep_all += $(src)
dep_all += $(obj)

# Private target specific variables
$(obj): private CFLAGS_priv:=$(CFLAGS_priv)

$(obj): $(obj_dir)/%.o: $(src_dir)/%.c
    gcc $(CFLAGS) $(CFLAGS_priv) -c -o $@ $<
# ------------------------------------------------------------------------------
# End Makefile.inc for project2


.PHONY: all
all: $(dep_all)

.PHONY: clean
clean:
    rm -f project1/obj/* project2/obj/*

输出是我想要的,因为现在我可以为每个项目定义私有 CFLAGS_priv 变量来设置 -I&lt;dir&gt;-L&lt;dir&gt; 路径并且它不会被其他 Makefile 覆盖。

$ make
gcc -Iproject2/inc -Iproject1/inc -Lproject1/obj -c -o project1/obj/fileA.o project1/src/fileA.c
gcc -Iproject2/inc -Iproject1/inc -Lproject1/obj -c -o project1/obj/fileB.o project1/src/fileB.c
gcc -Iproject2/inc -Iproject2/inc -Lproject2/obj -c -o project2/obj/fileX.o project2/src/fileX.c
gcc -Iproject2/inc -Iproject2/inc -Lproject2/obj -c -o project2/obj/fileY.o project2/src/fileY.c

我希望这将解决我遇到的所有问题,并且我不必使用递归 make 来解决各种相关的陷阱。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-12
    • 2013-03-22
    • 1970-01-01
    相关资源
    最近更新 更多