如何强制生成文件重建目标?

Posted

技术标签:

【中文标题】如何强制生成文件重建目标?【英文标题】:How do you force a makefile to rebuild a target? 【发布时间】:2010-10-23 09:58:15 【问题描述】:

我有一个生成文件,然后调用另一个生成文件。由于这个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 不是你唯一的问题,你不应该将解决方案编辑到问题中,或者至少不再是。) 【参考方案1】:

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

【讨论】:

@MarkKCowan 我完全同意!这个选项正是我想要的,而不是 Dave 建议的一些变通方法。 这种方法的警告是,它只是构建了太多东西。特别是使用 autotools,我看到它重新运行 configure .. 我希望可以构建一个基于 LD_PRELOAD 的解决方案! 是的,它甚至可以重写你不想要的文件!例如出现在依赖项中并被重建和覆盖的全局系统库...... CAVEAT:这将按照它所说的(LOL):从头开始重建您的目标,忽略所有时间采样。因此,如果您只想重建长链的最后一步(例如,用于测试工作流程的新部分),那么临时的 .PHONY 可能更实用。【参考方案2】:

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

虚假目标是不是真正的文件名的目标;而是它 只是当您显式执行时要执行的配方的名称 要求。使用虚假目标有两个原因: 与同名文件冲突,并提高性能。

...

虚假目标不应该是真实目标文件的先决条件;如果 是的,每次 make 更新它时都会运行它的配方 文件。只要虚假目标永远不是真实目标的先决条件 目标,假目标配方将仅在假的时候执行 target 是一个指定的目标

【讨论】:

这个答案,虽然它被“接受”并且高度“赞成”,但它确实是不合格的。首先,它说“声明目标是假的”,然后它说“假目标不是真正的文件名”。好吧,如果您的目标是一个文件,那么答案就是矛盾的。其次,它说“虚假目标不应该是真实目标的先决条件”——好吧,如果是这样呢?原来的问题,没有说明是不是。正确的答案是, 声明 your 目标是虚假的,而是声明一个额外的虚假目标,然后,依赖于您想要重建的目标. @MarkGaleck。当答案指出“虚假目标不是真正的文件名”时,它直接引用了 gcc make 手册。这是完全正确的。 "Target" 是一个 Make 术语,它指的是冒号左侧的文本 :,而不仅仅是您想要创建的最终结果(例如您的二进制文件)。在问题中,releasedebugcleaninstall 是 Make 目标,而不是 xxx_utilxxxcore.so 或其他任何目标。 这是如何获得 51 票反对的?!如果您仔细阅读 OP 的问题,make release 似乎有可能,甚至很可能会创建一个名为release 的目录。这意味着输出“发布是最新的”正是您所期望的,正确的答案将“发布”声明为.PHONY。原因是您只想运行“发布”配方,而不管“发布”文件或目录是否实际存在。这正是 .PHONY 的用途。【参考方案3】:

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 认为发布是最新的。这可能是因为您在执行的命令中有一个touch release 命令;这可能是因为存在一个名为“release”的文件或目录,并且没有依赖关系,因此是最新的。然后才是真正的原因……

【讨论】:

【参考方案4】:

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

话虽如此,您可能应该在 makefile 的顶部为各种文件名定义一些变量,并定义具有输入和输出部分的真实 make 规则,以便您可以使用 make 的好处,即您将只实际编译需要编译的东西!

编辑:添加示例。未经测试,但这就是你的做法 .PHONY

.PHONY: clean    
clean:
    $(clean)

【讨论】:

好吧,如果你能给我看一个例子,那就太好了。 Atm 我只是在试图让大坝工作:P .PHONY 目标的位置无关紧要。它可以在Makefile 中的任何位置。【参考方案5】:

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

【讨论】:

【参考方案6】:

这种简单的技术将允许 makefile 在不需要强制时正常运行。在 ma​​kefile 的末尾创建一个名为 force 的新目标。 force 目标将触及您的默认目标所依赖的文件。在下面的示例中,我添加了 touch myprogram.cpp。我还添加了对 ma​​ke 的递归调用。这将导致每次键入 ma​​ke force 时都会生成默认目标。

yourProgram: yourProgram.cpp
       g++ -o yourProgram yourProgram.cpp 

force:
       touch yourProgram.cpp
       make

【讨论】:

你不应该在 Makefile 中使用make。请改用$(MAKE)【参考方案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' do 'rm' 删除所有扩展名为 '.o' 的目标文件。

2) 'new' 调用'make'。 'make' 看到没有 '.o' 文件,所以它会再次创建所有的 '.o'。然后链接器将所有 .o 文件链接到一个可执行输出中

祝你好运

【讨论】:

new 的配方中使用$(MAKE) 比使用make 更好【参考方案8】:

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

【讨论】:

【参考方案9】:

如果您不需要保留已成功编译的任何输出

nmake /A 

全部重建

【讨论】:

【参考方案10】:

根据米勒的Recursive Make Considered Harmful,您应该避免致电$(MAKE)!在您展示的情况下,它是无害的,因为这不是一个真正的生成文件,只是一个包装脚本,它也可能是用 Shell 编写的。但是你说你在更深的递归级别继续这样,所以你可能遇到了那篇令人大开眼界的文章中显示的问题。

当然,使用 GNU 会避免它很麻烦。尽管他们意识到了这个问题,但这是他们记录在案的做事方式。

OTOH,makepp 是为解决此问题而创建的。您可以在每个目录级别编写您的 makefile,但它们都被绘制到您的项目的完整视图中。

但是传统的 makefile 是递归编写的。所以有一个解决方法,$(MAKE) 除了将子请求引导回主 makepp 进程之外什么都不做。只有当你在你的 submakes 之间做多余的或者更糟糕的矛盾的事情时,你必须请求--traditional-recursive-make(这当然会破坏 makepp 的这个优势)。我不知道你的其他 makefile,但如果它们写得很干净,那么 makepp 必要的重建应该会自动发生,而不需要其他人在这里建议的任何 hack。

【讨论】:

没有回答问题:与要点相切,应该是评论而不是答案。 也许我不够清楚。使用makepp 不需要整个包装器生成文件。通过了解确切的依赖关系(所有这些依赖关系,而不仅仅是: 之后列出的内容),它将始终在必要时重建。【参考方案11】:

已经提到过,但我想我可以添加到使用touch

如果您touch所有要编译的源文件,touch 命令将文件的时间戳更改为执行touch 命令的系统时间。

源文件 timstamp 是 make 用来“知道”文件已更改,需要重新编译的文件

例如:如果项目是c++项目,则执行touch *.cpp,然后再次运行make,make应该重新编译整个项目。

【讨论】:

【参考方案12】:

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

clean: FORCE
        rm $(objects)
FORCE: ; 

这将运行干净,而不管任何其他依赖项。

我在手册中的解决方案中添加了分号,否则需要一个空行。

【讨论】:

【参考方案13】:

这实际上取决于目标是什么。如果它是虚假目标(即目标与文件无关),则应将其声明为 .PHONY。

如果目标不是虚假目标,但您只是出于某种原因想重建它(例如,当您使用 __TIME__ 预处理宏时),您应该使用此处答案中描述的 FORCE 方案。

【讨论】:

【参考方案14】:

http://www.gnu.org/software/make/manual/html_node/Force-Targets.html#Force-Targets

【讨论】:

【参考方案15】:

在我的 Linux 系统(Centos 6.2)上,声明目标 .PHONY 和创建对 FORCE 的假依赖之间存在显着差异,而规则实际上确实创建了与目标匹配的文件。当必须每次重新生成文件时,它都需要 文件上的虚假依赖 FORCE,以及​​虚假依赖的 .PHONY。

错误:

date > $@

右:

FORCE
    date > $@
FORCE:
    .PHONY: FORCE

【讨论】:

以上是关于如何强制生成文件重建目标?的主要内容,如果未能解决你的问题,请参考以下文章

autotools:强制 make 不重建配置/Makefile

sh 每次构建项目时,如何强制Xcode重建项目中的Info.plist文件?

在自定义目标运行后重建依赖目标

重建 IntelliJ 项目索引

Android强制Fragment重建View

Visual Studio Docker Tools如何从头开始强制容器重建