Search code examples
cmakevisual-studio-2017qt5unresolved-external

CMake does not run moc for a Q_NAMESPACE in a shared library


Problem

I'm trying to register a namespace in QML from a shared library, but the compilation fails with an unresolved external symbol error:

failed (exit code 1120) with the following output:
main.cpp.obj : error LNK2019: unresolved external symbol "struct QMetaObject const EnumNamespace::staticMetaObject" (?staticMetaObject@EnumNamespace@@3UQMetaObject@@B) referenced in function "class QDebug __cdecl operator<<<enum EnumNamespace::MyEnum>(class QDebug,enum EnumNamespace::MyEnum)" (??$?6W4MyEnum@EnumNamespace@@@@YA?AVQDebug@@V0@W4MyEnum@EnumNamespace@@@Z)
StaticMetaObject.exe : fatal error LNK1120: 1 unresolved externals

I have searched the build directory for the moc file (moc_enum.cpp or similar) but I couldn't find it. The only moc related file I could find was mocs_compilation.cpp:

// This file is autogenerated. Changes will be overwritten.
// No files found that require moc or the moc files are included
enum some_compilers { need_more_than_nothing };

Why is CMake not finding the file it needs to moc?

PS: If instead of using CMake I use qmake, it compiles fine and the moc_enum.cpp file is present in the build directory.

PS2: There are several questions regarding this issue or similar ones, but unfortunately either they don't have an answer or it didn't work (e.g.: this, this or this)

Setup

  • Qt 5.12.10 & Qt Creator 10.0.0
  • CMake 3.23 (integrated in Qt Creator)
  • Visual Studio 2017 64bit compiler.

Code

Main CMakeLists.txt

cmake_minimum_required(VERSION 3.23 FATAL_ERROR)

project(StaticMetaObject VERSION 1.0.0 LANGUAGES CXX DESCRIPTION "Static Meta Object Test")

set(BUILD_SHARED_LIBS ON)
set(CMAKE_DEBUG_POSTFIX d)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

find_package(QT NAMES Qt5 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Qml)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

add_subdirectory(enum)

add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME} PUBLIC main.cpp)
target_link_libraries(${PROJECT_NAME} enumLib Qt${QT_VERSION_MAJOR}::Qml)

main.cpp

#include "enum/enum.h"
#include <QCoreApplication>
#include <QDebug>
#include <QQmlEngine>

// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
int main(int argc, char * *argv)
{
    QCoreApplication app{argc, argv};
    qInfo() << EnumNamespace::TWO;

    // Comenting this line out doesn't solve the problem.
    qmlRegisterUncreatableMetaObject(EnumNamespace::staticMetaObject, "ABCDE", 1, 0, "EnumNamespace", "Error: only enums");

    return app.exec();
}

Enum lib

CMakeLists.txt

cmake_minimum_required(VERSION "${CMAKE_MINIMUM_REQUIRED_VERSION}" FATAL_ERROR)

set(LIB_NAME enumLib)
add_library(${LIB_NAME} SHARED)
set_target_properties(${LIB_NAME} PROPERTIES LINKER_LANGUAGE CXX)
target_sources(${LIB_NAME} PUBLIC enum.h enumLib.h)
target_compile_definitions(${LIB_NAME} PUBLIC ENUMLIB_BUILD)
target_link_libraries(${LIB_NAME} PUBLIC Qt${QT_VERSION_MAJOR}::Core)

enum.h

#ifndef ENUM_H
#define ENUM_H

#include "enumLib.h"
#include <QObject>

namespace EnumNamespace {
    ENUMLIB_EXPORT Q_NAMESPACE
    enum MyEnum : unsigned int
    {
        ZERO = 0,
        ONE,
        TWO
    };
    Q_ENUM_NS(MyEnum);
}

#endif // ENUM_H

enumLib.h

#ifndef ENUMLIB_H
#define ENUMLIB_H

#if defined(ENUMLIB_BUILD)
#define ENUMLIB_EXPORT Q_DECL_EXPORT
#else
#define ENUMLIB_EXPORT Q_DECL_IMPORT
#endif

#endif // ENUMLIB_H

Pro file

QT = core

CONFIG += c++17 cmdline

QT += core qml

DEFINES += ENUMLIB_BUILD

HEADERS += \
        enum/enum.h \
        enum/enum.h \
        enum/enumLib.h

SOURCES += \
        main.cpp

Full CMake build log

16:00:30: Running steps for project StaticMetaObject...
16:00:30: Starting: "C:\Qt\Tools\CMake_64\bin\cmake.exe" --build C:/SRC/MyTests/StaticMetaObject/build_release --target all
[1/7 21.4/sec] Automatic MOC and UIC for target enumLib
[2/5 22.1/sec] Automatic MOC and UIC for target StaticMetaObject
[3/5 9.0/sec] Building CXX object CMakeFiles\StaticMetaObject.dir\StaticMetaObject_autogen\mocs_compilation.cpp.obj
[4/5 4.3/sec] Building CXX object CMakeFiles\StaticMetaObject.dir\main.cpp.obj
[5/5 4.8/sec] Linking CXX executable StaticMetaObject.exe
FAILED: StaticMetaObject.exe 
cmd.exe /C "cd . && C:\Qt\Tools\CMake_64\bin\cmake.exe -E vs_link_exe --intdir=CMakeFiles\StaticMetaObject.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\mt.exe --manifests  -- C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\Hostx64\x64\link.exe /nologo CMakeFiles\StaticMetaObject.dir\StaticMetaObject_autogen\mocs_compilation.cpp.obj CMakeFiles\StaticMetaObject.dir\main.cpp.obj  /out:StaticMetaObject.exe /implib:StaticMetaObject.lib /pdb:StaticMetaObject.pdb /version:0.0 /machine:x64 /INCREMENTAL:NO /subsystem:console  enum\enumLib.lib  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Qml.lib  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Network.lib  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Core.lib  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\Hostx64\x64\link.exe /nologo CMakeFiles\StaticMetaObject.dir\StaticMetaObject_autogen\mocs_compilation.cpp.obj CMakeFiles\StaticMetaObject.dir\main.cpp.obj /out:StaticMetaObject.exe /implib:StaticMetaObject.lib /pdb:StaticMetaObject.pdb /version:0.0 /machine:x64 /INCREMENTAL:NO /subsystem:console enum\enumLib.lib C:\Qt\5.12.10\msvc2017_64\lib\Qt5Qml.lib C:\Qt\5.12.10\msvc2017_64\lib\Qt5Network.lib C:\Qt\5.12.10\msvc2017_64\lib\Qt5Core.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:StaticMetaObject.exe.manifest" failed (exit code 1120) with the following output:
main.cpp.obj : error LNK2019: unresolved external symbol "struct QMetaObject const EnumNamespace::staticMetaObject" (?staticMetaObject@EnumNamespace@@3UQMetaObject@@B) referenced in function "class QDebug __cdecl operator<<<enum EnumNamespace::MyEnum>(class QDebug,enum EnumNamespace::MyEnum)" (??$?6W4MyEnum@EnumNamespace@@@@YA?AVQDebug@@V0@W4MyEnum@EnumNamespace@@@Z)
StaticMetaObject.exe : fatal error LNK1120: 1 unresolved externals
ninja: build stopped: subcommand failed.
16:00:32: The process "C:\Qt\Tools\CMake_64\bin\cmake.exe" exited with code 1.
Error while building/deploying project StaticMetaObject (kit: Desktop Qt 5.12.10 MSVC2017 64bit)
When executing step "Build"
16:00:32: Elapsed time: 00:01.

Verbose CMake log

19:08:34: Running steps for project StaticMetaObject...
19:08:34: Starting: "C:\Qt\Tools\CMake_64\bin\cmake.exe" --build C:/SRC/MyTests/StaticMetaObject/build_release --target all --verbose
[1/7 25.6/sec] cmd.exe /C "cd /D C:\SRC\MyTests\StaticMetaObject\build_release\enum && C:\Qt\Tools\CMake_64\bin\cmake.exe -E cmake_autogen C:/SRC/MyTests/StaticMetaObject/build_release/enum/CMakeFiles/enumLib_autogen.dir/AutogenInfo.json Release"
[2/7 8.7/sec] C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\HostX64\x64\cl.exe  /nologo /TP -DENUMLIB_BUILD -DQT_CORE_LIB -DQT_NO_DEBUG -DenumLib_EXPORTS -IC:\SRC\MyTests\StaticMetaObject\build_release\enum\enumLib_autogen\include -external:IC:\Qt\5.12.10\msvc2017_64\include -external:IC:\Qt\5.12.10\msvc2017_64\include\QtCore -external:IC:\Qt\5.12.10\msvc2017_64\.\mkspecs\win32-msvc -external:W0 /DWIN32 /D_WINDOWS /EHsc /O2 /Ob2 /DNDEBUG -MD -std:c++17 /showIncludes /Foenum\CMakeFiles\enumLib.dir\enumLib_autogen\mocs_compilation.cpp.obj /Fdenum\CMakeFiles\enumLib.dir\ /FS -c C:\SRC\MyTests\StaticMetaObject\build_release\enum\enumLib_autogen\mocs_compilation.cpp
[3/7 7.6/sec] cmd.exe /C "cmd.exe /C "C:\Qt\Tools\CMake_64\bin\cmake.exe -E __create_def C:\SRC\MyTests\StaticMetaObject\build_release\enum\CMakeFiles\enumLib.dir\.\exports.def C:\SRC\MyTests\StaticMetaObject\build_release\enum\CMakeFiles\enumLib.dir\.\exports.def.objs && cd C:\SRC\MyTests\StaticMetaObject\build_release" && C:\Qt\Tools\CMake_64\bin\cmake.exe -E vs_link_dll --intdir=enum\CMakeFiles\enumLib.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\mt.exe --manifests  -- C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\Hostx64\x64\link.exe /nologo enum\CMakeFiles\enumLib.dir\enumLib_autogen\mocs_compilation.cpp.obj  /out:enum\enumLib.dll /implib:enum\enumLib.lib /pdb:enum\enumLib.pdb /dll /version:0.0 /machine:x64 /INCREMENTAL:NO  /DEF:enum\CMakeFiles\enumLib.dir\.\exports.def  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Core.lib  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib  && cd ."
[4/7 9.2/sec] cmd.exe /C "cd /D C:\SRC\MyTests\StaticMetaObject\build_release && C:\Qt\Tools\CMake_64\bin\cmake.exe -E cmake_autogen C:/SRC/MyTests/StaticMetaObject/build_release/CMakeFiles/StaticMetaObject_autogen.dir/AutogenInfo.json Release"
[5/7 10.5/sec] C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\HostX64\x64\cl.exe  /nologo /TP -DENUMLIB_BUILD -DQT_CORE_LIB -DQT_NETWORK_LIB -DQT_NO_DEBUG -DQT_QML_LIB -IC:\SRC\MyTests\StaticMetaObject\build_release\StaticMetaObject_autogen\include -external:IC:\Qt\5.12.10\msvc2017_64\include -external:IC:\Qt\5.12.10\msvc2017_64\include\QtCore -external:IC:\Qt\5.12.10\msvc2017_64\.\mkspecs\win32-msvc -external:IC:\Qt\5.12.10\msvc2017_64\include\QtQml -external:IC:\Qt\5.12.10\msvc2017_64\include\QtNetwork -external:W0 /DWIN32 /D_WINDOWS /EHsc /O2 /Ob2 /DNDEBUG -MD -std:c++17 /showIncludes /FoCMakeFiles\StaticMetaObject.dir\StaticMetaObject_autogen\mocs_compilation.cpp.obj /FdCMakeFiles\StaticMetaObject.dir\ /FS -c C:\SRC\MyTests\StaticMetaObject\build_release\StaticMetaObject_autogen\mocs_compilation.cpp
[6/7 5.4/sec] C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\HostX64\x64\cl.exe  /nologo /TP -DENUMLIB_BUILD -DQT_CORE_LIB -DQT_NETWORK_LIB -DQT_NO_DEBUG -DQT_QML_LIB -IC:\SRC\MyTests\StaticMetaObject\build_release\StaticMetaObject_autogen\include -external:IC:\Qt\5.12.10\msvc2017_64\include -external:IC:\Qt\5.12.10\msvc2017_64\include\QtCore -external:IC:\Qt\5.12.10\msvc2017_64\.\mkspecs\win32-msvc -external:IC:\Qt\5.12.10\msvc2017_64\include\QtQml -external:IC:\Qt\5.12.10\msvc2017_64\include\QtNetwork -external:W0 /DWIN32 /D_WINDOWS /EHsc /O2 /Ob2 /DNDEBUG -MD -std:c++17 /showIncludes /FoCMakeFiles\StaticMetaObject.dir\main.cpp.obj /FdCMakeFiles\StaticMetaObject.dir\ /FS -c C:\SRC\MyTests\StaticMetaObject\src\main.cpp
[7/7 5.9/sec] cmd.exe /C "cd . && C:\Qt\Tools\CMake_64\bin\cmake.exe -E vs_link_exe --intdir=CMakeFiles\StaticMetaObject.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\mt.exe --manifests  -- C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\Hostx64\x64\link.exe /nologo CMakeFiles\StaticMetaObject.dir\StaticMetaObject_autogen\mocs_compilation.cpp.obj CMakeFiles\StaticMetaObject.dir\main.cpp.obj  /out:StaticMetaObject.exe /implib:StaticMetaObject.lib /pdb:StaticMetaObject.pdb /version:0.0 /machine:x64 /INCREMENTAL:NO /subsystem:console  enum\enumLib.lib  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Qml.lib  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Network.lib  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Core.lib  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
FAILED: StaticMetaObject.exe 
cmd.exe /C "cd . && C:\Qt\Tools\CMake_64\bin\cmake.exe -E vs_link_exe --intdir=CMakeFiles\StaticMetaObject.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\mt.exe --manifests  -- C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\Hostx64\x64\link.exe /nologo CMakeFiles\StaticMetaObject.dir\StaticMetaObject_autogen\mocs_compilation.cpp.obj CMakeFiles\StaticMetaObject.dir\main.cpp.obj  /out:StaticMetaObject.exe /implib:StaticMetaObject.lib /pdb:StaticMetaObject.pdb /version:0.0 /machine:x64 /INCREMENTAL:NO /subsystem:console  enum\enumLib.lib  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Qml.lib  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Network.lib  C:\Qt\5.12.10\msvc2017_64\lib\Qt5Core.lib  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\Hostx64\x64\link.exe /nologo CMakeFiles\StaticMetaObject.dir\StaticMetaObject_autogen\mocs_compilation.cpp.obj CMakeFiles\StaticMetaObject.dir\main.cpp.obj /out:StaticMetaObject.exe /implib:StaticMetaObject.lib /pdb:StaticMetaObject.pdb /version:0.0 /machine:x64 /INCREMENTAL:NO /subsystem:console enum\enumLib.lib C:\Qt\5.12.10\msvc2017_64\lib\Qt5Qml.lib C:\Qt\5.12.10\msvc2017_64\lib\Qt5Network.lib C:\Qt\5.12.10\msvc2017_64\lib\Qt5Core.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:StaticMetaObject.exe.manifest" failed (exit code 1120) with the following output:
main.cpp.obj : error LNK2019: unresolved external symbol "struct QMetaObject const EnumNamespace::staticMetaObject" (?staticMetaObject@EnumNamespace@@3UQMetaObject@@B) referenced in function "class QDebug __cdecl operator<<<enum EnumNamespace::MyEnum>(class QDebug,enum EnumNamespace::MyEnum)" (??$?6W4MyEnum@EnumNamespace@@@@YA?AVQDebug@@V0@W4MyEnum@EnumNamespace@@@Z)
StaticMetaObject.exe : fatal error LNK1120: 1 unresolved externals
ninja: build stopped: subcommand failed.
19:08:36: The process "C:\Qt\Tools\CMake_64\bin\cmake.exe" exited with code 1.
Error while building/deploying project StaticMetaObject (kit: Desktop Qt 5.12.10 MSVC2017 64bit)
When executing step "Build"
19:08:36: Elapsed time: 00:01.

Update

Using the CMake generated exports file didn't work either. The compilation fails with the same error.

enum/CMakeLists.txt using autogenerated export file

cmake_minimum_required(VERSION "${CMAKE_MINIMUM_REQUIRED_VERSION}" FATAL_ERROR)

set(LIB_NAME enumLib)
add_library(${LIB_NAME} SHARED)
set_target_properties(${LIB_NAME} PROPERTIES LINKER_LANGUAGE CXX)
target_sources(${LIB_NAME} PUBLIC enum.h)

include(GenerateExportHeader)
generate_export_header(${LIB_NAME} EXPORT_MACRO_NAME ENUMLIB_EXPORT)

target_compile_definitions(${LIB_NAME} PUBLIC enumLib_EXPORTS)
target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(${LIB_NAME} PUBLIC Qt${QT_VERSION_MAJOR}::Core)

Solution

  • The AUTOMOC_MACRO_NAMES is a variable used to tell CMake to run moc on the files that

    contains certain keywords as the first non space string on a new line or as the first non space string after a { on a new line.
    The default value is Q_OBJECT;Q_GADGET;Q_NAMESPACE;Q_NAMESPACE_EXPORT

    Because Q_NAMESPACE was preceded by ENUMLIB_EXPORT, it was not noticed by CMake as one of the keywords to moc. The solution is to put Q_NAMESPACE on its own line:

    namespace EnumNamespace {
        ENUMLIB_EXPORT
        Q_NAMESPACE
        
        // Rest of the code
    }