CMake target doesn't install to component - cmake

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
)

Related

CMake: Install an export file in Debug/Release alternatively?

Take the following example as a start:
...
add_library(Foo ...)
install(TARGETS Foo EXPORT FooTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
...
)
install(EXPORT FooTargets
FILE lib/cmake/Foo
...
)
Running this with
$ mkdir build; cd build
$ cmake -DCMAKE_BUILD_TYPE=Release .. (or in Debug)
$ cmake --build .
$ cmake --install . --prefix my/custom/prefix
This will create the files:
my/custom/prefix/lib/cmake/Foo/FooTargets.cmake
my/custom/prefix/lib/cmake/Foo/FooTargets-release.cmake (Or debug, respectively)
my/custom/prefix/lib/libFoo.a
And from what I managed to understand from the FooTargets.cmake file, it globs for all FooTargets-*.cmake files and includes() them all.
In turn, the FooTargets-release.cmake file is the one that references the libFoo.a file.
In the docs about the install command, it says that you can add the CONFIGURATIONS option to the install TARGETS command, so that if we change the above:
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Debug
LIBRARY DESTINATION lib/Debug
ARCHIVE DESTINATION lib/Debug
This will install the libFoo.a file in my/custom/path/lib/Debug/libFoo.a. Now let's say I want the Release library to be installed in lib/Release and the Debug library be installed in lib/Debug, and that when the downstream project will consume my package, it will have the right library depending on its configuration - i.e. - debug build will link against the Debug library, same for release.
What I thought I can do is:
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Debug
LIBRARY DESTINATION lib/Debug
ARCHIVE DESTINATION lib/Debug
)
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Release
LIBRARY DESTINATION lib/Release
ARCHIVE DESTINATION lib/Release
)
install(EXPORT FooTargets
DESTINATION lib/cmake/Foo
...
)
And what will happen is that when building in Debug, the FooTargets-debug.cmake will be generated, pointing to the lib/Debug/libFoo.a, and when building in Release, the FooTargets-release.cmake will be generated, pointing to the lib/Release/libFoo.a. The FooTargets.cmake will then check what configuration is the consuming project is building with, and include the right configuration.
When I try doing the above, I get:
-- Configuring done
CMake Error: install(EXPORT "FooTargets" ...) includes target "Foo" more than once in the export set.
-- Generating done
CMake Generate step failed. Build files cannot be regenerated correctly.
How is this should be done??
EDIT
I found out, in a not-very-straightforward way, that when I build the consuming project as such:
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
and like this:
$ cmake -DCMAKE_BUILD_TYPE=Release ..
I am linking against only the relevant library. Basically how this works is that the FooTargets.cmake file is included by the FooConfig.cmake file, which is called by find_package. The FooTargets.cmake file is the one that defines the imported target:
add_library(Foo IMPORTED)
This file then calls all the FooTargets-*.cmake, which adds the relevant library to some list called _IMPORT_CHECK_FILES_FOR_FOO.
What this file also does, is:
set_property(TARGET Foo::Foo APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
Apparently there is some property IMPORTED_CONFIGURATIONS that holds the imported configurations.
I suppose that somewhere down the road, find_package takes this list and filters it according to the CMAKE_BUILD_TYPE variable, and this way links only the relevant library.
What I still don't understand, is how to make it so that upon Release builds (of Foo), the FooTargets-release.cmake will be created, pointing to lib/Release/Foo.a, and the same for debug builds.
It seems that CMake went half-way with this, unless I'm seriously missing something.
It seems per-CONFIGURATIONS installs are not easily fit to EXPORT semantic.
However, in simple cases per-configuration's specific can be achieved by using generator expressions in DESTINATION:
install(TARGETS Foo EXPORT FooTargets
LIBRARY DESTINATION lib/$<CONFIG>
ARCHIVE DESTINATION lib/$<CONFIG>
)
install(EXPORT FooTargets
DESTINATION lib/cmake/Foo
...
)
The code above will install libraries into lib/Debug for Debug configuration, and into lib/Release for Release configuration.

How to install runtime files of Imported shared library in 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.

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.

CMake Error: TARGETS given no LIBRARY DESTINATION for shared library target

When building an opensource project with CMake (in my case, it was the lemon graph library), I got this error when I tried to build shared libaries via -DBUILD_SHARED_LIBS=1:
TARGETS given no LIBRARY DESTINATION for shared library target
Where does this error come from and how do I fix it?
In my CMakeLists.txt, my INSTALL command had no LIBRARY parameter.
Changing from this:
INSTALL(
TARGETS lemon
ARCHIVE DESTINATION lib
COMPONENT library
)
to this:
INSTALL(
TARGETS lemon
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib # <-- Add this line
COMPONENT library
)
fixed my problem.
I got this... Another reason this happens is when you create a shared library
add_library(${NAME} SHARED sources )
then when Cmake reaches the install command on Windows platform, it complains of these error, solution is to use RUNTIME instead of LIBRARY, like
if(WIN32)
install(TARGETS ${NAME}
RUNTIME DESTINATION path)
else()
install(TARGETS ${NAME}
LIBRARY DESTINATION path)
endif()
After DESTINATION, it should have bin, lib, include.
install lib or bin
install(TARGETS snappy
EXPORT SnappyTargets
# RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # DESTINATION error
RUNTIME DESTINATION bin ${CMAKE_INSTALL_BINDIR} # should add bin or other dir
LIBRARY DESTINATION lib ${CMAKE_INSTALL_LIBDIR}
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR # DESTINATION error
ARCHIVE DESTINATION lib ${CMAKE_INSTALL_LIBDIR} # should add lib
)
For example, install .h file:
install(
FILES
"${PROJECT_SOURCE_DIR}/test_hard1.h"
"${PROJECT_BINARY_DIR}/config.h"
# DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # error install FILES given no DESTINATION!
# add include after DESTINATION, then it works
DESTINATION include ${CMAKE_INSTALL_INCLUDEDIR}
)
see https://cmake.org/cmake/help/v3.0/command/install.html for more detail:
install(TARGETS myExe mySharedLib myStaticLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
install(TARGETS mySharedLib DESTINATION /some/full/path)
I just faced a similar problem. As #Fernando said in his answer, you can go with that solution. Another and similar solution is to just include(GNUInstallDirs)
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME}
EXPORT "${PROJECT_NAME}Config"
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
I had a similar, but not identical error message:
CMake Error at xxx/CMakeLists.txt:123 (install):
install Library TARGETS given no DESTINATION!
The fix in this case was to simply update CMake; from version 3.13.3 to 3.25.1 or really any newer version.
Note: I had originally posted my own question at CMake Error: install Library TARGETS given no DESTINATION but it was closed as a duplicate (although the error message was different), so I posted my own answer here.
I do not know if this would fix the TARGETS given no LIBRARY DESTINATION for shared library target error.
The other answers all talk about modifying the CMake configuration files (i.e. CMakeLists.txt), so this answer is new - and easier.
Our fix was just to update, and leave the configuration alone. As the error was thrown from another open source project, this was by far the better fix, for us.
Since CMake 3.14 the option DESTINATION is no longer required for executables, shared/static libraries and some other artifacts: CMake provides reasonable defaults for their installation directories. Alternatively, one could include module GNUInstallDirs, and set corresponding CMAKE_INSTALL_<dir> variable.
For regular executables, static libraries and shared libraries, the DESTINATION argument is not required. For these target types, when DESTINATION is omitted, a default destination will be taken from the appropriate variable from GNUInstallDirs, or set to a built-in default value if that variable is not defined. The same is true for the public and private headers associated with the installed targets through the PUBLIC_HEADER and PRIVATE_HEADER target properties.
https://cmake.org/cmake/help/v3.14/command/install.html#targets