How to use export custom target with CMake? - c++

I'm trying to export all of the dependent targets of an engine I've been developing with export command. Everything works fine with dependencies, but when I call the command with main "Nabla" target, I get a following error:
CMake Error in CMakeLists.txt:
export called with target "Nabla" which requires target "openssl_build"
that is not in any export set.
The problem is that the openssl_build is a custom target and I have no clue how to avoid this error, because when I try to export the target I get another error telling me that
-- Using static CRT ON
CMake Error at 3rdparty/CMakeLists.txt:556 (export):
export given custom target "openssl_build" which may not be exported.
the following commit contains my changes to the engine in reference to export command
The custom target generating the error is here
I wonder if I could set a cmake property for the openssl_build to make it work, but I have been looking for useful properties in cmake docs and could not find anything
Thank you in advance!

This usually happens when you use add_subdirectory to consume library.
When you add add_subdirectory, CMake will consider, for example, the whole openssl project become part of your project. They are not separable if they are the same project.
If you build openssl as part of your project, it's very doubting that your project will run properly without also having openssl installed, let alone users consuming your package!
You could simply add the openssl libraries you depend on to the export set:
install(TARGETS openssl_build EXPORT NablaTargets)
But that's not the proper way.
The proper way would be to use a package manager, such as vcpkg to install the dependencies for you.
First, replace the add_subdirectory by a find_package and a link:
find_package(OpenSSL REQUIRED)
target_link_libraries(Nabla PUBLIC OpenSSL::SSL OpenSSL::Crypto)
Create vcpkg.json with this content:
{
"name": "nabla",
"version-string": "0.1",
"dependencies": [
"openssl"
]
}
And when you use cmake, add the argument -DCMAKE_TOOLCHAIN_FILE=/opt/vcpkg/scripts/buildsystems/vcpkg.cmake (or where you installed it) and let the package manager do the work for you.
Remember that when you use a new toolchain file, you must re-create a new build directory since it cannot be added after creating it.

Related

If I find_package in CMakeLists.txt, must I find_dependency in my installed config.cmake?

I'm using CMake to build and to install a certain library, foo.
My library depends on some other library, bar, which has a config CMake script, so I have:
find_package(bar REQUIRED)
target_link_libraries(foo PUBLIC bar::bar)
That's as far as building goes. For installation, I have appropriate install() commands, with exports, and version configuration and all that stuff. This generates the package's -config.cmake file (as well as a version config file), so I don't need to keep one in the repository, nor generate one line-by-line within my CMakeLists.txt
Now, CMake has a module named find_dependency(), whose documentation suggests is to be used in package configuration files. But - I don't explicitly add it there. Should I? And more generally: Under which circumstances should I manually ensure a package configuration file has a find_dependency() for various find_package()'s?
First, CMake does not support "Transitive" behavior for find_package() (check this question).
The documentation recommends that "All REQUIRED dependencies of a package should be found in the Config.cmake file":
# <package>Config.cmake file
include(CMakeFindDependencyMacro)
find_dependency(Stats 2.6.4)
include("${CMAKE_CURRENT_LIST_DIR}/ClimbingStatsTargets.cmake") # They depend on Stats
include("${CMAKE_CURRENT_LIST_DIR}/ClimbingStatsMacros.cmake")
So, answering your questions:
"I don't explicitly add it there. Should I?" You actually have to, at least for REQUIRED packages.
"Under which circumstances should I manually ensure a package configuration file has a find_dependency() for various find_package()'s?" For required packages, you must. For optional package, you might want to add it in the configuration file so that optional features will be available.
I am working on a project which depends on an external package (Catch2). In my top level CMakelists.txt I have:
# Top level CMakelists.txt
set(Catch2_DIR "${PATH_TO_CATCH2}/lib/cmake/Catch2/")
find_package(Catch2 ${CATCH2_VERSION} REQUIRED)
Then I added the following to my package configuration file:
# <package>Config.cmake file
include(CMakeFindDependencyMacro)
set(Catch2_DIR "#PATH_TO_CATCH2#/lib/cmake/Catch2/") #be careful, path hard coded
find_dependency(Catch2 REQUIRED)
Just be careful because the find_dependency is a macro and it will change the value of PACKAGE_PREFIX_DIR variable in your package configuration file.

Multiple conanfile.py management

Let's say I have 2 different conanfile.py in a project and I'm calling conan install two times to install their dependencies. I'm having trouble while adding them to cmake.
If I use basic setup
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
it only includes latest one. Is it possible to include multiple conanbuildinfo.cmake files ?
If you have 2 completely separate projects, you can have 2 different conanfiles and put the generated files in different folders:
$ conan install conanfile1.py --install-folder=folder1
$ conan install conanfile2.py --install-folder=folder2
Then in your first project:
include(<...>/folder1/conanbuildinfo.cmake)
conan_basic_setup()
And in your second project:
include(<...>/folder2/conanbuildinfo.cmake)
conan_basic_setup()
You would need to define some consistent convention to locate the generated files for each project.
Note, however, that if the different modules are intended to use together, like linked together lately, if you don't use the same dependencies and same versions, you will probably get linking or runtime errors in your global application. If the modules are related and you want to use the same versions of the dependencies, then you definitely want to use just 1 conanfile with all dependencies defined in it.
Note that there are different ways to define the specific dependencies that you want, even if you use only 1 conanfile:
You can use the TARGETS of the cmake generator:
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS)```
add_library(mylib1 ... <sources>)
target_link_libraries(mylib1 PUBLIC CONAN_PKG::Dep1 CONAN_PKG::Dep2)
add_library(mylib2 ... <sources>)
target_link_libraries(mylib2 PUBLIC CONAN_PKG::Dep3 CONAN_PKG::Dep4)
The cmake_find_package generators also generate one findXXXX.cmake file for each package in the dependency graph. You can use the find_package(XXXX) and later the results, specifying different dependencies. The cmake_find_package_multi generator is recommended.

How do I export a target, then use it in another project via ExternalProject?

I have a CMake project named proj1, which I want to use as an external project in another project, proj2. Now, the (relevant) command in proj1's CMakeLists.txt is:
install(
TARGETS proj1
ARCHIVE
DESTINATION lib
EXPORT proj1_library
INCLUDES DESTINATION include
CONFIGURATIONS Release RelWithDebugInfo
)
and I want to use this static library in proj2, without explicitly "guessing" where it's installed to be proj1. I want to be able to get obtain this target from proj1 (which I obtain using ExternalProject), then use it - directly or indirectly - in add_target_libraries() commands.
How should I do that? And - do I only need to make changes to proj2 or also to proj1's CMakeLists.txt?
Exporting the targets is the right approach.
To support this, proj1 would have to generate a proj1Config.cmake in both its build tree and also in the install tree (so that a development package for proj1 can be used as a SDK [1])
I suggest you read the following section of the CMake documentation, it covers the different concepts and provides an example. See https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#creating-packages
[1] SDK: https://en.wikipedia.org/wiki/Software_development_kit

How can I make find_package search with config mode and fallback on module mode?

When a library defines a build with CMake and goes through the trouble of building an install package for itself, there will be a XXXConfig.cmake.
If a library doesn't have a way to export it's targets to CMake, CMake tries to bridge the gap by providing FindXXX.cmake scripts that attempt to locate such libraries.
In the docs, FindXXX.cmake (module mode), is attempted first, and only if that fails does it attempt to use XXXConfig.cmake (config mode). But this seems like a really backwards to me.
The problem is, for example, I have built CURL from source, and the ConfigXXX produces a different target name than FindXXX, so, when trying to use it, it fails because FindXXX took responsibility for the find_package request and loaded a different target name than what I was expecting.
Can I at least tell CMake somehow to do things the other way around? Config mode first.
I know I can disable module mode entirely, but I'd rather have it as a fallback option.
New in version 3.15:
Set CMAKE_FIND_PACKAGE_PREFER_CONFIG to TRUE to tell find_package() to first search using Config mode before falling back to Module mode.
References: 1, 2.
Just use find_package with CONFIG mode, check its result, and, if result is false, repeat the call with MODULE mode:
# First time do not use common *REQUIRED* but use QUIET for do not output error messages on fail.
find_package(XXX CONFIG QUIET)
if(NOT XXX_FOUND)
# Previous call has been failed. Fallback with MODULE mode.
find_package(XXX MODULE REQUIRED) # Now it is OK to use REQUIRED if needed.
# ... There could be additional actions for wrap result "as if" CONFIG mode.
endif()
# ... use XXX
You can try this find_package(XXX CONFIG REQUIRED).
see the link: CMake: Of what use is find_package() if you need to specify CMAKE_MODULE_PATH anyway?

How to install a target that link privately against an Interface library

Hello developper friends, I'm using CMake for a while now, but I could't figure how to install a target :
that is part of an export set
that link privately against interface library
The export set is created with the command
install(EXPORT MyExportSet DESTINATION MyExportDir
NAMESPACE Project FILE ProjectTargets.cmake)
the link to the interface library is done like this
target_link_libraries(exportedTarget
PRIVATE interfaceTargetLibrary
)
and finally i'm exporting the target like this :
install(TARGETS exportedTarget EXPORT MyExportSet
ARCHIVE DESTINATION lib/static
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
This look rights to me, and following the logic of the PRIVATE flag uppon linking, I shouldn't have to export the target interfaceTargetLibrary
But CMake throw me an error (This one)
CMake Error: install(EXPORT "ProjectTargets" ...) includes target"exportedTargets" which requires target "interfaceTargetLibrary" that is not in the export set.
So here my question, is that a bug ? or something I didn't understand ? And obviously did you achieve to make it work on way or another.
I'm using CMake version 3.7.
EDIT : Finded out, if someone interested. It's because, the library that is linked privately wont be packaged in the one I export. So, the comsummer of the library will need, to link against interfaceTargetLibary
too and so I need to export it as well.
Since it's linked privately, you can use the following trick: CMake won't try to install an IMPORTED library.
Also see "What is an INTERFACE IMPORTED library in CMake and what are its uses?".
I tried to find official documentation on this behaviour but I could not. I did find a related discussion ticket on kitware's repo though.