I have built and installed a cmake package (Foo) and was able to import it into another project (Bar) using find_package(Foo COMPONENTS A B C REQUIRED CONFIG). In this scenario, Foo::A depends on Foo::B and Foo::B is actually made up of 3 shared libraries Ba Bb Bc. In Bar when I call target_link_libraries(Bar PUBLIC Foo::A Foo::B) I get an error message "The following targets are referenced, but are missing: Foo::Bb". If I then edit find_package and target_link_libraries to include Foo::Bb I then get an error "include could not find requested file .../FooBbTargets.cmake".
The code to export/install Foo::B is below:
# using cmake 3.21
# Ba Bb Bc defined above as shared libraries
# Ba has the same name as B
# Bc is linked to Bb, Bb is linked to Ba, links are all public.
include(GNUInstallDirs)
install(
TARGETS Ba Bb Bc
EXPORT FooBTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(
FILES
# list of header files
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(
EXPORT FooBTargets
FILE FooBTargets.cmake
NAMESPACE Foo::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Foo
)
export(
EXPORT FooBTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/FooBTargets.cmake"
NAMESPACE Foo::
)
Ideally I would actually like to link to just Foo rather than having to list the components separately. It looks like my current solution is to turn each of the shared libraries in B into their own components and link to them individually but I would prefer not to.
Relevant part of top level CMakeLists.txt for Foo
# our program only needs to support windows
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CONFIGFILEINSTALLDIR lib/cmake/Foo)
set(LIBRARY_INSTALL_DIR lib)
set(INCLUDE_INSTALL_DIR include)
include(CMakePackageConfigHelpers)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/FooConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake"
INSTALL_DESTINATION "${CONFIGFILEINSTALLER}"
PATH_VARS
LIBRARY_INSTALL_DIR
INCLUDE_INSTALL_DIR
)
write_basic_package_version_file(
${CMAKE_BINARY_DIR}/cmake/FooConfigVersion.cmake
VERSION ${PACKAGE_VERSION}
COMPATIBILITY SameMajorVersion
)
install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/FooConfigVersion.cmake"
DESTINATION "${CONFIGFILEINSTALLER}"
)
FooConfig.cmake.in
#PACKAGE_INIT#
set_and_check(Foo_INCLUDE_DIRS "#PACKAGE_INCLUDE_INSTALL_DIR#")
set_and_check(Foo_LIBRARY_DIRS "#PACKAGE_LIBRARY_INSTALL_DIR#")
set(_supported_components A B C)
foreach(_comp ${Foo_FIND_COMPONENTS})
if(NOT _comp IN_LIST _supported_components)
set(Foo_FOUND False)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/Foo${_comp}Targets.cmake")
endforeach()
check_required_components(Foo)
Related
Now I have a lib I made my self that I want to use in another CMake c++ project. It exists in my computer like this.
${MY_LIB_PATH}\include
${MY_LIB_PATH}\lib\x86\debug\lib-files
${MY_LIB_PATH}\lib\x86\release\lib-files
${MY_LIB_PATH}\lib\x64\debug\lib-files
${MY_LIB_PATH}\lib\x64\release\lib-files
What would a basic config file be like which makes CMake find_package know those? I expected it would be very simple because it just doesn't have much information to provide. But this page just make my head hurt.
Sorry, I decided to copy the source code around so I don't really know which answer should be accepted.
Don't write a config yourself; use CMake's export command. It's too broad to cover here in its entirety, but here's a modified example from one of my projects:
install(TARGETS
your_target
EXPORT YourPackageConfig
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
export(TARGETS
your_target
NAMESPACE YourPackage::
FILE "${CMAKE_CURRENT_BINARY_DIR}/YourPackageConfig.cmake"
)
install(EXPORT
YourPackageConfig
DESTINATION "${CMAKE_INSTALL_DATADIR}/YourPackage/cmake"
NAMESPACE YourPackage::
)
This will create the config file for you, so other projects can use it via find_package.
find_package(YourPackage REQUIRED)
target_link_libraries(foo YouprPackage::your_target)
This handles the IMPORTED targets automatically, and also lets you embed compiler flags, include paths, library dependencies, and even which files are part of your interface (basically, anything that falls under the INTERFACE properties).
Put a "${libname}-config.cmake" in library's root.
Then add an IMPORTED target in that file.
There is a example for libprotobuf.
add_library(libprotobuf STATIC IMPORTED GLOBAL)
set_target_properties(libprotobuf PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/prebuilt/android/${ANDROID_ABI}/libprotobuf.a"
IMPORTED_LINK_INTERFACE_LIBRARIES "${ZLIB_LIBRARIES};${CMAKE_THREAD_LIBS_INIT}"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/src")
Set Env or CMake variable "${libname}_DIR" to "${MY_LIB_PATH}"
Use it.
find_package(${libname})
#.......
target_link_libraries(main ${libname})
Maybe this older doc could be a tad more lightweight. And there is also this tutorial or this other one. The last one is perhaps the simplest.
Hope to not have added more pain :-)
Following the docs should give something roughly like the following (supposing your library is mylib):
MyLib/MyLibConfig.cmake.in
# - Config file for the MyLib package
# It defines the following variables
# MYLIB_INCLUDE_DIRS - include directories for MyLib
# MYLIB_LIBRARIES - libraries to link against
# MYLIB_EXECUTABLE - the bar executable
# Compute paths
get_filename_component(MYLIB_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
set(MYLIB_INCLUDE_DIRS "#CONF_INCLUDE_DIRS#")
# Our library dependencies (contains definitions for IMPORTED targets)
if(NOT TARGET mylib AND NOT MyLib_BINARY_DIR)
include("${MYLIB_CMAKE_DIR}/MyLibTargets.cmake")
endif()
# These are IMPORTED targets created by MyLibTargets.cmake
set(MYLIB_LIBRARIES mylib)
MyLib/MyLibConfigVersion.cmake.in
set(PACKAGE_VERSION "#MYLIB_VERSION#")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
main MyLib/CMakeLists.txt
...
set(MYLIB_MAJOR_VERSION 0)
set(MYLIB_MINOR_VERSION 1)
set(MYLIB_PATCH_VERSION 0)
set(MYLIB_VERSION
${MYLIB_MAJOR_VERSION}.${MYLIB_MINOR_VERSION}.${MYLIB_PATCH_VERSION})
...
add_library(mylib SHARED mylib.c ...)
...
install(TARGETS mylib
# IMPORTANT: Add the mylib library to the "export-set"
EXPORT MyLibTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin
LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib
PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/mylib"
COMPONENT dev)
...
# The interesting stuff goes here
# ===============================
# Add all targets to the build-tree export set
export(TARGETS mylib
FILE "${PROJECT_BINARY_DIR}/MyLibTargets.cmake")
# Export the package for use from the build-tree
# (this registers the build-tree with a global CMake-registry)
export(PACKAGE MyLib)
# Create the MyLibConfig.cmake and MyLibConfigVersion files
file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}"
"${INSTALL_INCLUDE_DIR}")
# ... for the build tree
set(CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}")
configure_file(MyLibConfig.cmake.in
"${PROJECT_BINARY_DIR}/MyLibConfig.cmake" #ONLY)
# ... for the install tree
set(CONF_INCLUDE_DIRS "\${MYLIB_CMAKE_DIR}/${REL_INCLUDE_DIR}")
configure_file(MyLibConfig.cmake.in
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/MyLibConfig.cmake" #ONLY)
# ... for both
configure_file(MyLibConfigVersion.cmake.in
"${PROJECT_BINARY_DIR}/MyLibConfigVersion.cmake" #ONLY)
# Install the MyLibConfig.cmake and MyLibConfigVersion.cmake
install(FILES
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/MyLibConfig.cmake"
"${PROJECT_BINARY_DIR}/MyLibConfigVersion.cmake"
DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev)
# Install the export set for use with the install-tree
install(EXPORT MyLibTargets DESTINATION
"${INSTALL_CMAKE_DIR}" COMPONENT dev)
I am using yocto build environment for my project. There are multiple applications in the project and some of them are depending on one third party library(It contains the *.s0 files, header files). So I am planning to create one static wrapper library around the third party library and link the wrapper library for all the applications.
The structure of the project:
.
├── App1
├── App2
├── App3
└── third-party
└── inc
└── src
└── lib
└── libdvm-hash.so
└── libhash-ipc.so
└── CMakeLists.txt
CMakeLists.txt
project(hash LANGUAGES CXX VERSION "1.0.0")
set(LIBRARY_NAME hash)
set(HASH_LIBRARY_FILES ${CMAKE_CURRENT_SOURCE_DIR}/lib/libdvm-hash.so ${CMAKE_CURRENT_SOURCE_DIR}/lib/libhash-ipc.so
add_library(${LIBRARY_NAME} STATIC test.cpp)
target_include_directories(${LIBRARY_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc/>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(${LIBRARY_NAME} PUBLIC ${HASH_LIBRARY_FILES} ssl)
set_target_properties(${LIBRARY_NAME} PROPERTIES
OUTPUT_NAME ${LIBRARY_NAME})
install( FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
install(
TARGETS ${LIBRARY_NAME}
EXPORT ${PROJECT_NAME}-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(EXPORT ${PROJECT_NAME}-targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} NAMESPACE dvm::)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/inc/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT dev)
Now the problem is in the exported hash-targets.cmake the path to the library is hardcoded.
hash-targets.cmake
set_target_properties(hash PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
INTERFACE_LINK_LIBRARIES "/home/mypc/path/to/the/ibdvm-hash.so;/home/mypc/pathto/the/libhash-ipc.so;ssl"
)
Is there any way to fix the hardcode path .*so and use the installed *.so from /usr/lib?
Edit
I installed *.so files in the target using yocto(using do_install_append command in bb file).
Instead of linking with absolute paths, create an IMPORTED library for every such path and link with that libraries:
# CMakeLists.txt, building the library
# Create an IMPORTED target which points to the library shipped with the project.
add_library(dvm-hash IMPORTED)
set_target_properties(dvm-hash PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/libdvm-hash.so
)
# Another IMPORTED target
add_library(hash-ipc IMPORTED)
set_target_properties(hash-ipc PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/libhash-ipc.so
)
...
# Link with those IMPORTED targets
target_link_libraries(${LIBRARY_NAME} PUBLIC dvm-hash hash-ipc)
That way, when create an export file for your hash library, CMake will create its linkage with targets dvm-hash and hash-ipc, but won't create that targets.
Next step is creating those IMPORTED targets in the export file for your project.
# hash-config.cmake
# Create an IMPORTED target. That time it points to the library in the target system.
add_library(dvm-hash IMPORTED)
find_library(DVM_HASH_LIBRARY dvm-hash)
set_target_properties(dvm-hash PROPERTIES
IMPORTED_LOCATION ${DVM_HASH_LIBRARY}
)
# Another IMPORTED target.
add_library(hash-ipc IMPORTED)
find_library(HASH_IPC_LIBRARY hash-ipc)
set_target_properties(hash-ipc PROPERTIES
IMPORTED_LOCATION ${HASH_IPC_LIBRARY}
)
# Include the export file for the target `hash`
# That file uses IMPORTED targets defined above.
include("${CMAKE_CURRENT_LIST_DIR}/hash-targets.cmake")
I have a CMake project C that depends on project B, which in turn depends on project A. I've created a superbuild that builds all three. Project C uses headers from project B, and my CMakeLists for Project C looks like this:
cmake_minimum_required(VERSION 3.10)
project(code_c)
find_package(b REQUIRED)
add_library(c c.c)
target_link_libraries(c PUBLIC b)
target_include_directories(c INTERFACE
$<INSTALL_INTERFACE:include>)
install(TARGETS c EXPORT c)
install(FILES c.h DESTINATION include)
export(PACKAGE c)
export(TARGETS c FILE "${PROJECT_BINARY_DIR}/bTargets.cmake")
install(EXPORT c FILE aTargets.cmake DESTINATION lib/cmake/c)
include(CMakePackageConfigHelpers)
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/cConfig.cmake"
INSTALL_DESTINATION lib/cmake/c)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/cConfig.cmake"
DESTINATION lib/cmake/c)
install(EXPORT c
FILE cTargets.cmake
DESTINATION lib/cmake/c)
My CMakeLists for Project B looks like this:
cmake_minimum_required(VERSION 3.10)
project(code_b)
find_package(a REQUIRED)
add_library(b b.c)
target_link_libraries(b PUBLIC a)
target_include_directories(b INTERFACE
$<INSTALL_INTERFACE:include>)
install(TARGETS b EXPORT b)
install(FILES b.h DESTINATION include)
export(PACKAGE b)
export(TARGETS b FILE "${PROJECT_BINARY_DIR}/bTargets.cmake")
install(EXPORT b FILE aTargets.cmake DESTINATION lib/cmake/b)
include(CMakePackageConfigHelpers)
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/bConfig.cmake"
INSTALL_DESTINATION lib/cmake/b)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/bConfig.cmake"
DESTINATION lib/cmake/b)
install(EXPORT b
FILE bTargets.cmake
DESTINATION lib/cmake/b)
My Config.cmake.in for Project B looks like
#PACKAGE_INIT#
include("${CMAKE_CURRENT_LIST_DIR}/bTargets.cmake")
check_required_components(b)
When I attempt to build Project C, I get
/Users/jhaiduce/Development/cmake_include_test/build/b/include/b.h:4:10: fatal error:
'a.h' file not found
#include "a.h"
^~~~~
1 error generated.
However, if I add find_package(a REQUIRED) to the CMakeLists.txt for Project A, then the include directories from target 'a' are added to the build and Project C compiles successfully. Should this be necessary? I would have expected that CMake would export Project A's targets along with Project B, making it unneccesary to call find_package(a) from Project C. Does the fact that Project C fails to build without this indicate a problem with the way I exported Project B?
I think in BConfig.cmake.in you should use:
include(CMakeFindDependencyMacro)
if(NOT A_FOUND AND NOT TARGET a)
find_dependency(A REQUIRED)
endif()
ref:
https://cmake.org/cmake/help/latest/module/CMakeFindDependencyMacro.html
I am trying to export CMake targets such that they also include the public include directories of dependant targets.
Let there be three separate but depending CMake projects:
foobar_result defines a data type and methods for this datatype in result.hpp
foobar_interface defines a header-only abstract class in foobar_interface.hpp which uses the datatype defined in foobar_result (#include <result.hpp>)
foobar_implA is an implementation (foobar_implA.cpp, foobar_implA.hpp) of the abstract class in foobar_interface (#include <foobar_interface.hpp>), and hence indirectly depends on the datatype definition (it #include <result.hpp> via #include <foobar_interface.hpp>)
What I want to achieve is that when foobar_implA is find_package(foobar_interface), it should automatically populate the include directories and libraries with the dependencies of foobar_interface so that foobar_implA does not have to discover foobar_result's include directories manually.
So far, I tried to specify the three CMake projects as follows.
1. foobar_result
foobar_result defines a standard library with headers:
cmake_minimum_required(VERSION 3.5)
project(foobar_result)
include(GNUInstallDirs)
add_library(result SHARED src/result.cpp)
target_include_directories(result PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>
)
install(EXPORT ${PROJECT_NAME}
DESTINATION share/${PROJECT_NAME}/cmake
FILE ${PROJECT_NAME}Config.cmake
)
install(TARGETS result EXPORT ${PROJECT_NAME})
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.hpp"
)
The exported foobar_resultConfig.cmake:
add_library(result SHARED IMPORTED)
set_target_properties(result PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)
and foobar_resultConfig-noconfig.cmake:
set_target_properties(result PROPERTIES
IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/lib/libresult.so"
IMPORTED_SONAME_NOCONFIG "libresult.so"
)
correctly define the includes and libraries.
2. foobar_interface
The interface uses the result library:
cmake_minimum_required(VERSION 3.5)
project(foobar_interface)
include(GNUInstallDirs)
find_package(foobar_result REQUIRED)
add_library(foobar_interface INTERFACE)
target_link_libraries(foobar_interface INTERFACE result)
target_include_directories(${PROJECT_NAME} INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>
)
install(EXPORT ${PROJECT_NAME}
DESTINATION share/${PROJECT_NAME}/cmake
FILE ${PROJECT_NAME}Config.cmake
)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.hpp"
)
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME})
Now, although the interface foobar_interface links the result library, the exported configuration:
add_library(foobar_interface INTERFACE IMPORTED)
set_target_properties(foobar_interface PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
INTERFACE_LINK_LIBRARIES "result"
)
only depends on the result library, but not its include directories. foobar_interface only exports its own INTERFACE_INCLUDE_DIRECTORIES, but not those of its dependency foobar_result.
3. foobar_implA
The implementation of the interface depends only on foobar_implA:
cmake_minimum_required(VERSION 3.5)
project(foobar_implA)
include(GNUInstallDirs)
find_package(foobar_interface REQUIRED)
add_library(foobar_implA SHARED src/foobar_implA.cpp)
target_link_libraries(foobar_implA PUBLIC foobar_interface)
target_include_directories(foobar_implA PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>
)
install(EXPORT ${PROJECT_NAME}
DESTINATION share/${PROJECT_NAME}/cmake
FILE ${PROJECT_NAME}Config.cmake
)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.hpp"
)
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME})
fails with:
In file included from [...]/src/foobar_implA/include/foobar_implA.hpp:3,
from [...]/src/foobar_implA/src/foobar_implA.cpp:1:
[...]/install/foobar_interface/include/foobar_interface.hpp:3:10: fatal error: result.hpp: No such file or directory
3 | #include <result.hpp>
| ^~~~~~~~~~~~
because find_package(foobar_interface) will not populate the include directories with the one defined in foobar_result.
I can only work around this issue by manually including foobar_result again:
find_package(foobar_result REQUIRED)
target_link_libraries(foobar_implA PUBLIC result)
but this is obviously not what I want, as this would not scale well when the dependencies in foobar_interface increase.
I am using colcon to separate the build and install spaces for all three projects. This way, all projects have to properly export and import their include and link directories. Installing all projects in the same prefix would also circumvent this problem, as foobar_interface include directories would also contain headers from foobar_result.
I have static library Foo, static library Bar that depends on Foo and executable Baz that depends on Bar.
Relevant sections from Foo CMakeLists.txt:
# Specifying files to copy during "make install" command.
install(TARGETS Foo EXPORT FooConfig
INCLUDES DESTINATION include
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
# Specifying config file that will be used to find a library using find_package().
install(EXPORT FooConfig
FILE FooConfig.cmake
NAMESPACE Foo::
DESTINATION lib/cmake/Foo)
export(TARGETS Foo
NAMESPACE Foo::
FILE FooConfig.cmake)
Relevant sections from Bar CMakeLists.txt:
# Specifying libraries that are required for build.
find_package(Foo REQUIRED)
# Specifying libraries to link to for the users of the library.
target_link_libraries(Bar PUBLIC Foo::Foo)
# Specifying files to copy during "make install" command.
install(TARGETS Bar EXPORT BarConfig
INCLUDES DESTINATION include
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
# Specifying config file that will be used to find a library using find_package().
install(EXPORT BarConfig
FILE BarConfig.cmake
NAMESPACE Bar::
DESTINATION lib/cmake/Bar)
export(TARGETS Bar
NAMESPACE Bar::
FILE BarConfig.cmake)
And finally Baz CmakeLists.txt:
find_package(Bar REQUIRED)
target_link_libraries(Baz PRIVATE Bar::Bar)
Now when building Baz I get:
CMake Error at CMakeLists.txt:19 (add_executable):
Target "Baz" links to target "Foo::Foo" but the
target was not found. Perhaps a find_package() call is missing for an
IMPORTED target, or an ALIAS target is missing?
So build finds Bar and correctly determines that it depends on Foo but can't find Foo. I have another test project that directly depends on Foo and it builds fine. How to fix this?
Unfortunately, BarConfig.cmake generated like this does not handle finding dependencies, I had to modify Bar CMakeLists.txt to this:
# Specifying files to copy during "make install" command.
install(TARGETS Bar EXPORT BarTargets
INCLUDES DESTINATION include
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
install(FILES CMake/BarConfig.cmake DESTINATION lib/cmake/Bar)
# Specifying config file that will be used to find a library using find_package().
install(EXPORT BarTargets
FILE BarTargets.cmake
NAMESPACE Bar::
DESTINATION lib/cmake/Bar)
export(TARGETS Bar
NAMESPACE Bar::
FILE BarTargets.cmake)
Then I created a file CMake/BarConfig.cmake with this:
include("${CMAKE_CURRENT_LIST_DIR}/BarTargets.cmake")
find_package(Foo REQUIRED)
Now this BarConfig.cmake gets installed globally and calls find_package that finds Foo.