【问题标题】:Makefiles: how to get a file name for a rule target from outside, in a portable wayMakefiles:如何以可移植的方式从外部获取规则目标的文件名
【发布时间】:2010-03-08 18:58:48
【问题描述】:

我需要获取一些外部数据以在 Makefile 中形成输出文件名。

GNU Make 中我可以这样做:

NAME=$(shell some_commands)
$(NAME).out: file.in
     compile "$<" -o "$@"

其他一些规则有$(NAME).out 作为先决条件,所以我需要$(NAME).out 作为目标。我不能在这里使用替换+引号,因为这不是由 shell 解释的。

我从 BSD Make 手册页中看到,在 Mac OS X 上我应该能够做这样的事情(虽然我还不能测试它):

NAME!=some_commands
$(NAME).out: file.in
     compile "$<" -o "$@"

有没有一种技巧可以以便携的方式做这种事情?

我不想生成 Makefile 并使构建过程混乱。我虽然是包含,但它们在 Make 版本中也有不同的语法。

【问题讨论】:

  • 这是一个很好的问题,但我认为我从未见过这样做过。简单地坚持使用 GNU make 是很常见的。
  • 在 OS X 10.6.2 上:make --version 返回 GNU Make 3.81built for i386-apple-darwin10.0
  • 哦,我记得默认情况下在 Tiger 上安装了 BSD Make。但我认为如果可能的话,我仍然会保持它的便携性,有一个类似于 Pavel 方法 3 的解决方法。

标签: shell makefile


【解决方案1】:

我可以提出几个技巧。所有这些都是基于将some_commands的执行外包给一个规则体,并确保它们的结果得到正确利用。

  1. 这是我的最爱。包括一个您自己创建的 makefile,其中包含$(NAME) 的规则。在处理开始时,它将被重新创建并出现正确的目标。问题是这个makefile的创建过程是在shell命令中完成的:

    include aux_makefile
    .PHONY aux_makefile
    aux_makefile:
        NAME=`some_commands` ; ( \
            echo $$NAME.out: file.in ; \
            echo "\t rm aux_makefile" ; \
            echo "\t compile ..."  ) >$@
    
  2. 第一个变体:制作一个包装器 makefile,它首先写入辅助 makefile,然后递归调用原始 makefile,辅助 makefile 包含在其中。不需要特殊的构建目标。

  3. 使用静态名称创建一个虚拟目标;它将代表$(NAME).out。即:

    all: dummy    # Instead of all: $(NAME).out
    dummy: file.in
       NAME=`some_commands`; compile "$<" -o "$$NAME"
       touch $@
    

【讨论】:

  • 最终我只是做了方法 3 的变体(我希望它是最简单的,并且易于被其他人修改!!)。我只是创建了一个其他规则所依赖的静态规则,以及一个具有适当名称的副本作为用户输出。
  • @Piotr,这也行,但这里的关键是这条规则不应该是.PHONY,它应该是touch 与其名称相同的文件,以避免不必要的重新编译。
  • 是的,确切地说,我有一个非.PHONY 规则,因此有一个用于构建过程的静态输出文件名,然后是另一个具有所需名称的文件名。所以不是很漂亮,但避免了重新编译,并且所有文件都有重要的内容。但我曾经也用touch 重新发现了这个hack,试图做最少量的LaTeX 重新编译:)
【解决方案2】:

这有点笨拙,我对不同版本的 Make 了解不多,无法知道它是否真的可移植,但它可能会工作,并且满足您的严格条件(不生成 makefile,没有“包含”,事情可能取决于 $(NAME).out)。如果您只打算从命令行创建一个目标(例如,“全部”),那么您可以这样编写:

ifeq ($(origin NAME), undefined) 全部: @$(MAKE) -s all NAME=some_commands_using_substituion_and_quotes 别的 全部:some_preqs_maybe_including_NAME 做其他事 万一

当你运行 Make 时,NAME 是未定义的,所以规则再次运行 Make 并定义了 NAME——因为命令是由 shell 解释的。 (“@”和“-s”只是为了让它更安静。)如果你试图“手动”创建其他目标,你会遇到很多麻烦,因为 $(NAME) 将评估为空。

【讨论】:

  • 哦,这也很聪明,但我不太确定它是否可移植,从我在 BSD Make Manual 上看到的内容来看,指令有不同的语法 (.if ...)。跨度>
  • 像往常一样,比我的好:-)
  • @Pavel,哦,别说了。您提供了三个解决方案,我提供了一个严重限制 makefile 使用的解决方案,并且可能甚至不能移植(这就是重点)。比如说,也许我们可以编写一个 makefile 来检测哪个版本正在运行它,然后定义一个 IFTHEN 函数……或者我们可以只编写一个病毒来破坏所有非 GNU 的 makefile,这样就不会那么可怕了。
  • 您的方法完全不可移植。在 make 中实现 if-then-else 之类的唯一方法是使用模式宏扩展。
【解决方案3】:

如果您不尝试使用不可移植的 BSD 扩展,则有一种可移植的方式来执行此操作。

一个工作的 makefile 应该是这样的:

NAME = $(shell some command) # This is the non-portable gmake syntax
NAME :sh= some command       # This is mostly portable from SunPro Make

all:
    echo $(NAME)

第二种方法是在 1986 年由 SunPro Make 引入的,smakeBSD make 也支持。

如果您想验证这一点,SunPro Makesmake 是 schilytools 的一部分。

【讨论】:

    猜你喜欢
    • 2012-10-28
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 2011-01-09
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多