I have a shared library in my project with a cmake config file. How can I make it available in the build tree such that I don't have to install it before using find_package?
The mechanism I was looking for is export(PACKAGE <PackageName>). Note that since 3.15 the command does nothing by default because populating the user package registry has effects outside the source and build trees. You have to set the CMAKE_EXPORT_PACKAGE_REGISTRY variable to add build directories to the CMake user package registry.
I was able to get things working like this:
install(
TARGETS ${TARGET_LIB}
EXPORT TARGET_LIB_EXPORTS # Define exports
FILE "${PROJECT_NAME}-targets.cmake"
…
)
# Create targets file in build tree
export(
EXPORT TARGET_LIB_EXPORTS
FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-targets.cmake"
NAMESPACE ${PROJECT_NAME}::
)
# Create config file in build tree
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}-config.cmake.in"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION}
)
# Create version config file in build tree
write_basic_package_version_file(
"${PROJECT_NAME}-config-version.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
# Export the package to make find_package work in build
set(CMAKE_EXPORT_PACKAGE_REGISTRY true)
export(PACKAGE ${PROJECT_NAME})
A basic config file would look like this:
#PACKAGE_INIT#
include("${CMAKE_CURRENT_LIST_DIR}/#PROJECT_NAME#-targets.cmake")
Related
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.
I have a Library Project that is exported so consumers can use it like so:
find_package(myLibrary)
target_link_libraries(theirLibrary PUBLIC myNamespace::myLibrary)
MyLibrary is the main product but it lives alongside two other projects in our repository, and the layout looks like this:
MyRepository/
MyLibrary/
CMakeLists.txt
include/ //public headers
MyLibrary/ //sources and private headers
MyDependentLibrary/ //another library project
CMakeLists.txt
etc..
MyExample //executable project
CMakeLists.txt
etc..
The dependencies for each project are like so:
MyLibrary: None
MyDependentLibrary: MyLibrary
MyExample: MyLibrary, MyDependentLibrary
MyLibrary and MyDependentLibrary are both set up with install and build directory exports to be compatible with the find_package() command. So to build everthing you:
configure/build MyLibrary
configure MyDependantLibrary setting MyLibrary_DIR when prompted, then build it
configure MyExample setting MyLibrary_DIR and MyDependentLibrary_DIR when prompted, then build it
This workflow is great, most of the time we only want to package MyLibrary without the other projects when we send to customers, but occasionally we want to give them the source for all 3 projects so they have more examples to look at.
For that reason I would love to add a top level CMakeLists.txt file that would I imagine look something like this:
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(MyCombinedProject VERSION 1.0.0 LANGUAGES CXX)
add_subdirectory(MyLibrary)
add_subdirectory(MyDependentLibrary)
add_subdirectory(MyExample)
However this doesn't work. When configuring the "combined" project, MyDependentLibrary is unable to find MyLibrary_DIR, which makes sense, as MyLibrary hasn't been built yet.
Is there a way to add an export to each of the libraries so they can be found when added in this manner in addition to the find_package()? I really don't want to move any CMake code required to build MyLibrary into the top level CMakeLists.txt, as 90% of the time it will be delivered on its own.
You may ship the sources of your library (MyLibrary) with a pseudo config file, which just provides myNamespace::myLibrary target as alias for myLibrary.
MyLibrary/CMakeLists.txt:
# Assuming you create a library
add_library(mylibrary ...)
# Install and export it
install(TARGETS mylibrary EXPORT mylibraryTargets ...)
# and install the file which describes this installation
install(EXPORT mylibraryTargets NAMESPACE myNamespace ...)
# Then you install config file for your library:
install(FILES myLibrary.cmake ...)
# Then just set `MyLibrary_DIR` variable to point into in-source version of config file.
set(MyLibrary_DIR CACHE INTERNAL "A directory with the in-source config file"
${CMAKE_CURRENT_SOURCE_DIR}/cmake
)
And write that in-source config file as follows:
MyLibrary/cmake/MyLibraryConfig.cmake:
add_library(myNamespace::mylibrary ALIAS mylibrary)
That way, find_package(MyLibrary) will work if it is issued after configuring sources of your library (add_subdirectory(MyLibrary)).
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.
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.
I'd like to install a custom file which is generated during compilation of a CMake project:
In my project the file SampleDictionary_rdict.pcm is generated by a command from an external module during compilation, and I would like to have it copied to $CMAKE_INSTALL_PREFIX/lib with make install
The issue I'm running into is all the variants of install(...) I've tried require the file to exist before compilation.
The command from an external module that generates the file is:
ROOT_GENERATE_DICTIONARY(
SampleDictionary
...
)
This generates SampleDictionary_rdict.pcm in my build directory.
For example, if I try to add:
install(TARGETS SampleDictionary DESTINATION "${CMAKE_INSTALL_PREFIX}/lib")
to the end of my CMakeLists.txt I have the issue of the file not existing.
install(TARGETS ... is used to install targets built within the project. You want to install the file SampleDictionary_rdict.pcm so you need a variant for files: install(FILES ...