Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of "Users in enterprise environments require the ability to add custom URI schemes and override standard URI schemes" #567

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
295 changes: 253 additions & 42 deletions cmake/CPM.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,54 @@ set(CPM_DRY_RUN
OFF
CACHE INTERNAL "Don't download or configure dependencies (for testing)"
)
set(CPM_URI_SCHEMES
"gh|GITHUB_REPOSITORY|GIT_REPOSITORY|https://github.com|.git"
"gl|GITLAB_REPOSITORY|GIT_REPOSITORY|https://gitlab.com|.git"
"bb|BITBUCKET_REPOSITORY|GIT_REPOSITORY|https://bitbucket.org|.git"
CACHE INTERNAL ""
)
set(CPM_URI_SCHEME_PATTERN
"^([a-z][a-z0-9]*)\\|([A-Z_][A-Z0-9_]*)\\|(GIT_REPOSITORY|URL)\\|(.+)\\|(.*)$"
CACHE INTERNAL ""
)

function(
cpm_uri_scheme_from_string
# INPUTS
schemeStr
# OUTPUTS
alias
longName
uriType
uriRoot
uriSuffix
)
if("${schemeStr}" MATCHES "${CPM_URI_SCHEME_PATTERN}")
string(TOLOWER "${CMAKE_MATCH_1}" thisScheme)
set(${alias}
"${thisScheme}"
PARENT_SCOPE
)
set(${longName}
"${CMAKE_MATCH_2}"
PARENT_SCOPE
)
set(${uriType}
"${CMAKE_MATCH_3}"
PARENT_SCOPE
)
set(${uriRoot}
"${CMAKE_MATCH_4}"
PARENT_SCOPE
)
set(${uriSuffix}
"${CMAKE_MATCH_5}"
PARENT_SCOPE
)
else()
message(STATUS "Not a URI scheme: ${schemeStr}")
endif()
endfunction()

if(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE})
Expand Down Expand Up @@ -334,46 +382,150 @@ function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION)
endif()
endfunction()

function(cpm_infer_package_type uri uriType uriSuffix packageType)
if("${uriType}" STREQUAL "GIT_REPOSITORY")
set(${packageType}
"git"
PARENT_SCOPE
)
else()
if("${uri}" MATCHES ".git([@#$]?)")
set(${packageType}
"git"
PARENT_SCOPE
)
else()
if("${uriSuffix}" MATCHES ".git([@#$]?)")
set(${packageType}
"git"
PARENT_SCOPE
)
else()
set(${packageType}
"archive"
PARENT_SCOPE
)
endif()
endif()
endif()
endfunction()

# Look through the CPM_URI_SCHEMES (default and user-defined) to find one that matches the supplied
# scheme value. If a match is found, uriRoot will be populated and foundCustomScheme set to TRUE. If
# a match is NOT found, foundCustomScheme set to FALSE.
function(
cpm_expand_via_scheme
scheme
uriFragment
expanded
repoType
pkgType
success
)
set(${success}
FALSE
PARENT_SCOPE
)

foreach(mapping ${CPM_URI_SCHEMES})
cpm_uri_scheme_from_string(${mapping} alias longName uriType uriRoot uriSuffix)
if(scheme STREQUAL ${alias})
set(expandedValue "${longName};${uriFragment}")
set(${expanded}
${expandedValue}
PARENT_SCOPE
)

set(${repoType}
${CMAKE_MATCH_3}
PARENT_SCOPE
)
cpm_infer_package_type("${uriFragment}" "${uriType}" "${uriSuffix}" inferredPackageType)
set(${pkgType}
${inferredPackageType}
PARENT_SCOPE
)
set(${success}
"TRUE"
PARENT_SCOPE
)
break()
endif()
endforeach()
endfunction()

function(cpm_is_valid_scheme scheme result)
set(${result}
FALSE
PARENT_SCOPE
)

foreach(mapping ${CPM_URI_SCHEMES})
if("${mapping}" MATCHES "${CPM_URI_SCHEME_PATTERN}")
string(TOLOWER "${CMAKE_MATCH_1}" testScheme)
if(scheme STREQUAL ${testScheme})
set(${result}
TRUE
PARENT_SCOPE
)
break()
endif()
else()
cpm_message(STATUS "${CPM_INDENT} ${mapping} is not a valid mapping")
endif()
endforeach()
endfunction()

function(cpm_extract_scheme mapping scheme uriFragment success)
set(${success}
FALSE
PARENT_SCOPE
)

if("${mapping}" MATCHES "^([a-zA-Z][a-zA-Z0-9]*):(.+)$")
string(TOLOWER "${CMAKE_MATCH_1}" foundScheme)
cpm_is_valid_scheme(${foundScheme} isValidScheme)
if(${isValidScheme})
set(${scheme}
${foundScheme}
PARENT_SCOPE
)
set(${uriFragment}
"${CMAKE_MATCH_2}"
PARENT_SCOPE
)
set(${success}
TRUE
PARENT_SCOPE
)
else()

endif()
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/[email protected] 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")
# Look for a scheme. The uriFragment may also be returned with version and tag/hash info on the
# end of it.
cpm_extract_scheme(${arg} scheme uriFragment isScheme)
if(isScheme)
cpm_expand_via_scheme(
${scheme} ${uriFragment} outputVar repoType packageType successfulExpansion
)
if(successfulExpansion)
set(out "${outputVar}")
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
message(FATAL_ERROR "${CPM_INDENT} Failed to expand scheme from '${arg}'")
endif()
else()
if(arg MATCHES ".git/?(@|#|$)")
cpm_infer_package_type("${arg}" "" "" packageType)
if("${packageType}" STREQUAL "git")
set(out "GIT_REPOSITORY;${arg}")
set(packageType "git")
else()
# Give up
message(FATAL_ERROR "${CPM_INDENT} Can't determine package type of '${arg}'")
set(out "URL;${arg}")
endif()
endif()

Expand Down Expand Up @@ -586,9 +738,6 @@ function(CPMAddPackage)
VERSION
GIT_TAG
DOWNLOAD_ONLY
GITHUB_REPOSITORY
GITLAB_REPOSITORY
BITBUCKET_REPOSITORY
GIT_REPOSITORY
SOURCE_DIR
FIND_PACKAGE_ARGUMENTS
Expand All @@ -600,12 +749,24 @@ function(CPMAddPackage)
CUSTOM_CACHE_KEY
)

foreach(scheme ${CPM_URI_SCHEMES})
cpm_uri_scheme_from_string(
${scheme}
alias
longName
uriType
uriRoot
uriSuffix
isScheme
)
list(APPEND oneValueArgs ${longName})
endforeach()

set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES)

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)
Expand All @@ -618,13 +779,23 @@ function(CPMAddPackage)
set(DOWNLOAD_ONLY NO)
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()
foreach(schemeStr ${CPM_URI_SCHEMES})
unset(cpmRepoType)
cpm_uri_scheme_from_string(${schemeStr} alias longName uriType uriRoot uriSuffix)
if(DEFINED CPM_ARGS_${longName})
string(CONCAT cpmRepoType CPM_ARGS_ ${longName})
cpm_expand_via_scheme(
${alias} ${${cpmRepoType}} completeUri repoType pkgType successfulExpansion
)
if(NOT successfulExpansion)
message(FATAL_ERROR "${CPM_INDENT} Failed to generate repository URL from '${cpmRepoType}'")
endif()

string(CONCAT uriTypeArg CPM_ARGS_ ${uriType})
string(REPLACE ${longName} "" completeUri ${completeUri})
set(${uriTypeArg} ${uriRoot}/${completeUri}${uriSuffix})
endif()
endforeach()

if(DEFINED CPM_ARGS_GIT_REPOSITORY)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY})
Expand Down Expand Up @@ -1223,3 +1394,43 @@ function(cpm_prettify_package_arguments OUT_VAR IS_IN_COMMENT)
)

endfunction()

function(CPMDefineUriScheme)
set(oneValueArgs ALIAS LONG_NAME URI_TYPE URI_ROOT URI_SUFFIX)
cmake_parse_arguments(CPM_SCHEME "" "${oneValueArgs}" "" ${ARGN})

if(NOT DEFINED CPM_SCHEME_ALIAS)
message(FATAL_ERROR "${CPM_INDENT} CPMDefineUriScheme requires ALIAS argument")
endif()
if(NOT DEFINED CPM_SCHEME_LONG_NAME)
message(FATAL_ERROR "${CPM_INDENT} CPMDefineUriScheme requires LONG_NAME argument")
endif()
if(NOT DEFINED CPM_SCHEME_URI_ROOT)
message(FATAL_ERROR "${CPM_INDENT} CPMDefineUriScheme requires URI_ROOT argument")
endif()
if(NOT DEFINED CPM_SCHEME_URI_TYPE)
message(FATAL_ERROR "${CPM_INDENT} CPMDefineUriScheme requires URI_TYPE argument")
endif()
if(NOT DEFINED CPM_SCHEME_URI_SUFFIX)
if(${CPM_SCHEME_URI_TYPE} STREQUAL "GIT_REPOSITORY")
set(CPM_SCHEME_URI_SUFFIX ".git")
else()
set(CPM_SCHEME_URI_SUFFIX "")
endif()
endif()

set(newScheme
"${CPM_SCHEME_ALIAS}|${CPM_SCHEME_LONG_NAME}|${CPM_SCHEME_URI_TYPE}|${CPM_SCHEME_URI_ROOT}|${CPM_SCHEME_URI_SUFFIX}"
)

list(TRANSFORM CPM_URI_SCHEMES REPLACE "^${CPM_SCHEME_ALIAS}" "${newScheme}")
list(FIND CPM_URI_SCHEMES "${newScheme}" index)
if(index EQUAL -1)
list(APPEND CPM_URI_SCHEMES "${newScheme}")
endif()

set(CPM_URI_SCHEMES
${CPM_URI_SCHEMES}
CACHE INTERNAL ""
)
endfunction()
44 changes: 44 additions & 0 deletions test/unit/parse_add_package_single_arg.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,50 @@ assert_equal("GIT_REPOSITORY;[email protected]:user/pkg.git;VERSION;0.1.2" "${args}")
cpm_parse_add_package_single_arg("[email protected]:user/[email protected]#rc" args)
assert_equal("GIT_REPOSITORY;[email protected]:user/pkg.git;VERSION;0.1.2;GIT_TAG;rc" "${args}")

cpmdefineurischeme(
ALIAS
"ir"
LONG_NAME
"INTERNAL_REPOS"
URI_TYPE
"GIT_REPOSITORY"
URI_ROOT
"[email protected]"
)

cpmdefineurischeme(
ALIAS
"ir2"
LONG_NAME
"INTERNAL_REPOS2"
URI_TYPE
"GIT_REPOSITORY"
URI_ROOT
"https://company.internal.oldGitserver"
URI_SUFFIX
".gitz"
)

cpmdefineurischeme(
ALIAS
"af"
LONG_NAME
"ARTIFACTORY_PKG"
URI_TYPE
"URL"
URI_ROOT
"https://my.company.artifatory/pkgs"
)

cpm_parse_add_package_single_arg("ir:somegroup/[email protected]#asdf" args)
assert_equal("INTERNAL_REPOS;somegroup/somerepo;VERSION;0.20.3;GIT_TAG;asdf" "${args}")

cpm_parse_add_package_single_arg("ir2:somegroup/[email protected]#asdf" args)
assert_equal("INTERNAL_REPOS2;somegroup/somerepo;VERSION;0.20.3;GIT_TAG;asdf" "${args}")

cpm_parse_add_package_single_arg("af:somegroup/someitem.zip" args)
assert_equal("ARTIFACTORY_PKG;somegroup/someitem.zip" "${args}")

cpm_parse_add_package_single_arg(
"ssh://[email protected]:123/path/to/pkg.git#[email protected]#branch" args
)
Expand Down