Search code examples
c++qtcmakeqt5icu

Deploy icu libraries when linking a cmake project with Qt5


I've the following CMakeLists.txt for creating a little application:

cmake_minimum_required (VERSION 3.5.0)

project (sampleapp)

# Setting project flags
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib)
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib)
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/bin)
set (CMAKE_INCLUDE_CURRENT_DIR ON)
set (CMAKE_AUTOMOC ON)

# Finding needed packages
find_package (Qt5Widgets REQUIRED)
find_package (Qt5Core REQUIRED)
find_package (Qt5Gui REQUIRED)

# Setting project files
include_directories (${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR})
file (GLOB_RECURSE PROJECT_SRC *.cpp)

# Creating project
add_executable(${PROJECT_NAME} ${PROJECT_SRC})

# Linking dependencies
target_link_libraries (${PROJECT_NAME} somelibrary Qt5::Widgets Qt5::Gui Qt5::Core)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:${PROJECT_NAME}>)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:${PROJECT_NAME}>)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:${PROJECT_NAME}>)

When I run it and then build the application the Qt dlls are copied into target folder. In particular I copy:

  • Qt5Core.dll
  • Qt5Gui.dll
  • Qt5Widgets.dll

When I run the application it does not start anyway because the ICU libraries needed for Qt are not copied. When I start the application, Windows tells me that following libraries are missing:

  • icuin53.dll
  • icuuc53.dll

If I copy these libraries manually from Qt insallation folder it works (ok, I've an error regarding "platform plugins windows" missing but it's another story).

Is there a way to copy the icu libraries in a standard way, like the post build commands that I'm using for copying libraries? Or what's the best way to copy them in a transparent way, if possible (CMake can know what are dependencies of Qt and copy them where Qt are needed)?


Solution

  • Instead of copying each dll by hand, you can install all Qt dependencies using windeployqt, the tool provided with Qt for deployment on Windows.

    You can first declare windeployqt as an imported executable:

    find_package(Qt5
        # ...
    )
    
    if(Qt5_FOUND AND WIN32 AND TARGET Qt5::qmake AND NOT TARGET Qt5::windeployqt)
        get_target_property(_qt5_qmake_location
            Qt5::qmake IMPORTED_LOCATION
        )
    
        execute_process(
            COMMAND "${_qt5_qmake_location}" -query QT_INSTALL_PREFIX
            RESULT_VARIABLE return_code
            OUTPUT_VARIABLE qt5_install_prefix
            OUTPUT_STRIP_TRAILING_WHITESPACE
        )
    
        set(imported_location "${qt5_install_prefix}/bin/windeployqt.exe")
    
        if(EXISTS ${imported_location})
            add_executable(Qt5::windeployqt IMPORTED)
    
            set_target_properties(Qt5::windeployqt PROPERTIES
                IMPORTED_LOCATION ${imported_location}
            )
        endif()
    endif()
    

    Now the imported executable can be used as follows:

    add_executable(foo
        # ...
    )
    
    if(TARGET Qt5::windeployqt)
        add_custom_command(TARGET foo
            POST_BUILD
            COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt"
            COMMAND set PATH=%PATH%$<SEMICOLON>${qt5_install_prefix}/bin
            COMMAND Qt5::windeployqt --dir "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" "$<TARGET_FILE:foo>"
        )
    
        install(
            DIRECTORY
            "${CMAKE_CURRENT_BINARY_DIR}/windeployqt/"
            DESTINATION ${FOO_INSTALL_RUNTIME_DESTINATION}
        )
    endif()