我在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
隐含规则重复(不必要)用于说明 . 目标为 debug
或 release
, foo.o
和其他人将继承相应的 CFLAGS
和 CPPFLAGS
- 如果确实 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问题this和this) .
所以现在,添加特定于目标的标志:
debug : CPPFLAGS += -DDEBUG
release : CPPFLAGS += -DRELEASE
但这仍然受到影响:
make bin_debug/foo.o
不会从 debug
获得 CPPFLAGS
. 我在下面给出答案,因为它让我以更有帮助的方式思考问题,但也回答了我自己的一些修辞问题 .
5 回答
你试图解决的问题是合法的,但你正朝着解决它的更糟糕的道路前进 .
声明私人目标没有任何意义
当我们编写Makefile时,我们将根据目标,源和配方来描述编译工作 . 这项工作的进展由已经 Build 的一组目标来描述 . 现在你准确地观察了序列
将生成格式与
foo.o
不一致的对象,从而使您的构建目录处于不一致状态 . 但是推断出用户不能明确地构造foo.o
是非常错误的 . 请考虑以下顺序:由于
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?”中构建的目标集相关的一些其他方面 .
解决您问题的另一种方法
解决这个问题编译标志不一致,我们可以安排将构建的目标存储到一个特殊的目录中,具体取决于使用的编译标志 . 实现这一点可以解决问题,而不会迫使我们在恢复中断的编译工作时轻松辞职 .
以下是实施路线图:
识别构建配置文件,这里有
release
和build
.选择要用于每个构建配置文件的编译 .
选择存储每个构建配置文件的构建目标的目录 .
编写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世界中构建的操作系统构建和端口是由该程序编排的 .您可以通过使用两个连字符开始其名称来定义私有目标 .
您可以拨打
make public-target
但make --private-target
会抱怨未知选项:我不认为有任何“优雅”的方式让目标以某种方式私有化 . 我认为唯一可以称为优雅的解决方案是重写你的makefile,以便用户调用的目标无关紧要,正如Beta建议的那样 . 它还具有使makefile更易于维护和易于理解的优势 .
将目标设置为“私有”的不那么优雅但相当简单的方法是将makefile重命名为默认名称之外的其他名称 . 然后在其中放置一个新的makefile,调用“private”makefile来完成它的工作 . 就像是:
显然,这并不能阻止确定的用户调用“私有”目标,但希望它能说明他们不应该这样做 . 无论如何,这都是面向对象语言中私有化的东西 . 足够坚定的用户总是可以绕过它 .
该问题的一个解决方案是将
CPPFLAGS
迁移到模式规则(例如,bin_debug/%.o: CPPFLAGS...
)而不是常规规则(debug: CPPFLAGS...
),最终结果:所以
make bin_debug/foo.o
将获得CPPFLAGS
,包括-DDEBUG
.现在,假设您有>> 2规则:debug,release,config01,config02,config03,...每个都有自己的
CPPFLAGS
.一种方法可能是继续重复所有模式规则,但如果有任何必须改变,这会很烦人 . 此外,它不可能在_1127113中使用 . 这看起来很方便:
但仍然没有解决
make bin_debug/foo.o
的情况 - 仍然没有得到CPPFLAGS
.因此,不是像
debug: CPPFLAGS+=...
那样创建特定于目标的变量值,而是可以使用特定于目标的变量,例如CPPFLAGS_debug
,然后添加到每个规则:谨防;以上可能需要更多
$$(...)
s,未经测试 .问题?更好的方法?
考虑到这一点并尝试以下方法:
然后在命令行上执行
make test
似乎工作,我认为如果不使用测试目标就很难得到结果 . 也许这条路可以帮忙吗?