首页 > 解决方案 > 非递归 Makefile 中的变量

问题描述

我正在使用 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/*

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

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

标签: makefile

解决方案


我想我刚刚想出了一个解决方案。我正在阅读“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<dir>, -L<dir>paths 并且它不会被其他 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 来解决各种相关的陷阱。


推荐阅读