|
| 1 | +# TheLartians/CPM - A simple Git dependency manager |
| 2 | +# ================================================= |
| 3 | +# See https://github.com/TheLartians/CPM for usage and update instructions. |
| 4 | +# |
| 5 | +# MIT License |
| 6 | +# ----------- |
| 7 | +#[[ |
| 8 | + Copyright (c) 2019 Lars Melchior |
| 9 | +
|
| 10 | + Permission is hereby granted, free of charge, to any person obtaining a copy |
| 11 | + of this software and associated documentation files (the "Software"), to deal |
| 12 | + in the Software without restriction, including without limitation the rights |
| 13 | + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 14 | + copies of the Software, and to permit persons to whom the Software is |
| 15 | + furnished to do so, subject to the following conditions: |
| 16 | +
|
| 17 | + The above copyright notice and this permission notice shall be included in all |
| 18 | + copies or substantial portions of the Software. |
| 19 | +
|
| 20 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 21 | + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 22 | + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 23 | + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 24 | + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 25 | + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 26 | + SOFTWARE. |
| 27 | +]] |
| 28 | + |
| 29 | +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) |
| 30 | + |
| 31 | +set(CURRENT_CPM_VERSION 0.18) |
| 32 | + |
| 33 | +if(CPM_DIRECTORY) |
| 34 | + if(NOT ${CPM_DIRECTORY} MATCHES ${CMAKE_CURRENT_LIST_DIR}) |
| 35 | + if (${CPM_VERSION} VERSION_LESS ${CURRENT_CPM_VERSION}) |
| 36 | + message(AUTHOR_WARNING "${CPM_INDENT} \ |
| 37 | +A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \ |
| 38 | +It is recommended to upgrade CPM to the most recent version. \ |
| 39 | +See https://github.com/TheLartians/CPM.cmake for more information." |
| 40 | + ) |
| 41 | + endif() |
| 42 | + return() |
| 43 | + endif() |
| 44 | +endif() |
| 45 | + |
| 46 | +option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies" $ENV{CPM_USE_LOCAL_PACKAGES}) |
| 47 | +option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies" $ENV{CPM_LOCAL_PACKAGES_ONLY}) |
| 48 | +option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL}) |
| 49 | + |
| 50 | +set(CPM_VERSION ${CURRENT_CPM_VERSION} CACHE INTERNAL "") |
| 51 | +set(CPM_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "") |
| 52 | +set(CPM_PACKAGES "" CACHE INTERNAL "") |
| 53 | +set(CPM_DRY_RUN OFF CACHE INTERNAL "Don't download or configure dependencies (for testing)") |
| 54 | + |
| 55 | +if(DEFINED ENV{CPM_SOURCE_CACHE}) |
| 56 | + set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE}) |
| 57 | +else() |
| 58 | + set(CPM_SOURCE_CACHE_DEFAULT OFF) |
| 59 | +endif() |
| 60 | + |
| 61 | +set(CPM_SOURCE_CACHE ${CPM_SOURCE_CACHE_DEFAULT} CACHE PATH "Directory to downlaod CPM dependencies") |
| 62 | + |
| 63 | +include(FetchContent) |
| 64 | +include(CMakeParseArguments) |
| 65 | + |
| 66 | +# Initialize logging prefix |
| 67 | +if(NOT CPM_INDENT) |
| 68 | + set(CPM_INDENT "CPM:") |
| 69 | +endif() |
| 70 | + |
| 71 | +function(cpm_find_package NAME VERSION) |
| 72 | + string(REPLACE " " ";" EXTRA_ARGS "${ARGN}") |
| 73 | + find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET) |
| 74 | + if(${CPM_ARGS_NAME}_FOUND) |
| 75 | + message(STATUS "${CPM_INDENT} using local package ${CPM_ARGS_NAME}@${${CPM_ARGS_NAME}_VERSION}") |
| 76 | + CPMRegisterPackage(${CPM_ARGS_NAME} "${${CPM_ARGS_NAME}_VERSION}") |
| 77 | + set(CPM_PACKAGE_FOUND YES PARENT_SCOPE) |
| 78 | + else() |
| 79 | + set(CPM_PACKAGE_FOUND NO PARENT_SCOPE) |
| 80 | + endif() |
| 81 | +endfunction() |
| 82 | + |
| 83 | +# Find a package locally or fallback to CPMAddPackage |
| 84 | +function(CPMFindPackage) |
| 85 | + set(oneValueArgs |
| 86 | + NAME |
| 87 | + VERSION |
| 88 | + FIND_PACKAGE_ARGUMENTS |
| 89 | + ) |
| 90 | + |
| 91 | + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN}) |
| 92 | + |
| 93 | + if (CPM_DOWNLOAD_ALL) |
| 94 | + CPMAddPackage(${ARGN}) |
| 95 | + cpm_export_variables() |
| 96 | + return() |
| 97 | + endif() |
| 98 | + |
| 99 | + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) |
| 100 | + |
| 101 | + if(NOT CPM_PACKAGE_FOUND) |
| 102 | + CPMAddPackage(${ARGN}) |
| 103 | + cpm_export_variables() |
| 104 | + endif() |
| 105 | + |
| 106 | +endfunction() |
| 107 | + |
| 108 | +# Download and add a package from source |
| 109 | +function(CPMAddPackage) |
| 110 | + |
| 111 | + set(oneValueArgs |
| 112 | + NAME |
| 113 | + VERSION |
| 114 | + GIT_TAG |
| 115 | + DOWNLOAD_ONLY |
| 116 | + GITHUB_REPOSITORY |
| 117 | + GITLAB_REPOSITORY |
| 118 | + SOURCE_DIR |
| 119 | + DOWNLOAD_COMMAND |
| 120 | + FIND_PACKAGE_ARGUMENTS |
| 121 | + ) |
| 122 | + |
| 123 | + set(multiValueArgs |
| 124 | + OPTIONS |
| 125 | + ) |
| 126 | + |
| 127 | + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") |
| 128 | + |
| 129 | + if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY) |
| 130 | + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) |
| 131 | + |
| 132 | + if(CPM_PACKAGE_FOUND) |
| 133 | + return() |
| 134 | + endif() |
| 135 | + |
| 136 | + if(CPM_LOCAL_PACKAGES_ONLY) |
| 137 | + message(SEND_ERROR "CPM: ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})") |
| 138 | + endif() |
| 139 | + endif() |
| 140 | + |
| 141 | + if (NOT DEFINED CPM_ARGS_VERSION) |
| 142 | + if (DEFINED CPM_ARGS_GIT_TAG) |
| 143 | + cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) |
| 144 | + endif() |
| 145 | + if (NOT DEFINED CPM_ARGS_VERSION) |
| 146 | + set(CPM_ARGS_VERSION 0) |
| 147 | + endif() |
| 148 | + endif() |
| 149 | + |
| 150 | + if (NOT DEFINED CPM_ARGS_GIT_TAG) |
| 151 | + set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION}) |
| 152 | + endif() |
| 153 | + |
| 154 | + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG}) |
| 155 | + |
| 156 | + if(CPM_ARGS_DOWNLOAD_ONLY) |
| 157 | + set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY}) |
| 158 | + else() |
| 159 | + set(DOWNLOAD_ONLY NO) |
| 160 | + endif() |
| 161 | + |
| 162 | + if (CPM_ARGS_GITHUB_REPOSITORY) |
| 163 | + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git") |
| 164 | + endif() |
| 165 | + |
| 166 | + if (CPM_ARGS_GITLAB_REPOSITORY) |
| 167 | + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git") |
| 168 | + endif() |
| 169 | + |
| 170 | + if (${CPM_ARGS_NAME} IN_LIST CPM_PACKAGES) |
| 171 | + CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION) |
| 172 | + if(${CPM_PACKAGE_VERSION} VERSION_LESS ${CPM_ARGS_VERSION}) |
| 173 | + message(WARNING "${CPM_INDENT} requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION}).") |
| 174 | + endif() |
| 175 | + if (CPM_ARGS_OPTIONS) |
| 176 | + foreach(OPTION ${CPM_ARGS_OPTIONS}) |
| 177 | + cpm_parse_option(${OPTION}) |
| 178 | + if(NOT "${${OPTION_KEY}}" STREQUAL ${OPTION_VALUE}) |
| 179 | + message(WARNING "${CPM_INDENT} ignoring package option for ${CPM_ARGS_NAME}: ${OPTION_KEY} = ${OPTION_VALUE} (${${OPTION_KEY}})") |
| 180 | + endif() |
| 181 | + endforeach() |
| 182 | + endif() |
| 183 | + cpm_fetch_package(${CPM_ARGS_NAME} ${DOWNLOAD_ONLY}) |
| 184 | + cpm_get_fetch_properties(${CPM_ARGS_NAME}) |
| 185 | + SET(${CPM_ARGS_NAME}_SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}") |
| 186 | + SET(${CPM_ARGS_NAME}_BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}") |
| 187 | + SET(${CPM_ARGS_NAME}_ADDED NO) |
| 188 | + cpm_export_variables() |
| 189 | + return() |
| 190 | + endif() |
| 191 | + |
| 192 | + CPMRegisterPackage(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION}) |
| 193 | + |
| 194 | + if (CPM_ARGS_OPTIONS) |
| 195 | + foreach(OPTION ${CPM_ARGS_OPTIONS}) |
| 196 | + cpm_parse_option(${OPTION}) |
| 197 | + set(${OPTION_KEY} ${OPTION_VALUE} CACHE INTERNAL "") |
| 198 | + endforeach() |
| 199 | + endif() |
| 200 | + |
| 201 | + set(FETCH_CONTENT_DECLARE_EXTRA_OPTS "") |
| 202 | + |
| 203 | + if (DEFINED CPM_ARGS_GIT_TAG) |
| 204 | + set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}") |
| 205 | + else() |
| 206 | + set(PACKAGE_INFO "${CPM_ARGS_VERSION}") |
| 207 | + endif() |
| 208 | + |
| 209 | + if (DEFINED CPM_ARGS_DOWNLOAD_COMMAND) |
| 210 | + set(FETCH_CONTENT_DECLARE_EXTRA_OPTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND}) |
| 211 | + elseif(DEFINED CPM_ARGS_SOURCE_DIR) |
| 212 | + set(FETCH_CONTENT_DECLARE_EXTRA_OPTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR}) |
| 213 | + elseif (CPM_SOURCE_CACHE) |
| 214 | + string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) |
| 215 | + set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS}) |
| 216 | + list(SORT origin_parameters) |
| 217 | + string(SHA1 origin_hash "${origin_parameters}") |
| 218 | + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}) |
| 219 | + list(APPEND FETCH_CONTENT_DECLARE_EXTRA_OPTS SOURCE_DIR ${download_directory}) |
| 220 | + if (EXISTS ${download_directory}) |
| 221 | + list(APPEND FETCH_CONTENT_DECLARE_EXTRA_OPTS DOWNLOAD_COMMAND ":") |
| 222 | + set(PACKAGE_INFO "${download_directory}") |
| 223 | + else() |
| 224 | + # remove timestamps so CMake will re-download the dependency |
| 225 | + file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/_deps/${lower_case_name}-subbuild) |
| 226 | + set(PACKAGE_INFO "${PACKAGE_INFO} -> ${download_directory}") |
| 227 | + endif() |
| 228 | + endif() |
| 229 | + |
| 230 | + cpm_declare_fetch(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION} ${PACKAGE_INFO} "${CPM_ARGS_UNPARSED_ARGUMENTS}" ${FETCH_CONTENT_DECLARE_EXTRA_OPTS}) |
| 231 | + cpm_fetch_package(${CPM_ARGS_NAME} ${DOWNLOAD_ONLY}) |
| 232 | + cpm_get_fetch_properties(${CPM_ARGS_NAME}) |
| 233 | + SET(${CPM_ARGS_NAME}_ADDED YES) |
| 234 | + cpm_export_variables() |
| 235 | +endfunction() |
| 236 | + |
| 237 | +# export variables available to the caller to the parent scope |
| 238 | +# expects ${CPM_ARGS_NAME} to be set |
| 239 | +macro(cpm_export_variables) |
| 240 | + SET(${CPM_ARGS_NAME}_SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}" PARENT_SCOPE) |
| 241 | + SET(${CPM_ARGS_NAME}_BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}" PARENT_SCOPE) |
| 242 | + SET(${CPM_ARGS_NAME}_ADDED "${${CPM_ARGS_NAME}_ADDED}" PARENT_SCOPE) |
| 243 | +endmacro() |
| 244 | + |
| 245 | +# declares that a package has been added to CPM |
| 246 | +function(CPMRegisterPackage PACKAGE VERSION) |
| 247 | + list(APPEND CPM_PACKAGES ${PACKAGE}) |
| 248 | + set(CPM_PACKAGES ${CPM_PACKAGES} CACHE INTERNAL "") |
| 249 | + set("CPM_PACKAGE_${PACKAGE}_VERSION" ${VERSION} CACHE INTERNAL "") |
| 250 | +endfunction() |
| 251 | + |
| 252 | +# retrieve the current version of the package to ${OUTPUT} |
| 253 | +function(CPMGetPackageVersion PACKAGE OUTPUT) |
| 254 | + set(${OUTPUT} "${CPM_PACKAGE_${PACKAGE}_VERSION}" PARENT_SCOPE) |
| 255 | +endfunction() |
| 256 | + |
| 257 | +# declares a package in FetchContent_Declare |
| 258 | +function (cpm_declare_fetch PACKAGE VERSION INFO) |
| 259 | + message(STATUS "${CPM_INDENT} adding package ${PACKAGE}@${VERSION} (${INFO})") |
| 260 | + |
| 261 | + if (${CPM_DRY_RUN}) |
| 262 | + message(STATUS "${CPM_INDENT} package not declared (dry run)") |
| 263 | + return() |
| 264 | + endif() |
| 265 | + |
| 266 | + FetchContent_Declare( |
| 267 | + ${PACKAGE} |
| 268 | + ${ARGN} |
| 269 | + ) |
| 270 | +endfunction() |
| 271 | + |
| 272 | +# returns properties for a package previously defined by cpm_declare_fetch |
| 273 | +function (cpm_get_fetch_properties PACKAGE) |
| 274 | + if (${CPM_DRY_RUN}) |
| 275 | + return() |
| 276 | + endif() |
| 277 | + FetchContent_GetProperties(${PACKAGE}) |
| 278 | + string(TOLOWER ${PACKAGE} lpackage) |
| 279 | + SET(${PACKAGE}_SOURCE_DIR "${${lpackage}_SOURCE_DIR}" PARENT_SCOPE) |
| 280 | + SET(${PACKAGE}_BINARY_DIR "${${lpackage}_BINARY_DIR}" PARENT_SCOPE) |
| 281 | +endfunction() |
| 282 | + |
| 283 | +# downloads a previously declared package via FetchContent |
| 284 | +function (cpm_fetch_package PACKAGE DOWNLOAD_ONLY) |
| 285 | + |
| 286 | + if (${CPM_DRY_RUN}) |
| 287 | + message(STATUS "${CPM_INDENT} package ${PACKAGE} not fetched (dry run)") |
| 288 | + return() |
| 289 | + endif() |
| 290 | + |
| 291 | + set(CPM_OLD_INDENT "${CPM_INDENT}") |
| 292 | + set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:") |
| 293 | + if(${DOWNLOAD_ONLY}) |
| 294 | + if(NOT "${PACKAGE}_POPULATED") |
| 295 | + FetchContent_Populate(${PACKAGE}) |
| 296 | + endif() |
| 297 | + else() |
| 298 | + FetchContent_MakeAvailable(${PACKAGE}) |
| 299 | + endif() |
| 300 | + set(CPM_INDENT "${CPM_OLD_INDENT}") |
| 301 | +endfunction() |
| 302 | + |
| 303 | +# splits a package option |
| 304 | +function(cpm_parse_option OPTION) |
| 305 | + string(REGEX MATCH "^[^ ]+" OPTION_KEY ${OPTION}) |
| 306 | + string(LENGTH ${OPTION} OPTION_LENGTH) |
| 307 | + string(LENGTH ${OPTION_KEY} OPTION_KEY_LENGTH) |
| 308 | + if (OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH) |
| 309 | + # no value for key provided, assume user wants to set option to "ON" |
| 310 | + set(OPTION_VALUE "ON") |
| 311 | + else() |
| 312 | + math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1") |
| 313 | + string(SUBSTRING ${OPTION} "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE) |
| 314 | + endif() |
| 315 | + set(OPTION_KEY "${OPTION_KEY}" PARENT_SCOPE) |
| 316 | + set(OPTION_VALUE "${OPTION_VALUE}" PARENT_SCOPE) |
| 317 | +endfunction() |
| 318 | + |
| 319 | +# guesses the package version from a git tag |
| 320 | +function(cpm_get_version_from_git_tag GIT_TAG RESULT) |
| 321 | + string(LENGTH ${GIT_TAG} length) |
| 322 | + if (length EQUAL 40) |
| 323 | + # GIT_TAG is probably a git hash |
| 324 | + SET(${RESULT} 0 PARENT_SCOPE) |
| 325 | + else() |
| 326 | + string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG}) |
| 327 | + SET(${RESULT} ${CMAKE_MATCH_1} PARENT_SCOPE) |
| 328 | + endif() |
| 329 | +endfunction() |
0 commit comments