使用QT5_ADD_RESOURCES和CMake进行多线程编译时,资源.cpp文件已损坏

Corrupted resource .cpp file when using QT5_ADD_RESOURCES and multithread compiling with CMake

本文关键字:资源 编译 cpp 已损坏 文件 多线程 ADD QT5 RESOURCES CMake 使用      更新时间:2023-10-16

Qt的5.0版本为使用CMake构建Qt项目带来了一组更简单的命令。看见http://qt-project.org/doc/qt-5/cmake-manual.html.需要使用QT5_ADD_Resources命令包含项目的资源。

如果我的资源文件被命名为例如Icon32.qrc,QT5_ADD_RESOURCES(RESOURCES Icon32.qrc)命令将自动将其转换为qrc_Icon32.cpp文件,并定义${RESOURCES}变量,然后我将能够将其包含到适当的目标中。

这样做效果很好,只是我在CDash中大约每20个构建中就会出现一次编译错误。错误通常为以下形式:

/.../CMake/build/qrc_Icon32.cpp:272380:1: error: unknown type name 'qCleanupResources_Icon32'

发生的情况是qrc_Icon32.cpp文件最后一行的可变部分在通常应该是文件末尾的部分之后重复,从而为编译器创建最后一行无意义的行。

记录CMake的操作,似乎QT5_ADD_RESOURCES的行为如下:每当它到达需要相关资源的项目时,它都会执行一个特定于编译目标的dependentmake文件,但该文件仍会在生成目录的根目录下写入qrc_Icon32.cpp,这适用于所有目标。因此,如果并行编译两个目标,则rcc的两个调用可能同时写入同一文件,从而导致损坏。

我在网上没有找到任何关于这个问题/功能的报告/讨论,所以我想知道我是否错过了什么:

是否有方法告诉CMake将生成的qrc_Icon32.cpp保存在每个目标的不同位置?更好的是,是否可以告诉CMake从其主make文件中只调用rcc一次,以便qrc_Icon32.cpp稍后可用于所有目标?

我想一个变通办法是创建一个静态库,它将是唯一一个使用${RESOURCES}的库,然后将该库链接到所有目标。但是,我仍然认为CMake在使用多线程-j标志进行编译时应该能够正确地管理其依赖关系。


要重现该问题,请在一个空文件夹中创建一个包含以下的CMakeList.txt

CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
PROJECT(SSCCE CXX)
set(CMAKE_PREFIX_PATH /usr/local/Qt-5.3.0 ${CMAKE_PREFIX_PATH})
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
INCLUDE_DIRECTORIES(SYSTEM "/usr/local/Qt-5.3.0/include/QtCore")
find_package(Qt5Core REQUIRED)
QT5_ADD_RESOURCES(RESOURCES Icon32.qrc)
SET(LIBLIST gobject-2.0 X11-xcb Xi xcb-render-util SM ICE xcb-glx xcb-render xcb-atom xcb-property xcb-event dbus-1 xcb xcb-image xcb-icccm xcb-sync xcb-xfixes xcb-shm xcb-randr xcb-shape xcb-keysyms fontconfig freetype Xrender Xext X11 jpeg png Qt5::Core z m dl gthread-2.0 rt glib-2.0 GL pthread)
ADD_EXECUTABLE(FirstTarget Main1.cpp ${RESOURCES})
TARGET_LINK_LIBRARIES(FirstTarget ${LIBLIST})
ADD_EXECUTABLE(SecondTarget Main2.cpp ${RESOURCES})
TARGET_LINK_LIBRARIES(SecondTarget ${LIBLIST})

然后使用创建Main1.cpp和Main2.cpp

#include <iostream>
using namespace std;
int main(int argc, char** argv) {
        std::cout<<"Hello World 1"<<std::endl;
        return 0;
}

qrc文件是

<RCC>
    <qresource prefix="/">
        <file>Icon32/YourImage.png</file>
    </qresource>
</RCC>

然后创建一个名为Icon32的文件夹,并添加您选择的名称YourImage.png.的png图像

最后,创建一个构建目录,输入它并运行:

cmake -DCMAKE_CXX_COMPILER=g++-4.8 -DCMAKE_CXX_FLAGS='-std=c++11 -fPIE' ..
make -j2

输出应该类似

Scanning dependencies of target FirstTarget_automoc
Scanning dependencies of target SecondTarget_automoc
[ 10%] [ 20%] Automoc for target FirstTarget
Automoc for target SecondTarget
[ 20%] [ 20%] Built target FirstTarget_automoc
Built target SecondTarget_automoc
[ 30%] [ 40%] Generating qrc_Icon32.cpp
Generating qrc_Icon32.cpp
Scanning dependencies of target SecondTarget
Scanning dependencies of target FirstTarget
[ 50%] [ 60%] Building CXX object CMakeFiles/SecondTarget.dir/Main2.cpp.o
Building CXX object CMakeFiles/FirstTarget.dir/Main1.cpp.o
[ 70%] [ 80%] Building CXX object CMakeFiles/SecondTarget.dir/qrc_Icon32.cpp.o
Building CXX object CMakeFiles/FirstTarget.dir/qrc_Icon32.cpp.o
[ 90%] [100%] Building CXX object CMakeFiles/SecondTarget.dir /SecondTarget_automoc.cpp.o
Building CXX object CMakeFiles/FirstTarget.dir/FirstTarget_automoc.cpp.o
Linking CXX executable SecondTarget
Linking CXX executable FirstTarget

您可以看到qrc_Icon32.cpp在构建目录的根目录中同时创建了两次。qrc_Icon32.cpp.o文件是在FirstTarget.dir和SecondTarget.dir中正确创建的,因此没有冲突。

我的观点是:1) qrc_Icon32.cpp也应在FirstTarget.dir和SecondTarget.dir中创建,或者2) 它应该在构建目录的根目录下创建,但对于所有目标只能创建一次。

qt5_add_resources将文件写入CMAKE_CURRENT_BINARY_DIR,而不是CMAKE_BINARY_DIR

https://qt.gitorious.org/qt/qtbase/source/d953d9a4c3bdc5ed3b8d380c4b893b51b523bc50:src/corelib/Qt5CoreMacros.cmake#L205

同上Qt 4:

http://cmake.org/gitweb?p=cmake.git;a=斑点;f=模块/Qt4Macros.cmake;h=b1b12d68b07aac076719c681fb844f4f98ba8151;hb=封头#1212

更新:

使用您在更新中提供的源代码,可以看到问题。

http://www.cmake.org/pipermail/cmake/2008-October/024492.html

http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=0 ece8f79

http://public.kitware.com/Bug/view.php?id=12311

解决方法是添加自定义目标并添加显式目标。

cmake_minimum_required(VERSION 2.8.11)
project(MyTest)
find_package(Qt5Core)
qt5_add_resources(RSCS somefile.qrc)
add_custom_target(gen_qrc DEPENDS ${RSCS})
add_executable(foo foo.cpp ${RSCS})
add_dependencies(foo gen_qrc)
add_executable(bar bar.cpp ${RSCS})
add_dependencies(bar gen_qrc)

CMake 3.0具有AUTORCC功能:

http://www.cmake.org/cmake/help/v3.0/manual/cmake-qt.7.html#autorcc

它还将生成的qrc_文件放在当前的构建目录中。CMake 3.1将把它放在特定于目标的目录中,使这个问题消失:

http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=33774ca2

报告的行为是由于QT5_ADD_RESOURCES在过程的早期、配置期间和编译实际开始之前被调用。此时,steveire指出的变量${CMAKE_CURRENT_BINARY_DIR}被定义为根构建文件夹。(这可以通过在Qt5CoreMMacros.cmake.中添加MESSAGE(输出)来轻松测试

函数QT5_ADD_RESOURCES的结果是创建将在编译过程中调用的自定义命令:https://qt.gitorious.org/qt/qtbase/source/d953d9a4c3bdc5ed3b8d380c4b893b51b523bc50%3src/corelib/Qt5CoreMMacros.cmake#L231

如果定义了多个目标,那么以后可以同时并行调用这些自定义命令。此时,${outfile}将不会使用当时的${CMAKE_current_BINARY_DIR}重新定义,因此并行进程将在构建目录根目录的同一文件中写入。

查看QT5_ADD_RESOURCES的代码,在多线程编译的情况下,似乎没有任何机制/选项可以纠正这种行为,然而,正如steveire所指出的,底层自定义命令函数的解决方法也可以成功应用。