首页 文章

如何在GNU make中将目标设为“私有”仅供内部使用?或者:如何最好地执行特定于目标的变量值?

提问于
浏览
7

我在makefile中有一些辅助目标,我想在makefile中限制内部或"private"使用(仅限) . 也就是说,我希望能够在makefile中将这些目标指定为依赖项,但我想阻止将目标从命令行指定为构建目标 . 有点类似于OOP的 private 函数:目标是单独构建的有害(或根本没有意义) .

我希望有一个特殊目标 .HIDDEN.PRIVATE 或类似的东西,类似于 .PHONY 对非文件目标所做的,但我认为这不存在 . private 关键字仅适用于变量 .

What is a good/general/elegant way to protect a target for internal/private use only?

我能想到的最好的解决方法是检查 $(MAKECMDGOALS) 是否为"unacceptable"目标,然后如果指定错误输出;这似乎不优雅 . 我'm sure the makefile could be rewritten to avoid this situation -- perhaps a superior solution -- but that'在这里不实用 .


在切割线下方......这是一个人为的例子 .

虽然我正在寻找一般解决方案,但作为个人/主要目标有害的目标的一个示例是继承特定于目标的变量值:

override CFLAGS += -Wall
all : debug
%.o : %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
debug   : CFLAGS += -g3 -O0
release : CFLAGS +=     -O3
debug   : CPPFLAGS += -DDEBUG
release : CPPFLAGS += -DRELEASE
debug release : foo.o bar.o main.o
    $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
clean:
    -rm -f *.o debug release
.PHONY: all clean

隐含规则重复(不必要)用于说明 . 目标为 debugreleasefoo.o 和其他人将继承相应的 CFLAGSCPPFLAGS - 如果确实 make clean debug 所有对象将保持一致 . 但是,例如,如果有人单独构建 foo.o ,它将无法继承适当的标志;例如, make clean foo.o debug 你将使用默认 CFLAGS 构建 foo.o ;然后在构建 debug 时不需要更新它,因此它将与具有不同优化或不同宏设置的其他对象链接 . 它可能适用于这种情况,但它不是预期的 . 将 foo.o 等标记为非法目标会阻止这种情况 .


编辑:

很明显,我的例子(上图)对于我更普遍的问题来说不是一个好的选择:隐藏目标并不是解决我的例子问题的最佳方法 . 这是一个修改过的例子,说明了修改过的问题"How to enforce target-specific values?" - 它 Build 在@Michael,@ Bed,@ Ross下面的评论的基础上,并允许冒充和回答这个更有限的场景 .

如下面的前面的响应中所述,在这种情况下,更好的想法是创建在不同位置具有不同构建标志的对象 . 例如 . ,

bin_debug/%.o   : %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
bin_release/%.o : %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c)
DEBUG_OBJS   = $(addprefix bin_debug/,$OBJS)
RELEASE_OBJS = $(addprefix bin_release/,$OBJS)
debug   : $(DEBUG_OBJS)
release : $(RELEASE_OBJS)
debug release :
    $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)

模式规则重复,因为我认为它必须是(多个"pattern targets"( % )说服 make 所有目标都是使用一个配方一次构建的;参见SO问题thisthis) .

所以现在,添加特定于目标的标志:

debug   : CPPFLAGS += -DDEBUG
release : CPPFLAGS += -DRELEASE

但这仍然受到影响:

make bin_debug/foo.o

不会从 debug 获得 CPPFLAGS . 我在下面给出答案,因为它让我以更有帮助的方式思考问题,但也回答了我自己的一些修辞问题 .

5 回答

  • 9

    你试图解决的问题是合法的,但你正朝着解决它的更糟糕的道路前进 .

    声明私人目标没有任何意义

    当我们编写Makefile时,我们将根据目标,源和配方来描述编译工作 . 这项工作的进展由已经 Build 的一组目标来描述 . 现在你准确地观察了序列

    make clean
    make foo.o
    make debug
    

    将生成格式与 foo.o 不一致的对象,从而使您的构建目录处于不一致状态 . 但是推断出用户不能明确地构造 foo.o 是非常错误的 . 请考虑以下顺序:

    make clean
    # Wait for foo.o being compiles and
    #  interrupt the build job with a signal
    make debug
    

    由于 make 看到 foo.o 它将恢复其所处的任务,并且在编译具有不同标志的后续单元时保持不变,使构建目录保持与第一个场景中相同的不一致状态 .

    因此,如果我们可以在Makefile中实现私有目标,那么这将是无效的并且可能传达错误的安全感,这甚至比不安全感本身更糟糕 . 您想象的解决方案也消除了使用Makefiles而不是shell脚本的最重要优势之一:Make可以轻松地继续执行中断任务 .

    我记录了使用Makefiles与已经在my answer to the question “What is the purpose of linking object files separately in a Makefile?”中构建的目标集相关的一些其他方面 .

    解决您问题的另一种方法

    解决这个问题编译标志不一致,我们可以安排将构建的目标存储到一个特殊的目录中,具体取决于使用的编译标志 . 实现这一点可以解决问题,而不会迫使我们在恢复中断的编译工作时轻松辞职 .

    以下是实施路线图:

    • 识别构建配置文件,这里有 releasebuild .

    • 选择要用于每个构建配置文件的编译 .

    • 选择存储每个构建配置文件的构建目标的目录 .

    • 编写Makefile,以便将构建的目标存储在您选择的目录中 . 请参考Gnu make - how to get object files in separate subdirectory .

    Note. 在我看来, make 的BSD变体对在特殊目录中编写目标有更好的支持,请参阅my answer to the question “How to write a Makefile using different directories for targets and sources” . 一般来说,我更喜欢 make 的BSD变体,因为它的文档很简短,而且它有很多有用的高级示例,因为在BSD世界中构建的操作系统构建和端口是由该程序编排的 .

  • 4

    您可以通过使用两个连字符开始其名称来定义私有目标 .

    --private-target:
        @echo private
    
    public-target: --private-target
        @echo public
    

    您可以拨打 make public-targetmake --private-target 会抱怨未知选项:

    $ make public-target
    private
    public
    
    $ make --private-target
    /Library/Developer/CommandLineTools/usr/bin/make: unrecognized option `--private-target'
    

    $ make --version
    GNU Make 3.81
    Copyright (C) 2006  Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.
    There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
    PARTICULAR PURPOSE.
    
    This program built for i386-apple-darwin11.3.0
    
  • 2

    我不认为有任何“优雅”的方式让目标以某种方式私有化 . 我认为唯一可以称为优雅的解决方案是重写你的makefile,以便用户调用的目标无关紧要,正如Beta建议的那样 . 它还具有使makefile更易于维护和易于理解的优势 .

    将目标设置为“私有”的不那么优雅但相当简单的方法是将makefile重命名为默认名称之外的其他名称 . 然后在其中放置一个新的makefile,调用“private”makefile来完成它的工作 . 就像是:

    .SUFFIXES:
    
    PUBLIC_TARGETS = all debug release clean
    REAL_MAKEFILE = private.mak
    
    define invoke_make
     $(1): $(REAL_MAKEFILE)
        $(MAKE) -f $(REAL_MAKEFILE) $(1)
    endef
    
    $(foreach target, $(PUBLIC_TARGETS), $(eval $(call invoke_make,$(target))))
    
    .PHONY: $(PUBLIC_TARGETS)
    

    显然,这并不能阻止确定的用户调用“私有”目标,但希望它能说明他们不应该这样做 . 无论如何,这都是面向对象语言中私有化的东西 . 足够坚定的用户总是可以绕过它 .

  • 1

    该问题的一个解决方案是将 CPPFLAGS 迁移到模式规则(例如, bin_debug/%.o: CPPFLAGS... )而不是常规规则( debug: CPPFLAGS... ),最终结果:

    bin_debug/%.o   : CPPFLAGS += -DDEBUG
    bin_debug/%.o   : %.c
        $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
    bin_release/%.o : CPPFLAGS += -DRELEASE
    bin_release/%.o : %.c
        $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
    OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c)
    DEBUG_OBJS   = $(addprefix bin_debug/,$OBJS)
    RELEASE_OBJS = $(addprefix bin_release/,$OBJS)
    debug   : $(DEBUG_OBJS)
    release : $(RELEASE_OBJS)
    debug release :
        $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
    

    所以 make bin_debug/foo.o 将获得 CPPFLAGS ,包括 -DDEBUG .

    现在,假设您有>> 2规则:debug,release,config01,config02,config03,...每个都有自己的 CPPFLAGS .

    一种方法可能是继续重复所有模式规则,但如果有任何必须改变,这会很烦人 . 此外,它不可能在_1127113中使用 . 这看起来很方便:

    debug    : CPPFLAGS+=-DDEBUG
    release  : CPPFLAGS+=-DRELEASE
    config01 : CPPFLAGS+=-DSOMETHING
    config02 : CPPFLAGS+=-DSOMETHINGELSE
    TARGETS = debug release config01 config02
    OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c)
    define TARGET_template
     bin_$(1)/%.o : %.c
        $$(CC) $$(CFLAGS) $$(CPPFLAGS) -c -o $@ $<
     $(1): $(addprefix bin_$(1)/,$(OBJS))
     # other TARGET-specific stuff here
    endef
    $(foreach tgt,$(TARGETS),$(eval $(call TARGET_template,$(tgt))))
    

    但仍然没有解决 make bin_debug/foo.o 的情况 - 仍然没有得到 CPPFLAGS .

    因此,不是像 debug: CPPFLAGS+=... 那样创建特定于目标的变量值,而是可以使用特定于目标的变量,例如 CPPFLAGS_debug ,然后添加到每个规则:

    CPPFLAGS_debug    = -DDEBUG
    CPPFLAGS_release  = -DRELEASE
    CPPFLAGS_config01 = -DSOMETHING
    CPPFLAGS_config02 = -DSOMETHINGELSE
    TARGETS = debug release config01 config02
    OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c)
    define TARGET_template
     bin_$(1)/%.o : CPPFLAGS+=$$(CPPFLAGS_$(1))
     bin_$(1)/%.o : %.c
        $$(CC) $$(CFLAGS) $$(CPPFLAGS) -c -o $$@ $$<
     $(1): $(addprefix bin_$(1)/,$(OBJS))
     # other TARGET-specific stuff here
    endef
    $(foreach tgt,$(TARGETS),$(eval $(call TARGET_template,$(tgt))))
    

    谨防;以上可能需要更多 $$(...) s,未经测试 .

    问题?更好的方法?

  • 0

    考虑到这一点并尝试以下方法:

    TEST := $(shell echo $$RANDOM)
    
    test : $(TEST)
    
    $(TEST):
    <tab>@echo tada $(TEST)
    

    然后在命令行上执行 make test 似乎工作,我认为如果不使用测试目标就很难得到结果 . 也许这条路可以帮忙吗?

相关问题