【问题标题】:Dynamic Makefile variable assignment动态 Makefile 变量赋值
【发布时间】:2012-02-08 02:25:06
【问题描述】:

我有以下 Makefile,我想创建影响 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

我遇到的问题是“调试”和“最佳”内部的变量分配不会延续到其他目标(尽管如果我将 @echo $(CPPFLAGS) 放在最佳内部,那可以工作)。做“make optimization appmount”和“make appmount_optimal”都没有得到我期望的结果。

肯定有一种方法可以根据您是否要调试来定义 CPPFLAGS 和 CFLAGS,对吧?

【问题讨论】:

    标签: makefile


    【解决方案1】:

    如果您使用的是 GNU make,您有两个选择(除了递归 make 调用,它存在上述问题)。

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

    debug: CPPFLAGS=$(CPPFLAGS_DEBUG)
    debug: CFLAGS=$(CFLAGS_DEBUG)
    
    optimal: CPPFLAGS=$(CPPFLAGS_OPTIMAL)
    optimal: CFLAGS=$(CFLAGS_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 optimization”,它将使用最佳设置。

    但是,这与递归调用 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
    

    或者,如果您愿意,您可以选择默认行为(如果没有给出),而不是抛出错误;这默认选择“调试”,例如:

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

    但是,重要的是要注意,这样做看起来很棘手的原因是您所要求的内容本身就有缺陷。建议应该基于构建时间参数以两种不同方式之一构建目录中的单个派生文件是自找麻烦。你怎么知道最后使用了哪个变体?假设您运行 make 时进行了优化,然后修改了一些文件,然后这次再次运行 make 时进行了调试......现在您的一些文件已优化,有些文件正在调试。

    处理代码的不同构建变体的正确方法是确保派生文件是唯一的。这意味着调试目标被写入一个目录,而优化目标被写入另一个目录。一旦你做出了这种区分,剩下的就很容易消失了:你只需在为调试目标编写规则时使用调试标志,在为优化目标编写规则时使用最佳标志。

    【讨论】:

    • 必须有更好的方法 - '由先决条件继承'是我不知道的一点。
    • 好吧,我现在正在使用这个解决方案(尽管它更特定于 GNU make)。我还在样式检查之后添加了“$(if $(filter 1, $(words $(MAKECMDGOALS))),$(error $(ERROR_NOTARGET)),)”,以确保用户提供构建目标(例如“所有")。
    【解决方案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”。
    • 好的,我将其标记为答案,因为这是我正在使用的(因为原始请求实际上在 make 中是不可能的)。
    • 可能有更好的方法来做到这一点,特别是如果您对 GNU make 的特殊功能有足够的了解并且可以使用它。显然,您使用的是 GNU make。我没有研究过 GNU make 的高级工具,因为它们并非在我需要 makefile 工作的任何地方都可用。
    猜你喜欢
    • 1970-01-01
    • 2017-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多