动态Makefile变量赋值

3

我有以下Makefile,我想创建“debug”和“optimal”目标,它们会影响CPPFLAGS和CFLAGS中的值,如下所示:

include Makefile.inc

DIRS    = applib
EXE_APPFS       = appfs
EXE_APPMOUNT    = appmount
EXE_APPINSPECT  = appinspect
EXE_APPCREATE   = appcreate
BUILD_APPFS     =
BUILD_APPMOUNT  = -DAPPMOUNT
OBJS_APPFS      = main.o appfs.o
OBJS_APPMOUNT   = main.o appmount.o
OBJS_APPINSPECT = appinspect.o
OBJS_APPCREATE  = appcreate.o
OBJLIBS = libapp.a
LIBS    = -L. -lpthread -lstdc++ -ldl -lrt -largtable2 -lm ./libapp.a     /usr/lib64/libfuse.a

# Optimization settings.
debug: CPPFLAGS=$(CPPFLAGS_DEBUG)
debug: CFLAGS=$(CFLAGS_DEBUG)
debug:
    @true

optimal: CPPFLAGS=$(CPPFLAGS_OPTIMAL)
optimal: CFLAGS=$(CFLAGS_OPTIMAL)
optimal:
    @true

appfs: appfs.o $(OBJLIBS)
    @echo "stuff is done here"

appmount: appmount.o $(OBJLIBS)
    @echo "stuff is done here"

appmount_optimal: optimal appmount

我遇到的问题是,“debug”和“optimal”内部的变量赋值无法传递到其他目标(尽管如果我在优化中放置@ echo $(CPPFLAGS),那么这将起作用)。既不是“make optimal appmount”,也不是“make appmount_optimal”能给我期望的结果。
肯定有一种方法可以根据需要是否调试来定义CPPFLAGS和CFLAGS,对吧?
2个回答

7

如果您正在使用GNU make,除了递归make调用(存在上述问题)外,还有两个选项。

第一个选项是使用目标特定变量。您在原始示例中使用了它们:

debug: CPPFLAGS=$(CPPFLAGS_DEBUG)
debug: CFLAGS=$(CFLAGS_DEBUG)

optimal: CPPFLAGS=$(CPPFLAGS_OPTIMAL)
optimal: CFLAGS=$(CFLAGS_OPTIMAL)

你所遗漏的是目标特定变量会被其先决条件继承。因此,你需要声明“debug”和“optimal”的先决条件(它们本身不必有配方;事实上,它们可以被声明为.PHONY)。例如:
debug: CPPFLAGS=$(CPPFLAGS_DEBUG)
debug: CFLAGS=$(CFLAGS_DEBUG)
debug: appfs appmount

optimal: CPPFLAGS=$(CPPFLAGS_OPTIMAL)
optimal: CFLAGS=$(CFLAGS_OPTIMAL)
optimal: appfs appmount

现在,如果您运行“make debug”,它将使用CPPFLAGS和CFLAGS的调试设置构建appfs和appmount;如果您运行“make optimal”,它将使用最佳设置。然而,这与递归调用make具有相同的缺点;如果您直接运行“make appfs”,则不会使用任何一个设置;目标特定变量是从导致在此次make调用中构建目标的父级继承的。如果这些目标都不在父目标列表中,则不会使用它们的目标特定变量。第二个选项几乎完全提供了您所寻找的界面,即使用MAKECMDGOALS变量来决定用户是否要求优化或调试构建。例如,像这样的内容:
CPPFLAGS_debug = <debug CPPFLAGS>
CFLAGS_debug = <debug CFLAGS>

CPPFLAGS_optimal = <optimal CPPFLAGS>
CFLAGS_optimal = <optimal CFLAGS>

STYLE := $(firstword $(filter debug optimal,$(MAKECMDGOALS)))
$(if $(STYLE),,$(error No style "debug" or "optimal" set))

CPPFLAGS = $(CPPFLAGS_$(STYLE))
CFLAGS = $(CFLAGS_$(STYLE))

debug optimal:
.PHONY: debug optimal

或者,如果您愿意的话,可以选择默认行为,而不是抛出错误;例如,默认情况下选择“debug”:

STYLE := $(firstword $(filter optimal,$(MAKECMDGOALS)) debug)

然而,需要注意的是,这个看似棘手的问题实际上本质上是有缺陷的。在一个目录中构建单个派生文件,并根据构建时参数的不同方式进行构建,这样做可能会导致麻烦。你如何知道上次使用了哪种变体?假设你以优化方式运行make,然后修改了一些文件,然后再次运行make,这次以调试方式运行...现在你的某些文件是优化的,某些文件是调试的。
处理代码的不同构建变体的正确方法是确保派生文件是唯一的。这意味着将调试目标写入一个目录,将优化目标写入另一个目录。一旦做出这个区分,其余部分就很容易解决:当你为调试目标编写规则时,只需使用调试标志,当你为优化目标编写规则时,只需使用优化标志即可。

一定有更好的方法 - 而“通过先决条件继承”是我不知道的部分。 - Jonathan Leffler
好的,我现在正在使用这个解决方案(尽管它更具体地针对GNU make)。我还在样式检查后添加了"$(if $(filter 1, $(words $(MAKECMDGOALS))),$(error $(ERROR_NOTARGET)),)"以确保用户提供构建目标(例如"all")。 - June Rhodes

2

一种可怕但相当有效的技术是:

# Optimization settings.
debug:
    +$(MAKE) CPPFLAGS="$(CPPFLAGS_DEBUG)" CFLAGS="$(CFLAGS_DEBUG)"

optimal:
    +$(MAKE) CPPFLAGS="$(CPPFLAGS_OPTIMAL)" CFLAGS="$(CFLAGS_OPTIMAL)"

这意味着make debug会重新调用make并在命令行上设置调试标志,而make optimal会重新调用make并在命令行上设置最佳标志。这远非完美;这意味着当重新调用make时将运行默认规则。您可以通过以下方式进行变化:
# Optimization settings.
debug:
    +$(MAKE) CPPFLAGS="$(CPPFLAGS_DEBUG)" CFLAGS="$(CFLAGS_DEBUG)" debug_build

optimal:
    +$(MAKE) CPPFLAGS="$(CPPFLAGS_OPTIMAL)" CFLAGS="$(CFLAGS_OPTIMAL)" optimal_build

现在,它可以为调试版本和最优版本运行两个不同的构建目标。其他规则,如cleandepend没有特殊处理。

+符号在命令行上是POSIX的方式,表示“即使在make -n下也要运行此规则”,这可能是您想要的。 $(MAKE)符号也可以实现相同的效果。如果您的make不喜欢+符号,请尝试删除它们。


但这样我不能用'make debug appmount'来构建调试模式下的appmount目标,对吗?它只允许'make debug'构建所有调试目标。

你说得对;这就是为什么它不完美的原因。如果你幸运的话,其他人会提出更好的解决方案。有一种稍微狡猾的方法可以基本实现您想要的功能:

BUILD_TARGET = all

# Optimization settings.
debug:
    +$(MAKE) CPPFLAGS="$(CPPFLAGS_DEBUG)" CFLAGS="$(CFLAGS_DEBUG)" $(BUILD_TARGET)

optimal:
    +$(MAKE) CPPFLAGS="$(CPPFLAGS_OPTIMAL)" CFLAGS="$(CFLAGS_OPTIMAL)" $(BUILD_TARGET)

现在当你运行make debug时,它会默认重新运行并构建带有调试选项的all目标。但是,你可以使用以下命令更改:

make optimal BUILD_TARGET="appmount totherprog"

这将使用最佳标志构建两个指定目标。初始命令行并不是非常优美,但它可以将你带到目的地-几乎达成。通过仔细选择默认值和覆盖它们的能力,应该可以帮助你到达目的地。


这个不能让我执行“make debug appmount”以构建调试模式下的appmount目标,对吗?它只允许执行“make debug”,而该命令会构建所有的调试目标。 - June Rhodes
好的,我会将这个标记为答案,因为这是我正在使用的(因为在make中原始请求实际上是不可能的)。 - June Rhodes
可能有更好的方法来完成它,特别是如果您足够了解GNU make的特殊功能并且使用它是可以接受的。显然,您正在使用GNU make。我没有研究过GNU make的高级功能,因为它们不在我需要让makefile工作的所有地方都可用。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接