声明所有目标为伪目标(PHONY)

44

考虑一个长长的makefile,其中有许多目标,所有这些目标都是PHONY(表示目标名称不代表一个现有的文件)。

我可以执行以下任一操作:

.PHONY: a
a:
    do someting a

.PHONY: b
b:
    do someting b

.PHONY: c
c:
    do someting c
或者:
.PHONY: a b c
a:
    do someting a

b:
    do someting b

c:
    do someting c

第一种选项很麻烦,第二种选项容易出错,当未来的我添加一个目标并忘记将其声明为PHONY时。

是否有一种标准方法将makefile中的所有目标声明为PHONY


4
如果所有的目标都是“PHONY”,那么“make”不是正确的工具,因为它与普通脚本相比没有任何优势....PHONY: $(MAKECMDGOALS)会使命令行中给出的目标成为虚拟目标,如果你的目标之间不存在依赖关系,这将对你有用。 - user2371524
2
我同意Felix的观点。我认为第二个版本比第一个更标准。我不认为它会出现太多错误,无论Makefile的大小如何,你都应该在某种程度上控制它。 - Tim
29
我不同意Felix和TimF的观点。相对于“普通脚本”,make确实有其优势,因为你可以使用依赖项来指定配方和外部脚本运行的顺序。你还可以使用Makefile来干净地存储和调用多个配方/脚本,这样Makefile就成为了一个命令目录,你可能不想一遍又一遍地输入它们。目标名称可以用作实际命令/脚本名称的别名。 - Shammel Lee
7
@FelixPalmen 这没有任何意义,我经常使用它来达到这个目的。Make的语法要比传递参数给脚本并由switch语句进行解释的丑陋语法简单得多。所以为什么不像.PHONY最初的目的一样使用它呢?也就是无论是否过时都会触发目标。关于Make的“整体思想”,你忘记提到“整体思想”的另一个部分……依赖管理。在我的情况下,这就是为什么我更喜欢使用Make而不是在shell脚本中重新编写(和调试)依赖逻辑。 - Shammel Lee
14
以下是类似Facebook这样的公司使用Makefiles的示例,正如我所描述的那样 => https://github.com/graphql-python/graphene/blob/master/docs/Makefile - Shammel Lee
显示剩余11条评论
4个回答

16

如果真的所有目标都是PHONY,那这有点毫无意义。make的意思是“做必要的事情来更新我的输出”,如果答案总是一样的,简单的脚本可以用更少的开销完成同样的工作。

话虽如此,我可以想象一种情况,即所有打算在命令行中给出的目标都应该是PHONY -- 假设你想构建一些文档,就像这样:

all: doc1.pdf doc2.pdf doc3.pdf doc4.pdf doc5.pdf

manuals: doc3.pdf doc4.pdf

faqs: doc1.pdf

designdocs: doc2.pdf

apidocs: doc5.pdf

developerdocs: doc2.pdf doc5.pdf

userdocs: doc1.pdf doc3.pdf doc4.pdf

%.pdf: %.md
     $(somedocgenerator) -o $@ $<

那么你可以做以下操作:

.PHONY: all $(MAKECMDGOALS)

这将动态地使命令行中给定的任何目标成为PHONY。请注意,您必须在此处包括您的默认目标,因为可以通过简单地键入make来调用它而不明确地给出它。

我不建议使用此方法,因为它有两个缺点:

  • 对于不同版本的make,它的可移植性较差。
  • 如果有人决定像这样制作特定的输出文件make doc3.pdf,它将表现不良。
  • 如果您决定让其中一个PHONY目标依赖于另一个PHONY目标,则它也会表现不良。

因此,最好采用一种方法来声明所有PHONY目标的一行。如果您的Makefile非常庞大,您可以将其拆分为多个文件并使用include--并在每个文件中有一条.PHONY:线。


32
我不同意认为一个包含所有虚假目标的Makefile应该被任意脚本替代。Make是一种极其常用的工具,已经成为构建大多数项目的事实标准。如果在项目中使用任意构建脚本来替换它,将会违背这个标准,很可能会给开发人员和用户带来困惑。 - Alex Jansen
在项目中使用Make进行依赖管理非常棒!Make是一个非常好的工具,鉴于2020年软件开发的本质,逐个文件地构建可能不是大多数人想要解决的用例。.PHONY:%将执行大多数人想要的操作。 - Julius Ecker
1
@JuliusEcker 在GNU make 4.3中,.PHONY: % 对我不起作用。 - vlz
1
make的自动完成和已经安装的巨大机会使其成为在项目中分组工作流脚本的非常方便的工具,即使这不是该工具的预期用途。 - Hugo Mota
如果我需要目标相互依赖,那该怎么办?使用make非常容易编写和维护。但如果只是一堆Shell脚本,会有什么结果呢? - Alexei Sosin
我已经给这个回答点了个踩。"如果真的所有目标都是假的,那就有点毫无意义了" <- 这是一个错误的陈述。 - undefined

13

只需使用MAKEFLAGS并设置选项--always-make,如下所示:

MAKEFLAGS += --always-make

这应该放在Makefile的顶部? - Michael Pacheco
如何使用这个? - undefined
你可以在Makefile的顶部定义MAKEFLAGS,它是一个环境变量,make会根据它来选择选项。你可以在这里阅读更多相关信息:https://www.gnu.org/software/make/manual/html_node/Options_002fRecursion.html#Options_002fRecursion - undefined

13

对我来说,.PHONY: * 起作用了。(GNU make 4.2.1)


这里无法运行。 GNU make 4.4.1 - FelipeC
在GNU Make 3.82中无法工作。 - undefined
谢谢!适用于GNU Make 4.3,构建于x86_64-pc-linux-gnu。在Makefile的顶部设置了.PHONY: *并进行了测试。 - undefined

3

一种方法:

.PHONY: $(shell sed -n -e '/^$$/ { n ; /^[^ .\#][^ ]*:/ { s/:.*$$// ; p ; } ; }' $(MAKEFILE_LIST))

即使针对被列为依赖项的目标,也能完美运行。


1
实际上回答了问题。 - Timo Huovinen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接