From 2b0302498201757ca610564e4c150ceab04cd7c9 Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Thu, 28 Feb 2019 06:19:59 +0100 Subject: [PATCH 1/8] CMake: Replace ExternalProject_Add with FetchContent --- CMakeLists.txt | 3 +- cmake/OpenCensusDeps.cmake | 95 ++++++++++------------------- cmake/abseil.CMakeLists.txt | 31 ---------- cmake/googletest.CMakeLists.txt | 35 ----------- cmake/prometheus-cpp.CMakeLists.txt | 31 ---------- 5 files changed, 35 insertions(+), 160 deletions(-) delete mode 100644 cmake/abseil.CMakeLists.txt delete mode 100644 cmake/googletest.CMakeLists.txt delete mode 100644 cmake/prometheus-cpp.CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 45e76dae..53e89f31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.11) # Use ccache if it's present. find_program(CCACHE_PROGRAM ccache) @@ -30,6 +30,7 @@ include(CTest) # Defines option BUILD_TESTING. enable_testing() list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) + include(OpenCensusDeps) include(OpenCensusHelpers) diff --git a/cmake/OpenCensusDeps.cmake b/cmake/OpenCensusDeps.cmake index 469ed53d..1e36a029 100644 --- a/cmake/OpenCensusDeps.cmake +++ b/cmake/OpenCensusDeps.cmake @@ -12,10 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -if(BUILD_TESTING) - if(NOT TARGET gtest_main) - message(STATUS "Dependency: googletest (BUILD_TESTING=${BUILD_TESTING})") +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/abseil/googletest + GIT_TAG ed2fe122f8dc9aca844d724986d1d5cf5b65ea4e +) +FetchContent_Declare( + abseil + GIT_REPOSITORY https://github.com/abseil/abseil-cpp + GIT_TAG master +) +FetchContent_Declare( + prometheus + GIT_REPOSITORY https://github.com/jupp0r/prometheus-cpp + GIT_TAG master +) +FetchContent_GetProperties(googletest) +if(BUILD_TESTING) + message(STATUS "Dependency: googletest (BUILD_TESTING=${BUILD_TESTING})") + + if(NOT googletest_POPULATED) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # All the libraries in the build must use either /MD or /MT (runtime # library to link) @@ -28,74 +47,26 @@ if(BUILD_TESTING) "Use shared (DLL) run-time lib even when Google Test is built as static lib." ON) endif() - - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/googletest.CMakeLists.txt - ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt) - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) - if(result) - message(FATAL_ERROR "CMake step failed: ${result}") - endif() - execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) - if(result) - message(FATAL_ERROR "Build step failed: ${result}") - endif() - - add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL) + + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL) endif() endif() -# Load abseil second, it depends on googletest. -if(NOT TARGET absl::base) - message(STATUS "Dependency: abseil") - - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/abseil.CMakeLists.txt - ${CMAKE_BINARY_DIR}/abseil-download/CMakeLists.txt) - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/abseil-download) - if(result) - message(FATAL_ERROR "CMake step failed: ${result}") - endif() - execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/abseil-download) - if(result) - message(FATAL_ERROR "Build step failed: ${result}") - endif() - - add_subdirectory(${CMAKE_BINARY_DIR}/abseil-src - ${CMAKE_BINARY_DIR}/abseil-build EXCLUDE_FROM_ALL) +FetchContent_GetProperties(abseil) +if(NOT abseil_POPULATED) + FetchContent_Populate(abseil) + add_subdirectory(${abseil_SOURCE_DIR} ${abseil_BINARY_DIR} EXCLUDE_FROM_ALL) endif() -if(NOT TARGET prometheus-cpp::core) - message(STATUS "Dependency: prometheus-cpp") - - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/prometheus-cpp.CMakeLists.txt - ${CMAKE_BINARY_DIR}/prometheus-download/CMakeLists.txt) - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/prometheus-download) - if(result) - message(FATAL_ERROR "CMake step failed: ${result}") - endif() - execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/prometheus-download) - if(result) - message(FATAL_ERROR "Build step failed: ${result}") - endif() - +FetchContent_GetProperties(prometheus) +if(NOT prometheus_POPULATED) set(ENABLE_PUSH OFF CACHE BOOL "Build prometheus-cpp push library" FORCE) set(ENABLE_PULL OFF CACHE BOOL "Build prometheus-cpp pull library" FORCE) set(ENABLE_COMPRESSION OFF CACHE BOOL "Enable gzip compression for prometheus-cpp" FORCE) set(ENABLE_TESTING OFF CACHE BOOL "Build test for prometheus-cpp" FORCE) - add_subdirectory(${CMAKE_BINARY_DIR}/prometheus-src - ${CMAKE_BINARY_DIR}/prometheus-build EXCLUDE_FROM_ALL) + FetchContent_Populate(prometheus) + add_subdirectory(${prometheus_SOURCE_DIR} ${prometheus_BINARY_DIR} EXCLUDE_FROM_ALL) endif() diff --git a/cmake/abseil.CMakeLists.txt b/cmake/abseil.CMakeLists.txt deleted file mode 100644 index 429adbfa..00000000 --- a/cmake/abseil.CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2018, OpenCensus Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -cmake_minimum_required(VERSION 3.5) - -project(abseil-download NONE) - -include(ExternalProject) -externalproject_add(abseil_project - GIT_REPOSITORY https://github.com/abseil/abseil-cpp - GIT_TAG "master" - SOURCE_DIR "${CMAKE_BINARY_DIR}/abseil-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/abseil-build" - UPDATE_COMMAND "" - PATCH_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - LOG_DOWNLOAD ON) diff --git a/cmake/googletest.CMakeLists.txt b/cmake/googletest.CMakeLists.txt deleted file mode 100644 index 6fb9054d..00000000 --- a/cmake/googletest.CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2018, OpenCensus Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -cmake_minimum_required(VERSION 3.5) - -project(googletest-download NONE) - -include(ExternalProject) - -# TODO: Un-pin googletest version, go back to living at HEAD. It's currently -# pinned to a commit right before a build break. Un-pin it once -# https://github.com/google/googletest/issues/2136 is fixed. -externalproject_add(googletest_project - GIT_REPOSITORY https://github.com/abseil/googletest - GIT_TAG "ed2fe122f8dc9aca844d724986d1d5cf5b65ea4e" - SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" - UPDATE_COMMAND "" - PATCH_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - LOG_DOWNLOAD ON) diff --git a/cmake/prometheus-cpp.CMakeLists.txt b/cmake/prometheus-cpp.CMakeLists.txt deleted file mode 100644 index e9e60d88..00000000 --- a/cmake/prometheus-cpp.CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2018, OpenCensus Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -cmake_minimum_required(VERSION 3.5) - -project(prometheus-cpp-download NONE) - -include(ExternalProject) -externalproject_add(prometheus_cpp_project - GIT_REPOSITORY https://github.com/jupp0r/prometheus-cpp - GIT_TAG "master" - SOURCE_DIR "${CMAKE_BINARY_DIR}/prometheus-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/prometheus-build" - UPDATE_COMMAND "" - PATCH_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - LOG_DOWNLOAD ON) From d00d813e0bdfa30f4cb598c84c8da3b2a6fdfacb Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Thu, 28 Feb 2019 07:47:12 +0100 Subject: [PATCH 2/8] Update to xenial in cmake build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 13fe5a24..60dc4fe4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ matrix: env: BAZEL_OS=linux script: tools/travis/build_bazel.sh - os: linux - dist: trusty + dist: xenial sudo: false env: CMAKE_OS=linux script: tools/travis/build_cmake.sh From fda1c2f3bfbdbb30cbee071c26aa05a9cf1ed6a3 Mon Sep 17 00:00:00 2001 From: easy Date: Thu, 28 Feb 2019 18:43:48 +1100 Subject: [PATCH 3/8] format --- cmake/OpenCensusDeps.cmake | 52 ++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/cmake/OpenCensusDeps.cmake b/cmake/OpenCensusDeps.cmake index 1e36a029..0cac7250 100644 --- a/cmake/OpenCensusDeps.cmake +++ b/cmake/OpenCensusDeps.cmake @@ -14,26 +14,26 @@ include(FetchContent) -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/abseil/googletest - GIT_TAG ed2fe122f8dc9aca844d724986d1d5cf5b65ea4e -) -FetchContent_Declare( - abseil - GIT_REPOSITORY https://github.com/abseil/abseil-cpp - GIT_TAG master -) -FetchContent_Declare( - prometheus - GIT_REPOSITORY https://github.com/jupp0r/prometheus-cpp - GIT_TAG master -) +fetchcontent_declare(googletest + GIT_REPOSITORY + https://github.com/abseil/googletest + GIT_TAG + ed2fe122f8dc9aca844d724986d1d5cf5b65ea4e) +fetchcontent_declare(abseil + GIT_REPOSITORY + https://github.com/abseil/abseil-cpp + GIT_TAG + master) +fetchcontent_declare(prometheus + GIT_REPOSITORY + https://github.com/jupp0r/prometheus-cpp + GIT_TAG + master) -FetchContent_GetProperties(googletest) +fetchcontent_getproperties(googletest) if(BUILD_TESTING) message(STATUS "Dependency: googletest (BUILD_TESTING=${BUILD_TESTING})") - + if(NOT googletest_POPULATED) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # All the libraries in the build must use either /MD or /MT (runtime @@ -47,19 +47,20 @@ if(BUILD_TESTING) "Use shared (DLL) run-time lib even when Google Test is built as static lib." ON) endif() - - FetchContent_Populate(googletest) - add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL) + + fetchcontent_populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} + EXCLUDE_FROM_ALL) endif() endif() -FetchContent_GetProperties(abseil) +fetchcontent_getproperties(abseil) if(NOT abseil_POPULATED) - FetchContent_Populate(abseil) + fetchcontent_populate(abseil) add_subdirectory(${abseil_SOURCE_DIR} ${abseil_BINARY_DIR} EXCLUDE_FROM_ALL) endif() -FetchContent_GetProperties(prometheus) +fetchcontent_getproperties(prometheus) if(NOT prometheus_POPULATED) set(ENABLE_PUSH OFF CACHE BOOL "Build prometheus-cpp push library" FORCE) set(ENABLE_PULL OFF CACHE BOOL "Build prometheus-cpp pull library" FORCE) @@ -67,6 +68,7 @@ if(NOT prometheus_POPULATED) CACHE BOOL "Enable gzip compression for prometheus-cpp" FORCE) set(ENABLE_TESTING OFF CACHE BOOL "Build test for prometheus-cpp" FORCE) - FetchContent_Populate(prometheus) - add_subdirectory(${prometheus_SOURCE_DIR} ${prometheus_BINARY_DIR} EXCLUDE_FROM_ALL) + fetchcontent_populate(prometheus) + add_subdirectory(${prometheus_SOURCE_DIR} ${prometheus_BINARY_DIR} + EXCLUDE_FROM_ALL) endif() From 65aa4329e217c8130eadac5d833399c3536b48a5 Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Tue, 5 Mar 2019 19:13:59 +0100 Subject: [PATCH 4/8] added FetchContent files directly --- cmake/FetchContent.cmake | 1061 +++++++++++++++++++++++++++++++++++ cmake/FetchContent.cmake.in | 23 + 2 files changed, 1084 insertions(+) create mode 100644 cmake/FetchContent.cmake create mode 100644 cmake/FetchContent.cmake.in diff --git a/cmake/FetchContent.cmake b/cmake/FetchContent.cmake new file mode 100644 index 00000000..5716b011 --- /dev/null +++ b/cmake/FetchContent.cmake @@ -0,0 +1,1061 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FetchContent +------------------ + +.. only:: html + + .. contents:: + +Overview +^^^^^^^^ + +This module enables populating content at configure time via any method +supported by the :module:`ExternalProject` module. Whereas +:command:`ExternalProject_Add` downloads at build time, the +``FetchContent`` module makes content available immediately, allowing the +configure step to use the content in commands like :command:`add_subdirectory`, +:command:`include` or :command:`file` operations. + +Content population details would normally be defined separately from the +command that performs the actual population. This separation ensures that +all of the dependency details are defined before anything may try to use those +details to populate content. This is particularly important in more complex +project hierarchies where dependencies may be shared between multiple projects. + +The following shows a typical example of declaring content details: + +.. code-block:: cmake + + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.8.0 + ) + +For most typical cases, populating the content can then be done with a single +command like so: + +.. code-block:: cmake + + FetchContent_MakeAvailable(googletest) + +The above command not only populates the content, it also adds it to the main +build (if possible) so that the main build can use the populated project's +targets, etc. In some cases, the main project may need to have more precise +control over the population or may be required to explicitly define the +population steps (e.g. if CMake versions earlier than 3.14 need to be +supported). The typical pattern of such custom steps looks like this: + +.. code-block:: cmake + + FetchContent_GetProperties(googletest) + if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) + endif() + +Regardless of which population method is used, when using the +declare-populate pattern with a hierarchical project arrangement, projects at +higher levels in the hierarchy are able to override the population details of +content specified anywhere lower in the project hierarchy. The ability to +detect whether content has already been populated ensures that even if +multiple child projects want certain content to be available, the first one +to populate it wins. The other child project can simply make use of the +already available content instead of repeating the population for itself. +See the :ref:`Examples ` section which demonstrates +this scenario. + +The ``FetchContent`` module also supports defining and populating +content in a single call, with no check for whether the content has been +populated elsewhere in the project already. This is a more low level +operation and would not normally be the way the module is used, but it is +sometimes useful as part of implementing some higher level feature or to +populate some content in CMake's script mode. + + +Declaring Content Details +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. command:: FetchContent_Declare + + .. code-block:: cmake + + FetchContent_Declare( ...) + + The ``FetchContent_Declare()`` function records the options that describe + how to populate the specified content, but if such details have already + been recorded earlier in this project (regardless of where in the project + hierarchy), this and all later calls for the same content ```` are + ignored. This "first to record, wins" approach is what allows hierarchical + projects to have parent projects override content details of child projects. + + The content ```` can be any string without spaces, but good practice + would be to use only letters, numbers and underscores. The name will be + treated case-insensitively and it should be obvious for the content it + represents, often being the name of the child project or the value given + to its top level :command:`project` command (if it is a CMake project). + For well-known public projects, the name should generally be the official + name of the project. Choosing an unusual name makes it unlikely that other + projects needing that same content will use the same name, leading to + the content being populated multiple times. + + The ```` can be any of the download or update/patch options + that the :command:`ExternalProject_Add` command understands. The configure, + build, install and test steps are explicitly disabled and therefore options + related to them will be ignored. In most cases, ```` will + just be a couple of options defining the download method and method-specific + details like a commit tag or archive hash. For example: + + .. code-block:: cmake + + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.8.0 + ) + + FetchContent_Declare( + myCompanyIcons + URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz + URL_HASH 5588a7b18261c20068beabfb4f530b87 + ) + + FetchContent_Declare( + myCompanyCertificates + SVN_REPOSITORY svn+ssh://svn.mycompany.com/srv/svn/trunk/certs + SVN_REVISION -r12345 + ) + +Populating The Content +^^^^^^^^^^^^^^^^^^^^^^ + +For most common scenarios, population means making content available to the +main build according to previously declared details for that dependency. +There are two main patterns for populating content, one based on calling +:command:`FetchContent_GetProperties` and +:command:`FetchContent_Populate` for more precise control and the other on +calling :command:`FetchContent_MakeAvailable` for a simpler, more automated +approach. The former generally follows this canonical pattern: + +.. _`fetch-content-canonical-pattern`: + +.. code-block:: cmake + + # Check if population has already been performed + FetchContent_GetProperties() + string(TOLOWER "" lcName) + if(NOT ${lcName}_POPULATED) + # Fetch the content using previously declared details + FetchContent_Populate() + + # Set custom variables, policies, etc. + # ... + + # Bring the populated content into the build + add_subdirectory(${${lcName}_SOURCE_DIR} ${${lcName}_BINARY_DIR}) + endif() + +The above is such a common pattern that, where no custom steps are needed +between the calls to :command:`FetchContent_Populate` and +:command:`add_subdirectory`, equivalent logic can be obtained by calling +:command:`FetchContent_MakeAvailable` instead (and should be preferred where +it meets the needs of the project). + +.. command:: FetchContent_Populate + + .. code-block:: cmake + + FetchContent_Populate( ) + + In most cases, the only argument given to ``FetchContent_Populate()`` is the + ````. When used this way, the command assumes the content details have + been recorded by an earlier call to :command:`FetchContent_Declare`. The + details are stored in a global property, so they are unaffected by things + like variable or directory scope. Therefore, it doesn't matter where in the + project the details were previously declared, as long as they have been + declared before the call to ``FetchContent_Populate()``. Those saved details + are then used to construct a call to :command:`ExternalProject_Add` in a + private sub-build to perform the content population immediately. The + implementation of ``ExternalProject_Add()`` ensures that if the content has + already been populated in a previous CMake run, that content will be reused + rather than repopulating them again. For the common case where population + involves downloading content, the cost of the download is only paid once. + + An internal global property records when a particular content population + request has been processed. If ``FetchContent_Populate()`` is called more + than once for the same content name within a configure run, the second call + will halt with an error. Projects can and should check whether content + population has already been processed with the + :command:`FetchContent_GetProperties` command before calling + ``FetchContent_Populate()``. + + ``FetchContent_Populate()`` will set three variables in the scope of the + caller; ``_POPULATED``, ``_SOURCE_DIR`` and + ``_BINARY_DIR``, where ```` is the lowercased ````. + ``_POPULATED`` will always be set to ``True`` by the call. + ``_SOURCE_DIR`` is the location where the + content can be found upon return (it will have already been populated), while + ``_BINARY_DIR`` is a directory intended for use as a corresponding + build directory. The main use case for the two directory variables is to + call :command:`add_subdirectory` immediately after population, i.e.: + + .. code-block:: cmake + + FetchContent_Populate(FooBar ...) + add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR}) + + The values of the three variables can also be retrieved from anywhere in the + project hierarchy using the :command:`FetchContent_GetProperties` command. + + A number of cache variables influence the behavior of all content population + performed using details saved from a :command:`FetchContent_Declare` call: + + ``FETCHCONTENT_BASE_DIR`` + In most cases, the saved details do not specify any options relating to the + directories to use for the internal sub-build, final source and build areas. + It is generally best to leave these decisions up to the ``FetchContent`` + module to handle on the project's behalf. The ``FETCHCONTENT_BASE_DIR`` + cache variable controls the point under which all content population + directories are collected, but in most cases developers would not need to + change this. The default location is ``${CMAKE_BINARY_DIR}/_deps``, but if + developers change this value, they should aim to keep the path short and + just below the top level of the build tree to avoid running into path + length problems on Windows. + + ``FETCHCONTENT_QUIET`` + The logging output during population can be quite verbose, making the + configure stage quite noisy. This cache option (``ON`` by default) hides + all population output unless an error is encountered. If experiencing + problems with hung downloads, temporarily switching this option off may + help diagnose which content population is causing the issue. + + ``FETCHCONTENT_FULLY_DISCONNECTED`` + When this option is enabled, no attempt is made to download or update + any content. It is assumed that all content has already been populated in + a previous run or the source directories have been pointed at existing + contents the developer has provided manually (using options described + further below). When the developer knows that no changes have been made to + any content details, turning this option ``ON`` can significantly speed up + the configure stage. It is ``OFF`` by default. + + ``FETCHCONTENT_UPDATES_DISCONNECTED`` + This is a less severe download/update control compared to + ``FETCHCONTENT_FULLY_DISCONNECTED``. Instead of bypassing all download and + update logic, the ``FETCHCONTENT_UPDATES_DISCONNECTED`` only disables the + update stage. Therefore, if content has not been downloaded previously, + it will still be downloaded when this option is enabled. This can speed up + the configure stage, but not as much as + ``FETCHCONTENT_FULLY_DISCONNECTED``. It is ``OFF`` by default. + + In addition to the above cache variables, the following cache variables are + also defined for each content name (```` is the uppercased value of + ````): + + ``FETCHCONTENT_SOURCE_DIR_`` + If this is set, no download or update steps are performed for the specified + content and the ``_SOURCE_DIR`` variable returned to the caller is + pointed at this location. This gives developers a way to have a separate + checkout of the content that they can modify freely without interference + from the build. The build simply uses that existing source, but it still + defines ``_BINARY_DIR`` to point inside its own build area. + Developers are strongly encouraged to use this mechanism rather than + editing the sources populated in the default location, as changes to + sources in the default location can be lost when content population details + are changed by the project. + + ``FETCHCONTENT_UPDATES_DISCONNECTED_`` + This is the per-content equivalent of + ``FETCHCONTENT_UPDATES_DISCONNECTED``. If the global option or this option + is ``ON``, then updates will be disabled for the named content. + Disabling updates for individual content can be useful for content whose + details rarely change, while still leaving other frequently changing + content with updates enabled. + + + The ``FetchContent_Populate()`` command also supports a syntax allowing the + content details to be specified directly rather than using any saved + details. This is more low-level and use of this form is generally to be + avoided in favour of using saved content details as outlined above. + Nevertheless, in certain situations it can be useful to invoke the content + population as an isolated operation (typically as part of implementing some + other higher level feature or when using CMake in script mode): + + .. code-block:: cmake + + FetchContent_Populate( + [QUIET] + [SUBBUILD_DIR ] + [SOURCE_DIR ] + [BINARY_DIR ] + ... + ) + + This form has a number of key differences to that where only ```` is + provided: + + - All required population details are assumed to have been provided directly + in the call to ``FetchContent_Populate()``. Any saved details for + ```` are ignored. + - No check is made for whether content for ```` has already been + populated. + - No global property is set to record that the population has occurred. + - No global properties record the source or binary directories used for the + populated content. + - The ``FETCHCONTENT_FULLY_DISCONNECTED`` and + ``FETCHCONTENT_UPDATES_DISCONNECTED`` cache variables are ignored. + + The ``_SOURCE_DIR`` and ``_BINARY_DIR`` variables are still + returned to the caller, but since these locations are not stored as global + properties when this form is used, they are only available to the calling + scope and below rather than the entire project hierarchy. No + ``_POPULATED`` variable is set in the caller's scope with this form. + + The supported options for ``FetchContent_Populate()`` are the same as those + for :command:`FetchContent_Declare()`. Those few options shown just + above are either specific to ``FetchContent_Populate()`` or their behavior is + slightly modified from how :command:`ExternalProject_Add` treats them. + + ``QUIET`` + The ``QUIET`` option can be given to hide the output associated with + populating the specified content. If the population fails, the output will + be shown regardless of whether this option was given or not so that the + cause of the failure can be diagnosed. The global ``FETCHCONTENT_QUIET`` + cache variable has no effect on ``FetchContent_Populate()`` calls where the + content details are provided directly. + + ``SUBBUILD_DIR`` + The ``SUBBUILD_DIR`` argument can be provided to change the location of the + sub-build created to perform the population. The default value is + ``${CMAKE_CURRENT_BINARY_DIR}/-subbuild`` and it would be unusual + to need to override this default. If a relative path is specified, it will + be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`. + + ``SOURCE_DIR``, ``BINARY_DIR`` + The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments are supported by + :command:`ExternalProject_Add`, but different default values are used by + ``FetchContent_Populate()``. ``SOURCE_DIR`` defaults to + ``${CMAKE_CURRENT_BINARY_DIR}/-src`` and ``BINARY_DIR`` defaults to + ``${CMAKE_CURRENT_BINARY_DIR}/-build``. If a relative path is + specified, it will be interpreted as relative to + :variable:`CMAKE_CURRENT_BINARY_DIR`. + + In addition to the above explicit options, any other unrecognized options are + passed through unmodified to :command:`ExternalProject_Add` to perform the + download, patch and update steps. The following options are explicitly + prohibited (they are disabled by the ``FetchContent_Populate()`` command): + + - ``CONFIGURE_COMMAND`` + - ``BUILD_COMMAND`` + - ``INSTALL_COMMAND`` + - ``TEST_COMMAND`` + + If using ``FetchContent_Populate()`` within CMake's script mode, be aware + that the implementation sets up a sub-build which therefore requires a CMake + generator and build tool to be available. If these cannot be found by + default, then the :variable:`CMAKE_GENERATOR` and/or + :variable:`CMAKE_MAKE_PROGRAM` variables will need to be set appropriately + on the command line invoking the script. + + +.. command:: FetchContent_GetProperties + + When using saved content details, a call to :command:`FetchContent_Populate` + records information in global properties which can be queried at any time. + This information includes the source and binary directories associated with + the content and also whether or not the content population has been processed + during the current configure run. + + .. code-block:: cmake + + FetchContent_GetProperties( + [SOURCE_DIR ] + [BINARY_DIR ] + [POPULATED ] + ) + + The ``SOURCE_DIR``, ``BINARY_DIR`` and ``POPULATED`` options can be used to + specify which properties should be retrieved. Each option accepts a value + which is the name of the variable in which to store that property. Most of + the time though, only ```` is given, in which case the call will then + set the same variables as a call to + :command:`FetchContent_Populate(name) `. This allows + the following canonical pattern to be used, which ensures that the relevant + variables will always be defined regardless of whether or not the population + has been performed elsewhere in the project already: + + .. code-block:: cmake + + FetchContent_GetProperties(foobar) + if(NOT foobar_POPULATED) + FetchContent_Populate(foobar) + ... + endif() + + The above pattern allows other parts of the overall project hierarchy to + re-use the same content and ensure that it is only populated once. + + +.. command:: FetchContent_MakeAvailable + + .. code-block:: cmake + + FetchContent_MakeAvailable( [...] ) + + This command implements the common pattern typically needed for most + dependencies. It iterates over each of the named dependencies in turn + and for each one it loosely follows the same + :ref:`canonical pattern ` as + presented at the beginning of this section. One small difference to + that pattern is that it will only call :command:`add_subdirectory` on the + populated content if there is a ``CMakeLists.txt`` file in its top level + source directory. This allows the command to be used for dependencies + that make downloaded content available at a known location but which do + not need or support being added directly to the build. + + +.. _`fetch-content-examples`: + +Examples +^^^^^^^^ + +This first fairly straightforward example ensures that some popular testing +frameworks are available to the main build: + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.8.0 + ) + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v2.5.0 + ) + + # After the following call, the CMake targets defined by googletest and + # Catch2 will be defined and available to the rest of the build + FetchContent_MakeAvailable(googletest Catch2) + + +In more complex project hierarchies, the dependency relationships can be more +complicated. Consider a hierarchy where ``projA`` is the top level project and +it depends directly on projects ``projB`` and ``projC``. Both ``projB`` and +``projC`` can be built standalone and they also both depend on another project +``projD``. ``projB`` additionally depends on ``projE``. This example assumes +that all five projects are available on a company git server. The +``CMakeLists.txt`` of each project might have sections like the following: + +*projA*: + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + projB + GIT_REPOSITORY git@mycompany.com:git/projB.git + GIT_TAG 4a89dc7e24ff212a7b5167bef7ab079d + ) + FetchContent_Declare( + projC + GIT_REPOSITORY git@mycompany.com:git/projC.git + GIT_TAG 4ad4016bd1d8d5412d135cf8ceea1bb9 + ) + FetchContent_Declare( + projD + GIT_REPOSITORY git@mycompany.com:git/projD.git + GIT_TAG origin/integrationBranch + ) + FetchContent_Declare( + projE + GIT_REPOSITORY git@mycompany.com:git/projE.git + GIT_TAG origin/release/2.3-rc1 + ) + + # Order is important, see notes in the discussion further below + FetchContent_MakeAvailable(projD projB projC) + +*projB*: + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + projD + GIT_REPOSITORY git@mycompany.com:git/projD.git + GIT_TAG 20b415f9034bbd2a2e8216e9a5c9e632 + ) + FetchContent_Declare( + projE + GIT_REPOSITORY git@mycompany.com:git/projE.git + GIT_TAG 68e20f674a48be38d60e129f600faf7d + ) + + FetchContent_MakeAvailable(projD projE) + +*projC*: + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + projD + GIT_REPOSITORY git@mycompany.com:git/projD.git + GIT_TAG 7d9a17ad2c962aa13e2fbb8043fb6b8a + ) + + # This particular version of projD requires workarounds + FetchContent_GetProperties(projD) + if(NOT projd_POPULATED) + FetchContent_Populate(projD) + + # Copy an additional/replacement file into the populated source + file(COPY someFile.c DESTINATION ${projd_SOURCE_DIR}/src) + + add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR}) + endif() + +A few key points should be noted in the above: + +- ``projB`` and ``projC`` define different content details for ``projD``, + but ``projA`` also defines a set of content details for ``projD``. + Because ``projA`` will define them first, the details from ``projB`` and + ``projC`` will not be used. The override details defined by ``projA`` + are not required to match either of those from ``projB`` or ``projC``, but + it is up to the higher level project to ensure that the details it does + define still make sense for the child projects. +- In the ``projA`` call to :command:`FetchContent_MakeAvailable`, ``projD`` + is listed ahead of ``projB`` and ``projC`` to ensure that ``projA`` is in + control of how ``projD`` is populated. +- While ``projA`` defines content details for ``projE``, it does not need + to explicitly call ``FetchContent_MakeAvailable(projE)`` or + ``FetchContent_Populate(projD)`` itself. Instead, it leaves that to the + child ``projB``. For higher level projects, it is often enough to just + define the override content details and leave the actual population to the + child projects. This saves repeating the same thing at each level of the + project hierarchy unnecessarily. + + +Projects don't always need to add the populated content to the build. +Sometimes the project just wants to make the downloaded content available at +a predictable location. The next example ensures that a set of standard +company toolchain files (and potentially even the toolchain binaries +themselves) is available early enough to be used for that same build. + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.14) + + include(FetchContent) + FetchContent_Declare( + mycom_toolchains + URL https://intranet.mycompany.com//toolchains_1.3.2.tar.gz + ) + FetchContent_MakeAvailable(mycom_toolchains) + + project(CrossCompileExample) + +The project could be configured to use one of the downloaded toolchains like +so: + +.. code-block:: shell + + cmake -DCMAKE_TOOLCHAIN_FILE=_deps/mycom_toolchains-src/toolchain_arm.cmake /path/to/src + +When CMake processes the ``CMakeLists.txt`` file, it will download and unpack +the tarball into ``_deps/mycompany_toolchains-src`` relative to the build +directory. The :variable:`CMAKE_TOOLCHAIN_FILE` variable is not used until +the :command:`project` command is reached, at which point CMake looks for the +named toolchain file relative to the build directory. Because the tarball has +already been downloaded and unpacked by then, the toolchain file will be in +place, even the very first time that ``cmake`` is run in the build directory. + +Lastly, the following example demonstrates how one might download and unpack a +firmware tarball using CMake's :manual:`script mode `. The call to +:command:`FetchContent_Populate` specifies all the content details and the +unpacked firmware will be placed in a ``firmware`` directory below the +current working directory. + +*getFirmware.cmake*: + +.. code-block:: cmake + + # NOTE: Intended to be run in script mode with cmake -P + include(FetchContent) + FetchContent_Populate( + firmware + URL https://mycompany.com/assets/firmware-1.23-arm.tar.gz + URL_HASH MD5=68247684da89b608d466253762b0ff11 + SOURCE_DIR firmware + ) + +#]=======================================================================] + + +set(__FetchContent_privateDir "${CMAKE_CURRENT_LIST_DIR}/FetchContent") + +#======================================================================= +# Recording and retrieving content details for later population +#======================================================================= + +# Internal use, projects must not call this directly. It is +# intended for use by FetchContent_Declare() only. +# +# Sets a content-specific global property (not meant for use +# outside of functions defined here in this file) which can later +# be retrieved using __FetchContent_getSavedDetails() with just the +# same content name. If there is already a value stored in the +# property, it is left unchanged and this call has no effect. +# This allows parent projects to define the content details, +# overriding anything a child project may try to set (properties +# are not cached between runs, so the first thing to set it in a +# build will be in control). +function(__FetchContent_declareDetails contentName) + + string(TOLOWER ${contentName} contentNameLower) + set(propertyName "_FetchContent_${contentNameLower}_savedDetails") + get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED) + if(NOT alreadyDefined) + define_property(GLOBAL PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} ${ARGN}) + endif() + +endfunction() + + +# Internal use, projects must not call this directly. It is +# intended for use by the FetchContent_Declare() function. +# +# Retrieves details saved for the specified content in an +# earlier call to __FetchContent_declareDetails(). +function(__FetchContent_getSavedDetails contentName outVar) + + string(TOLOWER ${contentName} contentNameLower) + set(propertyName "_FetchContent_${contentNameLower}_savedDetails") + get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED) + if(NOT alreadyDefined) + message(FATAL_ERROR "No content details recorded for ${contentName}") + endif() + get_property(propertyValue GLOBAL PROPERTY ${propertyName}) + set(${outVar} "${propertyValue}" PARENT_SCOPE) + +endfunction() + + +# Saves population details of the content, sets defaults for the +# SOURCE_DIR and BUILD_DIR. +function(FetchContent_Declare contentName) + + set(options "") + set(oneValueArgs SVN_REPOSITORY) + set(multiValueArgs "") + + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + unset(srcDirSuffix) + unset(svnRepoArgs) + if(ARG_SVN_REPOSITORY) + # Add a hash of the svn repository URL to the source dir. This works + # around the problem where if the URL changes, the download would + # fail because it tries to checkout/update rather than switch the + # old URL to the new one. We limit the hash to the first 7 characters + # so that the source path doesn't get overly long (which can be a + # problem on windows due to path length limits). + string(SHA1 urlSHA ${ARG_SVN_REPOSITORY}) + string(SUBSTRING ${urlSHA} 0 7 urlSHA) + set(srcDirSuffix "-${urlSHA}") + set(svnRepoArgs SVN_REPOSITORY ${ARG_SVN_REPOSITORY}) + endif() + + string(TOLOWER ${contentName} contentNameLower) + __FetchContent_declareDetails( + ${contentNameLower} + SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src${srcDirSuffix}" + BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build" + ${svnRepoArgs} + # List these last so they can override things we set above + ${ARG_UNPARSED_ARGUMENTS} + ) + +endfunction() + + +#======================================================================= +# Set/get whether the specified content has been populated yet. +# The setter also records the source and binary dirs used. +#======================================================================= + +# Internal use, projects must not call this directly. It is +# intended for use by the FetchContent_Populate() function to +# record when FetchContent_Populate() is called for a particular +# content name. +function(__FetchContent_setPopulated contentName sourceDir binaryDir) + + string(TOLOWER ${contentName} contentNameLower) + set(prefix "_FetchContent_${contentNameLower}") + + set(propertyName "${prefix}_sourceDir") + define_property(GLOBAL PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} ${sourceDir}) + + set(propertyName "${prefix}_binaryDir") + define_property(GLOBAL PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} ${binaryDir}) + + set(propertyName "${prefix}_populated") + define_property(GLOBAL PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} True) + +endfunction() + + +# Set variables in the calling scope for any of the retrievable +# properties. If no specific properties are requested, variables +# will be set for all retrievable properties. +# +# This function is intended to also be used by projects as the canonical +# way to detect whether they should call FetchContent_Populate() +# and pull the populated source into the build with add_subdirectory(), +# if they are using the populated content in that way. +function(FetchContent_GetProperties contentName) + + string(TOLOWER ${contentName} contentNameLower) + + set(options "") + set(oneValueArgs SOURCE_DIR BINARY_DIR POPULATED) + set(multiValueArgs "") + + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT ARG_SOURCE_DIR AND + NOT ARG_BINARY_DIR AND + NOT ARG_POPULATED) + # No specific properties requested, provide them all + set(ARG_SOURCE_DIR ${contentNameLower}_SOURCE_DIR) + set(ARG_BINARY_DIR ${contentNameLower}_BINARY_DIR) + set(ARG_POPULATED ${contentNameLower}_POPULATED) + endif() + + set(prefix "_FetchContent_${contentNameLower}") + + if(ARG_SOURCE_DIR) + set(propertyName "${prefix}_sourceDir") + get_property(value GLOBAL PROPERTY ${propertyName}) + if(value) + set(${ARG_SOURCE_DIR} ${value} PARENT_SCOPE) + endif() + endif() + + if(ARG_BINARY_DIR) + set(propertyName "${prefix}_binaryDir") + get_property(value GLOBAL PROPERTY ${propertyName}) + if(value) + set(${ARG_BINARY_DIR} ${value} PARENT_SCOPE) + endif() + endif() + + if(ARG_POPULATED) + set(propertyName "${prefix}_populated") + get_property(value GLOBAL PROPERTY ${propertyName} DEFINED) + set(${ARG_POPULATED} ${value} PARENT_SCOPE) + endif() + +endfunction() + + +#======================================================================= +# Performing the population +#======================================================================= + +# The value of contentName will always have been lowercased by the caller. +# All other arguments are assumed to be options that are understood by +# ExternalProject_Add(), except for QUIET and SUBBUILD_DIR. +function(__FetchContent_directPopulate contentName) + + set(options + QUIET + ) + set(oneValueArgs + SUBBUILD_DIR + SOURCE_DIR + BINARY_DIR + # Prevent the following from being passed through + CONFIGURE_COMMAND + BUILD_COMMAND + INSTALL_COMMAND + TEST_COMMAND + # We force both of these to be ON since we are always executing serially + # and we want all steps to have access to the terminal in case they + # need input from the command line (e.g. ask for a private key password) + # or they want to provide timely progress. We silently absorb and + # discard these if they are set by the caller. + USES_TERMINAL_DOWNLOAD + USES_TERMINAL_UPDATE + ) + set(multiValueArgs "") + + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT ARG_SUBBUILD_DIR) + message(FATAL_ERROR "Internal error: SUBBUILD_DIR not set") + elseif(NOT IS_ABSOLUTE "${ARG_SUBBUILD_DIR}") + set(ARG_SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SUBBUILD_DIR}") + endif() + + if(NOT ARG_SOURCE_DIR) + message(FATAL_ERROR "Internal error: SOURCE_DIR not set") + elseif(NOT IS_ABSOLUTE "${ARG_SOURCE_DIR}") + set(ARG_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SOURCE_DIR}") + endif() + + if(NOT ARG_BINARY_DIR) + message(FATAL_ERROR "Internal error: BINARY_DIR not set") + elseif(NOT IS_ABSOLUTE "${ARG_BINARY_DIR}") + set(ARG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_BINARY_DIR}") + endif() + + # Ensure the caller can know where to find the source and build directories + # with some convenient variables. Doing this here ensures the caller sees + # the correct result in the case where the default values are overridden by + # the content details set by the project. + set(${contentName}_SOURCE_DIR "${ARG_SOURCE_DIR}" PARENT_SCOPE) + set(${contentName}_BINARY_DIR "${ARG_BINARY_DIR}" PARENT_SCOPE) + + # The unparsed arguments may contain spaces, so build up ARG_EXTRA + # in such a way that it correctly substitutes into the generated + # CMakeLists.txt file with each argument quoted. + unset(ARG_EXTRA) + foreach(arg IN LISTS ARG_UNPARSED_ARGUMENTS) + set(ARG_EXTRA "${ARG_EXTRA} \"${arg}\"") + endforeach() + + # Hide output if requested, but save it to a variable in case there's an + # error so we can show the output upon failure. When not quiet, don't + # capture the output to a variable because the user may want to see the + # output as it happens (e.g. progress during long downloads). Combine both + # stdout and stderr in the one capture variable so the output stays in order. + if (ARG_QUIET) + set(outputOptions + OUTPUT_VARIABLE capturedOutput + ERROR_VARIABLE capturedOutput + ) + else() + set(capturedOutput) + set(outputOptions) + message(STATUS "Populating ${contentName}") + endif() + + if(CMAKE_GENERATOR) + set(generatorOpts "-G${CMAKE_GENERATOR}") + if(CMAKE_GENERATOR_PLATFORM) + list(APPEND generatorOpts "-A${CMAKE_GENERATOR_PLATFORM}") + endif() + if(CMAKE_GENERATOR_TOOLSET) + list(APPEND generatorOpts "-T${CMAKE_GENERATOR_TOOLSET}") + endif() + + if(CMAKE_MAKE_PROGRAM) + list(APPEND generatorOpts "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}") + endif() + + else() + # Likely we've been invoked via CMake's script mode where no + # generator is set (and hence CMAKE_MAKE_PROGRAM could not be + # trusted even if provided). We will have to rely on being + # able to find the default generator and build tool. + unset(generatorOpts) + endif() + + # Create and build a separate CMake project to carry out the population. + # If we've already previously done these steps, they will not cause + # anything to be updated, so extra rebuilds of the project won't occur. + # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project + # has this set to something not findable on the PATH. + configure_file("${__FetchContent_privateDir}/CMakeLists.cmake.in" + "${ARG_SUBBUILD_DIR}/CMakeLists.txt") + execute_process( + COMMAND ${CMAKE_COMMAND} ${generatorOpts} . + RESULT_VARIABLE result + ${outputOptions} + WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" + ) + if(result) + if(capturedOutput) + message("${capturedOutput}") + endif() + message(FATAL_ERROR "CMake step for ${contentName} failed: ${result}") + endif() + execute_process( + COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + ${outputOptions} + WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" + ) + if(result) + if(capturedOutput) + message("${capturedOutput}") + endif() + message(FATAL_ERROR "Build step for ${contentName} failed: ${result}") + endif() + +endfunction() + + +option(FETCHCONTENT_FULLY_DISCONNECTED "Disables all attempts to download or update content and assumes source dirs already exist") +option(FETCHCONTENT_UPDATES_DISCONNECTED "Enables UPDATE_DISCONNECTED behavior for all content population") +option(FETCHCONTENT_QUIET "Enables QUIET option for all content population" ON) +set(FETCHCONTENT_BASE_DIR "${CMAKE_BINARY_DIR}/_deps" CACHE PATH "Directory under which to collect all populated content") + +# Populate the specified content using details stored from +# an earlier call to FetchContent_Declare(). +function(FetchContent_Populate contentName) + + if(NOT contentName) + message(FATAL_ERROR "Empty contentName not allowed for FetchContent_Populate()") + endif() + + string(TOLOWER ${contentName} contentNameLower) + + if(ARGN) + # This is the direct population form with details fully specified + # as part of the call, so we already have everything we need + __FetchContent_directPopulate( + ${contentNameLower} + SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-subbuild" + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-build" + ${ARGN} # Could override any of the above ..._DIR variables + ) + + # Pass source and binary dir variables back to the caller + set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE) + set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE) + + # Don't set global properties, or record that we did this population, since + # this was a direct call outside of the normal declared details form. + # We only want to save values in the global properties for content that + # honours the hierarchical details mechanism so that projects are not + # robbed of the ability to override details set in nested projects. + return() + endif() + + # No details provided, so assume they were saved from an earlier call + # to FetchContent_Declare(). Do a check that we haven't already + # populated this content before in case the caller forgot to check. + FetchContent_GetProperties(${contentName}) + if(${contentNameLower}_POPULATED) + message(FATAL_ERROR "Content ${contentName} already populated in ${${contentNameLower}_SOURCE_DIR}") + endif() + + string(TOUPPER ${contentName} contentNameUpper) + set(FETCHCONTENT_SOURCE_DIR_${contentNameUpper} + "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}" + CACHE PATH "When not empty, overrides where to find pre-populated content for ${contentName}") + + if(FETCHCONTENT_SOURCE_DIR_${contentNameUpper}) + # The source directory has been explicitly provided in the cache, + # so no population is required + set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}") + set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build") + + elseif(FETCHCONTENT_FULLY_DISCONNECTED) + # Bypass population and assume source is already there from a previous run + set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src") + set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build") + + else() + # Support both a global "disconnect all updates" and a per-content + # update test (either one being set disables updates for this content). + option(FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper} + "Enables UPDATE_DISCONNECTED behavior just for population of ${contentName}") + if(FETCHCONTENT_UPDATES_DISCONNECTED OR + FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper}) + set(disconnectUpdates True) + else() + set(disconnectUpdates False) + endif() + + if(FETCHCONTENT_QUIET) + set(quietFlag QUIET) + else() + unset(quietFlag) + endif() + + __FetchContent_getSavedDetails(${contentName} contentDetails) + if("${contentDetails}" STREQUAL "") + message(FATAL_ERROR "No details have been set for content: ${contentName}") + endif() + + __FetchContent_directPopulate( + ${contentNameLower} + ${quietFlag} + UPDATE_DISCONNECTED ${disconnectUpdates} + SUBBUILD_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-subbuild" + SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src" + BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build" + # Put the saved details last so they can override any of the + # the options we set above (this can include SOURCE_DIR or + # BUILD_DIR) + ${contentDetails} + ) + endif() + + __FetchContent_setPopulated( + ${contentName} + ${${contentNameLower}_SOURCE_DIR} + ${${contentNameLower}_BINARY_DIR} + ) + + # Pass variables back to the caller. The variables passed back here + # must match what FetchContent_GetProperties() sets when it is called + # with just the content name. + set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE) + set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE) + set(${contentNameLower}_POPULATED True PARENT_SCOPE) + +endfunction() + +# Arguments are assumed to be the names of dependencies that have been +# declared previously and should be populated. It is not an error if +# any of them have already been populated (they will just be skipped in +# that case). The command is implemented as a macro so that the variables +# defined by the FetchContent_GetProperties() and FetchContent_Populate() +# calls will be available to the caller. +macro(FetchContent_MakeAvailable) + + foreach(contentName IN ITEMS ${ARGV}) + string(TOLOWER ${contentName} contentNameLower) + FetchContent_GetProperties(${contentName}) + if(NOT ${contentNameLower}_POPULATED) + FetchContent_Populate(${contentName}) + + # Only try to call add_subdirectory() if the populated content + # can be treated that way. Protecting the call with the check + # allows this function to be used for projects that just want + # to ensure the content exists, such as to provide content at + # a known location. + if(EXISTS ${${contentNameLower}_SOURCE_DIR}/CMakeLists.txt) + add_subdirectory(${${contentNameLower}_SOURCE_DIR} + ${${contentNameLower}_BINARY_DIR}) + endif() + endif() + endforeach() + +endmacro() diff --git a/cmake/FetchContent.cmake.in b/cmake/FetchContent.cmake.in new file mode 100644 index 00000000..0095b111 --- /dev/null +++ b/cmake/FetchContent.cmake.in @@ -0,0 +1,23 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION ${CMAKE_VERSION}) + +# We name the project and the target for the ExternalProject_Add() call +# to something that will highlight to the user what we are working on if +# something goes wrong and an error message is produced. + +project(${contentName}-populate NONE) + +include(ExternalProject) +ExternalProject_Add(${contentName}-populate + ${ARG_EXTRA} + SOURCE_DIR "${ARG_SOURCE_DIR}" + BINARY_DIR "${ARG_BINARY_DIR}" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + USES_TERMINAL_DOWNLOAD YES + USES_TERMINAL_UPDATE YES +) From db4f456714fdef89c09c6b17ef5c876ba5e3a562 Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Tue, 5 Mar 2019 19:19:36 +0100 Subject: [PATCH 5/8] Revert "Update to xenial in cmake build" This reverts commit 3964a1b99110f308153ae030849ce85238937e2a. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 60dc4fe4..13fe5a24 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ matrix: env: BAZEL_OS=linux script: tools/travis/build_bazel.sh - os: linux - dist: xenial + dist: trusty sudo: false env: CMAKE_OS=linux script: tools/travis/build_cmake.sh From 86b05133e17f092c117ff5fbd1476eee34823ac2 Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Tue, 5 Mar 2019 19:29:00 +0100 Subject: [PATCH 6/8] file include --- cmake/OpenCensusDeps.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/OpenCensusDeps.cmake b/cmake/OpenCensusDeps.cmake index 0cac7250..f0dae6bd 100644 --- a/cmake/OpenCensusDeps.cmake +++ b/cmake/OpenCensusDeps.cmake @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -include(FetchContent) +include(FetchContent.cmake) fetchcontent_declare(googletest GIT_REPOSITORY From a53c414b855d9c5299e207e917aab0ad2d3bd2cd Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Tue, 5 Mar 2019 19:36:51 +0100 Subject: [PATCH 7/8] fix --- CMakeLists.txt | 2 +- cmake/OpenCensusDeps.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53e89f31..f4a1bd79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.11) +cmake_minimum_required(VERSION 3.5) # Use ccache if it's present. find_program(CCACHE_PROGRAM ccache) diff --git a/cmake/OpenCensusDeps.cmake b/cmake/OpenCensusDeps.cmake index f0dae6bd..0cac7250 100644 --- a/cmake/OpenCensusDeps.cmake +++ b/cmake/OpenCensusDeps.cmake @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -include(FetchContent.cmake) +include(FetchContent) fetchcontent_declare(googletest GIT_REPOSITORY From 9db3523f4aa2b21cab8c27d4b243ae165fade8bb Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Tue, 5 Mar 2019 19:45:19 +0100 Subject: [PATCH 8/8] move to folder --- cmake/{FetchContent.cmake.in => FetchContent/CMakeLists.cmake.in} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmake/{FetchContent.cmake.in => FetchContent/CMakeLists.cmake.in} (100%) diff --git a/cmake/FetchContent.cmake.in b/cmake/FetchContent/CMakeLists.cmake.in similarity index 100% rename from cmake/FetchContent.cmake.in rename to cmake/FetchContent/CMakeLists.cmake.in