gnu 的正确语法用于以不同方式处理不同的先决条件

Posted

技术标签:

【中文标题】gnu 的正确语法用于以不同方式处理不同的先决条件【英文标题】:Proper syntax for gnu make for handling different pre-requisites differently 【发布时间】:2014-05-20 03:24:08 【问题描述】:

我最近刚刚在我的程序中从静态库切换到共享对象,并试图弄清楚如何正确相应地更新我的 Makefile。我找到了一个似乎可以正常工作的解决方案,使用双冒号规则。但是,我真的不确定我所做的事情的含义,以及这实际上是否是解决此问题的正确方法。

我不确定这是否有什么不同,但作为背景,我的 Makefile 遵循 Emile van Bergen 建议的方法:http://evbergen.home.xs4all.nl/nonrecursive-make.html

共享对象都收集在一个名为 TGT_SO 的变量中。我还有一堆目标文件需要包含在可执行文件中——我正在处理的特定规则是针对我的单元测试,这些目标文件都收集在一个名为 TGT_TESTS 的变量中。

以前,规则/配方很简单:

tests : $(TGT_LIB) $(TGT_TESTS)
        $(BUILD_TESTS)

BUILD_TESTS = $(CXX) $(CF_ALL) $(LF_ALL) -o test/$@ $^

当然,这适用于TGT_LIB,这是一堆档案。当然,这根本不适用于TGT_SO,因为这些都是共享对象,需要包含在-l... 中。

毫不奇怪,将其分解为 2 个单独的规则(使用单冒号语法)并不能解决问题。它仍然包含构建命令中的所有 .so 对象。

经过一番修改和阅读,我得出了以下规则:

tests :: $(TGT_SO)

tests :: $(TGT_TESTS)
    $(BUILD_TESTS)

据我了解,双冒号有效地将它们变成了 2 个完全独立的规则——第一个规则确保共享对象由它们各自的所有规则更新。第二条规则提供了构建共享对象的方法。

正如我在开头所说的那样,上述规则似乎是有效的,但鉴于我对 Make 的经验相当有限,我真的不确定我所做的事情的含义——尤其是使用文档中的一个功能描述为:“有点晦涩,通常不是很有用......”,然后继续提出一个用例:“更新目标取决于哪些先决条件文件导致更新,这种情况很少见。” (引自Make documentation)

鉴于我正在尝试做的事情一定很常见,并且我发现的解决方案似乎滥用/滥用了 Make 的一项功能,我希望有人可以就此给我一些建议。

编辑 2014-05-21

作为附加背景信息/澄清,项目的文件夹结构中散布着一些规则,其效果与以下内容相同:

$(TGT_TESTS) : $(TGT_SO)

具体说一下迄今为止收到的一个答案中提出的观点——如果(其中一个)TGT_SO 更改但(其中一个)双冒号规则可能会导致执行空白规则) TGT_TESTS 不会(因为这种情况只会导致上面两个规则中的第一个被执行),实际上,任何一个 TGT_SO 对象的更改也会导致部分/全部 TGT_TESTS 也被重建,从而导致第二条规则也被执行。

谢谢! 什穆尔

【问题讨论】:

【参考方案1】:

怎么样:

tests : $(TGT_SO) $(TGT_TESTS)
        $(BUILD_TESTS)

BUILD_TESTS = $(CXX) $(CF_ALL) $(LF_ALL) -o test/$@ $(filter %.o,$^)

您可能可以对 $(TGT_SO) 或 `$(filter %.so,$^) 进行一些转换以到达 -lXX 列表

我从未使用过双冒号规则,但是从手册中的描述来看,如果您只更改一个库(并且对象没有更改),那么只会执行第一个规则,这将导致在尝试应用隐式规则时

(来自手册,重点是我的)

具有相同目标的双冒号规则实际上是完全 彼此分开。处理每个双冒号规则 单独处理,就像处理具有不同目标的规则一样。

目标的双冒号规则按照它们的顺序执行 出现在makefile中。但是,双冒号规则的情况 真正有意义的是那些执行配方的顺序 没关系。

双冒号规则有些晦涩难懂,通常不是很有用; 它们为 方法用于更新的情况提供了一种机制 目标因哪些先决条件文件导致 更新,这种情况很少见。

每个双冒号规则都应该指定一个配方;如果没有,一个 如果适用,将使用隐式规则。请参阅使用隐式规则。

【讨论】:

确实,当我在撰写问题时,我脑海中形成了一个与您建议的过滤器功能模糊的概念-尽管我缺乏 Makefiles 经验意味着实现(即使用过滤器) 对我来说并不明显。至于你提到的潜在陷阱:这对我来说没有发生,如果仅仅是因为 $(TGT_TESTS) 依赖于 $(TGT_SO),所以这不是一个问题(这根本没有在我原来的问题——我会更新它)。在这两种方法之间进行选择时,这一事实有什么不同吗? 还有一个小细节(实际上只是一个小细节)——变量应该命名为 TGT_SO。 TGT_LIB 是包含档案的旧变量; TGT_SO 是包含共享对象的新变量。我没有重复使用相同的名称来让我选择恢复(如果我能够解决过度热心的编译器优化破坏我的工厂自注册类的问题......)。另外-感谢有关将 TGT_SO 转换为 -lXX 列表的提示。

以上是关于gnu 的正确语法用于以不同方式处理不同的先决条件的主要内容,如果未能解决你的问题,请参考以下文章

读取文件:以不同方式处理行

Python基础 ----- 流程控制

#pragma使用分析

使用 jquery datepicker 时,处理人们以不同格式输入日期的正确方法是啥?

parted命令

如何从现实世界中的多个队列中读取?