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

Added relative git URL support #333

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
141 changes: 140 additions & 1 deletion cmake/CPM.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ function(cpm_parse_add_package_single_arg arg outArgs)
elseif(scheme STREQUAL "bb")
set(out "BITBUCKET_REPOSITORY;${uri}")
set(packageType "git")
elseif(scheme STREQUAL "rel")
set(out "RELATIVE_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/?(@|#|$)")
Expand Down Expand Up @@ -394,7 +397,6 @@ 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)
Expand Down Expand Up @@ -454,6 +456,139 @@ function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean)

endfunction()

# Set a specific URL to be used as a base for all relative URIs, e.g. "https://github.com/myorg"
function(CPMSetRelativeUriBaseUrl baseUrl)
set(CPM_RELATIVE_URI_BASE_URL
"${baseUrl}"
PARENT_SCOPE
)
endfunction()

# Specify that the base URL for relative URIs should be inferred from the current directory (default
# behavior)
function(CPMSetRelativeUriBaseAuto)
unset(CPM_RELATIVE_URI_BASE_URL PARENT_SCOPE)
endfunction()

# Normalize the URI, mainly for "cd up" (..) directives, as those are not supported by SSH
function(cpm_normalize_uri input output)
# Extract the scheme and the authority (if present) from the path can either be
# http(s)://authority/ or git@authority:
string(REGEX MATCH "^(https?:\\/\\/[^\\/]+\\/)(.*)$" match "${input}")

if(match)
set(schemeAndAuthority "${CMAKE_MATCH_1}")
set(path "${CMAKE_MATCH_2}")
else()
string(REGEX MATCH "^(git@[^:]+:)(.*)$" match "${input}")

if(match)
set(schemeAndAuthority "${CMAKE_MATCH_1}")
set(path "${CMAKE_MATCH_2}")
else()
set(schemeAndAuthority "")
set(path "${input}")
endif()
endif()

# Normalize the path, which should get rid of the "cd up" directives
cmake_path(NORMAL_PATH path)

set(${output}
"${schemeAndAuthority}${path}"
PARENT_SCOPE
)
endfunction()

# Convert a URI relative to that of the URL of the remote of our current Git repository e.g.
# github.com/user/repo.git + ../other.git = github.com/user/other.git
function(cpm_git_relative_uri_to_url relativeUri name url)
if(${CMAKE_VERSION} VERSION_LESS "3.20.0")
message(ERROR "Relative URIs require CMake 3.20+")
return()
endif()

if(NOT name)
# If no name was provided, get it from the relative uri
cpm_package_name_from_git_uri("${relativeUri}.git" name)

if(NOT name)
message(SEND_ERROR "Name of the project couldn't be inferred from the relative URI")
return()
endif()
endif()

# If it has been cached, do not resolve it
if(DEFINED CPM_PACKAGE_${name}_RESOLVED_URL AND "${CPM_PACKAGE_${name}_RELATIVE_URI}" STREQUAL
"${relativeUri}"
)
set(${url}
"${CPM_PACKAGE_${name}_RESOLVED_URL}"
PARENT_SCOPE
)
return()
endif()

# Save the relative URI that was passed inside of the cache
set("CPM_PACKAGE_${name}_RELATIVE_URI"
"${relativeUri}"
CACHE INTERNAL ""
)

# If a base URL is specified, use that
if(CPM_RELATIVE_URI_BASE_URL)
set(remoteUrl "${CPM_RELATIVE_URI_BASE_URL}")
else()
find_package(Git REQUIRED)

if(NOT Git_FOUND)
message(SEND_ERROR "Git not found, cannot convert relative URI to absolute Git URL")
return()
endif()

# Get the list of remotes available
execute_process(
COMMAND ${GIT_EXECUTABLE} remote -v
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE remotesInfos
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET
)

string(REPLACE "\n" ";" remotesInfosList "${remotesInfos}")

# Since this was a verbose output, the output is in the format <remote_name> <remote_url>
# (fetch/push) Get the first remote fetch URL
foreach(remoteInfo ${remotesInfosList})
string(REGEX MATCH "^[^ \t]+[ \t]+([^ \t]+) \\(fetch\\)$" match "${remoteInfo}")

if(match)
set(remoteUrl "${CMAKE_MATCH_1}")
break()
endif()
endforeach()

if(NOT DEFINED remoteUrl)
message(SEND_ERROR "No remote with fetch ability was detected in this repository")
return()
endif()
endif()

string(CONCAT absoluteUrl "${remoteUrl}" "/" "${relativeUri}")

cpm_normalize_uri("${absoluteUrl}" absoluteUrlNormalized)

# Save it within the cache
set("CPM_PACKAGE_${name}_RESOLVED_URL"
"${absoluteUrlNormalized}"
CACHE INTERNAL ""
)

set(${url}
"${absoluteUrlNormalized}"
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:
Expand Down Expand Up @@ -516,6 +651,7 @@ function(CPMAddPackage)
GITHUB_REPOSITORY
GITLAB_REPOSITORY
BITBUCKET_REPOSITORY
RELATIVE_REPOSITORY
GIT_REPOSITORY
SOURCE_DIR
DOWNLOAD_COMMAND
Expand Down Expand Up @@ -550,6 +686,9 @@ function(CPMAddPackage)
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")
elseif(DEFINED CPM_ARGS_RELATIVE_REPOSITORY)
cpm_git_relative_uri_to_url("${CPM_ARGS_RELATIVE_REPOSITORY}" "${CPM_ARGS_NAME}" absoluteGitUrl)
set(CPM_ARGS_GIT_REPOSITORY "${absoluteGitUrl}.git")
endif()

if(DEFINED CPM_ARGS_GIT_REPOSITORY)
Expand Down