自动重新构建Qt Creator中的依赖项

13

Qt Creator(4.6.1)让我抓狂。我的应用程序拆分成三个部分:

  • 应用程序
  • 单元测试应用程序

当我更改库中的文件并重新构建应用程序时,编译器不会重新编译库,而是链接到旧版本的库。

此外,当我更改库、重新编译它,然后编译应用程序时,不会进行任何编译,因为它使用了缓存的应用程序。

有没有什么设置可以改变这种情况?这是我的项目文件:

TEMPLATE = subdirs

SUBDIRS += \
    app \
    lib_mylib \
    tests

app.depends = lib_mylib
tests.depends = lib_mylib

该库被构建为静态库:

TEMPLATE = lib
TARGET = mylib
CONFIG += staticlib

你使用的操作系统是什么?我经常在Linux上使用这样的结构,从未遇到过这样的问题。 - Felix
@Felix:Windows 7和MSVC - Georg Schölly
那么这可能与jom/nmake有关 - 您可以尝试在构建配置中禁用jom,但这将显着减慢构建速度。 - Felix
3个回答

9
我知道有点晚了,但我想给出更详细的答案,解释为什么会发生这种情况,以及其他解决方案如何帮助。

一个可行的解决方案是:像之前一样使用b.depends += a,或者使用CONFIG += ordered 并且b中添加PRE_TARGETDEPS += ...。(顺便说一句:不建议使用ordered,因为它可能会极大地减慢构建速度,并且通常被认为是不好的实践)

简而言之: 这种特殊组合需要的原因是:在子目录项目中app.depends = lib_mylib确保在启动应用程序之前始终先构建库,而PRE_TARGETDEPS确保每次库更改时都会重新构建应用程序。


长说明:

要理解为什么这样做有效,我们需要了解qmake如何处理子目录。 qmake是一个Makefile生成器,这意味着它只会创建Makefile。所以所有的依赖关系排序必须使用make提供的方法。首先,我们必须理解make的工作方式。

在make中,依赖关系相对简单:

some_target: dep1 dep2 dep3
    some_command

这意味着如果您想创建some_target,make将首先以未指定的顺序创建dep1dep2dep3,一旦这三个都完成后,执行some_command
然而,make会针对文件进行优化。考虑以下情况:
hello.txt:
    echo "creating hello"
    echo "hello" > hello.txt

hello2.txt: hello.txt
    echo "creating hello2"
    echo "hello2" > hello2.txt

运行make命令将创建两个文件并打印两个消息。第二次运行不会有任何操作。原因是make跟踪已经创建的文件和文件更改。由于hello.txt已经存在,它不会再次创建。由于hello.txt没有更改,因此无需再次创建hello2.txt。如果你现在外部更改hello.txt的内容并再次运行make,则hello2.txt将重新创建,并且您将看到该消息。
现在对于子目录项目,这变得有点复杂,因为我们现在需要在多个不同的makefile之间建立依赖关系!这通常通过递归调用make来解决。对于您的示例,qmake创建以下代码(简化):
lib_mylib: FORCE
    $(MAKE) lib_mylib/Makefile

app: lib_mylib FORCE
    $(MAKE) app/Makefile

这段代码将会按照预期创建lib_mylib(阻塞式,即只有整个库被构建完成后才会完成lib_mylib的构建),然后再创建app。使用FORCE依赖项确保该命令始终运行,即使目标已经存在。


了解了这些基础知识后,我们现在可以重构qmake的运行过程。使用b.depends += a将生成如上所示的代码 - 它确保所有依赖项按正确顺序构建,但没有其他作用!使用有序配置将自动创建这些依赖规则,因此它们的工作方式在逻辑上没有区别。

然而,这还不足以在lib_mylib更改时重新构建app。它只确保在make开始构建app之前构建lib_mylib

为了重新构建app,我们使用PRE_TARGETDEPS - 这将向应用程序makefile中的make目标添加一个依赖项,如上所示。

app.exe: mylib.lib:
    #linker code

这意味着每次更改lib_mylib时,app也会被重建。但是,在没有有序配置的情况下使用此功能可能会失败,因为 make 可能会首先尝试构建 app(如果 lib 没有更改,则什么都不做,或者如果 lib 不存在则会失败),然后重新构建 lib_mylib。再次运行 make 将重建 app,但这相当不方便。

所以,这就是为什么我们需要将这两者结合起来。我们需要控制不同 makefile 的执行顺序,并引用另一个 makefile 创建的工件 - 这正是这些命令所做的事情。

你可以像之前一样使用 b.depends += a,或者使用 CONFIG += ordered 并将 PRE_TARGETDEPS += ... 添加到 b 中。我最初误读了这个含糊的陈述,我认为你的意思是先添加 (b.depends += a 或 CONFIG += ordered) ,然后再将 PRE_TARGETDEPS += ... 添加到 b 中。 - nyanpasu64

7

我使用了CONFIG += ordered、DEPENDPATH和PRE_TARGETDEPS来解决同样的问题。在Linux上和使用MSVC的win下都能正常工作,您可以尝试一下。

在你的项目pro文件中加入以下内容:

CONFIG += ordered

附注:您的库应该首先列出。例如:

SUBDIRS += \
    lib \
    app \
    tests

在您的 exe .pro 文件中添加以下内容,正确配置路径:

DEPENDPATH += $$PWD/../lib
PRE_TARGETDEPS += $$OUT_PWD/../lib/liblib.a

在此处可以找到更多的选项和标志:这里

2
CONFIG += ordered 不是必需的,也不会改变任何东西 - 它只会减慢构建速度(基本上与基于顺序的自动依赖项相同)。然而,DEPENDPATHPRE_TARGETDEPS 应该可以工作。 - Felix
3
“PRE_TARGETDEPS”是使其工作的关键。感谢您!据我所读,Qt5不再需要“DEPENDPATH”。 - Georg Schölly

1

无论我尝试用长而易懂的解释来说明

TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS += \
    dynamiclib \
    staticlib \
    testlibs

对于我这个相对较小且简短的项目,它对我很有效。

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