如何强制让 makefile 重新构建目标?

251

我有一个Makefile,它构建并调用另一个Makefile。由于该Makefile调用更多执行工作的Makefile,因此它不会真正改变。因此,它始终认为项目已经构建完毕且是最新的。

dnetdev11 ~ # make
make: `release' is up to date.

如何强制makefile重新构建目标?

clean = $(MAKE) -f ~/xxx/xxx_compile.workspace.mak clean


build = svn up ~/xxx                                                       \
        $(clean)                                                                \
        ~/cbp2mak/cbp2mak -C ~/xxx ~/xxx/xxx_compile.workspace        \
        $(MAKE) -f ~/xxx/xxx_compile.workspace.mak $(1)                    \


release:
        $(build )

debug:
        $(build DEBUG=1)

clean:
        $(clean)

install:
        cp ~/xxx/source/xxx_utility/release/xxx_util /usr/local/bin
        cp ~/xxx/source/xxx_utility/release/xxxcore.so /usr/local/lib

注意:为了保护当事人隐私,姓名已被删除

最终修复版本:

clean = $(MAKE) -f xxx_compile.workspace.mak clean;


build = svn up;                                         \
        $(clean)                                        \
        ./cbp2mak/cbp2mak -C . xxx_compile.workspace;   \
        $(MAKE) -f xxx_compile.workspace.mak    $(1);   \


.PHONY: release debug clean install

release:
        $(call build,)

debug:
        $(call build,DEBUG=1)

clean:
        $(clean)

install:
        cp ./source/xxx_utillity/release/xxx_util /usr/bin
        cp ./dlls/Release/xxxcore.so /usr/lib

Lodle,既然这是一个经常被访问的问题,你想编辑一下让它更现代化吗?(看起来.PHONY并不是你唯一的问题,而且你不应该将解决方案编辑到问题中,或者至少不再如此。) - Keith M
15个回答

866

-B开关用于make命令,其长格式为--always-make,告诉make命令忽略时间戳并创建指定的目标。这可能会破坏使用make命令的目的,但这可能正是您所需要的。


9
@MarkKCowan 我完全同意!这个选项正是我一直在寻找的,而不是像戴夫建议的那样采用一些绕过办法。 - Maarten Bamelis
9
这种方法的注意事项是它构建了太多的东西。特别是对于自动工具(autotools),我看到它重新运行了 configure..。我希望能够构建一个基于 LD_PRELOAD 的解决方案! - vrdhn
是的,它甚至可以重写你不想要的文件!例如出现在依赖项中并被重新构建和覆盖的全局系统库... - Julio Guerra
2
注意:这将按照字面意思执行(哈哈):从头开始重建您的目标,忽略所有时间戳。因此,如果您只想重新构建长链的最后一步(例如,为了测试工作流程的新部分),那么临时的.PHONY可能更实用。 - Leo

40

您可以将一个或多个目标声明为虚拟目标。

虚拟目标不是实际文件的名称;而仅是在您进行显式请求时要执行的配方的名称。使用虚拟目标的两个原因:避免与同名文件冲突,提高性能。

...

虚拟目标不应该是真实目标文件的前提条件;如果它是,则在每次更新该文件时都会运行其配方。只要虚拟目标从未成为真实目标的前提条件,那么当指定虚拟目标为目标时,虚拟目标配方就只会被执行一次。


95
尽管此回答被"接受"并高度"赞同",但实际上有些不准确。首先,它说"声明目标为虚假",但随后又说"虚假目标并不是一个文件的名称"。如果你的目标是一个文件,那么这就是回答中的矛盾之处。其次,它说"虚假目标不应该是真实目标的前提" -那如果它是呢?原始问题没有说明它是否是这样。正确的答案是,不要声明"你的"目标是虚假的,而是声明一个额外的虚假目标,然后将你想要重建的目标依赖于它。 - Mark Galeck
6
当回答声明"A phony target is one that is not really the name of a file",它是直接引用了gcc make手册的内容。这是完全正确的。 - drlolly
“Target” 是 Makefile 中的一个术语,指的是冒号 : 左侧的文本,而不仅仅是你想要创建的最终结果(例如二进制文件)。在这个问题中,“release”、“debug”、“clean” 和 “install” 都是 Makefile 的目标,而不是 xxx_utilxxxcore.so 或其他任何东西。 - Keith M
2
这怎么会有51个踩?!如果你仔细阅读OP的问题,似乎很可能make release实际上创建了一个名为release的目录。这意味着输出“release is up to date”正是你所期望的,正确的答案是将'release'声明为.PHONY。原因是你只想运行'release'配方,无论是否存在'release'文件或目录。这正是.PHONY的用途。 - EML
由于release目标在其他Makefile中调用其他目标来完成实际工作,因此在这些Makefile中使用.PHONY来留下增量构建检查是非常合理的,这符合OP的问题。我曾经遇到过完全相同的情况,这个答案对我的问题非常完美。 - Leonardo
针对我刚遇到的具体问题,这是最好的解决方案。 - Bathsheba

25

在Sun的make手册中曾经记录了一种技巧,使用一个(不存在的)目标".FORCE"。你可以通过创建一个名为force.mk的文件来实现这一点,其中包含以下内容:

.FORCE:
$(FORCE_DEPS): .FORCE

假设你现有的makefile文件名为makefile,那么你可以运行以下命令:

make FORCE_DEPS=release -f force.mk -f makefile release

由于 .FORCE 不存在,任何依赖于它的东西都将过时并被重新构建。

这在任何版本的 make 中都适用;在 Linux 上,您有 GNU Make 并且可以像讨论的那样使用 .PHONY 目标。

还值得考虑为什么 make 认为 release 是最新的。这可能是因为在执行的命令中有 touch release 命令;也可能是因为存在一个名为“release”的文件或目录,没有依赖项,因此是最新的。然后就是实际的原因......


20

有人建议使用.PHONY,这绝对是正确的。对于任何输入和输出之间的日期比较无效的规则都应该使用.PHONY。由于您没有任何类似于 output: input 的目标,因此您应该对所有目标都使用.PHONY!

话虽如此,您可能应该在Makefile的顶部定义一些变量来表示各个文件名,并定义实际的Make规则,其中包括输入和输出部分,以便您可以利用Make的好处,即只编译必要的内容!

编辑:添加示例。未经测试,但这就是如何使用 .PHONY。

.PHONY: clean    
clean:
    $(clean)

1
如果您能给我展示一个例子,那就太好了。目前我只是在试图让这个该死的东西工作起来 :P - Lodle
2
.PHONY 目标的位置并不重要,它可以放在 Makefile 的任何位置。 - Adrian W

9

make clean 命令会删除所有已编译的目标文件。


7

我尝试了这个方法,它对我有效。

在Makefile文件中添加以下几行:

clean:
    rm *.o output

new: clean
    $(MAKE)     #use variable $(MAKE) instead of make to get recursive make calls

保存并现在调用

make new 

并且它将重新编译所有内容

发生了什么?

1)'new' 调用 clean。'clean' 执行 'rm' 命令,删除所有后缀名为 '.o' 的对象文件。

2)'new' 调用 'make'。'make' 发现没有 '.o' 文件,因此它再次创建所有的 '.o' 文件。然后链接器将所有 .o 文件链接成一个可执行的输出。

祝好运


1
在“新建”配方中,最好使用$(MAKE)而不是make - Basile Starynkevitch

7
如果我没记错的话,'make'使用时间戳(文件修改时间)来确定目标是否是最新的。强制重新构建的常见方法是使用'touch'命令更新该时间戳。您可以尝试在makefile中调用'touch'来更新其中一个目标的时间戳(也许是其中一个子makefile),这可能会强制Make执行该命令。

6
这项简单的技巧可以使makefile在不需要强制时正常运行。在你的makefile末尾创建一个名为force的新目标。 force 目标将触及默认目标所依赖的一个文件。 在下面的示例中,我添加了touch myprogram.cpp。我还添加了一个递归调用make。这将导致每次键入make force时都会生成默认目标。
yourProgram: yourProgram.cpp
       g++ -o yourProgram yourProgram.cpp 

force:
       touch yourProgram.cpp
       make

2
在Makefile中,您永远不应该使用make。请改用$(MAKE) - Benjamin Crawford Ctrl-Alt-Tut

5

正如abernier所指出的,GNU make手册中有一个推荐的解决方案,它使用一个“假”目标来强制重建目标:

clean: FORCE
        rm $(objects)
FORCE: ; 

无论其他依赖项如何,此操作都将干净地运行。我从手册中添加了分号到解决方案中,否则需要一个空行。

我不认为在这里需要空行。我本来可以想象需要换行,但我测试了一下,连那个都不需要。那个冒号(在最后一行的FORCE之后)可以是文件中的最后一个字符,这仍然有效。也许在你的测试环境中,后面还有其他需要分号的内容? - undefined

2
如果您不需要保留已成功编译的任何输出,可以忽略它们。
nmake /A 

重新构建所有内容。

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