@@ -42,7 +42,11 @@ if(NOT COMMAND cpm_message)
4242 endfunction ()
4343endif ()
4444
45- set (CURRENT_CPM_VERSION 0.40.0)
45+ if (DEFINED EXTRACTED_CPM_VERSION)
46+ set (CURRENT_CPM_VERSION "${EXTRACTED_CPM_VERSION}${CPM_DEVELOPMENT} " )
47+ else ()
48+ set (CURRENT_CPM_VERSION 0.42.0)
49+ endif ()
4650
4751get_filename_component (CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR} " REALPATH)
4852if (CPM_DIRECTORY)
@@ -162,7 +166,7 @@ set(CPM_SOURCE_CACHE
162166 CACHE PATH "Directory to download CPM dependencies"
163167)
164168
165- if (NOT CPM_DONT_UPDATE_MODULE_PATH)
169+ if (NOT CPM_DONT_UPDATE_MODULE_PATH AND NOT DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR )
166170 set (CPM_MODULE_PATH
167171 "${CMAKE_BINARY_DIR} /CPM_modules"
168172 CACHE INTERNAL ""
@@ -198,6 +202,60 @@ function(cpm_package_name_from_git_uri URI RESULT)
198202 endif ()
199203endfunction ()
200204
205+ # Find the shortest hash that can be used eg, if origin_hash is
206+ # cccb77ae9609d2768ed80dd42cec54f77b1f1455 the following files will be checked, until one is found
207+ # that is either empty (allowing us to assign origin_hash), or whose contents matches ${origin_hash}
208+ #
209+ # * .../cccb.hash
210+ # * .../cccb77ae.hash
211+ # * .../cccb77ae9609.hash
212+ # * .../cccb77ae9609d276.hash
213+ # * etc
214+ #
215+ # We will be able to use a shorter path with very high probability, but in the (rare) event that the
216+ # first couple characters collide, we will check longer and longer substrings.
217+ function (cpm_get_shortest_hash source_cache_dir origin_hash short_hash_output_var)
218+ # for compatibility with caches populated by a previous version of CPM, check if a directory using
219+ # the full hash already exists
220+ if (EXISTS "${source_cache_dir} /${origin_hash} " )
221+ set (${short_hash_output_var}
222+ "${origin_hash} "
223+ PARENT_SCOPE
224+ )
225+ return ()
226+ endif ()
227+
228+ foreach (len RANGE 4 40 4)
229+ string (SUBSTRING "${origin_hash} " 0 ${len} short_hash)
230+ set (hash_lock ${source_cache_dir} /${short_hash} .lock)
231+ set (hash_fp ${source_cache_dir} /${short_hash} .hash)
232+ # Take a lock, so we don't have a race condition with another instance of cmake. We will release
233+ # this lock when we can, however, if there is an error, we want to ensure it gets released on
234+ # it's own on exit from the function.
235+ file (LOCK ${hash_lock} GUARD FUNCTION)
236+
237+ # Load the contents of .../${short_hash}.hash
238+ file (TOUCH ${hash_fp} )
239+ file (READ ${hash_fp} hash_fp_contents)
240+
241+ if (hash_fp_contents STREQUAL "" )
242+ # Write the origin hash
243+ file (WRITE ${hash_fp} ${origin_hash} )
244+ file (LOCK ${hash_lock} RELEASE)
245+ break ()
246+ elseif (hash_fp_contents STREQUAL origin_hash)
247+ file (LOCK ${hash_lock} RELEASE)
248+ break ()
249+ else ()
250+ file (LOCK ${hash_lock} RELEASE)
251+ endif ()
252+ endforeach ()
253+ set (${short_hash_output_var}
254+ "${short_hash} "
255+ PARENT_SCOPE
256+ )
257+ endfunction ()
258+
201259# Try to infer package name and version from a url
202260function (cpm_package_name_and_ver_from_url url outName outVer)
203261 if (url MATCHES "[/\\ ?]([a-zA-Z0-9_\\ .-]+)\\ .(tar|tar\\ .gz|tar\\ .bz2|zip|ZIP)(\\ ?|/|$)" )
@@ -269,10 +327,25 @@ endfunction()
269327# finding the system library
270328function (cpm_create_module_file Name )
271329 if (NOT CPM_DONT_UPDATE_MODULE_PATH)
272- # erase any previous modules
273- file (WRITE ${CPM_MODULE_PATH} /Find ${Name} .cmake
274- "include(\" ${CPM_FILE} \" )\n ${ARGN} \n set(${Name} _FOUND TRUE)"
275- )
330+ if (DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR)
331+ # Redirect find_package calls to the CPM package. This is what FetchContent does when you set
332+ # OVERRIDE_FIND_PACKAGE. The CMAKE_FIND_PACKAGE_REDIRECTS_DIR works for find_package in CONFIG
333+ # mode, unlike the Find${Name}.cmake fallback. CMAKE_FIND_PACKAGE_REDIRECTS_DIR is not defined
334+ # in script mode, or in CMake < 3.24.
335+ # https://cmake.org/cmake/help/latest/module/FetchContent.html#fetchcontent-find-package-integration-examples
336+ string (TOLOWER ${Name} NameLower)
337+ file (WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR} /${NameLower} -config.cmake
338+ "include(\"\$ {CMAKE_CURRENT_LIST_DIR}/${NameLower} -extra.cmake\" OPTIONAL)\n "
339+ "include(\"\$ {CMAKE_CURRENT_LIST_DIR}/${Name} Extra.cmake\" OPTIONAL)\n "
340+ )
341+ file (WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR} /${NameLower} -config-version .cmake
342+ "set(PACKAGE_VERSION_COMPATIBLE TRUE)\n " "set(PACKAGE_VERSION_EXACT TRUE)\n "
343+ )
344+ else ()
345+ file (WRITE ${CPM_MODULE_PATH} /Find ${Name} .cmake
346+ "include(\" ${CPM_FILE} \" )\n ${ARGN} \n set(${Name} _FOUND TRUE)"
347+ )
348+ endif ()
276349 endif ()
277350endfunction ()
278351
@@ -475,15 +548,18 @@ function(cpm_add_patches)
475548
476549 # Find the patch program.
477550 find_program (PATCH_EXECUTABLE patch)
478- if (WIN32 AND NOT PATCH_EXECUTABLE)
551+ if (CMAKE_HOST_WIN32 AND NOT PATCH_EXECUTABLE)
479552 # The Windows git executable is distributed with patch.exe. Find the path to the executable, if
480- # it exists, then search `../../usr/bin` for patch.exe.
553+ # it exists, then search `../usr/bin` and `../ ../usr/bin` for patch.exe.
481554 find_package (Git QUIET )
482555 if (GIT_EXECUTABLE)
483556 get_filename_component (extra_search_path ${GIT_EXECUTABLE} DIRECTORY )
484- get_filename_component (extra_search_path ${extra_search_path} DIRECTORY )
485- get_filename_component (extra_search_path ${extra_search_path} DIRECTORY )
486- find_program (PATCH_EXECUTABLE patch HINTS "${extra_search_path} /usr/bin" )
557+ get_filename_component (extra_search_path_1up ${extra_search_path} DIRECTORY )
558+ get_filename_component (extra_search_path_2up ${extra_search_path_1up} DIRECTORY )
559+ find_program (
560+ PATCH_EXECUTABLE patch HINTS "${extra_search_path_1up} /usr/bin"
561+ "${extra_search_path_2up} /usr/bin"
562+ )
487563 endif ()
488564 endif ()
489565 if (NOT PATCH_EXECUTABLE)
@@ -572,14 +648,6 @@ endfunction()
572648function (CPMAddPackage)
573649 cpm_set_policies()
574650
575- list (LENGTH ARGN argnLength)
576- if (argnLength EQUAL 1)
577- cpm_parse_add_package_single_arg("${ARGN} " ARGN)
578-
579- # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM
580- set (ARGN "${ARGN} ;EXCLUDE_FROM_ALL;YES;SYSTEM;YES;" )
581- endif ()
582-
583651 set (oneValueArgs
584652 NAME
585653 FORCE
@@ -602,10 +670,26 @@ function(CPMAddPackage)
602670
603671 set (multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES)
604672
673+ list (LENGTH ARGN argnLength)
674+
675+ # Parse single shorthand argument
676+ if (argnLength EQUAL 1)
677+ cpm_parse_add_package_single_arg("${ARGN} " ARGN)
678+
679+ # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM
680+ set (ARGN "${ARGN} ;EXCLUDE_FROM_ALL;YES;SYSTEM;YES;" )
681+
682+ # Parse URI shorthand argument
683+ elseif (argnLength GREATER 1 AND "${ARGV0} " STREQUAL "URI" )
684+ list (REMOVE_AT ARGN 0 1) # remove "URI gh:<...>@version#tag"
685+ cpm_parse_add_package_single_arg("${ARGV1} " ARGV0)
686+
687+ set (ARGN "${ARGV0} ;EXCLUDE_FROM_ALL;YES;SYSTEM;YES;${ARGN} " )
688+ endif ()
689+
605690 cmake_parse_arguments (CPM_ARGS "" "${oneValueArgs} " "${multiValueArgs} " "${ARGN} " )
606691
607692 # Set default values for arguments
608-
609693 if (NOT DEFINED CPM_ARGS_VERSION)
610694 if (DEFINED CPM_ARGS_GIT_TAG)
611695 cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG} " CPM_ARGS_VERSION)
@@ -776,9 +860,19 @@ function(CPMAddPackage)
776860 set (download_directory ${CPM_SOURCE_CACHE} /${lower_case_name} /${CPM_ARGS_CUSTOM_CACHE_KEY} )
777861 elseif (CPM_USE_NAMED_CACHE_DIRECTORIES)
778862 string (SHA1 origin_hash "${origin_parameters} ;NEW_CACHE_STRUCTURE_TAG" )
863+ cpm_get_shortest_hash(
864+ "${CPM_SOURCE_CACHE} /${lower_case_name} " # source cache directory
865+ "${origin_hash} " # Input hash
866+ origin_hash # Computed hash
867+ )
779868 set (download_directory ${CPM_SOURCE_CACHE} /${lower_case_name} /${origin_hash} /${CPM_ARGS_NAME} )
780869 else ()
781870 string (SHA1 origin_hash "${origin_parameters} " )
871+ cpm_get_shortest_hash(
872+ "${CPM_SOURCE_CACHE} /${lower_case_name} " # source cache directory
873+ "${origin_hash} " # Input hash
874+ origin_hash # Computed hash
875+ )
782876 set (download_directory ${CPM_SOURCE_CACHE} /${lower_case_name} /${origin_hash} )
783877 endif ()
784878 # Expand `download_directory` relative path. This is important because EXISTS doesn't work for
@@ -845,7 +939,9 @@ function(CPMAddPackage)
845939 endif ()
846940 endif ()
847941
848- cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\" ${ARGN} \" )" )
942+ if (NOT "${DOWNLOAD_ONLY} " )
943+ cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\" ${ARGN} \" )" )
944+ endif ()
849945
850946 if (CPM_PACKAGE_LOCK_ENABLED)
851947 if ((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK)
@@ -862,14 +958,39 @@ function(CPMAddPackage)
862958 )
863959
864960 if (NOT CPM_SKIP_FETCH)
961+ # CMake 3.28 added EXCLUDE, SYSTEM (3.25), and SOURCE_SUBDIR (3.18) to FetchContent_Declare.
962+ # Calling FetchContent_MakeAvailable will then internally forward these options to
963+ # add_subdirectory. Up until these changes, we had to call FetchContent_Populate and
964+ # add_subdirectory separately, which is no longer necessary and has been deprecated as of 3.30.
965+ # A Bug in CMake prevents us to use the non-deprecated functions until 3.30.3.
966+ set (fetchContentDeclareExtraArgs "" )
967+ if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3" )
968+ if (${CPM_ARGS_EXCLUDE_FROM_ALL} )
969+ list (APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL )
970+ endif ()
971+ if (${CPM_ARGS_SYSTEM} )
972+ list (APPEND fetchContentDeclareExtraArgs SYSTEM )
973+ endif ()
974+ if (DEFINED CPM_ARGS_SOURCE_SUBDIR)
975+ list (APPEND fetchContentDeclareExtraArgs SOURCE_SUBDIR ${CPM_ARGS_SOURCE_SUBDIR} )
976+ endif ()
977+ # For CMake version <3.28 OPTIONS are parsed in cpm_add_subdirectory
978+ if (CPM_ARGS_OPTIONS AND NOT DOWNLOAD_ONLY)
979+ foreach (OPTION ${CPM_ARGS_OPTIONS} )
980+ cpm_parse_option("${OPTION} " )
981+ set (${OPTION_KEY} "${OPTION_VALUE} " )
982+ endforeach ()
983+ endif ()
984+ endif ()
865985 cpm_declare_fetch(
866- "${CPM_ARGS_NAME} " " ${CPM_ARGS_VERSION} " " ${PACKAGE_INFO} " "${CPM_ARGS_UNPARSED_ARGUMENTS} "
986+ "${CPM_ARGS_NAME} " ${fetchContentDeclareExtraArgs} "${CPM_ARGS_UNPARSED_ARGUMENTS} "
867987 )
868- cpm_fetch_package("${CPM_ARGS_NAME} " populated)
988+
989+ cpm_fetch_package("${CPM_ARGS_NAME} " ${DOWNLOAD_ONLY} populated ${CPM_ARGS_UNPARSED_ARGUMENTS} )
869990 if (CPM_SOURCE_CACHE AND download_directory)
870991 file (LOCK ${download_directory} /../cmake.lock RELEASE)
871992 endif ()
872- if (${populated} )
993+ if (${populated} AND ${CMAKE_VERSION} VERSION_LESS "3.30.3" )
873994 cpm_add_subdirectory(
874995 "${CPM_ARGS_NAME} "
875996 "${DOWNLOAD_ONLY} "
@@ -980,7 +1101,7 @@ function(CPMGetPackageVersion PACKAGE OUTPUT)
9801101endfunction ()
9811102
9821103# declares a package in FetchContent_Declare
983- function (cpm_declare_fetch PACKAGE VERSION INFO )
1104+ function (cpm_declare_fetch PACKAGE)
9841105 if (${CPM_DRY_RUN} )
9851106 cpm_message(STATUS "${CPM_INDENT} Package not declared (dry run)" )
9861107 return ()
@@ -1056,7 +1177,7 @@ endfunction()
10561177
10571178# downloads a previously declared package via FetchContent and exports the variables
10581179# `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope
1059- function (cpm_fetch_package PACKAGE populated)
1180+ function (cpm_fetch_package PACKAGE DOWNLOAD_ONLY populated)
10601181 set (${populated}
10611182 FALSE
10621183 PARENT_SCOPE
@@ -1071,7 +1192,24 @@ function(cpm_fetch_package PACKAGE populated)
10711192 string (TOLOWER "${PACKAGE} " lower_case_name)
10721193
10731194 if (NOT ${lower_case_name} _POPULATED)
1074- FetchContent_Populate(${PACKAGE} )
1195+ if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3" )
1196+ if (DOWNLOAD_ONLY)
1197+ # MakeAvailable will call add_subdirectory internally which is not what we want when
1198+ # DOWNLOAD_ONLY is set. Populate will only download the dependency without adding it to the
1199+ # build
1200+ FetchContent_Populate(
1201+ ${PACKAGE}
1202+ SOURCE_DIR "${CPM_FETCHCONTENT_BASE_DIR} /${lower_case_name} -src"
1203+ BINARY_DIR "${CPM_FETCHCONTENT_BASE_DIR} /${lower_case_name} -build"
1204+ SUBBUILD_DIR "${CPM_FETCHCONTENT_BASE_DIR} /${lower_case_name} -subbuild"
1205+ ${ARGN}
1206+ )
1207+ else ()
1208+ FetchContent_MakeAvailable(${PACKAGE} )
1209+ endif ()
1210+ else ()
1211+ FetchContent_Populate(${PACKAGE} )
1212+ endif ()
10751213 set (${populated}
10761214 TRUE
10771215 PARENT_SCOPE
0 commit comments