[CMake] Linking errors using MSVC
-
Hi everyone,
I am trying to switch a Qt app from QMake to CMake for building on Windows, and I am trying to use MSVC as the compiler. I am encountering issues at the link stage when building, so I started over with a fresh CMake project created in Qt Creator to try and troubleshoot.
So I have created a fresh project by using File > New file or project... > Qt Widgets Application, and choosing CMake as the build system. I left everything as it was, and tried to build it by using Qt 5.9.9 MSVC 2017 kit, and it builds and runs fine.
Now when I try to build it directly in the command line :
- If I use the "Visual Studio 16 2019" generator, I get a lot of linking errors, as if it is unable to locate the lib files
- If I use the "Visual Studio 15 2017" generator, I get a lot of linking errors as well, and also some warnings
- If I use the "MinGW Makefiles" generator, it builds and runs fine.
What am I missing when using the command line for MSVC builds ?
Here are the 3 runs :
- Visual Studio 16 2019
C:\Users\rsd.DIGIDOM\dev\qt-cmake> cmake -B build . -G "Visual Studio 16 2019" -- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.19043. -- The CXX compiler identification is MSVC 19.29.30138.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: C:/Users/rsd.DIGIDOM/dev/qt-cmake/build C:\Users\rsd.DIGIDOM\dev\qt-cmake> cmake --build .\build\ Microsoft (R) Build Engine version 16.11.2+f32259642 for .NET Framework Copyright (C) Microsoft Corporation. All rights reserved. Checking Build System Automatic MOC and UIC for target qt-cmake Building Custom Rule C:/Users/rsd.DIGIDOM/dev/qt-cmake/CMakeLists.txt mocs_compilation_Debug.cpp main.cpp mainwindow.cpp Generating Code... mocs_compilation_Debug.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: struct QMetaObject * __cdecl QObjectData::dynamicMetaObject(void)const " (__imp_?dynamicMetaObject@QOb jectData@@QEBAPEAUQMetaObject@@XZ) referenced in function "public: virtual struct QMetaObject const * __cdecl MainWindow::metaObject(void)const " (?metaObject@MainWindow@@UEBAPEBUQMetaObject@@XZ) [C:\User s\rsd.DIGIDOM\dev\qt-cmake\build\qt-cmake.vcxproj] <<<< A lot of other LNK2019 errors >>>> mainwindow.obj : error LNK2001: unresolved external symbol "protected: virtual void __cdecl QMenuBar::actionEvent(class QActionEvent *)" (?actionEvent@QMenuBar@@MEAAXPEAVQActionEvent@@@Z) [C:\Users\rsd.DI GIDOM\dev\qt-cmake\build\qt-cmake.vcxproj] <<<< A lot of other LNK2001 errors >>>> C:\Users\rsd.DIGIDOM\dev\qt-cmake\build\Debug\qt-cmake.exe : fatal error LNK1120: 109 unresolved externals [C:\Users\rsd.DIGIDOM\dev\qt-cmake\build\qt-cmake.vcxproj] C:\Users\rsd.DIGIDOM\dev\qt-cmake>
- Visual Studio 15 2017
C:\Users\rsd.DIGIDOM\dev\qt-cmake> cmake -B build . -G "Visual Studio 15 2017" -- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.19043. -- The CXX compiler identification is MSVC 19.16.27045.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/VC/Tools/MSVC/14.16.27023/bin/Hostx86/x86/cl.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: C:/Users/rsd.DIGIDOM/dev/qt-cmake/build C:\Users\rsd.DIGIDOM\dev\qt-cmake> cmake --build .\build\ Microsoft (R) Build Engine version 15.9.21+g9802d43bc3 for .NET Framework Copyright (C) Microsoft Corporation. All rights reserved. Checking Build System Automatic MOC and UIC for target qt-cmake Building Custom Rule C:/Users/rsd.DIGIDOM/dev/qt-cmake/CMakeLists.txt mocs_compilation_Debug.cpp main.cpp mainwindow.cpp Generating Code... mocs_compilation_Debug.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: struct QMetaObject * __thiscall QObjectData::dynamicMetaObject(void)const " (__imp_?dynamicMetaObject@ QObjectData@@QBEPAUQMetaObject@@XZ) referenced in function "public: virtual struct QMetaObject const * __thiscall MainWindow::metaObject(void)const " (?metaObject@MainWindow@@UBEPBUQMetaObject@@XZ) [C:\Us ers\rsd.DIGIDOM\dev\qt-cmake\build\qt-cmake.vcxproj] <<<< A lot of other LNK2019 errors >>>> mainwindow.obj : error LNK2001: unresolved external symbol "protected: virtual void __thiscall QMenuBar::actionEvent(class QActionEvent *)" (?actionEvent@QMenuBar@@MAEXPAVQActionEvent@@@Z) [C:\Users\rsd.D IGIDOM\dev\qt-cmake\build\qt-cmake.vcxproj] <<<< A lot of other LNK2001 errors >>>> C:\Qt\5.15.2\mingw81_64\lib\libQt5Widgets.a : warning LNK4272: library machine type 'x64' conflicts with target machine type 'x86' [C:\Users\rsd.DIGIDOM\dev\qt-cmake\build\qt-cmake.vcxproj] C:\Qt\5.15.2\mingw81_64\lib\libQt5Gui.a : warning LNK4272: library machine type 'x64' conflicts with target machine type 'x86' [C:\Users\rsd.DIGIDOM\dev\qt-cmake\build\qt-cmake.vcxproj] C:\Qt\5.15.2\mingw81_64\lib\libQt5Core.a : warning LNK4272: library machine type 'x64' conflicts with target machine type 'x86' [C:\Users\rsd.DIGIDOM\dev\qt-cmake\build\qt-cmake.vcxproj] C:\Users\rsd.DIGIDOM\dev\qt-cmake\build\Debug\qt-cmake.exe : fatal error LNK1120: 109 unresolved externals [C:\Users\rsd.DIGIDOM\dev\qt-cmake\build\qt-cmake.vcxproj] C:\Users\rsd.DIGIDOM\dev\qt-cmake>
- MinGW Makefiles
C:\Users\rsd.DIGIDOM\dev\qt-cmake> cmake -B build . -G "MinGW Makefiles" -- The CXX compiler identification is GNU 8.3.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: C:/Strawberry-Perl/c/bin/g++.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: C:/Users/rsd.DIGIDOM/dev/qt-cmake/build C:\Users\rsd.DIGIDOM\dev\qt-cmake> cmake --build .\build\ [ 20%] Automatic MOC and UIC for target qt-cmake [ 20%] Built target qt-cmake_autogen [ 40%] Building CXX object CMakeFiles/qt-cmake.dir/qt-cmake_autogen/mocs_compilation.cpp.obj [ 60%] Building CXX object CMakeFiles/qt-cmake.dir/main.cpp.obj [ 80%] Building CXX object CMakeFiles/qt-cmake.dir/mainwindow.cpp.obj [100%] Linking CXX executable qt-cmake.exe [100%] Built target qt-cmake C:\Users\rsd.DIGIDOM\dev\qt-cmake> .\build\qt-cmake.exe C:\Users\rsd.DIGIDOM\dev\qt-cmake>
The currently installed Qt binaries I have are :
Thanks for your help !
-
Modify your PATH so the correct qmake is found. The path to qmake is also stored in CMakeCache.txt: QT_QMAKE_EXECUTABLE
-
@dgsham said in [CMake] Linking errors using MSVC:
C:\Qt\5.15.2\mingw81_64\lib\libQt5Widgets.a : warning LNK4272: library machine type 'x64' conflicts with target machine type 'x86'
This should tell you enough - you're trying to link against the Qt libs compiled for MinGW while using the MSVC compiler. Make sure that cmake picks up the correct qmake.exe so the correct libs are used.
/edit: and this is for sure the wrong MSVC compiler for your select Qt version:
Hostx86/x86/cl.exe
- properly setup your runtime (by opening the correct msvc command prompt) and ass the correct architecture to -G -
@Christian-Ehrlicher Thanks for your answer. You're right, this shows up in the MVSC 2017 logs. However, the MSVC 2019 logs show that it's indeed using x64.
Just to confirm, I did the following :
C:\Users\rsd.DIGIDOM\dev\qt-cmake> cmake -B build . -G "Visual Studio 15 2017" -T "host=x64" -A "x64" -- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.19043. -- The CXX compiler identification is MSVC 19.16.27045.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: C:/Users/rsd.DIGIDOM/dev/qt-cmake/build
But I still get the same link errors.
EDIT : And to clarify, I delete the whole
build
directory between each test -
@dgsham said in [CMake] Linking errors using MSVC:
But I still get the same link errors.
Because you still pick u the wrong Qt libs. Your cmake output does not show that Qt is searched at all - so how to you link against Qt?
-
@Christian-Ehrlicher You got a point, but in the MinGW logs the Qt5 search does not show up either, and it links fine.
As another test I did this :
C:\Users\rsd.DIGIDOM\dev\qt-cmake> cmake -B build . -G "Visual Studio 15 2017" -T "host=x64" -A "x64" -- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.19043. -- The CXX compiler identification is MSVC 19.16.27045.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Qt5_FOUND="1" -- Configuring done -- Generating done -- Build files have been written to: C:/Users/rsd.DIGIDOM/dev/qt-cmake/build
It looks like the Qt5 libs are properly discovered. Here is the complete
CMakeLists.txt
file. The only modification I have made is theQt5_FOUND
variable printing :cmake_minimum_required(VERSION 3.5) project(qt-cmake VERSION 0.1 LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED) include(CMakePrintHelpers) cmake_print_variables(Qt5_FOUND) set(PROJECT_SOURCES main.cpp mainwindow.cpp mainwindow.h mainwindow.ui ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) qt_add_executable(qt-cmake MANUAL_FINALIZATION ${PROJECT_SOURCES} ) # Define target properties for Android with Qt 6 as: # set_property(TARGET qt-cmake APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR # ${CMAKE_CURRENT_SOURCE_DIR}/android) # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation else() if(ANDROID) add_library(qt-cmake SHARED ${PROJECT_SOURCES} ) # Define properties for Android with Qt 5 after find_package() calls as: # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") else() add_executable(qt-cmake ${PROJECT_SOURCES} ) endif() endif() target_link_libraries(qt-cmake PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) set_target_properties(qt-cmake PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} ) if(QT_VERSION_MAJOR EQUAL 6) qt_finalize_executable(qt-cmake) endif()
-
As I said above - make sure the correct qmake.exe is picked up by cmake .
-
@Christian-Ehrlicher You are absolutely right, the
find_package
command is trying to use the MinGW libs. I could find out about this by usingcmake --trace
, here is an extract :C:/Users/rsd.DIGIDOM/dev/qt-cmake/CMakeLists.txt(14): find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED ) C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5ConfigVersion.cmake(2): set(PACKAGE_VERSION 5.15.2 ) C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5ConfigVersion.cmake(4): if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION ) C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5ConfigVersion.cmake(6): else() C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5ConfigVersion.cmake(7): set(PACKAGE_VERSION_COMPATIBLE TRUE ) C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5ConfigVersion.cmake(8): if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION ) C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5Config.cmake(2): if(CMAKE_VERSION VERSION_LESS 3.1.0 ) C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5Config.cmake(6): if(NOT Qt5_FIND_COMPONENTS ) C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5Config.cmake(7): set(Qt5_NOT_FOUND_MESSAGE The Qt5 package requires at least one component ) C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5Config.cmake(8): set(Qt5_FOUND False ) C:/Qt/5.15.2/mingw81_64/lib/cmake/Qt5/Qt5Config.cmake(9): return()
But I can't find how to force the use of the MSVC libs. Do you know where I can read some documentation about this ?
-
Modify your PATH so the correct qmake is found. The path to qmake is also stored in CMakeCache.txt: QT_QMAKE_EXECUTABLE
-
@Christian-Ehrlicher You nailed it, thanks.
The problem is that I previously configured my system PATH to point to
C:/Qt/5.15.2/mingw81_64/bin/
. I tried addingC:/Qt/5.9.9/msvc2017_64/bin/
, moving it up before the mingw entry, and rebooting : it didn't work. I had to remove the mingw entry for it to work.Does that mean that there is no way to use several versions of the Qt libs on the same system ? Even if both versions use the same compiler, say if I want one project to point to 5.9, and the other to 5.15, is there a way to do that ?
Thanks a lot for your help.
-
Simply don't add any of those paths to your default PATH. Set up two batch files which adds the appropriate path and call them. Or Use QtCreator and add the different Qt versions there - it will automatically switch the paths when you switch the Kit.
-
By googling after your answer, I think I found an even better solution : set the
CMAKE_PREFIX_PATH
variable to point toC:/Qt/5.9.9/msvc2017_64/lib/cmake/
. This can be done inCMakeLists.txt
, and probably inCMakePresets.json
too.This is actually said in the documentation, but it could be more clear in my opinion : https://doc.qt.io/qt-5/cmake-get-started.html
For find_package to be successful, CMake must find the Qt installation in one of the following ways: Set your CMAKE_PREFIX_PATH environment variable to the Qt 5 installation prefix. This is the recommended way.
I think it needs to be emphasized that :
- It needs to point to the
lib/cmake
subdirectory - It can be used to make several projects point to several different versions
Thanks again for your help !
- It needs to point to the
-
But then you hard-code your path to Qt in your CMakeLists.txt which is not portable at all and you won't be able to run the applications because the PATH is wrong and therefore the DLLs are not found.