PDA

View Full Version : Cmake resource file link error



JanW
15th February 2017, 13:00
Hi,

I'm setting up my environment for a C++/Qt Quick application (Windows VS2013 x64) with cmake. I'm having trouble to get a (shared) library be compiled and linked to my executable and using a resource file from it (just a simple qml file to test the setup.

I have set(CMAKE_AUTOMOC ON) and set(CMAKE_AUTORCC ON) in my root CMakeLists.txt
The CMakeLists.txt of my shared library is as follows (BEQMLResource.qrc is in the same directory as CMakeLists.txt):



cmake_minimum_required(VERSION 3.2)

if(UNIX)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=gnu++11")
endif()

SET(QT_ROOT_DIR "C:/Qt/5.7/msvc2013_64/")

set(QT_QMAKE_EXECUTABLE ${QT_ROOT_DIR}/bin/qmake)

find_package(Qt5 COMPONENTS Quick Core Network Widgets Gui Qml Positioning PATHS ${QT_ROOT_DIR})

project("BEQML")
set(PROJECT_RESOURCES BEQMLResource.qrc)
qt5_add_resources(BEQML_RESOURCES ${PROJECT_RESOURCES})
file(GLOB BEQML_SRC_LIBRARY
"BEQMLLibrary.h" #contains dll import/export defines for windows.
"BEQMLLibraryInitializer.cpp" #dummy class with some functions so the library is created.
"BEQMLLibraryInitializer.h"
)
source_group("Library" FILES ${BEQML_SRC_LIBRARY})
add_library(BEQML SHARED ${BEQML_SRC_LIBRARY} ${BEQML_RESOURCES})
target_include_directories(BEQML PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../BELibraries/)


When I compile the library, I get two linker errors in qrc_BEQMLResource.obje (unresolved external symbol for qRegisterResourceData and qUnregisterResourceData).
Any idea what can be wrong?
Kind regards,

Jan

JanW
16th February 2017, 08:45
Adding qt5_use_module(BEQML core) solved my first problem. The BEQML library is building without errors, but now I got a link error in my executable project: unresolved external symbol qInitResource_BEQMLResource.
The CMakeLists.txt from my exe project is as follows:


cmake_minimum_required(VERSION 3.2 FATAL_ERROR)

if(UNIX)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=gnu++11")
endif()

set(CMAKE_INCLUDE_CURRENT_DIR ON)

SET(QT_ROOT_DIR "C:/Qt/5.7/msvc2013_64/")

set(QT_QMAKE_EXECUTABLE ${QT_ROOT_DIR}/bin/qmake)

find_package(Qt5 COMPONENTS Quick Core Network Widgets Gui Qml Positioning PATHS ${QT_ROOT_DIR})

project("StormExe")

qt5_add_resources(RESOURCES StormExe.qrc)

file(GLOB STORMEXE_SRC_QML
"QML/Main.qml"
)
file(GLOB STORMEXE_SRC_EXE
"Main.cpp"
)

source_group("Exe" FILES ${STORMEXE_SRC_EXE})
source_group("QML" FILES ${STORMEXE_SRC_QML})

add_executable(Storm ${STORMEXE_SRC_EXE} ${RESOURCES} ${STORMEXE_SRC_QML})
qt5_use_modules(Storm Quick Core Network Widgets Gui Qml Positioning)

target_link_libraries(Storm BEQML)
target_include_directories(Storm PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../BELibraries/)

In my Main.cpp i have:


#include <QApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

Q_INIT_RESOURCE(BEQMLResource);

QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/Main.qml")));

return app.exec();
}

Do I need to do something special to get access to the resources from the shared library?
Regards,

Jan

Btw, if i build as a static lib, everything works just fine...

JanW
16th February 2017, 22:33
Ok, found a workaround by exporting a custom method from the dll which calls the initialisation of the resource like this:



int BEQMLLibraryInitializer::init()
{
extern int qInitResources_BEQMLResource();
return qInitResources_BEQMLResource();
}


Is there a better way to do it?
Regards,

Jan

d_stranz
21st February 2017, 04:09
Is there a better way to do it?

Almost certainly. I've never had to write such code to initialize resources in a DLL. A call with the macro Q_INIT_RESOURCE( MyDll ) after the DLL is loaded has always worked. It appears that there might be something wrong with the way your DLL is built if this symbol isn't defined. Does your QRC file have the same base name as your DLL? (i.e. MyDll.qrc for MyDll.dll) That could very well be the cause of the failure, because compiling a QRC file with another name generates a different symbol name.

JanW
21st February 2017, 09:14
Thanks for the suggestion, that was not the case. Though after changing the name to BEQML.qrc (the dll is called BEQML.dll), I still get the same link error:

Main.obj : error LNK2019: unresolved external symbol "int __cdecl qInitResources_BEQML(void)" (?qInitResources_BEQML@@YAHXZ) referenced in function main

Jan

d_stranz
22nd February 2017, 18:48
RCC should be generating a qrc_BEQML.cpp file for you. Look for it and compare the last part of it to the comparable file I have for my project (called "Composer"); it should look similar to this:



#ifdef QT_NAMESPACE
# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name
# define QT_RCC_MANGLE_NAMESPACE0(x) x
# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b
# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)
# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \
QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))
#else
# define QT_RCC_PREPEND_NAMESPACE(name) name
# define QT_RCC_MANGLE_NAMESPACE(name) name
#endif

#ifdef QT_NAMESPACE
namespace QT_NAMESPACE {
#endif

bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);

bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);

#ifdef QT_NAMESPACE
}
#endif

int QT_RCC_MANGLE_NAMESPACE(qInitResources_composer)() ;
int QT_RCC_MANGLE_NAMESPACE(qInitResources_composer)()
{
QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData)
(0x01, qt_resource_struct, qt_resource_name, qt_resource_data);
return 1;
}

int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_composer )();
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_composer )()
{
QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData)
(0x01, qt_resource_struct, qt_resource_name, qt_resource_data);
return 1;
}

namespace {
struct initializer {
initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources_composer)() ; }
~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources_composer )(); }
} dummy;
}


It is the struct initializer at the end that's giving rise to your undefined symbol references. Check the Makefile to be sure the command line is generating this code as you expect. There might be something in the CMake commands that is causing something different to occur.

I suppose the next thing to look at after that is to be sure that the calling convention is indeed __cdecl.