8

New to CMake, and I'm having a hard time understanding how to use generator expressions. I'm trying to use add_custom_command to create a post-build command to copy Qt DLLs to the executable directory.

In Qt5WidgetsConfig.cmake I can see it creates different properties for the Qt5::Widgets target to refer to the DLL, depending on the currently active configuration. Either IMPORTED_LOCATION_DEBUG or IMPORTED_LOCATION_RELEASE. I expected to be able to use the $<CONFIG:Debug> generator expression as a condition in an if() but that doesn't work.

My CMakeLists.txt:

# minimum version required for proper support of C++11 features in Qt
cmake_minimum_required(VERSION 3.1.0)

set(CMAKE_CONFIGURATION_TYPES Debug;Release)

# project name and version
project(TPBMon VERSION 0.0.0.1)

# Qt5 libs
find_package(Qt5Widgets REQUIRED)

# run Qt's MOC when needed
set(CMAKE_AUTOMOC ON)

add_executable(
    tpbmon
    src/main.cpp
    src/mainwindow.hpp
    src/mainwindow.cpp
)
target_link_libraries(tpbmon Qt5::Widgets)
set_target_properties(
    tpbmon
    PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin
)
if(WIN32)
    if($<CONFIG:Debug>)
        get_target_property(WIDGETDLL Qt5::Widgets IMPORTED_LOCATION_DEBUG)
    else($<CONFIG:Debug>)
        get_target_property(WIDGETDLL Qt5::Widgets IMPORTED_LOCATION_RELEASE)
    endif($<CONFIG:Debug>)
    add_custom_command(
        TARGET tpbmon POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy ${WIDGETDLL} $<TARGET_FILE_DIR:tpbmon>
    )
endif(WIN32)
Jehjoa
  • 551
  • 8
  • 23

3 Answers3

13

Figured it out myself by modifying the add_custom_command call to

add_custom_command(
    TARGET tpbmon POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
        $<TARGET_FILE:Qt5::Widgets>
        $<TARGET_FILE_DIR:tpbmon>
)

It's amazing what a fresh perspective after a good night's sleep can do. ;)

Jehjoa
  • 551
  • 8
  • 23
  • 2
    Good answer; however `Widgets` depend on `Gui`, and `Core`, and `qwindows.dll` etc. which you should manually append. **`windeployqt`** handles all these dependencies. – Burak Oct 20 '21 at 10:45
  • This script doesn't work for me. Visual Studio complains error evaluating expression: Target "Qt6::Widgets" is not an executable or library. – gargantuadev Jan 06 '22 at 10:40
9

You can use windeployqt program which is part of Qt binary release. It will scan your binary and collect all used Qt DLLs, plugins and QML modules. It could be wrapped in CMake as a post-build event by add_custom_command(TARGET target_name POST_BUILD ...) signature.

jiandingzhe
  • 1,881
  • 15
  • 35
  • This is the right answer. windeployqt is a simple and effective tool built by Qt for Qt. Here's a [tutorial](youtube.com/watch?v=8qozxqSZQEg) – ecstrema Mar 30 '20 at 03:28
  • 2
    Example usage: https://stackoverflow.com/a/60856725 – Burak Oct 20 '21 at 10:30
2

For the future you can add all Qt5 dependencies to your executable folder:

 find_package(Qt5 COMPONENTS Core Gui Widgets)
 ...   
 add_custom_command(TARGET MyQtProj POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:MyQtProj>
        COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:MyQtProj>
        COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:MyQtProj>
        )
irbis
  • 21
  • 1