5.including_other_project
Including other projects
Git submodule in CMake
Adding a Git Submodule
To add a Git submodule (for example, to the extern/ directory) from a repository hosted on the same service (e.g., GitHub, GitLab, BitBucket):
gitbook $ git submodule add ../../owner/repo.git extern/repo
- Relative path: Keeps the access method (SSH or HTTPS) consistent with the parent repository.
- Inside the submodule: It behaves like a normal Git repo.
- In the parent repository: You can update the submodule by changing the current commit pointer.
Typical Submodule Drawbacks
Users need to know Git submodule commands to initialize and update the submodule, or they must clone the repository with the --recursive option. However, CMake can automate this process.
CMake Submodule Handling
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
# Update submodules as needed
option(GIT_SUBMODULE "Check submodules during build" ON)
if(GIT_SUBMODULE)
message(STATUS "Submodule update")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
endif()
endif()
endif()
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/extern/repo/CMakeLists.txt")
message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
-
Checking for Git:
- The script checks if Git is installed using CMake’s
FindGit.cmake.
- The script checks if Git is installed using CMake’s
-
Submodule Update Logic:
- Adds an option
GIT_SUBMODULE(defaultON) to let developers disable submodule checks if necessary. - Runs the Git command to initialize and update the submodules:
git submodule update --init --recursive - Displays a clear error message if the update fails.
- Adds an option
-
Verification:
- Verifies that the submodule files (like
CMakeLists.txt) exist, ensuring the submodule is correctly initialized.
- Verifies that the submodule files (like
Including Submodules in the Build
- Use
add_subdirectory(extern/repo)to include submodules that provide CMake support. - For header-only projects: You can build an interface library target yourself if needed.
- Using
find_package: If supported, you can usefind_packageto locate the submodule, especially if you have aFind*.cmakefile.
Enhancing CMake Support
- You can append directories to
CMAKE_MODULE_PATHto add helper CMake files (e.g., addingpybind11's improvedFindPython*.cmakefiles).
Git Version Numbe
To capture the current Git commit hash as the version number, use the following CMake code:
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE PACKAGE_GIT_VERSION
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
This retrieves the short version of the Git commit hash and stores it in PACKAGE_GIT_VERSION, which can be used elsewhere in the build system.
FetchContent in CMake (3.11+)
FetchContent is a CMake module introduced in version 3.11 that allows downloading and integrating external dependencies as part of the configure step, instead of the build step. It consolidates functionality that was previously implemented in various third-party modules.
Key Features
FetchContent_Declare(MyName): Declares a dependency by specifying a URL, Git repository, or other source.FetchContent_GetProperties(MyName): Retrieves properties related to the dependency, such as its source and binary directories.FetchContent_Populate(MyName): Downloads and prepares the content if not already available.add_subdirectory("${MyName_SOURCE_DIR}" "${MyName_BINARY_DIR}"): Adds the downloaded content to your project.
Example: Downloading Catch2
To download and use Catch2 in your project with CMake 3.14+:
FetchContent_Declare(
catch
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.13.6
)
# CMake 3.14+ provides a simplified syntax
FetchContent_MakeAvailable(catch)
CMake 3.11-3.13 Method:
If you're using CMake 3.11-3.13, the following is the classic way to fetch and integrate a dependency:
FetchContent_GetProperties(catch)
if(NOT catch_POPULATED)
FetchContent_Populate(catch)
add_subdirectory(${catch_SOURCE_DIR} ${catch_BINARY_DIR})
endif()
Macro for CMake 3.11-3.13:
To enable the simplified FetchContent_MakeAvailable() syntax in older versions (pre-3.14), you can create a macro:
if(${CMAKE_VERSION} VERSION_LESS 3.14)
macro(FetchContent_MakeAvailable NAME)
FetchContent_GetProperties(${NAME})
if(NOT ${NAME}_POPULATED)
FetchContent_Populate(${NAME})
add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR})
endif()
endmacro()
endif()
This macro replicates the CMake 3.14+ functionality, allowing you to use the modern syntax in older versions.
More Information
- For more details, refer to the official CMake FetchContent documentation.
- For examples, see this FetchContent example.