Make FetchContent compatible with find_package - cmake

I try to add all dependencies needed for my project to compile over CMake. This should reduce the overhead others will have when they want to compile the project for the first time.
To achive this, I tried to use FetchContent. So far so good, when I link the generated targets its not a problem at all. But now I have a library depending itself on annother lib which isn't included as submodule. The lib tries to find the dependency over find_package. How can I get find_package to find the library?
What I tried so far:
adding an alias target and defined all variables set by find_package
Setting the LIB_DIR to the build directory and called find_package
Here a minimal snipped of my CMake code of the later:
cmake_minimum_required(VERSION 3.14)
find_package(ZLIB)
if (NOT ZLIB_FOUND)
FetchContent_Declare(zlib_fetch
GIT_REPOSITORY https://github.com/madler/zlib.git
GIT_TAG cacf7f1d4e3d44d871b605da3b647f07d718623f
)
FetchContent_MakeAvailable(zlib_fetch)
set(ZLIB_DIR ${zlib_fetch_BINARY_DIR})
message(${zlib_fetch_BINARY_DIR})
#simulates the call in the other library:
find_package(ZLIB REQUIRED)
endif (NOT ZLIB_FOUND)

Starting with CMake 3.24, FetchContent_Declare has OVERRIDE_FIND_PACKAGE option which, when specified, makes CMake redirect the subsequent calls to find_package(<name>) so that FetchContent_Declare(<name> ...) satisfies the dependency (note that <name> must stay the same here).

Related

How to use FetchContent_Populate with Eigen?

I want to use FetchContent to automatically manage the dependency to Eigen for my project, which works in general. However, when using the recommended method of FetchContent_Declare() and FetchContent_MakeAvailable() a subsequent call to install also installs all Eigen headers and documentation which is not necessary in my case.
To circumvent this behavior, I tried the method suggested in this answer: Disable install for FetchContent
FetchConten_Populate() however fails to fill the variables ${Eigen_SOURCE_DIR} and ${Eigen_BIN_DIR} (which the documentation told me should happen) so that I cannot call add_subdirectory().
Here is a minimal CMakeLists.txt:
cmake_minimum_required (VERSION 3.12)
project (FetchContentExample)
include (FetchContent)
FetchContent_Declare(
Eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
)
FetchContent_GetProperties(Eigen)
if(NOT Eigen_POPULATED)
FetchContent_Populate(Eigen)
message("SRC; ${Eigen_SOURCE_DIR}") # Apparently empty?
message("BIN: ${Eigen_BINARY_DIR}") # Apparently empty?
add_subdirectory(${Eigen_SOURCE_DIR} ${Eigen_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
add_executable(FetchContentExample
main.cpp
)
target_link_libraries (FetchContentExample
PRIVATE
Eigen3::Eigen
)
install(
TARGETS FetchContentExample
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT Runtime
)
The same setup works fine when I use e.g.
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 5.3.0
)
instead of Eigen.
What specifically am I doing wrong when it comes to Eigen?
FetchContent_Populate() however fails to fill the variables ${Eigen_SOURCE_DIR} and ${Eigen_BINARY_DIR} (which the documentation told me should happen).
Actually, FetchContent fills variables ${eigen_SOURCE_DIR} and ${eigen_BINARY_DIR} which names are constructed from the lowercase variant of the project's name. This is written in the documentation:
FetchContent_Populate() will set three variables in the scope of the caller:
<lowercaseName>_POPULATED
This will always be set to TRUE by the call.
<lowercaseName>_SOURCE_DIR
The location where the populated content can be found upon return.
<lowercaseName>_BINARY_DIR
A directory intended for use as a corresponding build directory.
So the correct sequence of commands for EXCLUDE_FROM_ALL inclusion of Eigen would be:
FetchContent_GetProperties(Eigen)
if(NOT eigen_POPULATED)
FetchContent_Populate(Eigen)
add_subdirectory(${eigen_SOURCE_DIR} ${eigen_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()

How do I use find_package in CMakeLists.txt such that if an optional dependency is not installed, I still get a makefile?

My goal is to create a CMakeLists.txt file that builds a suite of programs for the local system. If the conditions for building some programs are not satisfied, I wish to be able to build the rest of the programs. The sticking point seems to be gtk, but bear in mind that I don't think a gtk-specific method is the best choice.
For many modules you'd want to build with, you can use
find_package(PkgConfig REQUIRED)
To set up the pkg-config wrapper pkg_check_Modules and if you don't use REQUIRED you can check the _FOUND variable:
pkg_check_Modules(FT REQUIRED freetype2)
if (FT_FOUND)
message(STATIC "Found freetype2")
#Continue and succeed, using FreeType library
else()
message(STATIC "Didn't find freetype2")
#Continue and succeed, without using FreeType library
endif()
Other libraries don't seem to work with pkg_check_modules so I have resorted to find_package
find_package(GTK2 2.0 QUIET gtk)
if (GTK2_FOUND)
...
endif()
Here's a complete reduction:
cmake_minimum_required(VERSION 3.6.2)
project (test C CXX)
set (SOURCE a.cpp b.cpp)
find_package(AMAZING 2.0 QUIET amazing_testing)
if (AMAZING_FOUND)
list(APPEND SOURCE c.cpp)
else()
list(APPEND SOURCE d.cpp)
endif()
add_executable(test ${SOURCE})
The above allows my whole CMakeLists.txt to proceed to the end, but I get an error, no makefile is created, and build/CMakeFiles/CMakeOutput.log doesn't even mention gtk:
CMake Error at gtk/CMakeLists.txt:4 (find_package):
find_package called with invalid argument "amazing_testing"
## here would be further cmake processing logs from other necessary setup
-- Configuring incomplete, errors occurred!
See also "/home/menright/bit/build/CMakeFiles/CMakeOutput.log".
How can I tweak such a usage of find_package so that if the optional package does not exist, the configure will complete successfully anyway? Alternatively, what should I use instead of find_package that will concisely set up usage of a library the way pkg_check_modules does and yet allow success if the library is not available?

What is the recommended way of using GLib2 with CMake

Id like to use GLib in my C application which uses CMake as the build system.
Now, I'm somehow confused how I should enable GLib in my CMakeLists.txt. Basically, you add libraries in cmake using the find_package command, so I tried, according to this bugreport
find_package(GLib2)
But nothing is found. In the GLib documentation it is suggested to use pkg-config, on the other hand.
What is the recommended way of enabling glib in a cmake-based project?
Since CMake 3.6 (released in July 2016), pkg_check_modules supports IMPORTED_TARGET argument, reducing the dependency configuration to a single target_link_libraries statement, which will take care of all required compiler and linker options:
find_package(PkgConfig REQUIRED)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET glib-2.0)
target_link_libraries(target PkgConfig::deps)
(above I used the name deps because one can list multiple dependencies with a single pkg_check_modules statement)
In your CMakeLists.txt:
find_package(PkgConfig REQUIRED)
pkg_search_module(GLIB REQUIRED glib-2.0)
target_include_directories(mytarget PRIVATE ${GLIB_INCLUDE_DIRS})
target_link_libraries(mytarget INTERFACE ${GLIB_LDFLAGS})
Give a look at my answer on using CMake with GTK
It's pretty much the same with GLib.
GLib (and various other C libraries using autotools) provide a pkg-config file for declaring:
compiler flags
linker flags
build-time variables
dependencies
The appropriate way to discover where these libraries are with CMake is to use the FindPkgConfig CMake module:
https://cmake.org/cmake/help/v3.0/module/FindPkgConfig.html
yet another version, combination of multiple answers and what actually worked for me (on Linux)!
cmake_minimum_required(VERSION 2.6.4)
project(my_proj)
find_package(PkgConfig REQUIRED)
pkg_search_module(GLIB REQUIRED glib-2.0)
include_directories(${GLIB_INCLUDE_DIRS})
link_directories(${GLIB_LIBRARY_DIRS})
add_executable(my_proj main.c)
add_definitions(${GLIB_CFLAGS_OTHER})
target_link_libraries(my_proj ${GLIB_LIBRARIES})
I've been working on some CMake modules for GNOME (including one for GLib) which you might want to try. Basically, just find_package(GLib), then you can use the glib-2.0 imported target to link to it.

CMake dependencies: force recompile on external library change

I'm trying to correctly manage a dependency of a target on a externally built library, and somehow I'm not succeeding. I have read tutorials, posts and examples aplenty and yet, since I'm new to CMake, I guess I'm missing some obvious thing.
Setup is as follows. An external library built in another (CMake unsupported) language produces a libadatest.a. I've used ExternalProject_Add for this. Then, there is another regular C target that uses this lib. Everything works fine, but if I change the original lib, even if I recompile it, the C target is not recompiled. Here is a complete sample. I'm using CMake 2.8.12:
cmake_minimum_required(VERSION 2.8)
include(ExternalProject)
ExternalProject_Add(
AdaTestExternal # Not important
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
BUILD_COMMAND gprbuild -P${CMAKE_CURRENT_SOURCE_DIR}/adalibtest -XOBJ_DIR=${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY} -XLIB_DIR=${CMAKE_CURRENT_BINARY_DIR}
ALWAYS 1 # Force build, gprbuild will take care of dependencies
# BUILD_ALWAYS 1 # For 3.0 higher versions?
INSTALL_COMMAND ""
)
add_custom_target(AdaTest DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/libadatest.a)
link_directories(${CMAKE_CURRENT_BINARY_DIR}) # Needed or won't find it
add_executable(main_ada main.c)
add_dependencies(main_ada AdaTest) # We must depend on the final output lib
target_link_libraries(main_ada adatest)
What I've attempted is to create an intermediate custom target which depends on the actual library, and in turn make the main C target depend on this target.
When I remove the externally built library (libadatest.a), that's properly externally recompiled but the main executable is not re-linked. Plainly seen in that the timestamp of the library is fresher than the executable which uses it.
I've also tried this instead of the custom target, with same negative result:
add_library(AdaTest
UNKNOWN IMPORTED
IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/libadatest.a)
Found the proper solution (which was, as expected, simple) in this old post: http://www.cmake.org/pipermail/cmake/2010-November/041072.html
The gist is to use the actual file in target_link_libraries, so its timestamp is checked. So no need for intermediate or custom dependencies:
set(AdaTestLib ${CMAKE_CURRENT_BINARY_DIR}/libadatest.a)
add_executable(main_ada main.c)
add_dependencies(main_ada AdaTestExternal)
target_link_libraries(main_ada ${AdaTestLib})

How would I build and link an external library using CMake?

To start off, I should say that I've seen the answers on questions that were similar to mine, yet none of them seem to make sense to me. Here goes:
Say I have the following simple CMakeLists:
cmake_minimum_required (VERSION 2.8)
project (Example)
add_executable (Example Main.cpp)
I would then like to link a library that already has its own CMakeLists (for example, glfw) against Example. Of course, this brings into question how to properly set up the build order.
How would I go about doing this?
First let me describe what to do for GLFW. See the notes below for variations.
Build GLFW. As the official GLFW head's CMake support is currently broken, use this shaxbee fork:
git clone https://github.com/shaxbee/glfw.git
cmake -Hglfw -Bbuild/glfw -DCMAKE_INSTALL_PREFIX=inst -DCMAKE_BUILD_TYPE=Release
cmake --build build/glfw --target install --config Release
This install step installs inst/lib/cmake/glfw3/glfw3Config.cmake, the glfw3 config-module which will be used by client projects to find GLFW.
Your project should look like this:
cmake_minimum_required(VERSION 2.8)
project(Example)
find_package(glfw3 REQUIRED)
add_executable(Example Main.cpp)
target_link_libraries(Example glfw3)
The find_package command finds the glfw3Config.cmake which creates an IMPORTED library, a CMake target, which incorporates all the information needed to use GLFW in your project.
The target_link_libraries command tells CMake not only to link to GLFW but to use its include directories and compile flags, too.
Configure your project with -DCMAKE_PREFIX_PATH=inst (use full path if necessary)
Note: Other config-modules or find-modules may not provide IMPORTED targets. See the actual config or find-module for help. They usually provide <PACKAGE>_INCLUDE_DIRS and <PACKAGE>_LIBRARIES variables which should be added to the appropriate settings of your target:
target_include_directories(mytarget ${ZLIB_INCLUDE_DIRS})
or
include_directories(${ZLIB_INCLUDE_DIRS})
Same for target_link_libraries / link_libraries.
Build the external dependency first.
add a function call to find your external library. That can either be FindGLFW.cmake with find_package if you can find that or write it yourself using find_library et al. More information about including external libraries can be found in the CMake wiki
Add the includes and libraries to your Example executable.
This is how CMake should be used, I don't see why is not answered in the sources in the Internet.