The CMake\'s ExternalProject allows to define how to an external project is going to be downloaded, configured, built and installed. All whose steps are going to be performed at
If you don't want to build the project at configure-time†, but just want to download it, use FetchContent. FetchContent_Declare
uses many of the same arguments as ExternalProject_Add
, except it doesn't allow building the project.
The documentation has a great example on how to use this:
FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.8.0 ) FetchContent_GetProperties(googletest) if(NOT googletest_POPULATED) FetchContent_Populate(googletest) add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) endif()
This requires CMake 3.11 or later. For prior versions, you can download the FetchContent.cmake module from the CMake repository along with the FetchContent directory, ensuring you comply with the BSD 3-Clause license.
† Building at configure-time has some serious drawbacks. For example, users of your library can't control the build process unless you set it up very carefully. A package manager is a better solution
Since CMake 3.14 and up wards, this how you use FetchContent
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.8.0
)
FetchContent_MakeAvailable(googletest)
Then to build your test
test/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(${PROJECT_NAME}_test)
# Tests for mu_project
file(GLOB_RECURSE TEST_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/*.hpp
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)
add_executable(${PROJECT_NAME}_test ${TEST_SOURCES})
target_link_libraries(${PROJECT_NAME}_test gtest gmock_main)
# Register the tests
add_test(NAME ${PROJECT_NAME}_test
COMMAND ${PROJECT_NAME}_test)
enable_testing()
You're now good to go
ExternalProject is just a sequence of steps to perform. So you may use two instances of it:
other_project/CMakeLists.txt:
project(other_project)
include(ExternalProject)
ExternalProject_Add(<project_name> <options...>
BUILD_COMMAND "" # Disable build step.
INSTALL_COMMAND "" # Disable install step too.
)
CMakeLists.txt:
# The first external project will be built at *configure stage*
execute_process(
COMMAND ${CMAKE_COMMAND} --build . ${CMAKE_SOURCE_DIR}/other_project
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/other_project
)
CMakeLists.txt:
# The second external project will be built at *build stage*
ExternalProject_Add(<project_name> <options...>
CONFIGURE_COMMAND "" # Disable configure step. But other steps will be generated.
)
By using same <options> for both ExternalProject_Add()
calls we achieve "preemption" of both external projects created: build and follow steps of the second project will use result of configure step of the first one.
The Hunter C++ package manager does what I asked for. Hunter is based on CMake ExternalProject, but comes with a set of predefined projects. Also, it builds the dependencies during configuration and they can be accessed by find_package(... CONFIG)
. Very nice stuff!