How to install runtime files of Imported shared library in cmake - cmake

In my project I use an external library, that is integrated as a shared imported library. This library has some files, which are required at runtime. These files and also the license file should be installed when deploying the application.
The cmake of the imported libarary (MyImportedLibConfig.cmake) looks like this:
add_library(MyImportedLib SHARED IMPORTED)
set_target_properties(MyImportedLib PROPERTIES
IMPORTED_LOCATION "3rdparty/MyImportedLib /dev/lib/myimportedlib${CMAKE_SHARED_LIBRARY_SUFFIX}"
IMPORTED_IMPLIB "3rdparty/MyImportedLib/dev/lib/myimportedlib.lib"
INTERFACE_INCLUDE_DIRECTORIES "3rdparty/MyImportedLib/dev/include"
)
My initial approach for installing the files was this (also in MyImportedLibConfig.cmake):
# install license file
install(FILES "3rdparty/MyImportedLib/license.rtf" DESTINATION ${CMAKE_INSTALL_PREFIX}/licenses/MyImportedLib COMPONENT Runtime)
# install binary files
install(DIRECTORY "3rdparty/MyImportedLib/dev/bin/" DESTINATION ${CMAKE_INSTALL_PREFIX}/bin COMPONENT Runtime)
install(DIRECTORY "3rdparty/MyImportedLib/dev/bin/" DESTINATION ${CMAKE_INSTALL_PREFIX}/UnitTests COMPONENT Runtime)
This works on my local build, but the paths are not reliable (i.e. they are wrong when doing a package build).
What would be the proper way to handle this in CMake?
[Update]
I have been able to fix the messed up installation paths by avoiding the use of CMAKE_INSTALL_PREFIX.
# install license file
install(FILES "3rdparty/MyImportedLib/license.rtf" DESTINATION licenses/MyImportedLib COMPONENT Runtime)
# install binary files
install(DIRECTORY "3rdparty/MyImportedLib/dev/bin/" DESTINATION bin COMPONENT Runtime)
install(DIRECTORY "3rdparty/MyImportedLib/dev/bin/" DESTINATION UnitTests COMPONENT Runtime)
This is still not perfect, but it does the job.

Related

CMake target doesn't install to component

I have this install code:
add_library(foo SHARED ...)
install(TARGETS foo
RUNTIME DESTINATION bin # foo.dll
LIBRARY DESTINATION lib # libfoo.so
COMPONENT libs)
install(FILES conf DESTINATION etc COMPONENT libs)
On linux things work great. After cpack, the -libs package contains everything.
On windows, things don't work great. After cpack I get a -libs package containing etc/conf, but no bin/foo.dll.
If I make install (no component installation), then install_manifest.txt contains everything.
Why isn't foo.dll deployed in the windows component install?
This line doesn't do what you think it does:
install(TARGETS foo
RUNTIME DESTINATION bin # foo.dll
LIBRARY DESTINATION lib # libfoo.so
COMPONENT libs)
There is no RUNTIME DESTINATION argument. Instead there is a RUNTIME argument, and several commands which affect RUNTIME including DESTINATION and COMPONENT. With correct indenting, what you wrote is:
install(TARGETS foo
RUNTIME
DESTINATION bin
LIBRARY
DESTINATION lib
COMPONENT libs
)
Now you can see that COMPONENT only applies to LIBRARY and not RUNTIME. The solution is:
install(TARGETS foo
RUNTIME
DESTINATION bin # foo.dll
COMPONENT libs
LIBRARY
DESTINATION lib # libfoo.so
COMPONENT libs
)

CMake - Export target such that it can be Imported into another project and support relocation

The below command will generate a my-targets-build-tree.cmake
However, it will have absolute paths for the IMPORTED_LOCATION_RELEASE field for all targets.
export(
EXPORT my-targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/share/cmake/${PROJECT_NAME}/my-targets-build-tree.cmake"
)
This means that I can use the file to import targets into another project, but only if the libraries are not moved on disk. Meaning... things are not relocateable... i.e. you can't move the package anywhere randomly on another machine for instance.
Is there a way to make the generated cmake files support package relocation? For instance by having them consider an environment variable?
Use the install(TARGETS) command to tell cmake how the files should be placed on the disk when installing them and use install(EXPORT) to create the configuration file instead of export():
set(MYLB_INCLUDE_INSTALL_DESTINATION include/my_lib)
add_library(my_lib ...)
# need to provide location in installation dir and build dir separately here
target_include_directories(my_lib PUBLIC
$<BUILD_INTERFACE:include>
$<INSTALL_INTERFACE:${MYLB_INCLUDE_INSTALL_DESTINATION}>)
install(TARGETS my_lib EXPORT my_lib
ARCHIVE DESTINATION lib # static and import libs installed to lib
LIBRARY DESTINATION lib # .so installed to lib
RUNTIME DESTINATION bin # .dlls installed to bin
)
# install directory containing public headers to include/my_lib
install(DIRECTORY include DESTINATION ${MYLB_INCLUDE_INSTALL_DESTINATION}
FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ
DIRECTORY_PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
# create cmake config to lib/cmake/my_libConfig.cmake
install(EXPORT my_lib
DESTINATION lib/cmake
FILE my_libConfig.cmake)
This allows you to
Use the install target to write the files to a directory you specified via CMAKE_INSTALL_PREFIX or
Use cpack to create a package, assuming other info for the package for the package generator is set.

CMake copy dll transitively

Basically I want CMake to copy dependency's dll to the same directory of the executable. Suppose I have the following directory structure:
my-project/
CMakeLists.txt
lib/
CMakeLists.txt
... # Some source files
app/
CMakeLists.txt
... # Some source files
The library lib depends on some third party dll, say foo.dll. The executable in app, say app.exe, depends on lib.
I've written a FindFOO.cmake to make the third party library foo.dll an imported target named foo.
Now when I compile app, in order to run the executable, foo.dll is required to be in the same directory as app.exe. How can this be achieved automatically with cmake? And what if I want to use CPack to package the application into an installer?
CMAKE_RUNTIME_OUTPUT_DIRECTORY is your friend.
If this variable is created before creating some target, if the target is RUNTIME, it will define where the output of the target will be placed.
In your case, it can be used to force foo.dll and app.exe to be in the same folder. Your root CMakeLists.txt should look like this:
cmake_minimum_required(VERSION 3.15)
project(foo_and_app)
# app.exe and foo.dll will be in bin subfolder of build dir
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_subdirectory(lib)
add_subdirectory(app)
#not connected to placement, but only to package creation
include(CPack)
It should be noted that this variable is used to initialize the properties of the targets added, meaning that everything may also be achieved by directly manipulating appropriate target properties.
Regarding packaging, what you ask is possible, regardless of the placement of runtime targets, by using install cmake statement. In lib/CMakeLists.txt you should add something like this:
# suppose that the target named `foo`,
# i.e. it is added by add_library(foo SHARED .....)
install(TARGETS foo
RUNTIME DESTINATION bin
)
same should be done for app/CMakeLists.txt:
# suppose that the target named `app`,
# i.e. it is added by add_executable(app .....)
install(TARGETS app
RUNTIME DESTINATION bin
)
If you have these install statements, the final destination will be bin folder within the chosen install folder.
In the end, here are the links for CMake documentation describing:
CMAKE_RUNTIME_OUTPUT_DIRECTORY variable
RUNTIME cmake targets
install(TARGETS ...)

Using CMAKE_DEBUG_POSTFIX with exported targets

When I use set(CMAKE_DEBUG_POSTFIX "d"), the build and install targets work as expected. But in the libfooTargets-debug.cmake file with the exported targets, there is a path to libfoo and not libfood.
I exported the targets like this:
install(TARGETS libfoo EXPORT libfoo-targets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
install(EXPORT libfoo-targets FILE libfooTargets.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
which creates and installs libfooTargets.cmake and libfooTargets-debug.cmake when building in debug mode, and libfooTargets.cmake and libfooTargets-release.cmake when building in release mode.
Both libfooTargets-release.cmake and libfooTargets-debug.cmake reference the name without a postfix as:
list(APPEND _IMPORT_CHECK_FILES_FOR_libfoo "${_IMPORT_PREFIX}/lib/libfoo.lib" )
and thus a program linking against the debug target still uses the release-build library and I would need to install release and debug versions into different folders to be able to link against the debug target.
How can I get the exported targets to work with a debug postfix?
I could of course try to change the library name depending on CMAKE_RELEASE_TYPE or a CONFIGURATION generator expression, but this will probably break the multi-configuration features in MSVC and other IDEs supporting different targets and seems not to work in the sense of how the exported targets feature is meant to simplify and unify the build.
I suspect that the install(EXPORT ...) command somehow drops the CMAKE_DEBUG_POSTFIX or does not implement it for generating the libfooTargets-{release,debug}.cmake files, but possibly I overlooked how to make this variable visible to the generator of the exported targets or something like this.
All target code
cmake_minimum_required(VERSION 3.11.1)
project(foo)
include(CMakePackageConfigHelpers)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_DEBUG_POSTFIX "d")
# ...
add_library(libfoo STATIC somesource.cpp someheader.h)
target_include_directories(libfoo PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
target_link_libraries(libfoo
somelibrary
)
target_include_directories (libfoo PUBLIC
somelibrary_header_dirs
)
install(TARGETS libfoo EXPORT libfoo-targets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
install(EXPORT libfoo-targets FILE libfooTargets.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
configure_package_config_file(libfooConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libfooConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libfooConfig.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
install(DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.h")
The platform is a Windows 10 with cmake 3.11.1 and MSVC 2015. Of course the most general solution is probably the best one.
According to the documentation of the install command, you need to reference the configuration that you are interested in:
[...] If a CONFIGURATIONS option is given then the file will only be installed when one of the named configurations is installed. Additionally, the generated import file will reference only the matching target configurations. [...]
So, you need to add the CONFIGURATIONS option in both install commands and duplicate the commands for each configuration you want to install and export.

Why CPack generates an empty package?

There are exists one cmake project that creates one console application.
I add the ability of package generation to that cmake project:
# ... above cmake code for one console application creation
# below code that I add:
#install
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION "C:/Apps/Consolas" COMPONENT applications)
# pack
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VENDOR "MyOrg")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "CPack Component Installation Example")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_GENERATOR "ZIP")
include(CPack)
The package creation passes without any errors, (!) but the package is empty.
Why it happens and how to fix this issue? (I used cmake 3.12.0)
DESTINATION should be a relative directory within the package.
Consider the following instead:
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION "bin" COMPONENT applications)
Explanation: CPack will create the ZIP file after installing the project into a subdirectory of <build-dir>/_CPack_Packages. By specifying an absolute path, no file will be installed in the expected subdirectory and the package will be empty.