Copying Qt DLLs to executable directory on Windows using CMake - windows

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)

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. ;)

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.

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>
)

Related

CMake Visual Studio Generator Expression RelWithDebInfo

I am using a external library in my project and I need to to copy a dll next to the exe during the build process. The debug and release dll's are named the same and are in a "Debug" and "Release" directory.
Building with visual studio.
This is what I have right now:
add_custom_command(TARGET App POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${api}/lib/x64/$<CONFIG>/libfbxsdk.dll
$<TARGET_FILE_DIR:App>)
But this fails for RelWithDebInfo - how do I copy the Debug dll to the Debug directory and the Release dll to the Release and RelWithDebInfo directory?
This is what I ended up using...
add_custom_command(TARGET App POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${fbxroot}/lib/x64/$<$<CONFIG:RelWithDebInfo>:Release>$<$<NOT:$<CONFIG:RelWithDebInfo>>:$<CONFIG>>/libfbxsdk.dll
$<TARGET_FILE_DIR:App>)
which seems to be working.

Why cmake install() looks for output files in Release folder for VS

this is a simplified version of my cmake
cmake_minimum_required(VERSION 2.8.4)
project(math)
add_library(math math.cpp)
function(install_package)
install(TARGETS math
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
add_custom_command(TARGET math
POST_BUILD
COMMAND cmake ARGS -P cmake_install.cmake)
endfunction()
install_package()
But when I build Debug version I get the following error
CMake Error at cmake_install.cmake:55 (file):
file INSTALL cannot find
"<my project's root>/build/Release/math.lib".
Why is it looking in Release folder despite that I build for Debug?
When I build for Release then, obviously, everything works.
I tried to add CONFIGURATIONS option to install method, but it doesn't help.
I'm using Visual Studio 15.
If I look into my cmake_install.cmake, Release is the default if you don't specify anything in your add_custom_command() call:
# Set the install configuration name.
if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
if(BUILD_TYPE)
string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
else()
set(CMAKE_INSTALL_CONFIG_NAME "Release")
endif()
message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
endif()
So if you look into INSTALL.vcxproj the call that CMake is generating looks like:
"C:\Program Files\CMake\bin\cmake.exe" -DBUILD_TYPE=$(Configuration) -P cmake_install.cmake
Which would translate into:
add_custom_command(TARGET math
POST_BUILD
COMMAND ${CMAKE_COMMAND} ARGS -D BUILD_TYPE=$<CONFIG> -P cmake_install.cmake)

How can I use fixup_bundle to add libraries to a framework on OSX?

I've used CMake to create a framework on OSX, but the framework dynamically links with libusb. How can I use fixup_bundle() or something else to copy libusb.dylib into the framework bundle, and fix my library's rpath so it points into the framework, instead of the absolute path of the libusb I built?
Fixing the rpath turned out to be relatively easy:
set_target_properties(mylib PROPERTIES
# Build a framework on OSX.
FRAMEWORK TRUE
# Add a relative path for dependencies to the rpath of mylib (when installed).
INSTALL_RPATH "#loader_path"
# But also use the INSTALL_RPATH anyway in the build tree, before it is installed.
BUILD_WITH_INSTALL_RPATH TRUE
)
But that doesn't copy the dependencies into the framework. I do that manually:
# Copy libfoo into the bundle.
if (APPLE)
add_custom_command(TARGET mylib POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:libfoo>" "$<TARGET_FILE_DIR:mylib>"
COMMENT "Bundling libfoo"
VERBATIM
)
endif ()

Cross platform alternative `mkdir -p` in makefile + cmake

I'm writing makefile, that works with cmake. makefile just creates the build directory and start cmake in it, if build directory is not created yet, or redirects targets to makefile created by cmake, if it already exists. I used mkdir -p to create a build directory, but it is not cross platform.
Is there any cross platform variant for mkdir -p? May be cmake provides alternative command, like it does autoconf?
You can use cmake -E make_directory (see documentation). If the parent directories are not existing, it does create all directories up the given one.
If you use it inside a CMake script itself, e.g. as pre-build step: ${CMAKE_COMMAND} -E make_directory
References
Creating a directory in CMake
cmake: make_directory in built time

How to get CMake to use existing Makefile?

I have an existing project (wvdial) that has a working makefile. I'm trying to integrate it into our main build process which uses CMake. Can anyone advise on how to do this? I made an attempt below based on some of the other projects we build but the makefile is never called. All I want to do is call the makefile for wvdial and include the binary in the .deb package we build.
cmake_minimum_required(VERSION 2.6)
SET(COMPONENT_NAME roots-vendor-wvdial)
SET(DEBIAN_PACKAGE_VERSION 1.6.1)
SET(WVDIAL_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
SET(WVDIAL_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
SET(WVDIAL_INSTALLED ${CMAKE_CURRENT_BINARY_DIR})
ADD_CUSTOM_TARGET(
wvdial ALL
DEPENDS ${WVDIAL_INSTALLED}
)
IF (${ROOTS_TARGET_ARCHITECTURE} STREQUAL "armhf")
SET(TARGET_FLAG "--host=arm-linux-gnueabihf")
ENDIF()
ADD_CUSTOM_COMMAND(
WORKING_DIRECTORY ${WVDIAL_BINARY_DIR}
OUTPUT ${WVDIAL_INSTALLED}
COMMAND env CXXFLAGS=${ROOTS_COMPILER_FLAGS} ./configure ${TARGET_FLAG} ${ROOTS_HOST_OPTION}
COMMAND make
COMMENT "Building wvdial"
VERBATIM
)
INSTALL(
FILES ${CMAKE_CURRENT_BINARY_DIR}/wvdial
DESTINATION usr/local/bin
COMPONENT ${COMPONENT_NAME}
PERMISSIONS OWNER_EXECUTE OWNER_READ OWNER_WRITE GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
)
DEFINE_DEBIAN_PACKAGE(
NAME ${COMPONENT_NAME}
CONTROL_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/debian/control
CHANGELOG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/debian/changelog
)
Take a look at the ExternalProject module.
This will add a dummy target to your CMake project that is responsible for building the dependency. The command is quite complex and supports a lot of stuff that you probably won't need in your case. Kitware (the company behind CMake) did a nice post called Building External Projects with CMake 2.8 a while back explaining the basic use of that command.

Resources