Corrupted resource .cpp file when using QT5_ADD_RESOURCES and multithreaded compilation with CMake

advertisements

The 5.0 release of Qt has brought a set of easier commands to build Qt projects using CMake. See http://qt-project.org/doc/qt-5/cmake-manual.html. Resources for a project need to be included using the command QT5_ADD_RESOURCES.

If my resource file is named for instance Icon32.qrc, the QT5_ADD_RESOURCES(RESOURCES Icon32.qrc) command will automatically convert it into a qrc_Icon32.cpp file and define a ${RESOURCES} variable that I will then be able to include into the proper targets.

Doing this works perfectly, except that I get a compilation error in CDash roughly once every 20 builds. The error is typically of the following form:

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

What is happening is that a variable portion of the last line of the qrc_Icon32.cpp file is repeated after what should normally the end of file, thus creating one last nonsensical line for the compiler.

Logging what CMake does, it seems that the behavior of QT5_ADD_RESOURCES is the following: whenever it reaches a project that requires the resources in question, it executes a depend make file that is specific to the compilation target but that still would write the qrc_Icon32.cpp at the root of the build directory, and this for all targets. So if two targets are being compiled in parallel, two invocations of rcc could be writing into the same file at the same time, hence the corruption.

I have not found any report/discussion of this issue/feature on the web so I am wondering if I might have missed something:

Is there a way to tell CMake to save the generated qrc_Icon32.cpp in a different location for each target? Better yet, is it possible to tell CMake to call rcc only once from its main make file so that qrc_Icon32.cpp is later available for all targets?

I guess a workaround would be to create a static library that would be the only one using ${RESOURCES} and then to link that library into all targets. But still, I think CMake ought to be able to manage its dependencies properly when compiling with the multithreading -j flag.


To reproduce the issue, in an empty folder, create a CMakeList.txt containing the following

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})

Then Main1.cpp and Main2.cpp are created using

#include <iostream>

using namespace std;

int main(int argc, char** argv) {
        std::cout<<"Hello World 1"<<std::endl;
        return 0;
}

The qrc file is

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

Then create a folder named Icon32 and add a png image of your choice name YourImage.png.

Finally, create a build directory, enter it and run:

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

The output should be something like

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

You can see that qrc_Icon32.cpp is created twice at about the same time, at the root of the build directory. The qrc_Icon32.cpp.o files though are properly created in the FirstTarget.dir and SecondTarget.dir so there are no conflicts.

My point is that either: 1) qrc_Icon32.cpp should be created in FirstTarget.dir and SecondTarget.dir as well or 2) it should be created at the root of the build directory, but only once for all targets.


qt5_add_resources writes the file to CMAKE_CURRENT_BINARY_DIR, not CMAKE_BINARY_DIR

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

Ditto Qt 4:

http://cmake.org/gitweb?p=cmake.git;a=blob;f=Modules/Qt4Macros.cmake;h=b1b12d68b07aac076719c681fb844f4f98ba8151;hb=HEAD#l212

Update:

With the source code you provided in your update, it is possible to see the issue.

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

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

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

The workaround is to add a custom target and add explicit depends on that.

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 has an AUTORCC feature:

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

It also puts the generated qrc_ file in the current build dir. CMake 3.1 will put it in a target-specific directory, making this problem go away:

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