Skip to content

Instantly share code, notes, and snippets.

@scivision
Last active September 4, 2023 17:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scivision/2ac78749008520fc5a5f03d727a79886 to your computer and use it in GitHub Desktop.
Save scivision/2ac78749008520fc5a5f03d727a79886 to your computer and use it in GitHub Desktop.
CMake FetchContent specific source files from other project with current project
cmake_minimum_required(VERSION 3.14...3.27)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "please use out-of-source build
cmake -Bbuild")
endif()
project(otherSource LANGUAGES C)
set(NRF_SDK_NAME nRF5_SDK_17.1.0_ddde560)
if(NOT DEFINED FETCHCONTENT_QUIET)
set(FETCHCONTENT_QUIET FALSE)
endif()
set(CMAKE_TLS_VERIFY true)
# placeholders
set(BOARD_TARGET NRF52805_XXAA)
include(FetchContent)
# https://www.nordicsemi.com/Products/Development-software/nRF5-SDK/Download
set(NRF_SDK_DOWNLOAD_URL
https://www.nordicsemi.com/-/media/Software-and-other-downloads/SDKs/nRF5/Binaries/${NRF_SDK_NAME}.zip
)
# setting hash avoid redownload of big archive on each CMake configure,
# and provides a way to check if the archive is corrupted
set(NRF_SDK_DOWNLOAD_SHA256 5bfe38e744c39fd7f30e10077ba12df306ef91f368894795d6a3e7a62dc68061)
FetchContent_Declare(nrf5_sdk
URL ${NRF_SDK_DOWNLOAD_URL}
URL_HASH SHA256=${NRF_SDK_DOWNLOAD_SHA256}
TLS_VERIFY ${CMAKE_TLS_VERIFY}
INACTIVITY_TIMEOUT 60
)
FetchContent_Populate(nrf5_sdk)
# auto-ignore build dir
file(GENERATE OUTPUT .gitignore CONTENT "*")
# main program
add_library(system_nrf54 ${nrf5_sdk_SOURCE_DIR}/modules/nrfx/mdk/system_nrf52.c)
target_include_directories(system_nrf54 PRIVATE ${nrf5_sdk_SOURCE_DIR}/components/toolchain/cmsis/include)
target_compile_definitions(system_nrf54 PRIVATE ${BOARD_TARGET})
@nersoz-impinj
Copy link

nersoz-impinj commented Sep 3, 2023

Thank you - nice!

I like the additions. This a big help.
Also, nice to leave a trail of bread crumbs for the search engine, and then find a complete answer like this.

@craigscott-crascit
Copy link

A few comments about this:

  • FETCHCONTENT_QUIET and FETCHCONTENT_UPDATES_DISCONNECTED should never be hard-coded by the project. These are meant to be developer controls. The defaults should normally be appropriate and safe.
  • Don't call FetchContent_Populate(), especially without first checking if the dependency has already been populated. Call FetchContent_MakeAvailable() instead.
  • Unless you really need the source directory and its associated build directory to be at a specific place (and that is rarely necessary), don't specify the SOURCE_DIR or BINARY_DIR options in the call to FetchContent_Declare(). Instead, let FetchContent put things where it wants to. Both locations will be available to you after the call to FetchContent_MakeAvailable() in the nrf5_sdk_SOURCE_DIR and nrf5_sdk_BINARY_DIR variables, which are set for you automatically. By leaving these locations at their defaults, you leave open the opportunity to take advantage of any future improvements to FetchContent which may do things like caching, etc.
  • Don't add the source or binary directories of the dependency to the ADDITIONAL_CLEAN_FILES directory property. By doing so, you are removing things from under the feet of FetchContent, which uses time stamps internally to work out what steps it needs to re-execute each time CMake is re-run.
  • There's a bit of an overuse of CMake variables in the above. You'll get a simpler result if you limit the number of extra variables you define, eliminating most of the ones where a variable value is only used in one place.
  • INACTIVITY_TIMEOUT would not normally need to be set. It would typically only be needed if you're dealing with a poor network connection or a badly configured server. Only add it if you find you really need it (the setting is also only supported with CMake 3.19 or later).

Taking account of the above points, a more appropriate minimal demonstrator project would look something like this:

cmake_minimum_required(VERSION 3.14...3.27)
project(otherSource LANGUAGES C)

set(NRF_SDK_VERSION 17.1.0_ddde560)
set(NRF_SDK_SHA256  5bfe38e744c39fd7f30e10077ba12df306ef91f368894795d6a3e7a62dc68061)

# https://www.nordicsemi.com/Products/Development-software/nRF5-SDK/Download
# setting the hash avoids redownloading a big archive on each CMake configure,
# and provides a way to check if the archive is corrupted
include(FetchContent)
FetchContent_Declare(nrf5_sdk
   URL https://www.nordicsemi.com/-/media/Software-and-other-downloads/SDKs/nRF5/Binaries/nRF5_SDK_${NRF_SDK_VERSION}.zip
   URL_HASH SHA256=${NRF_SDK_SHA256}
   TLS_VERIFY true
)
FetchContent_MakeAvailable(nrf5_sdk)

# Example target using the dependency
add_library(system_nrf54 ${nrf5_sdk_SOURCE_DIR}/modules/nrfx/mdk/system_nrf52.c)
target_include_directories(system_nrf54 PRIVATE ${nrf5_sdk_SOURCE_DIR}/components/toolchain/cmsis/include)
target_compile_definitions(system_nrf54 PRIVATE NRF52805_XXAA)  # Board target

Even the NRF_SDK_VERSION and NRF_SDK_SHA256 variables are not needed. They are both only used once, so their values could be inlined directly in the call to FetchContent_Declare() too. I only left them separate since they are two values that you may want to change as you update to different versions of the dependency, so having them as separate variables may make them easier to spot.

@scivision
Copy link
Author

scivision commented Sep 4, 2023

I do differ in how I use FetchContent vs. the official recommendations:

  • fc_QUIET helps the user understand what's happening for a big download like this SDK. I don't use it for trivial downloads
  • This example is meant to show where only one or a couple files are wanted and/or where the other project's build system is not wanted. I have a popular MUMPS where my CMake build has become popular vs. the original developers' Makefile-only build. My MUMPS repo uses FetchContent_Populate to download the MUMPS source code, and then build MUMPS with CMake, ignoring the MUMPS Makefiles.
  • Yes I think the SOURCE_DIR and BINARY_DIR were not needed here. They would only be useful if someone was switching between BOARD_TYPE. But I think it's better in that case to use a new build dir.
  • I reduced the use of variables
  • I have found INACTIVITY_TIMEOUT useful for projects like MUMPS, where the MUMPS source-code server was previously not so reliable, and I put multiple URLs to fail and fallback to the backup URL in a reasonable time. I have also found timing out useful for anti-leeching systems on the source-code server, where again multiple URLs or a reasonable fatal error timeout help users.
  • I was showing a few practices I think are beneficial re: forcing out-of-source builds and auto-Git-ignoring build dir. With a FetchContent/ExternalProject, Git IDEs can become slow or stop reporting when there is a huge number of untracked files.

@craigscott-crascit
Copy link

Calling FetchContent_Populate() directly from projects will soon be deprecated, and eventually removed. See this forum post where I raised this recently. The timing of when this will occur isn't clear yet, but I'm aiming for either CMake 3.28 or 3.29 as the most likely timeframe.

@scivision
Copy link
Author

Okay I just saw that on discourse as well. What would I do then to download someone else's Git project but then use my own CMake script to build all or part of it. For example, I use a few big long-time projects but their CMakeLists is like CMake 2.6 era and they don't seem to care about fixing it. So I use my own CMake script to build their project via FetchContent.

@craigscott-crascit
Copy link

See the following from the FetchContent docs:

New in version 3.18: The SOURCE_SUBDIR option can be given in the declared details to look somewhere below the top directory instead (i.e. the same way that SOURCE_SUBDIR is used by the ExternalProject_Add() command). The path provided with SOURCE_SUBDIR must be relative and will be treated as relative to the top directory. It can also point to a directory that does not contain a CMakeLists.txt file or even to a directory that doesn't exist. This can be used to avoid adding a project that contains a CMakeLists.txt file in its top directory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment