I am trying to export a library from a CMake project. Internally, I've broken this library up into multiple sub-targets. I would now like to export just the full public library, without needing my private library binaries. The following doesn't work.
cmake_minimum_required(VERSION 3.2)
project(export-mwe)
add_library(priv priv.cpp)
add_library(exp-lib exp-lib.cpp)
target_link_libraries(exp-lib PRIVATE priv)
install(TARGETS exp-lib EXPORT export-mwe DESTINATION lib)
install(EXPORT export-mwe DESTINATION .)
When I try generating this project I get an error.
CMake Error: install(EXPORT "export-mwe" ...) includes target "exp-lib" which requires target "priv" that is not in the export set.
How can I export only exp-lib in this example, without having to export priv with it?
If you link (even privately) your library exp-lib with another shared library priv, CMake needs to be aware of that linking when link other executable with your main library. So, information of private linking is stored in the export file:
... to tell the importing CMake that it needs to ensure the linker can
find A when the application links to B even though A will not appear
on the link line.
As for
How can I export only exp-lib in this example, without having to export priv with it?
Make priv library STATIC. Information about private linking with static library isn't stored in the export file.
Related
I'm new to CMake and have trouble understanding how targets are exposed in the install interface. I have a CMakeLists.txt where I define two targets (PROJECT_NAME is "realtime"):
...
add_library(${PROJECT_NAME} realtime_client.cpp)
...
configure_file(version/realtime_version.cpp.in realtime_version.cpp #ONLY)
add_library(realtime_version STATIC ${CMAKE_CURRENT_BINARY_DIR}/realtime_version.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE realtime_version)
...
You can see that the realtime_version target is privately linked to the realtime target. For the install interface I just want the realtime target to be exposed to the outside world whereas the version target should vanish. So I would typically do this:
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME} EXPORT "${PROJECT_NAME}_Exports"
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT ${PROJECT_NAME}_Runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT ${PROJECT_NAME}_Runtime
NAMELINK_COMPONENT ${PROJECT_NAME}_Development
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT ${PROJECT_NAME}_Development
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
set(REALTIME_TARGET_EXPORT_NAME "${PROJECT_NAME}_Exports")
install(EXPORT ${REALTIME_TARGET_EXPORT_NAME}
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/realtime-${PROJECT_VERSION}
NAMESPACE supabase::
FILE ${PROJECT_NAME}_Development.cmake
COMPONENT ${PROJECT_NAME}_Development
)
But then CMake complains that the realtime_version target has no install rules:
[cmake] CMake Error: install(EXPORT "realtime_Exports" ...) includes target "realtime" which requires target "realtime_version" that is not in any export set.
What I really want is for this library to just be compiled into the binary of the realtime target. I thought this would happen automatically, but it seems I'm mistaken. I might have missed a few essential things. So how can I hide the realtime_version target from export_sets and instead just compile it into the binary?
I install multiple libraries and export with the same names(inside one component). like bellow
install(
TARGETS sip
EXPORT voip-protocols-config)
install(
TARGETS rtp
EXPORT voip-protocols-config)
install(
EXPORT voip-protocols-config
NAMESPACE voip)
In the App side use find_package(voip REQUIRED COMPONENTS voip-protocols) and then access to these libs but have a way to use some symbols like * or Cmake Generator Expression to list all libs inside the components?
With this approach, the details of the library side can be hidden in the program and for linking libs to app same as bellow:
find_package(voip REQUIRED COMPONENTS voip-protocols)
add_executable(${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} voip::*)
When export targets (with install(TARGETS .. EXPORT)) CMake doesn't create additional "useful" targets. Instead, you (as a project's developer) are free to add such targets explicitly.
First approach is to create additional INTERFACE target in the project's CMakeLists.txt and install it too:
# Create "all" library
add_library(voip INTERFACE)
# Link the library with those ones, which you want to represent.
target_link_libraries(voip INTERFACE sip rtp <list other targets here>)
# Install library, so it will be accessible to the user of the package
# via name voip::voip.
install(TARGETS voip
EXPORT voip-protocols-config)
The second approach would to add additional INTERFACE IMPORTED target to the he project config script.
It should be noted, that intended way for provide a config script is to write it manualy (or write its template and use configure_package_config_file). As for scripts generated by CMake with install(EXPORT), these files could be included into the handwritten file. CMake describes that process in documentation.
voip-protocols-config.cmake:
# Assume that install(EXPORT) creates file `voip-targets.cmake`
include (${CMAKE_CURRENT_LIST_DIR}/voip-targets.cmake)
# Create "all" library
add_library(voip::voip INTERFACE IMPORTED)
# Link the library with those ones, which you want to represent.
target_link_libraries(voip INTERFACE voip::sip voip::rtp <list other targets here>)
Note, that CMake doesn't automatically select libraries according to COMPONENTS parameter for find_package. It is a project's developer who should process that parameter in the package config script. CMake documentation provides example of such processing.
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.
I am trying to group multiple targets into a single one so the downstream user only need to link to that single one. The downstream user won't need to look up all the targets and all functionality from the upstream library will be available by linking to that single one. Please see below CMakeLists of my failed attempt.
cmake_minimum_required(VERSION 3.11)
project(modules)
# 10 libraries with actually functionality
add_subdirectory(mylib1)
add_subdirectory(mylib2)
...
add_subdirectory(mylib10)
# failed attempt to create a single library that links to the above 10
add_library(myliball)
target_link_libraries(myliball mylib1 mylib2 ... mylib10)
install(TARGETS myliball
EXPORT ${CMAKE_PROJECT_NAME}Targets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
export(TARGETS myliball
APPEND FILE ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Targets.cmake)
When I run cmake it shows this error
No SOURCES given to target: myliball
I can probably create an empty class for myliball to workaround this problem but that seems to be very messy. Is there a better way to do this?
CMake has a special type of library target which is intended for grouping - INTERFACE:
add_library(myliball INTERFACE)
target_link_libraries(myliball INTERFACE mylib1 mylib2 ... mylib10)
Such library target is not compiled, it just serves for propagate its INTERFACE properties when linked.
I created a shared library in CMakeLists.txt by adding:
add_library(mylib SHARED ${SourceDir})
install (TARGETS mylib DESTINATION lib)
Now I can see the file libmylib.so in the correct folder after installing, but I am not sure how can I import this into another separate project's CMakeLists.txt.
CMake allows you to not only install the library, it can also install files which can be used by another project to import all the details about the library target. This allows that imported target to be treated almost the same as if it were part of the target it is imported into.
The first step is to add additional install details to your library's project:
add_library(mylib SHARED ${SourceDir})
install(TARGETS mylib
EXPORT myproj-targets
DESTINATION lib
)
install(EXPORT myproj-targets
DESTINATION lib/myproj
)
Note the added EXPORT myproj-targets to your original install() call. This tells CMake to include your mylib target in the list of targets it will export as part of the export set myproj-targets, which the second install() then directs CMake to generate as part of the install step.
In your other project where you want to use the library, you just need to include() the file that the second of the above install() calls will create. Assuming you install to ${installBase}, the following will import the details exported by the above and make the mylib target available for direct use:
include("${installBase}/lib/cmake/myproj-targets.cmake)
add_executable(myexe exeSources.cpp)
target_link_libraries(myexe PRIVATE mylib)
You can find a reasonable explanation about how all this works from the CMake documentation for install() and this wiki article.