I have created cmake target, say A, and want to install it and create a Config file, so that the installed package could be relocatable. My code is:
install(EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION ??? )
Here, I am having a problem with the proper destination. I want the Config file to be installed where ${CMAKE_INSTALL_PREFIX} points to. But when I put ${CMAKE_INSTALL_PREFIX} at ???, my resulting ATargets.cmake file contains the line:
set(_IMPORT_PREFIX "C:/Libraries/...")
which is the actual value of ${CMAKE_INSTALL_PREFIX}. This _IMPORT_PREFIX is later prepended to the parameters of set_target_properties() command inside the auto-generated ATargets.cmake, resulting in hard coded paths, valid only on the installation system.
I tried to use some generator expressions like <$IMPORT_PREFIX> in place of ???, but this gave me an error at cmake generation. I also tried to omit DESTINATION which in my opinion should place the file in the location relative to ${CMAKE_INSTALL_PREFIX}, but cmake complained about it too.
Can you please help me with this issue?
This is an old question so you have probably solved it already (or given up).
I think the critical thing is that the export DESTINATION matches the one given to INSTALL_DESTINATION to configure_package_config_file().
what we are using is:
install(TARGETS
foobar
EXPORT foobarLibTargets
LIBRARY DESTINATION lib)
set(ConfigFileInstallDir lib/cmake/foobar)
set(INCLUDE_INSTALL_DIR include)
set(LIBRARY_INSTALL_DIR lib)
configure_package_config_file(src/main/cmake/foobarConfig.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/foobarConfig.cmake"
INSTALL_DESTINATION "${ConfigFileInstallDir}"
PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/foobarConfigVersion.cmake"
VERSION "${VERSION}"
COMPATIBILITY ExactVersion)
export(EXPORT foobarLibTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/foobarLibTargets.cmake")
install(EXPORT foobarLibTargets
FILE foobarLibTargets.cmake
DESTINATION "${ConfigFileInstallDir}")
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/foobarConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/foobarConfigVersion.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/foobarLibTargets.cmake"
DESTINATION "${ConfigFileInstallDir}")
${ConfigFileInstallDir} is replaced automagically by _IMPORT_PREFIX
so that the generated foobarLibTargets.cmake contains something like:
set_property(TARGET foobar APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(foobar PROPERTIES
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/foobar.so.2.0.0"
IMPORTED_SONAME_RELEASE "libfoobar.so.2"
)
There can be troubles getting this to work. See my questions:
correctly set the location of imported cmake targets for an installed package &
Strange issue with variables in a config-file cmake package.
See you in the ward for those suffering from cmake related nervous disorders in the near future...
Related
There is an object library in CMake:
add_library( librevolta_runtime_x86 OBJECT
crt0.S
crti.S
crtn.S
)
install( TARGETS librevolta_runtime_x86 EXPORT revolta
OBJECTS DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
which is linked to my static library:
add_library( revolta STATIC )
target_link_libraries( revolta
PUBLIC
librevolta-runtime-x86
)
# Install all the revolta headers into include directory and copy the built library
install( TARGETS revolta EXPORT revolta
FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
# Export librevolta targets
install( EXPORT revolta DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/revolta" )
The problem is once the library revota is build and installed, the result is:
<CMAKE_INSTALL_PREFIX>/lib/objects-Debug/librevolta-runtime-x86/crt0.S.obj
<CMAKE_INSTALL_PREFIX>/lib/objects-Debug/librevolta-runtime-x86/crti.S.obj
<CMAKE_INSTALL_PREFIX>/lib/objects-Debug/librevolta-runtime-x86/crtn.S.obj
But I need to make up the installation like:
<CMAKE_INSTALL_PREFIX>/lib/crt0.o
<CMAKE_INSTALL_PREFIX>/lib/crti.o
<CMAKE_INSTALL_PREFIX>/lib/crtn.o
Note: <CMAKE_INSTALL_PREFIX> is just the placeholder for the path being specified in the CMake variable.
So I need to:
Rename the object files from *.S.obj to simple *.o
Adapt the path so that all the object files are installed into pure <CMAKE_INSTALL_PREFIX>/lib/ directory
Due to target_link_libraries(...) dependency, make up the
librevolta_runtime_x86 the member of revolta export set
Many thanks to anyone willing to help me. Martin
EDIT: The simple macro as promised
macro(install_objects OBJFILE_LIST)
foreach(OBJFILE ${${OBJFILE_LIST}})
# Available since CMake 3.20 you can use get_filename_component
# To get any specific information you can wish for about a file
# Before 3.20 you would have to use string(REGEX MATCH ...)
# get_filename_component(OBJ_DIR ${OBJFILE} DIRECTORY) <-- path
# get_filename_component(OBJ_EXT ${OBJFILE} EXT) <-- extension
get_filename_component(OBJ_NAME ${OBJFILE} NAME)
# You can uncomment the message to see what you can get
# MESSAGE(STATUS "OBJ_DIR: ${OBJ_DIR} OBJ_NAME: ${OBJ_NAME} OBJ_EXT: ${OBJ_EXT}")
# This is just an example, but you can also use ${OBJ_DIR} for example to specify a better path
install(FILES ${OBJFILE} DESTINATION [wherever_you_want_them] RENAME "${OBJ_NAME}.o")
endforeach(OBJFILE)
endmacro(install_objects)
#example usage
set(MY_OBJECTS
"test.s.obj"
"somepath/test2.S.obj"
"whatever/whatever/test3.s.OBJ"
)
install_objects(MY_OBJECTS)
The above MY_OBJECTS would give you the following variables:
-- OBJ_DIR: OBJ_NAME: test.s.obj OBJ_EXT: .s.obj
-- OBJ_DIR: somepath OBJ_NAME: test2.S.obj OBJ_EXT: .S.obj
-- OBJ_DIR: whatever/whatever OBJ_NAME: test3.s.OBJ OBJ_EXT: .s.OBJ
As you can see it is pretty robust and keeps your CMakeLists.txt clean. Just make sure to create your OBJECT_LIST using pre-defined variables such as ${CMAKE_BINARY_DIR}
This will simplify your object files installs. IMO it also answers both ad1) ad2)
Ad3) I'm not sure what is your issue here or what exactly is happening, as the question itself is focused on installing objects.
Is there any specific reason why you wish to install the intermediate object files? This is an important question because a static library when linked will be unpacked and optimized - take a look at this question/answer.
The OBJECT libraries are a useful thing in CMake when you know that some parts of your codebase would be compiled multiple times (due to the fact that it uses the same code). It makes sense to couple these parts into a reusable object library.
All of this aside: What you are looking for is
INSTALL(FILES file-name.abc DESTINATION dest-folder RENAME file-name.xyz)
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.
I am trying to understand what I do with my cmake files. I'm sorry if my question seems too obvious or already reply in an other place but I have a real difficulty to understand modern cmake and find good and clear explanations.
I have write a minimal example :
hello.cpp:
#include<iostream>
int hello(){
std::cout << "Hello World!" << std::endl;
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project (hello)
SET(CMAKE_INSTALL_PREFIX "/home/guillaume/dev/C++/projects/test/cmake_hello_world/install")
set(INSTALL_LIB_DIR lib)
add_library(hello SHARED main.cpp)
set(LIBRARY_INSTALL_DIR lib)
set(INCLUDE_INSTALL_DIR include)
INSTALL(TARGETS hello
EXPORT helloTargets
LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR}
ARCHIVE DESTINATION ${LIBRARY_INSTALL_DIR}
INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR})
include(CMakePackageConfigHelpers)
set(ConfigFileInstallDir lib/cmake/hello)
set(INCLUDE_INSTALL_DIR include CACHE PATH "install path for include files")
set(LIBRARY_INSTALL_DIR lib CACHE PATH "install path for libraries")
configure_package_config_file(helloConfig.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/helloConfig.cmake"
INSTALL_DESTINATION "${ConfigFileInstallDir}"
PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/helloConfigVersion.cmake"
VERSION "0.0.0"
COMPATIBILITY SameMajorVersion)
EXPORT(EXPORT helloTargets
FILE helloTargets.cmake)
INSTALL(FILES
"${CMAKE_CURRENT_BINARY_DIR}/helloConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/helloConfigVersion.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/helloTargets.cmake"
DESTINATION "${ConfigFileInstallDir}")
and helloConfig.cmake.in :
set(helloLib_VERSION #VERSION#)
#PACKAGE_INIT#
INCLUDE("${CMAKE_CURRENT_LIST_DIR}/helloTargets.cmake")
SET_AND_CHECK(hello_LIB_DIR "#PACKAGE_LIBRARY_INSTALL_DIR#")
message(STATUS "hello library version: ${hello_VERSION}")
message(STATUS "hello library location: ${hello_LIB_DIR}")
check_required_components(hello)
Now, I have three files in cmake_hello_world/install/lib/cmake/hello
From configure_package_config_file, helloConfig.cmake
From write_basic_package_version_file, helloConfigVersion.cmake
From EXPORT(export ...), helloTargets.cmake
Ok, I believe I know that helloConfig.cmake will be used by future findpackage(hello), but what is the meaning of the two others files (helloConfigVersion.cmake and helloTargets.cmake)? When should I create them ?
what is the meaning of the two others files (helloConfigVersion.cmake
helloConfigVersion is used for detecting or choosing the version of the package, without including it. Basically it works like this, in psuedocode:
function(find_package VERSION some_version)
# blabla
# variable set by find_package before including
set(PACKAGE_FIND_VERSION ${some_version})
include(helloConfigVersion) # uses PACKAGE_FIND_VERSION
# if PACKAGE_FIND_VERSION is compatible with the version installed
# then the variable PACKAGE_VERSION_COMPATIBLE is set inside helloConfigVersion
# (this would all be easier if cmake wouldn't use so many global variables...)
if (NOT PACKAGE_VERSION_COMPATIBLE)
message(FATAL_ERROR "Och no, the installed package version is not compatible with what you want!")
endif()
# all ok, the version is compatible, we can do it
include(helloConfig.cmake)
# blabla
endfunction()
See the relevant section in find_package manual, all described there.
and helloTargets.cmake)?
This file defines (or switches between versions of) targets of your library. It basically does in pseudocode:
add_library(hello SHARED IMPORTED /usr/lib/the/path/to/libhello.so)
along with some other stuff. find_package sources that file, so you can later use target_link_libraries(soemtarget hello) like it would-have-been compiled as a cmake target.
In some cases the *Target.cmake file is missing, I think that is when the library exports no targets and does only functions or does a lot of manual stuff.
When should I create them ?
If you want your module to be found with find_package, then always. You could not create the Version.cmake file, then if the user does find_package(hello VERSION <any verison here>) it will always error.
How can I understand Config.cmake, ConfigVersion.cmake, Targets.cmake
They are just a part of cmake packaging system.
I am completely stuck :/. I created a simple test "library" with two
files fruits.cpp and fruits.h.
Then I created this CMakeLists.txt (following this tutorial)
cmake_minimum_required(VERSION 3.16)
project(fruits_Lib)
add_library(fruits_Lib STATIC)
target_sources(fruits_Lib
PRIVATE
"fruits.cpp"
)
set(include_dest "include/fruits-1.0")
set(main_lib_dest "lib/fruits-1.0")
set(lib_dest "${main_lib_dest}/${CMAKE_BUILD_TYPE}")
target_include_directories(fruits_Lib
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${include_dest}>"
)
add_library(fruits::fruits ALIAS fruits_Lib)
install(TARGETS fruits_Lib EXPORT fruits DESTINATION "${main_lib_dest}")
install(FILES "include/fruits/fruits.h" DESTINATION "${include_dest}")
install(EXPORT fruits DESTINATION "${lib_dest}")
This works in that it compiles and installs etc, and I can even use
it as a add_subdirectory in a parent project.
In fact, the files that this installs are:
lib/fruits-1.0/libfruits_Lib.a
lib/fruits-1.0/Debug/fruits-debug.cmake
lib/fruits-1.0/Debug/fruits.cmake
lib/fruits-1.0/libfruits_Libd.a
lib/fruits-1.0/Release/fruits-release.cmake
lib/fruits-1.0/Release/fruits.cmake
include/fruits-1.0/fruits.h
However, when I try to do the following in the parent project:
find_package(fruits CONFIG REQUIRED)
I get the error:
CMake Error at CMakeLists.txt:21 (find_package):
Could not find a package configuration file provided by "fruits" with any
of the following names:
fruitsConfig.cmake
fruits-config.cmake
Those files are definitely not created or installed by the above CMakeLists.txt :/.
What am I doing wrong? How can I create a static library that provides such config file
so that I can use it (after installation) with a find_package(fruits CONFIG) ?
I think find_package() is for locating and using external and already installed components/libraries so you'll have to install the library first.
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.