aboutsummaryrefslogtreecommitdiff
path: root/CMakeModules
diff options
context:
space:
mode:
authorcrueter <crueter@eden-emu.dev>2026-07-06 23:11:39 +0200
committercrueter <crueter@eden-emu.dev>2026-07-06 23:11:39 +0200
commit66b073ab8d427d8fc06ffd715151f372cdde818c (patch)
tree667e34c22f542d809a069738bd762a23786814c7 /CMakeModules
parent4c65780f11511b20c2e0288a287faace16516d38 (diff)
[cmake] CPMUtil rewrite of the day number 852 (#4130)
Composite PR for several changes to CPMUtil, most notably: - https://git.crueter.xyz/CMake/CPMUtil/pulls/19 - https://git.crueter.xyz/CMake/CPMUtil/pulls/22 - https://git.crueter.xyz/CMake/CPMUtil/pulls/24 - https://git.crueter.xyz/CMake/CPMUtil/pulls/25 These contain a lot of changes that generally simplify control flow and improve ease-of-use. Read those descriptions and patchsets for more info. Signed-off-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4130 Reviewed-by: CamilleLaVey <camillelavey99@gmail.com> Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Diffstat (limited to 'CMakeModules')
-rw-r--r--CMakeModules/CPM.cmake1310
-rw-r--r--CMakeModules/CPMUtil.cmake855
2 files changed, 767 insertions, 1398 deletions
diff --git a/CMakeModules/CPM.cmake b/CMakeModules/CPM.cmake
index 5544d8eefe..431766b579 100644
--- a/CMakeModules/CPM.cmake
+++ b/CMakeModules/CPM.cmake
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: Copyright 2026 crueter
+# SPDX-License-Identifier: LGPL-3.0-or-later
+
# CPM.cmake - CMake's missing package manager
# ===========================================
# See https://github.com/cpm-cmake/CPM.cmake for usage and update instructions.
@@ -26,65 +29,19 @@
SOFTWARE.
]]
-cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.31 FATAL_ERROR)
# Initialize logging prefix
if(NOT CPM_INDENT)
- set(CPM_INDENT
- "CPM:"
- CACHE INTERNAL ""
- )
+ set(CPM_INDENT "CPM:" CACHE INTERNAL "")
endif()
-if(NOT COMMAND cpm_message)
- function(cpm_message)
- message(${ARGV})
- endfunction()
+get_property(CPM_INITIALIZED GLOBAL PROPERTY CPM_INITIALIZED)
+if(CPM_INITIALIZED)
+ return()
endif()
-if(DEFINED EXTRACTED_CPM_VERSION)
- set(CURRENT_CPM_VERSION "${EXTRACTED_CPM_VERSION}${CPM_DEVELOPMENT}")
-else()
- set(CURRENT_CPM_VERSION 0.42.0)
-endif()
-
-get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH)
-if(CPM_DIRECTORY)
- if(NOT CPM_DIRECTORY STREQUAL CPM_CURRENT_DIRECTORY)
- if(CPM_VERSION VERSION_LESS CURRENT_CPM_VERSION)
- message(
- AUTHOR_WARNING
- "${CPM_INDENT} \
-A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \
-It is recommended to upgrade CPM to the most recent version. \
-See https://github.com/cpm-cmake/CPM.cmake for more information."
- )
- endif()
- if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
- include(FetchContent)
- endif()
- return()
- endif()
-
- get_property(
- CPM_INITIALIZED GLOBAL ""
- PROPERTY CPM_INITIALIZED
- SET
- )
- if(CPM_INITIALIZED)
- return()
- endif()
-endif()
-
-if(CURRENT_CPM_VERSION MATCHES "development-version")
- message(
- WARNING "${CPM_INDENT} Your project is using an unstable development version of CPM.cmake. \
-Please update to a recent release if possible. \
-See https://github.com/cpm-cmake/CPM.cmake for details."
- )
-endif()
-
-set_property(GLOBAL PROPERTY CPM_INITIALIZED true)
+set_property(GLOBAL PROPERTY CPM_INITIALIZED TRUE)
macro(cpm_set_policies)
# the policy allows us to change options without caching
@@ -92,85 +49,40 @@ macro(cpm_set_policies)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
# the policy allows us to change set(CACHE) without caching
- if(POLICY CMP0126)
- cmake_policy(SET CMP0126 NEW)
- set(CMAKE_POLICY_DEFAULT_CMP0126 NEW)
- endif()
+ cmake_policy(SET CMP0126 NEW)
+ set(CMAKE_POLICY_DEFAULT_CMP0126 NEW)
- # The policy uses the download time for timestamp, instead of the timestamp in the archive. This
+ # The policy uses the download time for timestamp,
+ # instead of the timestamp in the archive. This
# allows for proper rebuilds when a projects url changes
- if(POLICY CMP0135)
- cmake_policy(SET CMP0135 NEW)
- set(CMAKE_POLICY_DEFAULT_CMP0135 NEW)
- endif()
+ cmake_policy(SET CMP0135 NEW)
+ set(CMAKE_POLICY_DEFAULT_CMP0135 NEW)
- # treat relative git repository paths as being relative to the parent project's remote
- if(POLICY CMP0150)
- cmake_policy(SET CMP0150 NEW)
- set(CMAKE_POLICY_DEFAULT_CMP0150 NEW)
- endif()
+ # treat relative git repository paths as
+ # being relative to the parent project's remote
+ cmake_policy(SET CMP0150 NEW)
+ set(CMAKE_POLICY_DEFAULT_CMP0150 NEW)
+
+ # Disable sub-build nonsense
+ cmake_policy(SET CMP0168 NEW)
+ set(CMAKE_POLICY_DEFAULT_CMP0168 NEW)
endmacro()
+
cpm_set_policies()
-option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies"
- $ENV{CPM_USE_LOCAL_PACKAGES}
-)
-option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies"
- $ENV{CPM_LOCAL_PACKAGES_ONLY}
-)
-option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL})
-option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package"
- $ENV{CPM_DONT_UPDATE_MODULE_PATH}
-)
-option(CPM_DONT_CREATE_PACKAGE_LOCK "Don't create a package lock file in the binary path"
- $ENV{CPM_DONT_CREATE_PACKAGE_LOCK}
-)
-option(CPM_INCLUDE_ALL_IN_PACKAGE_LOCK
- "Add all packages added through CPM.cmake to the package lock"
- $ENV{CPM_INCLUDE_ALL_IN_PACKAGE_LOCK}
-)
-option(CPM_USE_NAMED_CACHE_DIRECTORIES
- "Use additional directory of package name in cache on the most nested level."
- $ENV{CPM_USE_NAMED_CACHE_DIRECTORIES}
-)
+option(CPM_DONT_UPDATE_MODULE_PATH
+ "Don't update the module path to allow using find_package"
+ $ENV{CPM_DONT_UPDATE_MODULE_PATH})
-set(CPM_VERSION
- ${CURRENT_CPM_VERSION}
- CACHE INTERNAL ""
-)
-set(CPM_DIRECTORY
- ${CPM_CURRENT_DIRECTORY}
- CACHE INTERNAL ""
-)
set(CPM_FILE
${CMAKE_CURRENT_LIST_FILE}
- CACHE INTERNAL ""
-)
-set(CPM_PACKAGES
- ""
- CACHE INTERNAL ""
-)
-set(CPM_DRY_RUN
- OFF
- CACHE INTERNAL "Don't download or configure dependencies (for testing)"
-)
-
-if(DEFINED ENV{CPM_SOURCE_CACHE})
- set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE})
-else()
- set(CPM_SOURCE_CACHE_DEFAULT OFF)
-endif()
+ CACHE INTERNAL "")
-set(CPM_SOURCE_CACHE
- ${CPM_SOURCE_CACHE_DEFAULT}
- CACHE PATH "Directory to download CPM dependencies"
-)
+set(CPM_PACKAGES "" CACHE INTERNAL "")
-if(NOT CPM_DONT_UPDATE_MODULE_PATH AND NOT DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR)
- set(CPM_MODULE_PATH
- "${CMAKE_BINARY_DIR}/CPM_modules"
- CACHE INTERNAL ""
- )
+if(NOT CPM_DONT_UPDATE_MODULE_PATH
+ AND NOT DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR)
+ set(CPM_MODULE_PATH "${CMAKE_BINARY_DIR}/CPM_modules" CACHE INTERNAL "")
# remove old modules
file(REMOVE_RECURSE ${CPM_MODULE_PATH})
file(MAKE_DIRECTORY ${CPM_MODULE_PATH})
@@ -178,602 +90,105 @@ if(NOT CPM_DONT_UPDATE_MODULE_PATH AND NOT DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_
set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}")
endif()
-if(NOT CPM_DONT_CREATE_PACKAGE_LOCK)
- set(CPM_PACKAGE_LOCK_FILE
- "${CMAKE_BINARY_DIR}/cpm-package-lock.cmake"
- CACHE INTERNAL ""
- )
- file(WRITE ${CPM_PACKAGE_LOCK_FILE}
- "# CPM Package Lock\n# This file should be committed to version control\n\n"
- )
-endif()
-
include(FetchContent)
-# Try to infer package name from git repository uri (path or url)
-function(cpm_package_name_from_git_uri URI RESULT)
- if("${URI}" MATCHES "([^/:]+)/?.git/?$")
- set(${RESULT}
- ${CMAKE_MATCH_1}
- PARENT_SCOPE
- )
- else()
- unset(${RESULT} PARENT_SCOPE)
- endif()
-endfunction()
-
-# Find the shortest hash that can be used eg, if origin_hash is
-# cccb77ae9609d2768ed80dd42cec54f77b1f1455 the following files will be checked, until one is found
-# that is either empty (allowing us to assign origin_hash), or whose contents matches ${origin_hash}
-#
-# * .../cccb.hash
-# * .../cccb77ae.hash
-# * .../cccb77ae9609.hash
-# * .../cccb77ae9609d276.hash
-# * etc
-#
-# We will be able to use a shorter path with very high probability, but in the (rare) event that the
-# first couple characters collide, we will check longer and longer substrings.
-function(cpm_get_shortest_hash source_cache_dir origin_hash short_hash_output_var)
- # for compatibility with caches populated by a previous version of CPM, check if a directory using
- # the full hash already exists
- if(EXISTS "${source_cache_dir}/${origin_hash}")
- set(${short_hash_output_var}
- "${origin_hash}"
- PARENT_SCOPE
- )
- return()
- endif()
-
- foreach(len RANGE 4 40 4)
- string(SUBSTRING "${origin_hash}" 0 ${len} short_hash)
- set(hash_lock ${source_cache_dir}/${short_hash}.lock)
- set(hash_fp ${source_cache_dir}/${short_hash}.hash)
- # Take a lock, so we don't have a race condition with another instance of cmake. We will release
- # this lock when we can, however, if there is an error, we want to ensure it gets released on
- # it's own on exit from the function.
- file(LOCK ${hash_lock} GUARD FUNCTION)
-
- # Load the contents of .../${short_hash}.hash
- file(TOUCH ${hash_fp})
- file(READ ${hash_fp} hash_fp_contents)
-
- if(hash_fp_contents STREQUAL "")
- # Write the origin hash
- file(WRITE ${hash_fp} ${origin_hash})
- file(LOCK ${hash_lock} RELEASE)
- break()
- elseif(hash_fp_contents STREQUAL origin_hash)
- file(LOCK ${hash_lock} RELEASE)
- break()
- else()
- file(LOCK ${hash_lock} RELEASE)
- endif()
- endforeach()
- set(${short_hash_output_var}
- "${short_hash}"
- PARENT_SCOPE
- )
-endfunction()
-
-# Try to infer package name and version from a url
-function(cpm_package_name_and_ver_from_url url outName outVer)
- if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)")
- # We matched an archive
- set(filename "${CMAKE_MATCH_1}")
-
- if(filename MATCHES "([a-zA-Z0-9_\\.-]+)[_-]v?(([0-9]+\\.)*[0-9]+[a-zA-Z0-9]*)")
- # We matched <name>-<version> (ie foo-1.2.3)
- set(${outName}
- "${CMAKE_MATCH_1}"
- PARENT_SCOPE
- )
- set(${outVer}
- "${CMAKE_MATCH_2}"
- PARENT_SCOPE
- )
- elseif(filename MATCHES "(([0-9]+\\.)+[0-9]+[a-zA-Z0-9]*)")
- # We couldn't find a name, but we found a version
- #
- # In many cases (which we don't handle here) the url would look something like
- # `irrelevant/ACTUAL_PACKAGE_NAME/irrelevant/1.2.3.zip`. In such a case we can't possibly
- # distinguish the package name from the irrelevant bits. Moreover if we try to match the
- # package name from the filename, we'd get bogus at best.
- unset(${outName} PARENT_SCOPE)
- set(${outVer}
- "${CMAKE_MATCH_1}"
- PARENT_SCOPE
- )
- else()
- # Boldly assume that the file name is the package name.
- #
- # Yes, something like `irrelevant/ACTUAL_NAME/irrelevant/download.zip` will ruin our day, but
- # such cases should be quite rare. No popular service does this... we think.
- set(${outName}
- "${filename}"
- PARENT_SCOPE
- )
- unset(${outVer} PARENT_SCOPE)
- endif()
- else()
- # No ideas yet what to do with non-archives
- unset(${outName} PARENT_SCOPE)
- unset(${outVer} PARENT_SCOPE)
- endif()
-endfunction()
-
-function(cpm_find_package NAME VERSION)
- string(REPLACE " " ";" EXTRA_ARGS "${ARGN}")
- find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET)
- if(${CPM_ARGS_NAME}_FOUND)
- if(DEFINED ${CPM_ARGS_NAME}_VERSION)
- set(VERSION ${${CPM_ARGS_NAME}_VERSION})
- endif()
- cpm_message(STATUS "${CPM_INDENT} Using local package ${CPM_ARGS_NAME}@${VERSION}")
- CPMRegisterPackage(${CPM_ARGS_NAME} "${VERSION}")
- set(CPM_PACKAGE_FOUND
- YES
- PARENT_SCOPE
- )
- else()
- set(CPM_PACKAGE_FOUND
- NO
- PARENT_SCOPE
- )
- endif()
-endfunction()
-
-# Create a custom FindXXX.cmake module for a CPM package This prevents `find_package(NAME)` from
-# finding the system library
+# Create a custom FindXXX.cmake module for a CPM package.
+# This prevents `find_package(NAME)` from finding the system library
function(cpm_create_module_file Name)
if(NOT CPM_DONT_UPDATE_MODULE_PATH)
if(DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR)
- # Redirect find_package calls to the CPM package. This is what FetchContent does when you set
- # OVERRIDE_FIND_PACKAGE. The CMAKE_FIND_PACKAGE_REDIRECTS_DIR works for find_package in CONFIG
- # mode, unlike the Find${Name}.cmake fallback. CMAKE_FIND_PACKAGE_REDIRECTS_DIR is not defined
- # in script mode, or in CMake < 3.24.
- # https://cmake.org/cmake/help/latest/module/FetchContent.html#fetchcontent-find-package-integration-examples
+ # Redirect find_package calls to the CPM package.
+ # This is what FetchContent does when you set
+ # OVERRIDE_FIND_PACKAGE. The CMAKE_FIND_PACKAGE_REDIRECTS_DIR works for
+ # find_package in CONFIG mode, unlike the Find${Name}.cmake fallback.
+ # CMAKE_FIND_PACKAGE_REDIRECTS_DIR is not defined in script mode
+ # https://cmake.org/cmake/help/latest/module/FetchContent.html
string(TOLOWER ${Name} NameLower)
file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config.cmake
- "include(\"\${CMAKE_CURRENT_LIST_DIR}/${NameLower}-extra.cmake\" OPTIONAL)\n"
- "include(\"\${CMAKE_CURRENT_LIST_DIR}/${Name}Extra.cmake\" OPTIONAL)\n"
- )
- file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config-version.cmake
- "set(PACKAGE_VERSION_COMPATIBLE TRUE)\n" "set(PACKAGE_VERSION_EXACT TRUE)\n"
- )
+ "include(\"\${CMAKE_CURRENT_LIST_DIR}/${NameLower}-extra.cmake\" \
+ OPTIONAL)\n"
+ "include(\"\${CMAKE_CURRENT_LIST_DIR}/${Name}Extra.cmake\" OPTIONAL)\n")
+
+ file(WRITE
+ ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config-version.cmake
+ "set(PACKAGE_VERSION_COMPATIBLE TRUE)\n"
+ "set(PACKAGE_VERSION_EXACT TRUE)\n")
else()
file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake
- "include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)"
- )
+ "include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)")
endif()
endif()
endfunction()
-# Find a package locally or fallback to CPMAddPackage
-function(CPMFindPackage)
- set(oneValueArgs NAME VERSION GIT_TAG FIND_PACKAGE_ARGUMENTS)
-
- cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN})
-
- if(NOT DEFINED CPM_ARGS_VERSION)
- if(DEFINED CPM_ARGS_GIT_TAG)
- cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION)
- endif()
- endif()
-
- set(downloadPackage ${CPM_DOWNLOAD_ALL})
- if(DEFINED CPM_DOWNLOAD_${CPM_ARGS_NAME})
- set(downloadPackage ${CPM_DOWNLOAD_${CPM_ARGS_NAME}})
- elseif(DEFINED ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}})
- set(downloadPackage $ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}})
- endif()
- if(downloadPackage)
- CPMAddPackage(${ARGN})
- cpm_export_variables(${CPM_ARGS_NAME})
- return()
- endif()
-
- cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS})
-
- if(NOT CPM_PACKAGE_FOUND)
- CPMAddPackage(${ARGN})
- cpm_export_variables(${CPM_ARGS_NAME})
- endif()
-
-endfunction()
-
# checks if a package has been added before
function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION)
if("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES)
CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION)
if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}")
- message(
- WARNING
- "${CPM_INDENT} Requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION})."
- )
+ message(WARNING
+ "${CPM_INDENT} Requires a newer version of ${CPM_ARGS_NAME} "
+ "(${CPM_ARGS_VERSION}) than currently included "
+ "(${CPM_PACKAGE_VERSION}).")
endif()
cpm_get_fetch_properties(${CPM_ARGS_NAME})
set(${CPM_ARGS_NAME}_ADDED NO)
- set(CPM_PACKAGE_ALREADY_ADDED
- YES
- PARENT_SCOPE
- )
+ set(CPM_PACKAGE_ALREADY_ADDED YES PARENT_SCOPE)
cpm_export_variables(${CPM_ARGS_NAME})
else()
- set(CPM_PACKAGE_ALREADY_ADDED
- NO
- PARENT_SCOPE
- )
- endif()
-endfunction()
-
-# Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of
-# arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted
-# to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3
-function(cpm_parse_add_package_single_arg arg outArgs)
- # Look for a scheme
- if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$")
- string(TOLOWER "${CMAKE_MATCH_1}" scheme)
- set(uri "${CMAKE_MATCH_2}")
-
- # Check for CPM-specific schemes
- if(scheme STREQUAL "gh")
- set(out "GITHUB_REPOSITORY;${uri}")
- set(packageType "git")
- elseif(scheme STREQUAL "gl")
- set(out "GITLAB_REPOSITORY;${uri}")
- set(packageType "git")
- elseif(scheme STREQUAL "bb")
- set(out "BITBUCKET_REPOSITORY;${uri}")
- set(packageType "git")
- # A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine
- # type
- elseif(arg MATCHES ".git/?(@|#|$)")
- set(out "GIT_REPOSITORY;${arg}")
- set(packageType "git")
- else()
- # Fall back to a URL
- set(out "URL;${arg}")
- set(packageType "archive")
-
- # We could also check for SVN since FetchContent supports it, but SVN is so rare these days.
- # We just won't bother with the additional complexity it will induce in this function. SVN is
- # done by multi-arg
- endif()
- else()
- if(arg MATCHES ".git/?(@|#|$)")
- set(out "GIT_REPOSITORY;${arg}")
- set(packageType "git")
- else()
- # Give up
- message(FATAL_ERROR "${CPM_INDENT} Can't determine package type of '${arg}'")
- endif()
- endif()
-
- # For all packages we interpret @... as version. Only replace the last occurrence. Thus URIs
- # containing '@' can be used
- string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}")
-
- # Parse the rest according to package type
- if(packageType STREQUAL "git")
- # For git repos we interpret #... as a tag or branch or commit hash
- string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}")
- elseif(packageType STREQUAL "archive")
- # For archives we interpret #... as a URL hash.
- string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}")
- # We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url
- # should do this at a later point
- else()
- # We should never get here. This is an assertion and hitting it means there's a problem with the
- # code above. A packageType was set, but not handled by this if-else.
- message(FATAL_ERROR "${CPM_INDENT} Unsupported package type '${packageType}' of '${arg}'")
- endif()
-
- set(${outArgs}
- ${out}
- PARENT_SCOPE
- )
-endfunction()
-
-# Check that the working directory for a git repo is clean
-function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean)
-
- find_package(Git REQUIRED)
-
- if(NOT GIT_EXECUTABLE)
- # No git executable, assume directory is clean
- set(${isClean}
- TRUE
- PARENT_SCOPE
- )
- return()
- endif()
-
- # check for uncommitted changes
- execute_process(
- COMMAND ${GIT_EXECUTABLE} status --porcelain
- RESULT_VARIABLE resultGitStatus
- OUTPUT_VARIABLE repoStatus
- OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET
- WORKING_DIRECTORY ${repoPath}
- )
- if(resultGitStatus)
- # not supposed to happen, assume clean anyway
- message(WARNING "${CPM_INDENT} Calling git status on folder ${repoPath} failed")
- set(${isClean}
- TRUE
- PARENT_SCOPE
- )
- return()
- endif()
-
- if(NOT "${repoStatus}" STREQUAL "")
- set(${isClean}
- FALSE
- PARENT_SCOPE
- )
- return()
- endif()
-
- # check for committed changes
- execute_process(
- COMMAND ${GIT_EXECUTABLE} diff -s --exit-code ${gitTag}
- RESULT_VARIABLE resultGitDiff
- OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_QUIET
- WORKING_DIRECTORY ${repoPath}
- )
-
- if(${resultGitDiff} EQUAL 0)
- set(${isClean}
- TRUE
- PARENT_SCOPE
- )
- else()
- set(${isClean}
- FALSE
- PARENT_SCOPE
- )
+ set(CPM_PACKAGE_ALREADY_ADDED NO PARENT_SCOPE)
endif()
-
endfunction()
-# Add PATCH_COMMAND to CPM_ARGS_UNPARSED_ARGUMENTS. This method consumes a list of files in ARGN
-# then generates a `PATCH_COMMAND` appropriate for `ExternalProject_Add()`. This command is appended
-# to the parent scope's `CPM_ARGS_UNPARSED_ARGUMENTS`.
-function(cpm_add_patches)
- # Return if no patch files are supplied.
- if(NOT ARGN)
- return()
- endif()
-
- # Find the patch program.
- find_program(PATCH_EXECUTABLE patch)
- if(CMAKE_HOST_WIN32 AND NOT PATCH_EXECUTABLE)
- # The Windows git executable is distributed with patch.exe. Find the path to the executable, if
- # it exists, then search `../usr/bin` and `../../usr/bin` for patch.exe.
- find_package(Git QUIET)
- if(GIT_EXECUTABLE)
- get_filename_component(extra_search_path ${GIT_EXECUTABLE} DIRECTORY)
- get_filename_component(extra_search_path_1up ${extra_search_path} DIRECTORY)
- get_filename_component(extra_search_path_2up ${extra_search_path_1up} DIRECTORY)
- find_program(
- PATCH_EXECUTABLE patch HINTS "${extra_search_path_1up}/usr/bin"
- "${extra_search_path_2up}/usr/bin"
- )
- endif()
- endif()
- if(NOT PATCH_EXECUTABLE)
- message(FATAL_ERROR "Couldn't find `patch` executable to use with PATCHES keyword.")
- endif()
-
- # Create a temporary
- set(temp_list ${CPM_ARGS_UNPARSED_ARGUMENTS})
-
- # Ensure each file exists (or error out) and add it to the list.
- set(first_item True)
- foreach(PATCH_FILE ${ARGN})
- # Make sure the patch file exists, if we can't find it, try again in the current directory.
- if(NOT EXISTS "${PATCH_FILE}")
- if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}")
- message(FATAL_ERROR "Couldn't find patch file: '${PATCH_FILE}'")
- endif()
- set(PATCH_FILE "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}")
- endif()
-
- # Convert to absolute path for use with patch file command.
- get_filename_component(PATCH_FILE "${PATCH_FILE}" ABSOLUTE)
-
- # The first patch entry must be preceded by "PATCH_COMMAND" while the following items are
- # preceded by "&&".
- if(first_item)
- set(first_item False)
- list(APPEND temp_list "PATCH_COMMAND")
- else()
- list(APPEND temp_list "&&")
- endif()
- # Add the patch command to the list
- list(APPEND temp_list "${PATCH_EXECUTABLE}" "-p1" "<" "${PATCH_FILE}")
- endforeach()
-
- # Move temp out into parent scope.
- set(CPM_ARGS_UNPARSED_ARGUMENTS
- ${temp_list}
- PARENT_SCOPE
- )
-
-endfunction()
-
-# method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload
-# FetchContent calls. As these are internal cmake properties, this method should be used carefully
-# and may need modification in future CMake versions. Source:
-# https://github.com/Kitware/CMake/blob/dc3d0b5a0a7d26d43d6cfeb511e224533b5d188f/Modules/FetchContent.cmake#L1152
-function(cpm_override_fetchcontent contentName)
- cmake_parse_arguments(PARSE_ARGV 1 arg "" "SOURCE_DIR;BINARY_DIR" "")
- if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "")
- message(FATAL_ERROR "${CPM_INDENT} Unsupported arguments: ${arg_UNPARSED_ARGUMENTS}")
- endif()
-
- 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} "${arg_SOURCE_DIR}")
-
- 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} "${arg_BINARY_DIR}")
-
- 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()
-
-# Download and add a package from source
function(CPMAddPackage)
- cpm_set_policies()
-
set(oneValueArgs
NAME
FORCE
VERSION
- GIT_TAG
DOWNLOAD_ONLY
- GITHUB_REPOSITORY
- GITLAB_REPOSITORY
- BITBUCKET_REPOSITORY
- GIT_REPOSITORY
SOURCE_DIR
- FIND_PACKAGE_ARGUMENTS
- NO_CACHE
SYSTEM
- GIT_SHALLOW
EXCLUDE_FROM_ALL
- SOURCE_SUBDIR
- CUSTOM_CACHE_KEY
- )
-
- set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES)
+ SOURCE_SUBDIR)
- list(LENGTH ARGN argnLength)
+ set(multiValueArgs OPTIONS)
- # Parse single shorthand argument
- if(argnLength EQUAL 1)
- cpm_parse_add_package_single_arg("${ARGN}" ARGN)
-
- # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM
- set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;")
-
- # Parse URI shorthand argument
- elseif(argnLength GREATER 1 AND "${ARGV0}" STREQUAL "URI")
- list(REMOVE_AT ARGN 0 1) # remove "URI gh:<...>@version#tag"
- cpm_parse_add_package_single_arg("${ARGV1}" ARGV0)
-
- set(ARGN "${ARGV0};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;${ARGN}")
- endif()
-
- cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
+ cmake_parse_arguments(CPM_ARGS
+ ""
+ "${oneValueArgs}"
+ "${multiValueArgs}"
+ "${ARGN}")
- # Set default values for arguments
- if(NOT DEFINED CPM_ARGS_VERSION)
- if(DEFINED CPM_ARGS_GIT_TAG)
- cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION)
- endif()
+ if (NOT DEFINED CPM_ARGS_SOURCE_DIR)
+ message(FATAL_ERROR "${CPM_INDENT} SOURCE_DIR is required")
endif()
if(CPM_ARGS_DOWNLOAD_ONLY)
set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY})
else()
- set(DOWNLOAD_ONLY NO)
+ # Default to DOWNLOAD_ONLY in script mode.
+ set(DOWNLOAD_ONLY ${CMAKE_SCRIPT_MODE_FILE})
endif()
- if(DEFINED CPM_ARGS_GITHUB_REPOSITORY)
- set(CPM_ARGS_GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git")
- elseif(DEFINED CPM_ARGS_GITLAB_REPOSITORY)
- set(CPM_ARGS_GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git")
- elseif(DEFINED CPM_ARGS_BITBUCKET_REPOSITORY)
- set(CPM_ARGS_GIT_REPOSITORY "https://bitbucket.org/${CPM_ARGS_BITBUCKET_REPOSITORY}.git")
- endif()
-
- if(DEFINED CPM_ARGS_GIT_REPOSITORY)
- list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY})
- if(NOT DEFINED CPM_ARGS_GIT_TAG)
- set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION})
- endif()
-
- # If a name wasn't provided, try to infer it from the git repo
- if(NOT DEFINED CPM_ARGS_NAME)
- cpm_package_name_from_git_uri(${CPM_ARGS_GIT_REPOSITORY} CPM_ARGS_NAME)
- endif()
- endif()
-
- set(CPM_SKIP_FETCH FALSE)
-
- if(DEFINED CPM_ARGS_GIT_TAG)
- list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG})
- # If GIT_SHALLOW is explicitly specified, honor the value.
- if(DEFINED CPM_ARGS_GIT_SHALLOW)
- list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW ${CPM_ARGS_GIT_SHALLOW})
- endif()
- endif()
-
- if(DEFINED CPM_ARGS_URL)
- # If a name or version aren't provided, try to infer them from the URL
- list(GET CPM_ARGS_URL 0 firstUrl)
- cpm_package_name_and_ver_from_url(${firstUrl} nameFromUrl verFromUrl)
- # If we fail to obtain name and version from the first URL, we could try other URLs if any.
- # However multiple URLs are expected to be quite rare, so for now we won't bother.
-
- # If the caller provided their own name and version, they trump the inferred ones.
- if(NOT DEFINED CPM_ARGS_NAME)
- set(CPM_ARGS_NAME ${nameFromUrl})
- endif()
-
- # this is dumb and should not be done
- # if(NOT DEFINED CPM_ARGS_VERSION)
- # set(CPM_ARGS_VERSION ${verFromUrl})
- # endif()
-
- list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS URL "${CPM_ARGS_URL}")
+ if(NOT DEFINED CPM_ARGS_NAME)
+ message(FATAL_ERROR "${CPM_INDENT} NAME is required")
endif()
- # Check for required arguments
-
- if(NOT DEFINED CPM_ARGS_NAME)
- message(
- FATAL_ERROR
- "${CPM_INDENT} 'NAME' was not provided and couldn't be automatically inferred for package added with arguments: '${ARGN}'"
- )
+ if(NOT DEFINED CPM_ARGS_VERSION)
+ message(FATAL_ERROR "${CPM_INDENT} VERSION is required")
endif()
- # Check if package has been added before
cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}")
if(CPM_PACKAGE_ALREADY_ADDED)
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
- # Check for manual overrides
if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "")
set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE})
set(CPM_${CPM_ARGS_NAME}_SOURCE "")
CPMAddPackage(
NAME "${CPM_ARGS_NAME}"
+ VERSION "${CPM_ARGS_VERSION}"
SOURCE_DIR "${PACKAGE_SOURCE}"
EXCLUDE_FROM_ALL "${CPM_ARGS_EXCLUDE_FROM_ALL}"
SYSTEM "${CPM_ARGS_SYSTEM}"
@@ -781,455 +196,116 @@ function(CPMAddPackage)
OPTIONS "${CPM_ARGS_OPTIONS}"
SOURCE_SUBDIR "${CPM_ARGS_SOURCE_SUBDIR}"
DOWNLOAD_ONLY "${DOWNLOAD_ONLY}"
- FORCE True
- )
- cpm_export_variables(${CPM_ARGS_NAME})
- return()
- endif()
+ FORCE True)
- # Check for available declaration
- if(NOT CPM_ARGS_FORCE AND NOT "${CPM_DECLARATION_${CPM_ARGS_NAME}}" STREQUAL "")
- set(declaration ${CPM_DECLARATION_${CPM_ARGS_NAME}})
- set(CPM_DECLARATION_${CPM_ARGS_NAME} "")
- CPMAddPackage(${declaration})
cpm_export_variables(${CPM_ARGS_NAME})
- # checking again to ensure version and option compatibility
- cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}")
return()
endif()
- if(NOT CPM_ARGS_FORCE)
- if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY)
- cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS})
-
- if(CPM_PACKAGE_FOUND)
- cpm_export_variables(${CPM_ARGS_NAME})
- return()
- endif()
-
- if(CPM_LOCAL_PACKAGES_ONLY)
- message(
- SEND_ERROR
- "${CPM_INDENT} ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})"
- )
- endif()
- endif()
- endif()
-
CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}")
- if(DEFINED CPM_ARGS_GIT_TAG)
- set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}")
- elseif(DEFINED CPM_ARGS_SOURCE_DIR)
- set(PACKAGE_INFO "${CPM_ARGS_SOURCE_DIR}")
- else()
- set(PACKAGE_INFO "${CPM_ARGS_VERSION}")
- endif()
-
if(DEFINED FETCHCONTENT_BASE_DIR)
- # respect user's FETCHCONTENT_BASE_DIR if set
set(CPM_FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR})
else()
set(CPM_FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps)
endif()
- cpm_add_patches(${CPM_ARGS_PATCHES})
+ # Subbuild, etc
+ string(TOLOWER ${CPM_ARGS_NAME} lower_case_name)
- if(DEFINED CPM_ARGS_DOWNLOAD_COMMAND)
- list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND})
- elseif(DEFINED CPM_ARGS_SOURCE_DIR)
- list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR})
- if(NOT IS_ABSOLUTE ${CPM_ARGS_SOURCE_DIR})
- # Expand `CPM_ARGS_SOURCE_DIR` relative path. This is important because EXISTS doesn't work
- # for relative paths.
- get_filename_component(
- source_directory ${CPM_ARGS_SOURCE_DIR} REALPATH BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}
- )
- else()
- set(source_directory ${CPM_ARGS_SOURCE_DIR})
- endif()
- if(NOT EXISTS ${source_directory})
- string(TOLOWER ${CPM_ARGS_NAME} lower_case_name)
- # remove timestamps so CMake will re-download the dependency
- file(REMOVE_RECURSE "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild")
- endif()
- elseif(CPM_SOURCE_CACHE AND NOT CPM_ARGS_NO_CACHE)
- string(TOLOWER ${CPM_ARGS_NAME} lower_case_name)
- set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS})
- list(SORT origin_parameters)
- if(CPM_ARGS_CUSTOM_CACHE_KEY)
- # Application set a custom unique directory name
- set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${CPM_ARGS_CUSTOM_CACHE_KEY})
- elseif(CPM_USE_NAMED_CACHE_DIRECTORIES)
- string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG")
- cpm_get_shortest_hash(
- "${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory
- "${origin_hash}" # Input hash
- origin_hash # Computed hash
- )
- set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME})
- else()
- string(SHA1 origin_hash "${origin_parameters}")
- cpm_get_shortest_hash(
- "${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory
- "${origin_hash}" # Input hash
- origin_hash # Computed hash
- )
- set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash})
- endif()
- # Expand `download_directory` relative path. This is important because EXISTS doesn't work for
- # relative paths.
- get_filename_component(download_directory ${download_directory} ABSOLUTE)
- list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${download_directory})
-
- if(CPM_SOURCE_CACHE)
- file(LOCK ${download_directory}/../cmake.lock)
- endif()
-
- if(EXISTS ${download_directory})
- if(CPM_SOURCE_CACHE)
- file(LOCK ${download_directory}/../cmake.lock RELEASE)
- endif()
-
- cpm_store_fetch_properties(
- ${CPM_ARGS_NAME} "${download_directory}"
- "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build"
- )
- cpm_get_fetch_properties("${CPM_ARGS_NAME}")
-
- if(DEFINED CPM_ARGS_GIT_TAG AND NOT (PATCH_COMMAND IN_LIST CPM_ARGS_UNPARSED_ARGUMENTS))
- # warn if cache has been changed since checkout
- cpm_check_git_working_dir_is_clean(${download_directory} ${CPM_ARGS_GIT_TAG} IS_CLEAN)
- if(NOT ${IS_CLEAN})
- message(
- WARNING "${CPM_INDENT} Cache for ${CPM_ARGS_NAME} (${download_directory}) is dirty"
- )
- endif()
- endif()
-
- cpm_add_subdirectory(
- "${CPM_ARGS_NAME}"
- "${DOWNLOAD_ONLY}"
- "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}"
- "${${CPM_ARGS_NAME}_BINARY_DIR}"
- "${CPM_ARGS_EXCLUDE_FROM_ALL}"
- "${CPM_ARGS_SYSTEM}"
- "${CPM_ARGS_OPTIONS}"
- )
- set(PACKAGE_INFO "${PACKAGE_INFO} at ${download_directory}")
-
- # As the source dir is already cached/populated, we override the call to FetchContent.
- set(CPM_SKIP_FETCH TRUE)
- cpm_override_fetchcontent(
- "${lower_case_name}" SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}"
- BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}"
- )
-
- else()
- # Enable shallow clone when GIT_TAG is not a commit hash. Our guess may not be accurate, but
- # it should guarantee no commit hash get mis-detected.
- if(NOT DEFINED CPM_ARGS_GIT_SHALLOW)
- cpm_is_git_tag_commit_hash("${CPM_ARGS_GIT_TAG}" IS_HASH)
- if(NOT ${IS_HASH})
- list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW TRUE)
- endif()
- endif()
-
- # remove timestamps so CMake will re-download the dependency
- file(REMOVE_RECURSE ${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild)
- set(PACKAGE_INFO "${PACKAGE_INFO} to ${download_directory}")
- endif()
+ set(fetch_source_dir ${CPM_ARGS_SOURCE_DIR})
+ if(NOT IS_ABSOLUTE ${CPM_ARGS_SOURCE_DIR})
+ get_filename_component(
+ fetch_source_dir ${CPM_ARGS_SOURCE_DIR}
+ REALPATH BASE_DIR ${CMAKE_CURRENT_BINARY_DIR})
endif()
- if(NOT "${DOWNLOAD_ONLY}")
- cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")")
+ # FetchContent args
+ set(fetchContentDeclareExtraArgs "")
+ if(${CPM_ARGS_EXCLUDE_FROM_ALL})
+ list(APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL)
endif()
- if(CPM_PACKAGE_LOCK_ENABLED)
- if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK)
- cpm_add_to_package_lock(${CPM_ARGS_NAME} "${ARGN}")
- elseif(CPM_ARGS_SOURCE_DIR)
- cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "local directory")
- else()
- cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "${ARGN}")
- endif()
+ if(CPM_ARGS_SYSTEM)
+ list(APPEND fetchContentDeclareExtraArgs SYSTEM)
endif()
- cpm_message(
- STATUS "${CPM_INDENT} Adding package ${CPM_ARGS_NAME}@${CPM_ARGS_VERSION} (${PACKAGE_INFO})"
- )
+ if(DEFINED CPM_ARGS_SOURCE_SUBDIR)
+ list(APPEND fetchContentDeclareExtraArgs
+ SOURCE_SUBDIR ${CPM_ARGS_SOURCE_SUBDIR})
+ endif()
- if(NOT CPM_SKIP_FETCH)
- # CMake 3.28 added EXCLUDE, SYSTEM (3.25), and SOURCE_SUBDIR (3.18) to FetchContent_Declare.
- # Calling FetchContent_MakeAvailable will then internally forward these options to
- # add_subdirectory. Up until these changes, we had to call FetchContent_Populate and
- # add_subdirectory separately, which is no longer necessary and has been deprecated as of 3.30.
- # A Bug in CMake prevents us to use the non-deprecated functions until 3.30.3.
- set(fetchContentDeclareExtraArgs "")
- if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3")
- if(${CPM_ARGS_EXCLUDE_FROM_ALL})
- list(APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL)
- endif()
- if(${CPM_ARGS_SYSTEM})
- list(APPEND fetchContentDeclareExtraArgs SYSTEM)
- endif()
- if(DEFINED CPM_ARGS_SOURCE_SUBDIR)
- list(APPEND fetchContentDeclareExtraArgs SOURCE_SUBDIR ${CPM_ARGS_SOURCE_SUBDIR})
- endif()
- # For CMake version <3.28 OPTIONS are parsed in cpm_add_subdirectory
- if(CPM_ARGS_OPTIONS AND NOT DOWNLOAD_ONLY)
- foreach(OPTION ${CPM_ARGS_OPTIONS})
- cpm_parse_option("${OPTION}")
- set(${OPTION_KEY} "${OPTION_VALUE}")
- endforeach()
- endif()
- endif()
- cpm_declare_fetch(
- "${CPM_ARGS_NAME}" ${fetchContentDeclareExtraArgs} "${CPM_ARGS_UNPARSED_ARGUMENTS}"
- )
+ if(CPM_ARGS_OPTIONS AND NOT DOWNLOAD_ONLY)
+ foreach(OPTION ${CPM_ARGS_OPTIONS})
+ cpm_parse_option("${OPTION}")
+ set(${OPTION_KEY} "${OPTION_VALUE}")
+ endforeach()
+ endif()
- cpm_fetch_package("${CPM_ARGS_NAME}" ${DOWNLOAD_ONLY} populated ${CPM_ARGS_UNPARSED_ARGUMENTS})
- if(CPM_SOURCE_CACHE AND download_directory)
- file(LOCK ${download_directory}/../cmake.lock RELEASE)
- endif()
- if(${populated} AND ${CMAKE_VERSION} VERSION_LESS "3.30.3")
- cpm_add_subdirectory(
- "${CPM_ARGS_NAME}"
- "${DOWNLOAD_ONLY}"
- "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}"
- "${${CPM_ARGS_NAME}_BINARY_DIR}"
- "${CPM_ARGS_EXCLUDE_FROM_ALL}"
- "${CPM_ARGS_SYSTEM}"
- "${CPM_ARGS_OPTIONS}"
- )
- endif()
- cpm_get_fetch_properties("${CPM_ARGS_NAME}")
+ if(NOT DOWNLOAD_ONLY)
+ cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")")
endif()
- set(${CPM_ARGS_NAME}_ADDED YES)
- cpm_export_variables("${CPM_ARGS_NAME}")
-endfunction()
+ FetchContent_Declare(${CPM_ARGS_NAME}
+ ${fetchContentDeclareExtraArgs}
+ SOURCE_DIR ${fetch_source_dir}
+ DOWNLOAD_NO_PROGRESS TRUE)
-# Fetch a previously declared package
-macro(CPMGetPackage Name)
- if(DEFINED "CPM_DECLARATION_${Name}")
- CPMAddPackage(NAME ${Name})
+ if(DOWNLOAD_ONLY)
+ FetchContent_Populate(${CPM_ARGS_NAME}
+ ${fetchContentDeclareExtraArgs}
+ SOURCE_DIR "${fetch_source_dir}"
+ BINARY_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build"
+ SUBBUILD_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild"
+ DOWNLOAD_NO_PROGRESS TRUE)
else()
- message(SEND_ERROR "${CPM_INDENT} Cannot retrieve package ${Name}: no declaration available")
+ FetchContent_MakeAvailable(${CPM_ARGS_NAME})
endif()
-endmacro()
-
-# export variables available to the caller to the parent scope expects ${CPM_ARGS_NAME} to be set
-macro(cpm_export_variables name)
- set(${name}_SOURCE_DIR
- "${${name}_SOURCE_DIR}"
- PARENT_SCOPE
- )
- set(${name}_BINARY_DIR
- "${${name}_BINARY_DIR}"
- PARENT_SCOPE
- )
- set(${name}_ADDED
- "${${name}_ADDED}"
- PARENT_SCOPE
- )
- set(CPM_LAST_PACKAGE_NAME
- "${name}"
- PARENT_SCOPE
- )
-endmacro()
-# declares a package, so that any call to CPMAddPackage for the package name will use these
-# arguments instead. Previous declarations will not be overridden.
-macro(CPMDeclarePackage Name)
- if(NOT DEFINED "CPM_DECLARATION_${Name}")
- set("CPM_DECLARATION_${Name}" "${ARGN}")
- endif()
-endmacro()
+ FetchContent_GetProperties(${CPM_ARGS_NAME})
+ cpm_store_fetch_properties(
+ ${CPM_ARGS_NAME}
+ ${${lower_case_name}_SOURCE_DIR}
+ ${${lower_case_name}_BINARY_DIR})
-function(cpm_add_to_package_lock Name)
- if(NOT CPM_DONT_CREATE_PACKAGE_LOCK)
- cpm_prettify_package_arguments(PRETTY_ARGN false ${ARGN})
- file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name}\nCPMDeclarePackage(${Name}\n${PRETTY_ARGN})\n")
- endif()
-endfunction()
+ cpm_get_fetch_properties("${CPM_ARGS_NAME}")
-function(cpm_add_comment_to_package_lock Name)
- if(NOT CPM_DONT_CREATE_PACKAGE_LOCK)
- cpm_prettify_package_arguments(PRETTY_ARGN true ${ARGN})
- file(APPEND ${CPM_PACKAGE_LOCK_FILE}
- "# ${Name} (unversioned)\n# CPMDeclarePackage(${Name}\n${PRETTY_ARGN}#)\n"
- )
- endif()
+ set(${CPM_ARGS_NAME}_ADDED YES)
+ cpm_export_variables("${CPM_ARGS_NAME}")
endfunction()
-# includes the package lock file if it exists and creates a target `cpm-update-package-lock` to
-# update it
-macro(CPMUsePackageLock file)
- if(NOT CPM_DONT_CREATE_PACKAGE_LOCK)
- get_filename_component(CPM_ABSOLUTE_PACKAGE_LOCK_PATH ${file} ABSOLUTE)
- if(EXISTS ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH})
- include(${CPM_ABSOLUTE_PACKAGE_LOCK_PATH})
- endif()
- if(NOT TARGET cpm-update-package-lock)
- add_custom_target(
- cpm-update-package-lock COMMAND ${CMAKE_COMMAND} -E copy ${CPM_PACKAGE_LOCK_FILE}
- ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}
- )
- endif()
- set(CPM_PACKAGE_LOCK_ENABLED true)
- endif()
+# export variables available to the caller to the
+# parent scope expects ${CPM_ARGS_NAME} to be set
+macro(cpm_export_variables name)
+ set(${name}_SOURCE_DIR "${${name}_SOURCE_DIR}" PARENT_SCOPE)
+ set(${name}_BINARY_DIR "${${name}_BINARY_DIR}" PARENT_SCOPE)
+ set(${name}_ADDED "${${name}_ADDED}" PARENT_SCOPE)
+ set(CPM_LAST_PACKAGE_NAME "${name}" PARENT_SCOPE)
endmacro()
# registers a package that has been added to CPM
function(CPMRegisterPackage PACKAGE VERSION)
list(APPEND CPM_PACKAGES ${PACKAGE})
- set(CPM_PACKAGES
- ${CPM_PACKAGES}
- CACHE INTERNAL ""
- )
- set("CPM_PACKAGE_${PACKAGE}_VERSION"
- ${VERSION}
- CACHE INTERNAL ""
- )
+ set(CPM_PACKAGES ${CPM_PACKAGES} CACHE INTERNAL "")
+ set("CPM_PACKAGE_${PACKAGE}_VERSION" ${VERSION}CACHE INTERNAL "")
endfunction()
# retrieve the current version of the package to ${OUTPUT}
function(CPMGetPackageVersion PACKAGE OUTPUT)
- set(${OUTPUT}
- "${CPM_PACKAGE_${PACKAGE}_VERSION}"
- PARENT_SCOPE
- )
+ set(${OUTPUT} "${CPM_PACKAGE_${PACKAGE}_VERSION}" PARENT_SCOPE)
endfunction()
-# declares a package in FetchContent_Declare
-function(cpm_declare_fetch PACKAGE)
- if(${CPM_DRY_RUN})
- cpm_message(STATUS "${CPM_INDENT} Package not declared (dry run)")
- return()
- endif()
-
- FetchContent_Declare(${PACKAGE} ${ARGN})
-endfunction()
-
-# returns properties for a package previously defined by cpm_declare_fetch
function(cpm_get_fetch_properties PACKAGE)
- if(${CPM_DRY_RUN})
- return()
- endif()
+ set(${PACKAGE}_SOURCE_DIR "${CPM_PACKAGE_${PACKAGE}_SOURCE_DIR}"
+ PARENT_SCOPE)
- set(${PACKAGE}_SOURCE_DIR
- "${CPM_PACKAGE_${PACKAGE}_SOURCE_DIR}"
- PARENT_SCOPE
- )
- set(${PACKAGE}_BINARY_DIR
- "${CPM_PACKAGE_${PACKAGE}_BINARY_DIR}"
- PARENT_SCOPE
- )
+ set(${PACKAGE}_BINARY_DIR "${CPM_PACKAGE_${PACKAGE}_BINARY_DIR}"
+ PARENT_SCOPE)
endfunction()
function(cpm_store_fetch_properties PACKAGE source_dir binary_dir)
- if(${CPM_DRY_RUN})
- return()
- endif()
-
- set(CPM_PACKAGE_${PACKAGE}_SOURCE_DIR
- "${source_dir}"
- CACHE INTERNAL ""
- )
- set(CPM_PACKAGE_${PACKAGE}_BINARY_DIR
- "${binary_dir}"
- CACHE INTERNAL ""
- )
-endfunction()
-
-# adds a package as a subdirectory if viable, according to provided options
-function(
- cpm_add_subdirectory
- PACKAGE
- DOWNLOAD_ONLY
- SOURCE_DIR
- BINARY_DIR
- EXCLUDE
- SYSTEM
- OPTIONS
-)
-
- if(NOT DOWNLOAD_ONLY AND EXISTS ${SOURCE_DIR}/CMakeLists.txt)
- set(addSubdirectoryExtraArgs "")
- if(EXCLUDE)
- list(APPEND addSubdirectoryExtraArgs EXCLUDE_FROM_ALL)
- endif()
- if("${SYSTEM}" AND "${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.25")
- # https://cmake.org/cmake/help/latest/prop_dir/SYSTEM.html#prop_dir:SYSTEM
- list(APPEND addSubdirectoryExtraArgs SYSTEM)
- endif()
- if(OPTIONS)
- foreach(OPTION ${OPTIONS})
- cpm_parse_option("${OPTION}")
- set(${OPTION_KEY} "${OPTION_VALUE}")
- endforeach()
- endif()
- set(CPM_OLD_INDENT "${CPM_INDENT}")
- set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:")
- add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs})
- set(CPM_INDENT "${CPM_OLD_INDENT}")
- endif()
-endfunction()
-
-# downloads a previously declared package via FetchContent and exports the variables
-# `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope
-function(cpm_fetch_package PACKAGE DOWNLOAD_ONLY populated)
- set(${populated}
- FALSE
- PARENT_SCOPE
- )
- if(${CPM_DRY_RUN})
- cpm_message(STATUS "${CPM_INDENT} Package ${PACKAGE} not fetched (dry run)")
- return()
- endif()
-
- FetchContent_GetProperties(${PACKAGE})
-
- string(TOLOWER "${PACKAGE}" lower_case_name)
-
- if(NOT ${lower_case_name}_POPULATED)
- if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3")
- if(DOWNLOAD_ONLY)
- # MakeAvailable will call add_subdirectory internally which is not what we want when
- # DOWNLOAD_ONLY is set. Populate will only download the dependency without adding it to the
- # build
- FetchContent_Populate(
- ${PACKAGE}
- SOURCE_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-src"
- BINARY_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build"
- SUBBUILD_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild"
- ${ARGN}
- )
- else()
- FetchContent_MakeAvailable(${PACKAGE})
- endif()
- else()
- FetchContent_Populate(${PACKAGE})
- endif()
- set(${populated}
- TRUE
- PARENT_SCOPE
- )
- endif()
-
- cpm_store_fetch_properties(
- ${CPM_ARGS_NAME} ${${lower_case_name}_SOURCE_DIR} ${${lower_case_name}_BINARY_DIR}
- )
-
- set(${PACKAGE}_SOURCE_DIR
- ${${lower_case_name}_SOURCE_DIR}
- PARENT_SCOPE
- )
- set(${PACKAGE}_BINARY_DIR
- ${${lower_case_name}_BINARY_DIR}
- PARENT_SCOPE
- )
+ set(CPM_PACKAGE_${PACKAGE}_SOURCE_DIR "${source_dir}" CACHE INTERNAL "")
+ set(CPM_PACKAGE_${PACKAGE}_BINARY_DIR "${binary_dir}" CACHE INTERNAL "")
endfunction()
# splits a package option
@@ -1237,6 +313,7 @@ function(cpm_parse_option OPTION)
string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}")
string(LENGTH "${OPTION}" OPTION_LENGTH)
string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH)
+
if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH)
# no value for key provided, assume user wants to set option to "ON"
set(OPTION_VALUE "ON")
@@ -1244,122 +321,7 @@ function(cpm_parse_option OPTION)
math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1")
string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE)
endif()
- set(OPTION_KEY
- "${OPTION_KEY}"
- PARENT_SCOPE
- )
- set(OPTION_VALUE
- "${OPTION_VALUE}"
- PARENT_SCOPE
- )
-endfunction()
-
-# guesses the package version from a git tag
-function(cpm_get_version_from_git_tag GIT_TAG RESULT)
- string(LENGTH ${GIT_TAG} length)
- if(length EQUAL 40)
- # GIT_TAG is probably a git hash
- set(${RESULT}
- 0
- PARENT_SCOPE
- )
- else()
- string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG})
- set(${RESULT}
- ${CMAKE_MATCH_1}
- PARENT_SCOPE
- )
- endif()
-endfunction()
-
-# guesses if the git tag is a commit hash or an actual tag or a branch name.
-function(cpm_is_git_tag_commit_hash GIT_TAG RESULT)
- string(LENGTH "${GIT_TAG}" length)
- # full hash has 40 characters, and short hash has at least 7 characters.
- if(length LESS 7 OR length GREATER 40)
- set(${RESULT}
- 0
- PARENT_SCOPE
- )
- else()
- if(${GIT_TAG} MATCHES "^[a-fA-F0-9]+$")
- set(${RESULT}
- 1
- PARENT_SCOPE
- )
- else()
- set(${RESULT}
- 0
- PARENT_SCOPE
- )
- endif()
- endif()
-endfunction()
-
-function(cpm_prettify_package_arguments OUT_VAR IS_IN_COMMENT)
- set(oneValueArgs
- NAME
- FORCE
- VERSION
- GIT_TAG
- DOWNLOAD_ONLY
- GITHUB_REPOSITORY
- GITLAB_REPOSITORY
- BITBUCKET_REPOSITORY
- GIT_REPOSITORY
- SOURCE_DIR
- FIND_PACKAGE_ARGUMENTS
- NO_CACHE
- SYSTEM
- GIT_SHALLOW
- EXCLUDE_FROM_ALL
- SOURCE_SUBDIR
- )
- set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND)
- cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-
- foreach(oneArgName ${oneValueArgs})
- if(DEFINED CPM_ARGS_${oneArgName})
- if(${IS_IN_COMMENT})
- string(APPEND PRETTY_OUT_VAR "#")
- endif()
- if(${oneArgName} STREQUAL "SOURCE_DIR")
- string(REPLACE ${CMAKE_SOURCE_DIR} "\${CMAKE_SOURCE_DIR}" CPM_ARGS_${oneArgName}
- ${CPM_ARGS_${oneArgName}}
- )
- endif()
- string(APPEND PRETTY_OUT_VAR " ${oneArgName} ${CPM_ARGS_${oneArgName}}\n")
- endif()
- endforeach()
- foreach(multiArgName ${multiValueArgs})
- if(DEFINED CPM_ARGS_${multiArgName})
- if(${IS_IN_COMMENT})
- string(APPEND PRETTY_OUT_VAR "#")
- endif()
- string(APPEND PRETTY_OUT_VAR " ${multiArgName}\n")
- foreach(singleOption ${CPM_ARGS_${multiArgName}})
- if(${IS_IN_COMMENT})
- string(APPEND PRETTY_OUT_VAR "#")
- endif()
- string(APPEND PRETTY_OUT_VAR " \"${singleOption}\"\n")
- endforeach()
- endif()
- endforeach()
-
- if(NOT "${CPM_ARGS_UNPARSED_ARGUMENTS}" STREQUAL "")
- if(${IS_IN_COMMENT})
- string(APPEND PRETTY_OUT_VAR "#")
- endif()
- string(APPEND PRETTY_OUT_VAR " ")
- foreach(CPM_ARGS_UNPARSED_ARGUMENT ${CPM_ARGS_UNPARSED_ARGUMENTS})
- string(APPEND PRETTY_OUT_VAR " ${CPM_ARGS_UNPARSED_ARGUMENT}")
- endforeach()
- string(APPEND PRETTY_OUT_VAR "\n")
- endif()
-
- set(${OUT_VAR}
- ${PRETTY_OUT_VAR}
- PARENT_SCOPE
- )
+ set(OPTION_KEY "${OPTION_KEY}" PARENT_SCOPE)
+ set(OPTION_VALUE "${OPTION_VALUE}" PARENT_SCOPE)
endfunction()
diff --git a/CMakeModules/CPMUtil.cmake b/CMakeModules/CPMUtil.cmake
index 94b0489432..3144f832c2 100644
--- a/CMakeModules/CPMUtil.cmake
+++ b/CMakeModules/CPMUtil.cmake
@@ -9,7 +9,22 @@ else()
set(BUNDLED_DEFAULT OFF)
endif()
-set(CPM_SOURCE_CACHE "${PROJECT_SOURCE_DIR}/.cache/cpm" CACHE STRING "" FORCE)
+# TODO: Handle nested cpmutil includes
+
+# Root directory detection
+if (CMAKE_SCRIPT_MODE_FILE)
+ # In script mode, default to PWD
+ # Or you can specify one yourself
+ if (NOT DEFINED CPMUTIL_ROOT)
+ set(CPMUTIL_ROOT ${CMAKE_SOURCE_DIR})
+ endif()
+else()
+ # A project exists, we can use its directory
+ set(CPMUTIL_ROOT ${PROJECT_SOURCE_DIR})
+endif()
+
+set(CPM_SOURCE_CACHE "${CPMUTIL_ROOT}/.cache/cpm"
+ CACHE PATH "Directory to download CPM dependencies")
option(CPMUTIL_FORCE_BUNDLED
"Force bundled packages for all CPM depdendencies" ${BUNDLED_DEFAULT})
@@ -17,13 +32,17 @@ option(CPMUTIL_FORCE_BUNDLED
option(CPMUTIL_FORCE_SYSTEM
"Force system packages for all CPM dependencies" OFF)
-set(CPMUTIL_PATCH_DIR "${PROJECT_SOURCE_DIR}/.patch" CACHE STRING
+set(CPMUTIL_PATCH_DIR "${CPMUTIL_ROOT}/.patch" CACHE STRING
"Directory containing patches for packages")
include(CPM)
# Rudimentary target architecture detection
-if (NOT DEFINED ARCHITECTURE)
+# This is in script mode, CI packages will be nonfunctional. TODO
+if (NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
+ message(DEBUG "[CPMUtil] No system architecture found, "
+ "CI packages are nonfunctional")
+elseif (NOT DEFINED ARCHITECTURE)
string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} processor)
if (processor MATCHES "x86|amd64")
set(CPMUTIL_AMD64 ON)
@@ -44,26 +63,391 @@ else()
endif()
# cpmfile parsing
-set(CPMUTIL_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json")
+set(CPMUTIL_JSON_FILE "${CPMUTIL_ROOT}/cpmfile.json")
if(EXISTS ${CPMUTIL_JSON_FILE})
file(READ ${CPMUTIL_JSON_FILE} CPMFILE_CONTENT)
- if (NOT TARGET cpmfiles)
- add_custom_target(cpmfiles)
- endif()
- target_sources(cpmfiles PRIVATE ${CPMUTIL_JSON_FILE})
- set_property(DIRECTORY APPEND PROPERTY
- CMAKE_CONFIGURE_DEPENDS
- "${CPMUTIL_JSON_FILE}")
+ # Script mode doesn't support add_custom_target etc.
+ if (NOT CMAKE_SCRIPT_MODE_FILE)
+ if (NOT TARGET cpmfiles)
+ add_custom_target(cpmfiles)
+ endif()
+
+ target_sources(cpmfiles PRIVATE ${CPMUTIL_JSON_FILE})
+ set_property(DIRECTORY APPEND PROPERTY
+ CMAKE_CONFIGURE_DEPENDS
+ "${CPMUTIL_JSON_FILE}")
+ endif()
else()
- message(DEBUG "[CPMUtil] cpmfile ${CPMUTIL_JSON_FILE}"
+ message(DEBUG "[CPMUtil] cpmfile ${CPMUTIL_JSON_FILE} "
"does not exist, AddJsonPackage will be a no-op")
endif()
-# Utility stuff
-function(cpm_utils_message level name message)
- message(${level} "[CPMUtil] ${name}: ${message}")
+#############
+# UTILITIES #
+#############
+
+# Analogous to POSIX echo
+macro(echo)
+ string(REPLACE ";" " " message "${ARGN}")
+ if (CMAKE_SCRIPT_MODE_FILE)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+ "${message}")
+ else()
+ message(STATUS "${message}")
+ endif()
+endmacro()
+
+# Analogous to POSIX echo, but to stderr
+macro(echo_error)
+ string(REPLACE ";" " " message "${ARGN}")
+ message(NOTICE "${message}")
+endmacro()
+
+# Fatal error, accounting for script mode
+macro(fatal)
+ if (CMAKE_SCRIPT_MODE_FILE)
+ echo_error(${ARGN})
+ cmake_language(EXIT 1)
+ else()
+ message(FATAL_ERROR ${ARGN})
+ endif()
+endmacro()
+
+# Analogous to POSIX sleep
+macro(sleep time)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${time})
+endmacro()
+
+# Analogous to GNU mktemp, with fallbacks
+function(mktempdir out)
+ # shell out to system mktemp if available
+ find_program(MKTEMP_EXECUTABLE mktemp)
+ if (MKTEMP_EXECUTABLE)
+ execute_process(COMMAND mktemp -d
+ OUTPUT_VARIABLE dir
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ RESULT_VARIABLE ret)
+
+ if (ret EQUAL 0)
+ set(${out} "${dir}" PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+
+ string(RANDOM LENGTH 10 rand_str)
+ set(tmp_str "tmp.${rand_str}")
+
+ # create something in /tmp if it exists
+ if(EXISTS "/tmp" AND IS_DIRECTORY "/tmp")
+ set(dir "/tmp/${tmp_str}")
+ file(MAKE_DIRECTORY "${dir}" RESULT res)
+ if (res EQUAL 0)
+ set(${out} "${dir}" PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+
+ # tmpdir does not exist, extremely legacy mode
+ set(dir "${CMAKE_CURRENT_LIST_DIR}/.tmp/${tmp_str}")
+ file(MAKE_DIRECTORY "${dir}" RESULT res)
+ if (res EQUAL 0)
+ set(${out} "${dir}" PARENT_SCOPE)
+ return()
+ endif()
+
+ fatal("Fatal: Could not create temporary directory. "
+ "Check write permissions to the current directory")
+endfunction()
+
+# Get a package's effective URL.
+function(get_package_url)
+ set(oneValueArgs
+ GIT_HOST
+ REPO
+ VERSION
+ ARTIFACT
+ PACKAGE
+
+ URL_OUT
+ GIT_URL_OUT)
+
+ cmake_parse_arguments(ARG "" "${oneValueArgs}" "" ${ARGN})
+
+ if (NOT DEFINED ARG_URL_OUT)
+ fatal("get_package_url: URL_OUT is required")
+ endif()
+
+ if(NOT DEFINED ARG_GIT_HOST)
+ set(ARG_GIT_HOST github.com)
+ endif()
+
+ if(DEFINED ARG_REPO)
+ set(url https://${ARG_GIT_HOST}/${ARG_REPO})
+
+ if (DEFINED ARG_GIT_URL_OUT)
+ set(${ARG_GIT_URL_OUT} "${url}")
+ endif()
+
+ if(DEFINED ARG_ARTIFACT)
+ set(url
+ "${url}/releases/download/${ARG_VERSION}/${ARG_ARTIFACT}")
+ else()
+ set(url "${url}/archive/${ARG_VERSION}.tar.gz")
+ endif()
+ else()
+ fatal("${package}: No URL or repository defined")
+ endif()
+
+ set(${ARG_URL_OUT} "${url}")
+
+ return(PROPAGATE ${ARG_URL_OUT} ${ARG_GIT_URL_OUT})
+endfunction()
+
+# Get a package's cache path.
+function(get_cache_path package key out)
+ string(TOLOWER ${package} lower_name)
+
+ # TODO: Figure out a sln to CPM_SOURCE_CACHE; CPMConfig.cmake?
+ set(${out} ${CPM_SOURCE_CACHE}/${lower_name}/${key})
+
+ return(PROPAGATE ${out})
+endfunction()
+
+# Download a URL to file, with a sha512 hash
+# And retry 5 times
+function(cpm_download url file)
+ list(LENGTH ARGN argn_len)
+ if(argn_len GREATER 0)
+ list(GET ARGN 0 hash)
+ set(args EXPECTED_HASH SHA512=${hash})
+ else()
+ unset(args)
+ endif()
+
+ foreach(i RANGE 5)
+ file(DOWNLOAD ${url} ${file}
+ ${args}
+ STATUS ret
+ LOG log)
+
+ list(GET ret 0 code)
+ if (code EQUAL 0)
+ break()
+ endif()
+
+ echo_error("Download failed. Trying again in 5 seconds")
+ sleep(5)
+ endforeach()
+
+ # TODO: use return code or something
+ if (NOT code EQUAL 0)
+ fatal("Download for ${pkg_url} failed after 5 tries")
+ endif()
+endfunction()
+
+# Wrapper around find_program that works with Git for Windows
+macro(cpm_find_program)
+ # Windows needs additional paths for some utilities.
+ if (CMAKE_HOST_WIN32)
+ find_package(Git QUIET)
+ if(Git_FOUND)
+ # Search within the Git for Windows paths.
+ get_filename_component(extra_search_path
+ ${GIT_EXECUTABLE} DIRECTORY)
+ get_filename_component(extra_search_path_1up
+ ${extra_search_path} DIRECTORY)
+ get_filename_component(extra_search_path_2up
+ ${extra_search_path_1up} DIRECTORY)
+
+ set(base_hints "${extra_search_path_1up}/usr/bin"
+ "${extra_search_path_2up}/usr/bin")
+
+ # Also add core_perl to the paths, for perl commands like json_pp
+ set(hints "")
+ foreach(hint ${base_hints})
+ list(APPEND hints
+ "${hint}"
+ "${hint}/core_perl")
+ endforeach()
+
+ find_program(${ARGN} HINTS ${hints})
+ endif()
+
+ # If no Git is found, continue as normal
+ endif()
+
+ find_program(${ARGN})
+endmacro()
+
+# Apply patches to a directory.
+function(apply_patches patches dir)
+ cpm_find_program(PATCH_EXE patch)
+ if (NOT PATCH_EXE)
+ fatal("Could not find patch executable")
+ endif()
+
+ foreach(patch ${patches})
+ get_filename_component(patch_name ${patch} NAME)
+ echo("-- Applying patch ${patch_name}")
+ execute_process(
+ COMMAND ${PATCH_EXE} -p1
+ INPUT_FILE ${patch}
+ WORKING_DIRECTORY ${dir})
+ endforeach()
+endfunction()
+
+# Fetches a file to the CPM source cache.
+function(fetch_package)
+ set(oneValueArgs
+ URL
+ HASH
+ PATH
+
+ PATCH_KEY)
+
+ set(multiValueArgs PATCHES)
+
+ set(optionArgs FORCE)
+
+ cmake_parse_arguments(ARG
+ "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if (NOT DEFINED ARG_URL)
+ fatal("fetch_package: URL is required")
+ endif()
+
+ if (NOT DEFINED ARG_HASH)
+ fatal("fetch_package: HASH is required")
+ endif()
+
+ if (NOT DEFINED ARG_PATH)
+ fatal("fetch_package: PATH is required")
+ endif()
+
+ if (NOT DEFINED ARG_PATCH_KEY)
+ compute_patch_key("${ARG_PATCHES}" ARG_PATCH_KEY)
+ endif()
+
+ # refetch if cache is invalid or forced
+ needs_refetch(${ARG_PATH} "${ARG_PATCH_KEY}" CACHE_INVALID)
+
+ if (ARG_FORCE OR CACHE_INVALID)
+ file(REMOVE_RECURSE ${ARG_PATH})
+ else()
+ return()
+ endif()
+
+ # Temporary directory.
+ mktempdir(TMP)
+
+ # Get filename from URL
+ get_filename_component(base_filename ${ARG_URL} NAME)
+
+ # Download
+ set(file ${TMP}/${base_filename})
+ cpm_download(${ARG_URL} ${file} ${ARG_HASH})
+ message(DEBUG "Downloaded ${base_filename}")
+
+ # Extract the downloaded archive
+ # TODO: Moar error handling
+ set(dir ${TMP}/${base_filename}-extracted)
+ file(MAKE_DIRECTORY ${dir})
+
+ file(ARCHIVE_EXTRACT
+ INPUT ${file}
+ DESTINATION ${dir})
+
+ # This is copied near-verbatim from ExternalProject/extractfile.cmake.in
+
+ # If there's just one subdirectory and nothing else, move it
+ file(GLOB contents "${dir}/*")
+ list(REMOVE_ITEM contents "${dir}/.DS_Store")
+ list(LENGTH contents n)
+
+ # If n == 1 and contents points to a directory, this is a GitHub-style pack
+ # In this case contents points to the subdir which will get renamed
+ # If not, contents will point to the parent dir which will get renamed
+ if (NOT n EQUAL 1 OR NOT IS_DIRECTORY "${contents}")
+ set(contents "${dir}")
+ endif()
+
+ file(REAL_PATH "${contents}" contents_abs)
+
+ # paths
+ cmake_path(ABSOLUTE_PATH ARG_PATH
+ NORMALIZE
+ OUTPUT_VARIABLE abs_path)
+
+ cmake_path(GET abs_path PARENT_PATH path_parent)
+ cmake_path(GET abs_path FILENAME path_name)
+
+ # rename tmp dir
+ set(tmp_renamed "${TMP}/${path_name}")
+ file(RENAME "${contents_abs}" "${tmp_renamed}")
+
+ # now copy
+ # TODO: Error handling beyond what cmake does????
+ file(COPY ${tmp_renamed} DESTINATION ${path_parent})
+
+ # TODO: only echo this in script mode
+ message(DEBUG "Extracted to ${abs_path}")
+
+ # Apply patches
+ apply_patches("${ARG_PATCHES}" "${abs_path}")
+
+ # Write patch key
+ file(WRITE "${abs_path}/.cpm_patch_key" ${ARG_PATCH_KEY})
+
+ # done! :)
+ file(REMOVE_RECURSE ${TMP})
+endfunction()
+
+# compute a hash of all patch file contents
+# if there are no patches, returns none
+function(compute_patch_key patches out)
+ if(NOT patches)
+ set("${out}" "none" PARENT_SCOPE)
+ return()
+ endif()
+
+ set(combined "")
+ foreach(PATCH ${patches})
+ file(READ "${PATCH}" contents)
+ string(APPEND combined "${contents}")
+ endforeach()
+
+ string(SHA512 key "${combined}")
+ set("${out}" "${key}" PARENT_SCOPE)
+endfunction()
+
+# Check if a package needs to be redownloaded. This can occur if:
+# - Patch key is missing or mismatched
+# - Package fetch dir is missing
+# path is the package cache path
+function(needs_refetch path patch_key out)
+ set(patch_key_file "${path}/.cpm_patch_key")
+
+ # download directory is empty or patch key file is missing
+ if (NOT EXISTS ${patch_key_file})
+ set(${out} TRUE PARENT_SCOPE)
+ return()
+ endif()
+
+ # compare patch key
+ file(READ "${patch_key_file}" current_patch_key)
+ if (NOT current_patch_key STREQUAL patch_key)
+ set(${out} TRUE PARENT_SCOPE)
+ else()
+ set(${out} FALSE PARENT_SCOPE)
+ endif()
+endfunction()
+
+# Message
+function(cpm_utils_message level)
+ string(REPLACE ";" " " message "${ARGN}")
+ message(${level} "[CPMUtil] ${message}")
endfunction()
# propagate a variable to parent scope
@@ -71,6 +455,18 @@ macro(Propagate var)
set(${var} ${${var}} PARENT_SCOPE)
endmacro()
+# Get cpmfile content
+function(get_cpmfile_content out)
+ set(${out} "${CPMFILE_CONTENT}" PARENT_SCOPE)
+endfunction()
+
+# Get absolute path to cpmfile
+function(get_cpmfile_path out)
+ file(REAL_PATH ${CPMUTIL_JSON_FILE} ${out})
+ return(PROPAGATE ${out})
+endfunction()
+
+# idk
function(array_to_list array length out)
math(EXPR range "${length} - 1")
@@ -83,11 +479,22 @@ function(array_to_list array length out)
set("${out}" "${NEW_LIST}" PARENT_SCOPE)
endfunction()
-function(get_json_element object out member default)
+# json
+function(get_json_element object out member)
+ list(LENGTH ARGN argn_len)
+ if(argn_len GREATER 0)
+ list(GET ARGN 0 default)
+ endif()
+
string(JSON out_type ERROR_VARIABLE err TYPE "${object}" ${member})
if(err)
- set("${out}" "${default}" PARENT_SCOPE)
+ if (DEFINED default)
+ set("${out}" "${default}" PARENT_SCOPE)
+ else()
+ unset("${out}" PARENT_SCOPE)
+ endif()
+
return()
endif()
@@ -101,6 +508,13 @@ function(get_json_element object out member default)
set("${out}" "${outvar}" PARENT_SCOPE)
endfunction()
+# register a package to CPMUtil's global registry
+function(cpmutil_register_package name url version)
+ set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${name})
+ set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS ${url})
+ set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS "${version}")
+endfunction()
+
# Determine whether or not a package has a viable system candidate.
function(SystemPackageViable JSON_NAME)
string(JSON object GET "${CPMFILE_CONTENT}" "${JSON_NAME}")
@@ -108,14 +522,17 @@ function(SystemPackageViable JSON_NAME)
parse_object(${object})
string(REPLACE " " ";" find_args "${find_args}")
- if (${package}_FORCE_BUNDLED)
+ if(${package}_FORCE_BUNDLED OR
+ (CPMUTIL_FORCE_BUNDLED
+ AND NOT ${package}_FORCE_SYSTEM
+ AND NOT CPMUTIL_FORCE_SYSTEM))
set(${package}_FOUND OFF)
else()
- find_package(${package} ${version} ${find_args} QUIET NO_POLICY_SCOPE)
+ find_package(${package} ${min_version} ${find_args} QUIET NO_POLICY_SCOPE)
endif()
- set(${pkg}_VIABLE ${${package}_FOUND} PARENT_SCOPE)
- set(${pkg}_PACKAGE ${package} PARENT_SCOPE)
+ set(${JSON_NAME}_VIABLE ${${package}_FOUND} PARENT_SCOPE)
+ set(${JSON_NAME}_PACKAGE ${package} PARENT_SCOPE)
endfunction()
# Add several packages such that if one is bundled,
@@ -166,19 +583,40 @@ function(AddDependentPackages)
endforeach()
endfunction()
+# process version/artifact replacements
+macro(process_version_replacements)
+ # numeric version text replacement
+ if (numeric_version)
+ string(REPLACE "%NUMERIC_VERSION%" "${numeric_version}"
+ version ${version})
+ string(REPLACE "%NUMERIC_VERSION%" "${numeric_version}"
+ artifact ${artifact})
+ endif()
+
+ # version text replacement
+ if (artifact)
+ string(REPLACE "%VERSION%" "${version}"
+ artifact ${artifact})
+ endif()
+endmacro()
+
# json util
macro(parse_object object)
get_json_element("${object}" package package ${JSON_NAME})
- get_json_element("${object}" repo repo "")
+ get_json_element("${object}" repo repo)
get_json_element("${object}" ci ci OFF)
- get_json_element("${object}" version version "")
- get_json_element("${object}" min_version min_version "")
+ get_json_element("${object}" version version)
+ get_json_element("${object}" min_version min_version)
get_json_element("${object}" git_host git_host "github.com")
+ if (NOT version)
+ cpm_utils_message(FATAL_ERROR "${JSON_NAME}: version is required")
+ endif()
+
if(ci)
get_json_element("${object}" name name "${JSON_NAME}")
get_json_element("${object}" extension extension "tar.zst")
- get_json_element("${object}" raw_disabled disabled_platforms "")
+ get_json_element("${object}" raw_disabled disabled_platforms)
if(raw_disabled)
array_to_list("${raw_disabled}"
@@ -187,34 +625,21 @@ macro(parse_object object)
set(disabled_platforms "")
endif()
else()
- get_json_element("${object}" hash hash "")
- get_json_element("${object}" sha sha "")
- get_json_element("${object}" url url "")
- get_json_element("${object}" tag tag "")
- get_json_element("${object}" artifact artifact "")
- get_json_element("${object}" source_subdir source_subdir "")
+ # TODO: correct hash if missing
+ get_json_element("${object}" hash hash)
+ get_json_element("${object}" url url)
+ get_json_element("${object}" artifact artifact)
+ get_json_element("${object}" source_subdir source_subdir)
get_json_element("${object}" bundled bundled "unset")
- get_json_element("${object}" find_args find_args "")
- get_json_element("${object}" raw_patches patches "")
-
- # okay here comes the fun part: REPLACEMENTS!
- # first: tag gets %VERSION% replaced if applicable,
- # with version
- # second: artifact gets %VERSION% and %TAG% replaced
- # accordingly (same rules for VERSION)
-
- # TODO(crueter): fmt module for cmake
- if(tag)
- string(REPLACE "%VERSION%" "${version}" tag ${tag})
- endif()
+ get_json_element("${object}" numeric_version numeric_version)
+ get_json_element("${object}" find_args find_args)
+ get_json_element("${object}" skip_updates skip_updates OFF)
+ get_json_element("${object}" raw_patches patches)
- if(artifact)
- string(REPLACE "%VERSION%" "${version}"
- artifact ${artifact})
- string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
- endif()
+ process_version_replacements()
# format patchdir
+ unset(patches)
if(raw_patches)
math(EXPR range "${raw_patches_LENGTH} - 1")
@@ -224,8 +649,9 @@ macro(parse_object object)
set(full_patch
"${CPMUTIL_PATCH_DIR}/${JSON_NAME}/${_patch}")
if(NOT EXISTS ${full_patch})
- cpm_utils_message(FATAL_ERROR ${JSON_NAME}
- "specifies patch ${full_patch} which does not exist")
+ cpm_utils_message(FATAL_ERROR
+ "${JSON_NAME} specifies patch"
+ "${full_patch} which does not exist")
endif()
list(APPEND patches "${full_patch}")
@@ -234,8 +660,9 @@ macro(parse_object object)
# end format patchdir
# options
- get_json_element("${object}" raw_options options "")
+ get_json_element("${object}" raw_options options)
+ unset(options)
if(raw_options)
array_to_list("${raw_options}" ${raw_options_LENGTH} options)
endif()
@@ -250,6 +677,29 @@ macro(parse_object object)
endif()
endmacro()
+# Get the JSON object for a key. Outputs to "object"
+function(get_json_object key)
+ if(NOT DEFINED CPMFILE_CONTENT)
+ cpm_utils_message(FATAL_ERROR "${key}: No cpmfile present")
+ endif()
+
+ if(NOT DEFINED key)
+ cpm_utils_message(FATAL_ERROR "No JSON key specified")
+ endif()
+
+ string(JSON object ERROR_VARIABLE
+ err GET "${CPMFILE_CONTENT}" "${key}")
+
+ if(err)
+ cpm_utils_message(FATAL_ERROR "${key} not found in cpmfile\n"
+ "${err}")
+ endif()
+
+ Propagate(object)
+endfunction()
+
+# Get the download URL given the parameters.
+
# The preferred usage
function(AddJsonPackage)
set(oneValueArgs
@@ -276,25 +726,10 @@ function(AddJsonPackage)
set(JSON_NAME "${ARGV0}")
endif()
- if(NOT DEFINED CPMFILE_CONTENT)
- cpm_utils_message(FATAL_ERROR ${name}
- "No cpmfile present")
- return()
- endif()
-
- if(NOT DEFINED JSON_NAME)
- cpm_utils_message(FATAL_ERROR "json package" "No name specified")
- endif()
-
- string(JSON object ERROR_VARIABLE
- err GET "${CPMFILE_CONTENT}" "${JSON_NAME}")
-
- if(err)
- cpm_utils_message(FATAL_ERROR ${JSON_NAME} "Not found in cpmfile")
- endif()
-
+ get_json_object("${JSON_NAME}")
parse_object(${object})
+ unset(EXTRA_ARGS)
if (JSON_MODULE_PATH)
list(APPEND EXTRA_ARGS MODULE_PATH)
endif()
@@ -310,7 +745,6 @@ function(AddJsonPackage)
REPO "${repo}"
PACKAGE "${package}"
EXTENSION "${extension}"
- MIN_VERSION "${min_version}"
DISABLED_PLATFORMS "${disabled_platforms}"
GIT_HOST "${git_host}"
@@ -327,7 +761,6 @@ function(AddJsonPackage)
MIN_VERSION "${min_version}"
URL "${url}"
HASH "${hash}"
- SHA "${sha}"
REPO "${repo}"
PATCHES "${patches}"
OPTIONS "${options}"
@@ -339,7 +772,6 @@ function(AddJsonPackage)
GIT_HOST "${git_host}"
ARTIFACT "${artifact}"
- TAG "${tag}"
${EXTRA_ARGS})
endif()
@@ -351,24 +783,23 @@ function(AddJsonPackage)
Propagate(CMAKE_PREFIX_PATH)
endfunction()
+# Internal function
function(AddPackage)
- cpm_set_policies()
set(EXTRA_ARGS "")
set(oneValueArgs
NAME
- VERSION
MIN_VERSION
GIT_HOST
REPO
- TAG
+ VERSION
ARTIFACT
- SHA
HASH
URL
+ CUSTOM_KEY
SOURCE_SUBDIR
BUNDLED_PACKAGE
@@ -386,7 +817,11 @@ function(AddPackage)
"${ARGN}")
if(NOT DEFINED PKG_ARGS_NAME)
- cpm_utils_message(FATAL_ERROR "package" "No package name defined")
+ cpm_utils_message(FATAL_ERROR "AddPackage: NAME is required")
+ endif()
+
+ if(NOT DEFINED PKG_ARGS_VERSION)
+ cpm_utils_message(FATAL_ERROR "${PKG_ARGS_NAME}: VERSION is required")
endif()
set(${PKG_ARGS_NAME}_CUSTOM_DIR "" CACHE STRING
@@ -396,176 +831,156 @@ function(AddPackage)
option(${PKG_ARGS_NAME}_FORCE_BUNDLED
"Force the bundled package for ${PKG_ARGS_NAME}")
- if (DEFINED ${PKG_ARGS_NAME}_CUSTOM_DIR AND
- NOT ${PKG_ARGS_NAME}_CUSTOM_DIR STREQUAL "")
- set(CPM_${PKG_ARGS_NAME}_SOURCE ${${PKG_ARGS_NAME}_CUSTOM_DIR})
- endif()
-
- # TODO: See if this can be delegated to subshells
- if(NOT DEFINED PKG_ARGS_GIT_HOST)
- set(git_host github.com)
+ # URL parsing
+ if (NOT DEFINED PKG_ARGS_URL)
+ get_package_url(URL_OUT "PKG_ARGS_URL"
+ GIT_URL_OUT "pkg_git_url"
+ GIT_HOST "${PKG_ARGS_GIT_HOST}"
+ REPO "${PKG_ARGS_REPO}"
+ VERSION "${PKG_ARGS_VERSION}"
+ ARTIFACT "${PKG_ARGS_ARTIFACT}"
+ PACKAGE "${PKG_ARGS_NAME}")
else()
- set(git_host ${PKG_ARGS_GIT_HOST})
- endif()
-
- if(DEFINED PKG_ARGS_URL)
- set(pkg_url ${PKG_ARGS_URL})
- set(pkg_git_url ${pkg_url})
- elseif(DEFINED PKG_ARGS_REPO)
- set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO})
-
- if(DEFINED PKG_ARGS_SHA)
- set(pkg_url "${pkg_git_url}/archive/${PKG_ARGS_SHA}.tar.gz")
- elseif(DEFINED PKG_ARGS_TAG)
- set(tag "${PKG_ARGS_TAG}")
- if(DEFINED PKG_ARGS_ARTIFACT)
- set(artifact "${PKG_ARGS_ARTIFACT}")
- set(pkg_url
- "${pkg_git_url}/releases/download/${tag}/${artifact}")
- else()
- set(pkg_url
- "${pkg_git_url}/archive/refs/tags/${tag}.tar.gz")
- endif()
- endif()
- else()
- cpm_utils_message(FATAL_ERROR ${PKG_ARGS_NAME}
- "No URL or repository defined")
+ set(pkg_git_url ${PKG_ARGS_URL})
endif()
- cpm_utils_message(DEBUG ${PKG_ARGS_NAME} "Download URL is ${pkg_url}")
+ cpm_utils_message(DEBUG "${PKG_ARGS_NAME} download URL is ${PKG_ARGS_URL}")
- if(DEFINED PKG_ARGS_SHA)
- string(SUBSTRING ${PKG_ARGS_SHA} 0 4 pkg_key)
- elseif(DEFINED PKG_ARGS_VERSION)
- set(pkg_key ${PKG_ARGS_VERSION})
- elseif(DEFINED PKG_ARGS_TAG)
- set(pkg_key ${PKG_ARGS_TAG})
- elseif(DEFINED PKG_ARGS_MIN_VERSION)
- set(pkg_key ${PKG_ARGS_MIN_VERSION})
- else()
- cpm_utils_message(FATAL_ERROR ${PKG_ARGS_NAME}
- "Could not determine cache key")
- endif()
-
- if(DEFINED PKG_ARGS_HASH)
- set(pkg_hash "SHA512=${PKG_ARGS_HASH}")
- else()
- cpm_utils_message(FATAL_ERROR ${PKG_ARGS_NAME}
- "No hash defined")
+ if(NOT DEFINED PKG_ARGS_HASH)
+ cpm_utils_message(FATAL_ERROR "${PKG_ARGS_NAME}: No hash defined")
endif()
- macro(set_precedence local force)
- set(CPM_USE_LOCAL_PACKAGES ${local})
- set(CPM_LOCAL_PACKAGES_ONLY ${force})
- endmacro()
-
#[[
Precedence:
- - package_FORCE_SYSTEM
- - package_FORCE_BUNDLED
- - CPMUTIL_FORCE_SYSTEM
- - CPMUTIL_FORCE_BUNDLED
- - BUNDLED_PACKAGE
- - default to allow local
+ - FORCE_BUNDLED_PACKAGE (caller override)
+ - package_FORCE_SYSTEM, package_FORCE_BUNDLED (user option)
+ - CPMUTIL_FORCE_SYSTEM, CPMUTIL_FORCE_BUNDLED (global option)
+ - BUNDLED_PACKAGE (json default)
+ - default to allow system
]]
if(PKG_ARGS_FORCE_BUNDLED_PACKAGE)
- set_precedence(OFF OFF)
+ set(use_system OFF)
elseif(${PKG_ARGS_NAME}_FORCE_SYSTEM)
- set_precedence(ON ON)
+ set(use_system ON)
+ set(force_system ON)
elseif(${PKG_ARGS_NAME}_FORCE_BUNDLED)
- set_precedence(OFF OFF)
+ set(use_system OFF)
elseif(CPMUTIL_FORCE_SYSTEM)
- set_precedence(ON ON)
+ set(use_system ON)
+ set(force_system ON)
elseif(CPMUTIL_FORCE_BUNDLED)
- set_precedence(OFF OFF)
+ set(use_system OFF)
elseif(DEFINED PKG_ARGS_BUNDLED_PACKAGE AND
NOT PKG_ARGS_BUNDLED_PACKAGE STREQUAL "unset")
if(PKG_ARGS_BUNDLED_PACKAGE)
- set(local OFF)
+ set(use_system OFF)
else()
- set(local ON)
+ set(use_system ON)
endif()
-
- set_precedence(${local} OFF)
else()
- set_precedence(ON OFF)
+ set(use_system ON)
endif()
- if(DEFINED PKG_ARGS_MIN_VERSION)
- list(APPEND EXTRA_ARGS
- VERSION ${PKG_ARGS_MIN_VERSION})
- endif()
+ # find_package nonsense
+ if(use_system)
+ set(find_args ${PKG_ARGS_NAME})
+ if(DEFINED PKG_ARGS_MIN_VERSION)
+ list(APPEND find_args ${PKG_ARGS_MIN_VERSION})
+ endif()
- if (PKG_ARGS_FIND_PACKAGE_ARGUMENTS)
- list(APPEND EXTRA_ARGS
- FIND_PACKAGE_ARGUMENTS "${PKG_ARGS_FIND_PACKAGE_ARGUMENTS}")
- endif()
+ if (DEFINED PKG_ARGS_FIND_PACKAGE_ARGUMENTS)
+ string(REPLACE " " ";"
+ passed_find_args
+ "${PKG_ARGS_FIND_PACKAGE_ARGUMENTS}")
- if (PKG_ARGS_PATCHES)
- list(APPEND EXTRA_ARGS
- PATCHES "${PKG_ARGS_PATCHES}")
+ set(find_args "${find_args};${passed_find_args}")
+ endif()
+
+ find_package(${find_args} QUIET)
+
+ if(${PKG_ARGS_NAME}_FOUND)
+ if(DEFINED ${PKG_ARGS_NAME}_VERSION)
+ set(sys_ver ${${PKG_ARGS_NAME}_VERSION})
+ else()
+ set(sys_ver ${PKG_ARGS_VERSION})
+ endif()
+
+ message(STATUS
+ "[CPMUtil] Using system package ${PKG_ARGS_NAME}@${sys_ver}")
+
+ CPMRegisterPackage(${PKG_ARGS_NAME} "${sys_ver}")
+
+ cpmutil_register_package(
+ ${PKG_ARGS_NAME}
+ ${pkg_git_url}
+ "${sys_ver} (system)")
+
+ set(${PKG_ARGS_NAME}_ADDED NO PARENT_SCOPE)
+
+ return()
+ endif()
+
+ if(force_system)
+ string(REPLACE ";" " " str_find_args "${find_args}")
+ message(FATAL_ERROR "[CPMUtil] ${PKG_ARGS_NAME} not found via "
+ "find_package(${str_find_args})")
+ endif()
endif()
+ # extra options
if (PKG_ARGS_OPTIONS)
- list(APPEND EXTRA_ARGS
- OPTIONS "${PKG_ARGS_OPTIONS}")
+ list(APPEND EXTRA_ARGS OPTIONS "${PKG_ARGS_OPTIONS}")
endif()
if (PKG_ARGS_SOURCE_SUBDIR)
- list(APPEND EXTRA_ARGS
- SOURCE_SUBDIR "${PKG_ARGS_SOURCE_SUBDIR}")
+ list(APPEND EXTRA_ARGS SOURCE_SUBDIR "${PKG_ARGS_SOURCE_SUBDIR}")
endif()
if (PKG_ARGS_DOWNLOAD_ONLY OR PKG_ARGS_MODULE_PATH)
list(APPEND EXTRA_ARGS DOWNLOAD_ONLY ON)
endif()
+ cpm_utils_message(STATUS
+ "Using bundled package ${PKG_ARGS_NAME}@${PKG_ARGS_VERSION}")
+
+ # Download/extract package
+ if (${PKG_ARGS_NAME}_CUSTOM_DIR STREQUAL "")
+ if (DEFINED PKG_ARGS_CUSTOM_KEY)
+ set(key ${PKG_ARGS_CUSTOM_KEY})
+ else()
+ set(key ${PKG_ARGS_VERSION})
+ endif()
+
+ # cache path
+ get_cache_path(${PKG_ARGS_NAME} ${key} cache_path)
+
+ fetch_package(
+ URL "${PKG_ARGS_URL}"
+ HASH "${PKG_ARGS_HASH}"
+ PATH "${cache_path}"
+ PATCHES "${PKG_ARGS_PATCHES}")
+
+ set(source_dir "${cache_path}")
+ else()
+ set(source_dir "${${PKG_ARGS_NAME}_CUSTOM_DIR}")
+ endif()
+
CPMAddPackage(
NAME ${PKG_ARGS_NAME}
- URL ${pkg_url}
- URL_HASH ${pkg_hash}
- CUSTOM_CACHE_KEY ${pkg_key}
+ VERSION ${PKG_ARGS_VERSION}
+ SOURCE_DIR ${source_dir}
EXCLUDE_FROM_ALL ON
${EXTRA_ARGS}
-
${PKG_ARGS_UNPARSED_ARGUMENTS})
- set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${PKG_ARGS_NAME})
- set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS ${pkg_git_url})
-
- if(${PKG_ARGS_NAME}_ADDED)
- if(DEFINED PKG_ARGS_SHA)
- set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
- ${PKG_ARGS_SHA})
- elseif(DEFINED PKG_ARGS_VERSION)
- set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
- ${PKG_ARGS_VERSION})
- elseif(DEFINED PKG_ARGS_TAG)
- set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
- ${PKG_ARGS_TAG})
- elseif(DEFINED PKG_ARGS_MIN_VERSION)
- set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
- ${PKG_ARGS_MIN_VERSION})
- else()
- cpm_utils_message(WARNING ${PKG_ARGS_NAME}
- "Package has no specified sha, tag, or version")
- set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS "unknown")
- endif()
- else()
- if(DEFINED CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION AND NOT
- "${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION}" STREQUAL "")
- set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
- "${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION} (system)")
- else()
- set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
- "unknown (system)")
- endif()
- endif()
+ cpmutil_register_package(
+ ${PKG_ARGS_NAME}
+ ${pkg_git_url}
+ ${PKG_ARGS_VERSION})
- # pass stuff to parent scope
- Propagate(${PKG_ARGS_NAME}_ADDED)
+ set(${PKG_ARGS_NAME}_ADDED YES PARENT_SCOPE)
Propagate(${PKG_ARGS_NAME}_SOURCE_DIR)
Propagate(${PKG_ARGS_NAME}_BINARY_DIR)
@@ -597,24 +1012,20 @@ function(AddCIPackage)
"${multiValueArgs}"
${ARGN})
- # TODO: use cpm_utils_message
if(NOT DEFINED PKG_ARGS_VERSION)
- message(FATAL_ERROR "[CPMUtil] VERSION is required")
+ cpm_utils_message(FATAL_ERROR "VERSION is required")
endif()
+
if(NOT DEFINED PKG_ARGS_NAME)
- message(FATAL_ERROR "[CPMUtil] NAME is required")
+ cpm_utils_message(FATAL_ERROR "NAME is required")
endif()
+
if(NOT DEFINED PKG_ARGS_REPO)
- message(FATAL_ERROR "[CPMUtil] REPO is required")
- endif()
- if(NOT DEFINED PKG_ARGS_PACKAGE)
- message(FATAL_ERROR "[CPMUtil] PACKAGE is required")
+ cpm_utils_message(FATAL_ERROR "REPO is required")
endif()
- if(NOT DEFINED PKG_ARGS_CMAKE_FILENAME)
- set(ARTIFACT_CMAKE ${PKG_ARGS_NAME})
- else()
- set(ARTIFACT_CMAKE ${PKG_ARGS_CMAKE_FILENAME})
+ if(NOT DEFINED PKG_ARGS_PACKAGE)
+ cpm_utils_message(FATAL_ERROR "PACKAGE is required")
endif()
if(NOT DEFINED PKG_ARGS_EXTENSION)
@@ -629,10 +1040,6 @@ function(AddCIPackage)
set(ARTIFACT_GIT_HOST "${PKG_ARGS_GIT_HOST}")
endif()
- if(DEFINED PKG_ARGS_MIN_VERSION)
- set(ARTIFACT_MIN_VERSION ${PKG_ARGS_MIN_VERSION})
- endif()
-
if(DEFINED PKG_ARGS_DISABLED_PLATFORMS)
set(DISABLED_PLATFORMS ${PKG_ARGS_DISABLED_PLATFORMS})
endif()
@@ -659,7 +1066,7 @@ function(AddCIPackage)
elseif(APPLE)
set(platname macos)
else()
- cpm_utils_message(WARNING ${PKG_ARGS_NAME}
+ cpm_utils_message(WARNING
"Unsupported platform ${CMAKE_SYSTEM_NAME} for CI packages")
endif()
@@ -674,7 +1081,7 @@ function(AddCIPackage)
elseif(ANDROID AND CPMUTIL_AMD64)
set(archname x86_64)
else()
- cpm_utils_message(WARNING ${PKG_ARGS_NAME}
+ cpm_utils_message(WARNING
"Unsupported platform/arch combo for CI packages")
endif()
@@ -692,7 +1099,7 @@ function(AddCIPackage)
endif()
# download sha512sum file
- # TODO:
+ # TODO: CI pkgs
set(sha512sum_url
"https://${ARTIFACT_GIT_HOST}/${ARTIFACT_REPO}/releases/download/v${ARTIFACT_VERSION}/${ARTIFACT}.sha512sum")
set(sha512sum_file
@@ -714,9 +1121,9 @@ function(AddCIPackage)
AddPackage(
NAME ${ARTIFACT_PACKAGE}
REPO ${ARTIFACT_REPO}
- TAG "v${ARTIFACT_VERSION}"
- MIN_VERSION ${ARTIFACT_VERSION}
+ VERSION "v${ARTIFACT_VERSION}"
ARTIFACT ${ARTIFACT}
+ CUSTOM_KEY "${ARTIFACT_VERSION}-${pkgname}"
HASH ${sha512sum_hash}
FORCE_BUNDLED_PACKAGE ON
${EXTRA_ARGS})
@@ -725,7 +1132,8 @@ function(AddCIPackage)
Propagate(${ARTIFACT_PACKAGE}_SOURCE_DIR)
Propagate(CMAKE_PREFIX_PATH)
else()
- find_package(${ARTIFACT_PACKAGE} ${ARTIFACT_MIN_VERSION} REQUIRED)
+ cpm_utils_message(FATAL_ERROR "${ARTIFACT_NAME}:"
+ "Unsupported platform ${pkgname} for CI package")
endif()
endfunction()
@@ -743,7 +1151,6 @@ function(AddQt repo version)
NAME qt
PACKAGE Qt6
VERSION ${version}
- MIN_VERSION 6
REPO ${repo}
DISABLED_PLATFORMS
android-x86_64 android-aarch64