我在makefile中有一些辅助目标,我想在makefile中限制内部或“私人”使用(仅限)。也就是说,我希望能够在makefile中将这些目标指定为依赖项,但我想阻止将目标从命令行指定为构建目标。有点类似于OOP中的private
函数:目标是单独构建的有害(或根本没有意义)。
我希望有一个特殊目标.HIDDEN
或.PRIVATE
或类似的东西,类似于.PHONY
对非文件目标的作用,但我不认为这存在。 private
关键字仅适用于变量。
保护目标仅供内部/私人使用的优秀/一般/优雅方式是什么?
我能提出的最佳解决方法是检查$(MAKECMDGOALS)
是否有“不可接受的”目标,如果指定则会出错;这似乎不优雅。我确信makefile可以被重写以避免这种情况 - 也许是一个更好的解决方案 - 但这在这里不实用。
在切割线下面......这是一个人为的例子。
虽然我正在寻找一般解决方案,但作为个人/主要目标有害的目标的一个例子是继承特定于目标的变量值:
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
隐含规则重复(不必要)用于说明。以debug
或release
为目标,foo.o
和其他人将继承相应的CFLAGS
和CPPFLAGS
- 如果有人make clean debug
所有对象将始终如一。但是,例如,如果有人单独构建foo.o
,它将无法继承适当的标志;例如,make clean foo.o debug
您将使用默认foo.o
构建CFLAGS
;然后在构建debug
时不需要更新它,因此它将与具有不同优化或不同宏设置的其他对象链接。它可能适用于这种情况,但它不是预期的。将foo.o
等标记为非法目标会阻止这种情况。
编辑:
很明显,我的例子(上面)对于我的更一般的问题不是一个好的选择:隐藏目标不是解决我的示例问题的最佳方法。这是一个修改过的示例,说明了修改后的问题“如何强制执行特定于目标的值?” - 它建立在@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)
模式规则重复,因为我认为它必须是(多个“模式目标”(%
)说服make
所有目标都是使用一个配方同时构建的;请参阅SO问题this和this)。
现在,添加特定于目标的标志:
debug : CPPFLAGS += -DDEBUG
release : CPPFLAGS += -DRELEASE
但这仍然受到影响:
make bin_debug/foo.o
不会从CPPFLAGS
获取debug
。我接受了@ Michael的答案,因为它让我以更有帮助的方式思考这个问题,但也回答了我自己的一些修辞问题。
答案 0 :(得分:10)
你试图解决的问题是合法的,但你正朝着更糟糕的方向前进去解决它。
当我们编写Makefile时,我们正在根据目标,来源和配方来描述编译工作。这项工作的进展由已经建立的一组目标来描述。现在你准确地观察了序列
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
它将恢复其所在的任务,并且在编译具有不同标志的后续单元时保持foo.o
不变,使构建目录保持与在第一个场景中。
因此,如果我们可以在Makefile中实现私有目标,那么这将是无效的并且可能传达错误的安全感,这甚至比不安全感本身更糟糕。您想象的解决方案也消除了使用Makefiles而不是shell脚本的最重要优势之一: Make可以轻松地继续执行中断任务。
我记录了使用Makefiles与my answer to the question “What is the purpose of linking object files separately in a Makefile?”中已构建的目标集相关的其他一些方面。
为了解决编译标志不一致的问题,我们可以安排将构建的目标存储到特殊目录中,具体取决于使用的编译标志。实现这一点可以解决问题,而不会强迫我们在恢复中断的编译工作时轻松辞职。
以下是实施路线图:
release
和build
。 注意。在我看来,make
的BSD变体对于在特殊目录中编写目标有 更好的支持,请参阅{{3} }。一般来说,我更喜欢make
的BSD变体,因为它的文档很简短,而且它有很多有用的高级示例,因为在BSD世界中构建的操作系统构建和端口是由该程序编排的。
答案 1 :(得分:6)
你可以通过用两个连字符开头来定义私人目标。
--private-target:
@echo private
public-target: --private-target
@echo public
您可以致电make public-target
,但make --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 :(得分: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)
显然,这并不能阻止确定的用户调用“私有”目标,但希望它能说明他们不应该这样做。无论如何,这都是面向对象语言中私有化的东西。足够坚定的用户总是可以绕过它。
答案 3 :(得分:2)
问题的一个解决方案是将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
。
现在,假设您有&gt;&gt; 2个规则:debug,release,config01,config02,config03,...每个都有自己的CPPFLAGS
。
一种方法可能是继续重复所有模式规则,但如果有任何必须改变,这会很烦人。此外,它不可能在foreach
中使用。这看起来很方便:
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+=...
那样创建特定于目标的变量值,而不是{{1然后添加到每个规则:
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,未经测试。
有问题吗?更好的方式?
答案 4 :(得分:0)
考虑到这一点并尝试以下方法:
TEST := $(shell echo $$RANDOM)
test : $(TEST)
$(TEST):
<tab>@echo tada $(TEST)
然后在命令行上执行make test
似乎工作,我认为如果不使用测试目标就很难得到结果。也许这条路可以帮忙吗?