|
| 1 | +# Third-party dependency fetching shared by all OpenVic repos. |
| 2 | +# |
| 3 | +# Deps are fetched as pinned GitHub source tarballs into the build tree's |
| 4 | +# `_deps/` dir (via FetchContent) instead of being read from the git submodule |
| 5 | +# checkouts. This decouples them from the working tree: switching branches (or |
| 6 | +# `git submodule update --force`, rebase, CI checkouts) rewrites submodule file |
| 7 | +# mtimes and makes Ninja rebuild byte-identical dep objects; fetched sources |
| 8 | +# live outside the working tree, so returning to an already-built pin is a |
| 9 | +# no-op. The submodules stay in place for the SCons build, which still reads |
| 10 | +# them; CMake simply stops looking at the vendored third-party dirs. |
| 11 | +# |
| 12 | +# First-party code (openvic-simulation, openvic-dataloader, lexy-vdf, and the |
| 13 | +# `scripts` repo itself) is NOT fetched -- it is the code under development and |
| 14 | +# must rebuild on branch switches, so it keeps building from the submodule tree |
| 15 | +# via add_subdirectory(). |
| 16 | + |
| 17 | +include_guard(GLOBAL) |
| 18 | + |
| 19 | +include(FetchContent) |
| 20 | + |
| 21 | +# CMP0168 (CMake >= 3.30): FetchContent populates directly during configure |
| 22 | +# with no per-dep sub-build -- a meaningful configure-time win across the ~15 |
| 23 | +# fetched deps. Guarded so it is a no-op on 3.28/3.29. Set at include (directory) |
| 24 | +# scope so it propagates to every repo's MakeAvailable call sites. |
| 25 | +if(POLICY CMP0168) |
| 26 | + cmake_policy(SET CMP0168 NEW) |
| 27 | +endif() |
| 28 | + |
| 29 | +#[[ openvic_declare_dep(<name> |
| 30 | + REPO <org/repo> GitHub owner/repo the tarball comes from |
| 31 | + SHA <full-commit-sha> commit the tarball is pinned to |
| 32 | + SHA256 <hash> tarball URL_HASH (integrity + download-skip) |
| 33 | + [SUBMODULE_PATH <path>] gitlink path (relative to REPO_DIR) to check |
| 34 | + the pin against; enables the drift warning |
| 35 | + [REPO_DIR <dir>] repo the SUBMODULE_PATH lives in |
| 36 | + (default: CMAKE_CURRENT_SOURCE_DIR) |
| 37 | + [DOWNLOAD_ONLY] populate the source but do NOT add_subdirectory |
| 38 | + it (for deps whose CMakeLists we don't use) |
| 39 | + [SOURCE_SUBDIR <dir>] add_subdirectory this subdir of the tarball |
| 40 | + [SOURCE_DIR <dir>] override the extraction directory |
| 41 | + ) |
| 42 | +
|
| 43 | +Declares a dep with FetchContent. The caller follows with |
| 44 | +FetchContent_MakeAvailable(<name>) (after any option-setting the dep needs), |
| 45 | +then uses ${<name>_SOURCE_DIR} for DOWNLOAD_ONLY deps. |
| 46 | +
|
| 47 | +Fetched as a source tarball rather than a git clone: a full-SHA GIT_TAG cannot |
| 48 | +use a shallow clone, so git-based fetches of the larger repos (godot-cpp, |
| 49 | +range-v3, spdlog) would pull full history. None of these deps need nested git |
| 50 | +submodules at their pinned commit, so tarballs are strictly cheaper. |
| 51 | +
|
| 52 | +Local-dev escape hatch: -DFETCHCONTENT_SOURCE_DIR_<UPPER_NAME>=<path> points a |
| 53 | +single dep back at a working-tree checkout (this reintroduces mtime coupling |
| 54 | +for that one dep -- it is a debugging tool, not the default). |
| 55 | +]] |
| 56 | +function(openvic_declare_dep name) |
| 57 | + cmake_parse_arguments( |
| 58 | + PARSE_ARGV 1 ARG |
| 59 | + "DOWNLOAD_ONLY" |
| 60 | + "REPO;SHA;SHA256;SUBMODULE_PATH;REPO_DIR;SOURCE_SUBDIR;SOURCE_DIR" |
| 61 | + "" |
| 62 | + ) |
| 63 | + |
| 64 | + if(NOT ARG_REPO OR NOT ARG_SHA OR NOT ARG_SHA256) |
| 65 | + message(FATAL_ERROR "openvic_declare_dep(${name}) requires REPO, SHA and SHA256") |
| 66 | + endif() |
| 67 | + |
| 68 | + _openvic_check_dep_pin("${name}" "${ARG_SHA}" "${ARG_SUBMODULE_PATH}" "${ARG_REPO_DIR}") |
| 69 | + |
| 70 | + set(declare_args "") |
| 71 | + if(ARG_DOWNLOAD_ONLY) |
| 72 | + # SOURCE_SUBDIR pointing at a nonexistent dir makes MakeAvailable |
| 73 | + # populate the source but skip add_subdirectory (documented behavior, |
| 74 | + # CMake >= 3.28). Used for deps whose upstream CMakeLists we replace |
| 75 | + # with a hand-rolled target (or whose CMakeLists pollutes global state). |
| 76 | + list(APPEND declare_args SOURCE_SUBDIR "_openvic_do_not_add") |
| 77 | + elseif(ARG_SOURCE_SUBDIR) |
| 78 | + list(APPEND declare_args SOURCE_SUBDIR "${ARG_SOURCE_SUBDIR}") |
| 79 | + endif() |
| 80 | + if(ARG_SOURCE_DIR) |
| 81 | + list(APPEND declare_args SOURCE_DIR "${ARG_SOURCE_DIR}") |
| 82 | + endif() |
| 83 | + |
| 84 | + FetchContent_Declare( |
| 85 | + ${name} |
| 86 | + URL "https://github.com/${ARG_REPO}/archive/${ARG_SHA}.tar.gz" |
| 87 | + URL_HASH SHA256=${ARG_SHA256} |
| 88 | + SYSTEM |
| 89 | + EXCLUDE_FROM_ALL |
| 90 | + ${declare_args} |
| 91 | + ) |
| 92 | +endfunction() |
| 93 | + |
| 94 | +#[[ Warn loudly when a dep's CMake pin has drifted from the submodule gitlink |
| 95 | +(e.g. a dependabot bump moved the submodule but not the pin here), and note |
| 96 | +when the same FetchContent name is declared twice with different SHAs (CMake |
| 97 | +silently keeps the first -- this is expected for the two vendored lexy copies, |
| 98 | +so it is only a STATUS message). Warn-only; tolerant of missing git/submodule. ]] |
| 99 | +function(_openvic_check_dep_pin name sha submodule_path repo_dir) |
| 100 | + # Duplicate-name detection: record name -> SHA in a global property. |
| 101 | + get_property(prev GLOBAL PROPERTY "_openvic_dep_pin_${name}") |
| 102 | + if(prev AND NOT prev STREQUAL sha) |
| 103 | + message(STATUS "OpenVic dep '${name}' already declared at ${prev}; ignoring later pin ${sha} (first declaration wins)") |
| 104 | + endif() |
| 105 | + set_property(GLOBAL PROPERTY "_openvic_dep_pin_${name}" "${sha}") |
| 106 | + |
| 107 | + if(NOT submodule_path) |
| 108 | + return() |
| 109 | + endif() |
| 110 | + if(NOT repo_dir) |
| 111 | + set(repo_dir "${CMAKE_CURRENT_SOURCE_DIR}") |
| 112 | + endif() |
| 113 | + |
| 114 | + find_program(GIT_EXECUTABLE git) |
| 115 | + if(NOT GIT_EXECUTABLE) |
| 116 | + return() |
| 117 | + endif() |
| 118 | + |
| 119 | + execute_process( |
| 120 | + COMMAND "${GIT_EXECUTABLE}" -C "${repo_dir}" ls-tree HEAD "${submodule_path}" |
| 121 | + OUTPUT_VARIABLE ls_tree |
| 122 | + ERROR_QUIET |
| 123 | + RESULT_VARIABLE git_result |
| 124 | + OUTPUT_STRIP_TRAILING_WHITESPACE |
| 125 | + ) |
| 126 | + if(NOT git_result EQUAL 0 OR ls_tree STREQUAL "") |
| 127 | + return() |
| 128 | + endif() |
| 129 | + |
| 130 | + # gitlink line: "160000 commit <sha>\t<path>" |
| 131 | + if(ls_tree MATCHES "160000 commit ([0-9a-f]+)") |
| 132 | + set(gitlink_sha "${CMAKE_MATCH_1}") |
| 133 | + if(NOT gitlink_sha STREQUAL sha) |
| 134 | + message( |
| 135 | + WARNING |
| 136 | + "OpenVic dep '${name}' pin drift: CMake fetches ${sha} but submodule " |
| 137 | + "'${submodule_path}' points at ${gitlink_sha}. Update the SHA/SHA256 in " |
| 138 | + "the openvic_declare_dep() call to match (or the submodule)." |
| 139 | + ) |
| 140 | + endif() |
| 141 | + endif() |
| 142 | +endfunction() |
0 commit comments