Skip to content

Commit 9bad044

Browse files
authored
updating CPM (#991)
1 parent c577b16 commit 9bad044

File tree

1 file changed

+165
-27
lines changed

1 file changed

+165
-27
lines changed

cmake/CPM.cmake

Lines changed: 165 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ if(NOT COMMAND cpm_message)
4242
endfunction()
4343
endif()
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

4751
get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH)
4852
if(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()
199203
endfunction()
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
202260
function(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
270328
function(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}\nset(${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}\nset(${Name}_FOUND TRUE)"
347+
)
348+
endif()
276349
endif()
277350
endfunction()
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()
572648
function(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)
9801101
endfunction()
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

Comments
 (0)