为啥在构建后源代码的更改并不总是反映在机器代码中而重建工作?

Posted

技术标签:

【中文标题】为啥在构建后源代码的更改并不总是反映在机器代码中而重建工作?【英文标题】:Why are changes in source not always reflected in machine code after build while rebuild works?为什么在构建后源代码的更改并不总是反映在机器代码中而重建工作? 【发布时间】:2011-07-25 03:14:02 【问题描述】:

有时,当我更改我的 Qt 项目(带有 mingw32 的 Qt Creator 2.1.0)中的代码时,这些更改在构建后不会反映在生成的机器代码中。这主要发生在我更改构造函数中的默认值或方法/构造函数中的参数顺序等内容时。通常,完全重建会修复该问题(但需要几分钟)。

我通过在构建之前删除生成的可执行文件或库来帮助自己,这似乎在大多数情况下都有帮助。这是否意味着链接目标文件时出现问题?

我来自 java/.net,我习惯了不同的行为。如果有人能解释我做错了什么和/或指出一些相关文章,我会很高兴。

谢谢!

【问题讨论】:

目标文件是否也保持不变? 【参考方案1】:

通常,在更改标头后,应重新构建包括该标头在内的所有源文件。 但是,qmake 在这方面有点特殊,需要为当前目录以外的包含文件夹设置 DEPENDPATH。例如,如果你有

INCLUDEPATH += somepath_in_my_project

还加

DEPENDPATH += some_path_in_my_project

仅使用 DEPENDPATH,如果 some_path_in_my_project 中的某些标头发生更改(如果它们包含该标头),则重新构建由 .pro 文件构建的文件!

我建议为每个 INCLUDEPATH 行添加一个相同的 DEPENDPATH 行,除非您包含一些您不希望更改的系统目录。

编辑:

使用 qmake 静态链接时存在类似问题:如果静态库 foo.a 更改,则链接到它的二进制文件不会重新链接。这是 QMake 中的一个错误,没有生成正确的依赖项。

我在以前的项目中找到的解决方法:

static:unix:TARGETDEPS += path_to_my/somestaticlib.a
static:win32:TARGETDEPS += path_to_my/somestaticlib.lib

编辑编辑:

从一段时间以来(Qt 5?),上面的代码应该使用 POST_TARGETDEPS 而不是 TARGETDEPS。

【讨论】:

如果 DEPENDPATH 中的任何包含文件发生更改,则意味着重新编译所有源文件?它似乎非常接近完全重新编译。 Serge Dundich:不,仅当 .cpp 文件包含已更改的标头时才会重新编译。它适用于每个文件级别。 DEPENDPATH 是我要找的关键字,非常感谢! 你是静态构建的吗?我在静态构建中遇到过这样的问题。 是的,我们正在进行静态构建。如果我发现任何新信息,我会进一步调查并及时更新这个问题。您不知道与静态构建和 Qt 的此类问题相关的任何内容吗?【参考方案2】:

最常见的情况是依赖关系损坏。在函数的默认参数的特殊情况下,它们在调用的地方被解析,所以如果你只是重新编译函数,代码将完全相同。您需要重新编译调用者。如果项目中的依赖不正确,并且构建系统没有检测到需要重新编译调用者,而只重新编译被调用者,那么你就会看到效果。

分析依赖关系并修复它们。

例子:

// what you write                  // what the compiler generates
void foo( int i = 0 )            void foo( int i )  // default removed
int main()                        int main() 
   foo();                             foo( 0 );         // compiler injected
                                  

【讨论】:

你如何建议在 QMAKE .pro 项目文件中指定一个已知的依赖项?【参考方案3】:

如果您在项目文件中列出所有相关的头文件,则不应发生这种情况。但实际上它一直在发生,因为 QMAKE 是错误的(它已经知道多年来未修复的依赖项生成问题)。所以最好清理它并重新编译或使用Cmake。而且 QMAKE 对源文件和头文件之间的依赖关系一无所知(并且几乎没有检测到),这可能会导致类似的问题。

【讨论】:

重命名和删除文件是搞砸 qmake 的最简单方法。非常糟糕的是,QT 团队中没有人优先修复此错误。【参考方案4】:

如果您的Makefile(或您的道德上等同于Makefile)缺少依赖信息,那么您可能会获得不同步的构建。对于每个带有#include "header.h" 的文件,您需要确保Makefileheader.h 作为该文件的依赖项。

这是我收到的Makefile 的一小部分:

parser_yacc.c parser_yacc.h: parser_yacc.y parser.h
        $(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y

parser_lex.c: parser_lex.l parser_yacc.h parser.h
        $(LEX) $LEXFLAGS -o$@ $<

parser_lex.o: parser_lex.c parser.h parser_yacc.h
        $(CC) $(EXTRA_CFLAGS) -c -o $@ $<

parser_misc.o: parser_misc.c parser.h parser_yacc.h af_names.h cap_names.h
        $(CC) $(EXTRA_CFLAGS) -c -o $@ $<

每个目标文件显然都依赖于相应的源文件和头文件。如果我们忘记了cap_names.h,那么当cap_names.h 被修改时,parser_misc.o 就不会被重建。可能会出现错误。

恐怕修复过程冗长乏味:检查每个文件,列出其依赖项,并将缺少的依赖项添加到Makefile。在 Linux 平台上,您可以使用strace(1) 工具来发现编译器在编译每个源文件时需要打开哪些文件,并使用该列表改进依赖关系。我不知道 Windows 上是否存在任何类似的工具,但在深入研究之前花几分钟寻找一个是值得的。

【讨论】:

Makefile 由 QMAKE 实用程序自动生成。

以上是关于为啥在构建后源代码的更改并不总是反映在机器代码中而重建工作?的主要内容,如果未能解决你的问题,请参考以下文章

React 源代码更改未反映在浏览器 DOM 中

为啥自定义指令不能立即反映其代码的变化

为啥c#中的main方法总是放在类中而不是c++中

React 状态更改并不总是适用于移动网络

求助:为啥我编的C++程序在自己电脑上能正常运行,但编译后挂在后台linux系统下就总是运行一半后出错,

Android Studio 3.0 Kotlin 更改未反映在构建中