Managing Dependendices in CMake
This is a quick overview of how to download, interact, and/or reference external files, modules, libraries, and frameworks in your cmake project. There are multiple methods to go about this, but we'll focus on the three base methods that come standard with your CMake installation. We will mention an external module that can help with this process but will not be discussed.
NOTE: Some of the material presented here is referenced from Modern CMake for C++ Second Edition by Rafał Świdziński . This one is definetly worth a read and provided really good reference on some of the finer details regarding CMake.
1. Include ( )
You will likely have to work with *.cmake files in your project. Generally, these files contain macros, functions, build options, or other configuration tools that simplify the organization or structuring of your project. To reference these projects in your (parent) file, we use the include()
command.
Command Syntax:
include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>])
Example Usage:
- Including a module that comes with cmake
include(CTest)
- Including a *.cmake file
include(${CMAKE_CURRENT_LIST_DIR}/../cross-compile/cortex-m3_softfloat.cmake)
NOTE: This example uses relative pathing to locate a *.cmake file. As an aside, the file referenced is for cross-compiling code for a cortex-m3 microprocessor. This file contains values for specific variables used by CMake for cross-compilation toolchains.
If no file or module is found when using this function, CMake will raise an error unless it is specified with the OPTIONAL
keyword.
2. Find_Package()
The Find_Package()
command is somewhat similar to the include()
command in the sense that it locates local packages installed on your host system. However, instead of only focusing on modules installed by CMake or in your project directory, Find_Package()
is system-wide.
Extended Command Syntax:
find_package(<PackageName> [<version>] [REQUIRED] [COMPONENTS <components>...])
Typical Command Usage:
find_package(<PackageName> [<version>] [EXACT] [QUIET] [REQUIRED])
Argument | Description |
---|---|
version | Minimum version of the package that can be used. Follows the following versioning scheme: <major> <minor> <patch> <tweak> |
EXACT | Informs CMake to only accepty a specific version of the package. Has Higher priority over the version |
QUIET | Suppresses/hides any messages or warnings on whether the package was found or not |
REQUIRED | Informs CMake that the build can only proceed if the package is found. If the package is not found, the build will stop and return an error message! |
NOTE: There are more arguments than the ones mentioned here. For a complete list, see the official CMake documentation: https://cmake.org/cmake/help/latest/command/find_package.html
If our package isn't supported, we have two options:
- We can write a small plugin called a
find-module
to help find_package() - We can use an older method called
pkg-config
Example Usage:
find_program(CLANG_FORMAT clang-format)
if(CLANG_FORMAT)
add_custom_target(format
COMMAND cmake/tools/format.sh
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)
3. FetchContent
The FetchContent
module is probably the most useful of the methods presented in this document. Unlike the previous two methods mentioned, FetchContent
has the advantage of being able to download/"fetch" content from remote sources or URLs of some kind (i.e., Git, Subversion, Websites/HTTP).
As a part of this module, there are multiple functions we can use to manage our newly acquired dependency, but for less complex projects, you'll typically only need two commands:
FetchContent_Declare()
FetchContent_MakeAvailable()
NOTE: To use the FetchContent module, include the module in your top-level CMakeLists.txt file or where appropriate for your project.
include(FetchContent)
Note
The following is an Excerpt from Modern Cmake for C++ 2nd Edition,
The usage of the
FetchContent
module involves three main steps:
- Add the module to your project with
include (FetchContent)
.- Configure the dependencies with the
FetchContent_Declare()
command. This will instructFetchContent
where the dependencies are and which version should be used.- Complete the dependency setup using the
FetchContent_MakeAvailable()
command. This will download, build, install, and add the list files to your main project for parsing.
FetchContent_Declare()
Command Syntax:
FetchContent_Declare(<name> <contentOptions>... [EXCLUDE_FROM_ALL] [SYSTEM] [OVERRIDE_FIND_PACKAGE | FIND_PACKAGE_ARGS args...]
)
Command Syntax:
FetchContent_Declare(<name> <contentOptions>)
Argument | Description |
name | The name of the package, library, dependency that is being fetched. it can be any string without spaces.
NOTE: The argument is case insensitive! |
contentOptions | Modifiers for fetching the dependency. This includes but is not limited to downloading the dependency, updating the dependency, or patching the dependency. For more information see the ExternalProject_Add() command. |
SYSTEM | Sets the SYSTEM property of a subdirectory to true. In a sense, this will allow the project to treat this particular dependency as a system dependency/include. |
NOTE: There are more arguments than the ones mentioned here. For a complete list, see the official CMake documentation: https://cmake.org/cmake/help/latest/module/FetchContent.html
NOTE: Think of the FetchContent_Declare()
command as telling the build system where to look for the dependency. Its counterpart FetchContent_MakeAvailable()
is the instruction that is responsible for downloading the dependency to your project.
FetchContent_MakeAvailable()
Command Syntax:
FetchContent_MakeAvailable(<name1> [<name2>...])
Argument | Description |
name | The name of the package, library, dependency that is being fetched. it can be any string without spaces. \
\
NOTE: This should match the name argument used for the respective dependency in the FetchContent_Declare() command.
|
NOTE: If you're finding that your dependency is missing, does not have a source path, and or is not being downloaded, try making all the characters lowercase.
FetchContent Module Full Example:
include(FetchContent)
# Get nrfx drivers
fetchcontent_declare(
nrfx-drivers
GIT_REPOSITORY https://github.com/NordicSemiconductor/nrfx.git)
GIT_TAG v3.6.0
)
fetchcontent_makeavailable(nrfx-drivers)
message("nrfx driver source directory: ${nrfx-drivers_SOURCE_DIR}")
#Create nrfx driver library target
add_library(nrfx_driver_package)
Downloading drivers for nrf5340 MCU
# GoogleTest requires at least C++14
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
Downloading GTest unit test framework from a specific URL
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)
FetchContent_Declare(
myCompanyIcons
URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
)
FetchContent_MakeAvailable(googletest myCompanyIcons)
Downloading Gtest and an icon pack