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

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?

Related

How to use export custom target with CMake?

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.

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.

How to let cmake use another opencv directory other than the system's in ubuntu?

Specifically, I want to include opencv from my/path/to/opencv/release where my own opencv is built other than the system's opencv lib in /usr/local/include. How can I set cmake to achieve this? I'm using Ubuntu 14.04.
To provide an example, below is a Find-CMake file for the Luajit library. Your CMake project has one that's probably called something like "FindOpenCV.cmake". It most likely has a default installation path that was manually added, such as: "/usr/include/luajit-2.0 /usr/local/include/luajit-2.0" and you would change these DIR's to your desired install directory. If the default path is contained in a variable, you could either find the definition of that variable and change it (preferably through a configuration option), or override it with a string.
# Try to find Lua or LuaJIT depending on the variable ENABLE_LUAJIT.
# Sets the following variables:
# LUA_FOUND
# LUA_INCLUDE_DIR
# LUA_LIBRARY
#
SET (LUA_FOUND FALSE)
SET (LUA_INTERPRETER_TYPE "")
SET (LUA_INTERPRETER_TYPE "LuaJIT")
SET (LUA_LIBRARY_NAME luajit-5.1)
SET (LUA_INCLUDE_DIRS /usr/include/luajit-2.0 /usr/local/include/luajit-2.0)
FIND_PATH (LUA_INCLUDE_DIR lua.h ${LUA_INCLUDE_DIRS}
HINT ${LUAJIT_INCLUDE_DIR_HINT}/include)
FIND_LIBRARY (LUA_LIBRARY NAMES ${LUA_LIBRARY_NAME} PATHS /usr/lib /usr/local/lib
HINT ${LUAJIT_INCLUDE_DIR_HINT}/bin)
#...
MARK_AS_ADVANCED ( LUA_INCLUDE_DIR LUA_LIBRARY)
There are multiple ways to achieve this, you can modify a FindOpenCV.cmake file, you set the cmake variable OpenCV_DIR before the library is found https://stackoverflow.com/a/9835300/2079934, you can export the environment variable OpenCV_DIR before you run CMake https://stackoverflow.com/a/16959771/2079934.
I would recommend not to hard-code any library paths into CMakeLists.txt, that would take away all the benefits of CMake. In Linux, I would use export to set OpenCV_DIR, in other OSes CMake GUI is more common there you could edit the path variable in the GUI.

How to configure DBus dependencies with CMake

I am new to CMake and DBus. I am following along the guide here and make a basic program compile and execute.
The first problem that I ran into was my program will not find
<dbus/dbus.h>
I got around that issue by adding some include directories to my CMakeList.txt.
Currently, my CMakeLists.txt looks like this:
...
include_directories(/usr/lib/)
include_directories(/usr/include/dbus-1.0/)
include_directories(/usr/lib/x86_64-linux-gnu/dbus-1.0/include)
include_directories(/usr/include/glib-2.0)
include_directories(/usr/lib/x86_64-linux-gnu/glib-2.0/include/)
set (LIBS
dbus-1
dbus-glib-1
)
add_executable(mydbus mydbus.cpp)
target_link_libraries(mydbus ${LIBS} )
Now, my program is complaining about not being able to find dbus-arch-deps.h
/usr/include/dbus-1.0/dbus/dbus.h:29:33: fatal error: dbus/dbus-arch-deps.h: No such file or directory
#include <dbus/dbus-arch-deps.h>
I know that the solution for this is to use proper command line flags or pkg-config. As discussed here and numerous other posts.
However, I do not know how to configure CMakeLists.txt to have similar effect.
My guess would be to add something like find_package(dbus-1) to CMakeLists.txt. And if that is correct, I am going to have to write my own Finddbus-1.cmake. Does this sound correct? Or is there an easier way?
I will appreciate any pointers.
You may get an existing FindDBus.cmake script (e.g., this one), copy it into your project, and use as
find_package(DBus REQUIRED)
# Use results of find_package() call.
include_directories(${DBUS_INCLUDE_DIRS})
add_executable(mydbus mydbus.cpp)
target_link_libraries(mydbus ${DBUS_LIBRARIES})
Alternatively, as you know pkgconfig can find DBus, you may use CMake module PkgConfig. Actually, FindDBus.cmake script, referenced above, uses PkgConfig module in its implementation. Possible usage could be:
find_package(PkgConfig REQUIRED) # Include functions provided by PkgConfig module.
pkg_check_modules(DBUS REQUIRED dbus-1) # This calls pkgconfig with appropriate arguments
# Use results of pkg_check_modules() call.
include_directories(${DBUS_INCLUDE_DIRS})
link_directories(${DBUS_LIBRARY_DIRS})
add_executable(mydbus mydbus.cpp)
target_link_libraries(mydbus ${DBUS_LIBRARIES})
However, using link_directories is not recommended, it is better to use absolute paths to libraries in target_link_libraries() call. That is why it is better to combine pkg_check_modules with find_library, as it is done in the referenced Find script. That answer describes generic way for use result of pkgconfig in CMake.

CMake building targets conditionally based on library existence

I have a large cross-platform project which needs to build in various places; in some places, different UI toolkits, sound APIs, etc. may be available, and I am trying to figure out the best way to automatically configure which targets get configured based on which libraries are present.
The code I am trying for that is, for example:
find_library(PC_EGL EGL)
find_library(PC_GLESv2 GLESv2)
find_library(PC_Xxf86vm Xxf86vm)
if (DEFINED PC_EGL AND DEFINED PC_GLESv2 AND DEFINED PC_Xxf86vm)
add_executable(foo foo.cpp)
target_link_libraries(foo ${PC_EGL} ${PC_GLESv2} ${PC_Xxf86vm})
endif()
However, in the case that I build this on a system which doesn't have libGLESv2 available, I get the error:
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
PC_GLESv2
linked by target "foo" in directory /path/to/platform
The find_library documentation implies that the variable PC_EGL_NOTFOUND should be getting set, but it isn't (CMake 2.8.5). So, what is the appropriate way to use find_library to determine whether a target should be made to exist at all? It seems like using
if (NOT PC_EGL MATCH "-NOTFOUND")
is a bit fragile and fiddly, so is there a better mechanism for determining a CMake command path based on wheter a library was found at all?
It's simply
if(PC_EGL AND PC_GLESv2 AND PC_GLESv2)
CMake treats 0, FALSE, OFF, ANYTHING-NOTFOUND as false.