blob: ca3cd9fec7c21024cf0b0bf670ad97c9647eb8ce [file] [log] [blame]
cmake_policy(VERSION 3.25)
# Determine the remote URL of the project containing the working_directory.
# This will leave output_variable unset if the URL can't be determined.
function(_ep_get_git_remote_url output_variable working_directory)
set("${output_variable}" "" PARENT_SCOPE)
find_package(Git QUIET REQUIRED)
execute_process(
COMMAND ${GIT_EXECUTABLE} symbolic-ref --short HEAD
WORKING_DIRECTORY "${working_directory}"
OUTPUT_VARIABLE git_symbolic_ref
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT git_symbolic_ref STREQUAL "")
# We are potentially on a branch. See if that branch is associated with
# an upstream remote (might be just a local one or not a branch at all).
execute_process(
COMMAND ${GIT_EXECUTABLE} config branch.${git_symbolic_ref}.remote
WORKING_DIRECTORY "${working_directory}"
OUTPUT_VARIABLE git_remote_name
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
endif()
if(NOT git_remote_name)
# Can't select a remote based on a branch. If there's only one remote,
# or we have multiple remotes but one is called "origin", choose that.
execute_process(
COMMAND ${GIT_EXECUTABLE} remote
WORKING_DIRECTORY "${working_directory}"
OUTPUT_VARIABLE git_remote_list
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
string(REPLACE "\n" ";" git_remote_list "${git_remote_list}")
list(LENGTH git_remote_list git_remote_list_length)
if(git_remote_list_length EQUAL 0)
message(FATAL_ERROR "Git remote not found in parent project.")
elseif(git_remote_list_length EQUAL 1)
list(GET git_remote_list 0 git_remote_name)
else()
set(base_warning_msg "Multiple git remotes found for parent project")
if("origin" IN_LIST git_remote_list)
message(WARNING "${base_warning_msg}, defaulting to origin.")
set(git_remote_name "origin")
else()
message(FATAL_ERROR "${base_warning_msg}, none of which are origin.")
endif()
endif()
endif()
if(GIT_VERSION VERSION_LESS 1.7.5)
set(_git_remote_url_cmd_args config remote.${git_remote_name}.url)
elseif(GIT_VERSION VERSION_LESS 2.7)
set(_git_remote_url_cmd_args ls-remote --get-url ${git_remote_name})
else()
set(_git_remote_url_cmd_args remote get-url ${git_remote_name})
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} ${_git_remote_url_cmd_args}
WORKING_DIRECTORY "${working_directory}"
OUTPUT_VARIABLE git_remote_url
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL LAST
ENCODING UTF-8 # Needed to handle non-ascii characters in local paths
)
set("${output_variable}" "${git_remote_url}" PARENT_SCOPE)
endfunction()
function(_ep_is_relative_git_remote output_variable remote_url)
if(remote_url MATCHES "^\\.\\./")
set("${output_variable}" TRUE PARENT_SCOPE)
else()
set("${output_variable}" FALSE PARENT_SCOPE)
endif()
endfunction()
# Return an absolute remote URL given an existing remote URL and relative path.
# The output_variable will be set to an empty string if an absolute URL
# could not be computed (no error message is output).
function(_ep_resolve_relative_git_remote
output_variable
parent_remote_url
relative_remote_url
)
set("${output_variable}" "" PARENT_SCOPE)
if(parent_remote_url STREQUAL "")
return()
endif()
string(REGEX MATCH
"^(([A-Za-z0-9][A-Za-z0-9+.-]*)://)?(([^/@]+)@)?(\\[[A-Za-z0-9:]+\\]|[^/:]+)?([/:]/?)(.+(\\.git)?/?)$"
git_remote_url_components
"${parent_remote_url}"
)
set(protocol "${CMAKE_MATCH_1}")
set(auth "${CMAKE_MATCH_3}")
set(host "${CMAKE_MATCH_5}")
set(separator "${CMAKE_MATCH_6}")
set(path "${CMAKE_MATCH_7}")
string(REPLACE "/" ";" remote_path_components "${path}")
string(REPLACE "/" ";" relative_path_components "${relative_remote_url}")
foreach(relative_path_component IN LISTS relative_path_components)
if(NOT relative_path_component STREQUAL "..")
break()
endif()
list(LENGTH remote_path_components remote_path_component_count)
if(remote_path_component_count LESS 1)
return()
endif()
list(POP_BACK remote_path_components)
list(POP_FRONT relative_path_components)
endforeach()
list(APPEND final_path_components ${remote_path_components} ${relative_path_components})
list(JOIN final_path_components "/" path)
set("${output_variable}" "${protocol}${auth}${host}${separator}${path}" PARENT_SCOPE)
endfunction()
# The output_variable will be set to the original git_repository if it
# could not be resolved (no error message is output). The original value is
# also returned if it doesn't need to be resolved.
function(_ep_resolve_git_remote
output_variable
git_repository
cmp0150
cmp0150_old_base_dir
)
if(git_repository STREQUAL "")
set("${output_variable}" "" PARENT_SCOPE)
return()
endif()
_ep_is_relative_git_remote(_git_repository_is_relative "${git_repository}")
if(NOT _git_repository_is_relative)
set("${output_variable}" "${git_repository}" PARENT_SCOPE)
return()
endif()
if(cmp0150 STREQUAL "NEW")
_ep_get_git_remote_url(_parent_git_remote_url "${CMAKE_CURRENT_SOURCE_DIR}")
_ep_resolve_relative_git_remote(_resolved_git_remote_url "${_parent_git_remote_url}" "${git_repository}")
if(_resolved_git_remote_url STREQUAL "")
message(FATAL_ERROR
"Failed to resolve relative git remote URL:\n"
" Relative URL: ${git_repository}\n"
" Parent URL: ${_parent_git_remote_url}"
)
endif()
set("${output_variable}" "${_resolved_git_remote_url}" PARENT_SCOPE)
return()
elseif(cmp0150 STREQUAL "")
cmake_policy(GET_WARNING CMP0150 _cmp0150_warning)
message(AUTHOR_WARNING
"${_cmp0150_warning}\n"
"A relative GIT_REPOSITORY path was detected. "
"This will be interpreted as a local path to where the project is being cloned. "
"Set GIT_REPOSITORY to an absolute path or set policy CMP0150 to NEW to avoid "
"this warning."
)
endif()
set("${output_variable}" "${cmp0150_old_base_dir}/${git_repository}" PARENT_SCOPE)
endfunction()