init-repository: Normalize git urls with more than one '../' segment

The qttools qlitehtml submodule url was recently updated to use a
relative url like '../../playground/qlitehtml.git' instead of an
absolute path to code.qt.io repo.
Same for the nested litehtml submodule.

This broke initialization of the qttools repository, because the
init-repository script only normalized one '../' segment in the
submodule url, instead of all of them, which ended up trying to clone
a non-existent git://code.qt.io/qt/../playground/qlitehtml.git repo.

Apply the url normalization in all the code paths where there might
still be '../' segments in the url.

Pick-to: 6.8 6.9
Change-Id: Iaa8e58104c92858318ad66aefa5a38d63ad7a155
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
Alexandru Croitor 2024-12-04 18:48:07 +01:00
parent 444167f5fb
commit ff25d705a3
2 changed files with 110 additions and 26 deletions

View File

@ -93,9 +93,15 @@ function(qt_ir_run_git_submodule_init submodules working_directory)
qt_ir_setup_commit_template("${working_directory}" "${working_directory}") qt_ir_setup_commit_template("${working_directory}" "${working_directory}")
endfunction() endfunction()
# Add gerrit remotes to the repository. # Add gerrit remotes to the repository located in the working_directory.
function(qt_ir_add_git_remotes repo_name working_directory) # repo_relative_url is the relative URL of the repository.
set(gerrit_ssh_base "ssh://@USER@codereview.qt-project.org@PORT@/qt/") # Examples:
# - qt5
# - qttools.git
# - ../playground/qlitehtml.git
# - ../qt/qttools-litehtml.git
function(qt_ir_add_git_remotes repo_relative_url working_directory)
set(gerrit_ssh_base "ssh://@USER@codereview.qt-project.org@PORT@/")
set(gerrit_repo_url "${gerrit_ssh_base}") set(gerrit_repo_url "${gerrit_ssh_base}")
qt_ir_get_option_value(codereview-username username) qt_ir_get_option_value(codereview-username username)
@ -110,7 +116,10 @@ function(qt_ir_add_git_remotes repo_name working_directory)
string(REPLACE "@PORT@" "" gerrit_repo_url "${gerrit_repo_url}") string(REPLACE "@PORT@" "" gerrit_repo_url "${gerrit_repo_url}")
endif() endif()
string(APPEND gerrit_repo_url "${repo_name}") set(namespace "qt")
set(repo_relative_url_with_namespace "${namespace}/${repo_relative_url}")
qt_ir_normalize_git_url("${repo_relative_url_with_namespace}" normalized_url)
string(APPEND gerrit_repo_url "${normalized_url}")
qt_ir_execute_process_and_log_and_handle_error( qt_ir_execute_process_and_log_and_handle_error(
COMMAND_ARGS git config remote.gerrit.url "${gerrit_repo_url}" COMMAND_ARGS git config remote.gerrit.url "${gerrit_repo_url}"
@ -193,15 +202,21 @@ function(qt_ir_clone_one_submodule submodule_name)
set(submodule_base_git_path "${${prefix}_${submodule_name}_base_git_path}") set(submodule_base_git_path "${${prefix}_${submodule_name}_base_git_path}")
set(submodule_url "${submodule_base_git_path}") set(submodule_url "${submodule_base_git_path}")
qt_ir_has_url_scheme("${submodule_url}" has_url_scheme) qt_ir_parse_git_url(
URL "${submodule_url}"
OUT_VAR_HAS_URL_SCHEME has_url_scheme
)
if(NOT has_url_scheme AND arg_BASE_URL) if(NOT has_url_scheme AND arg_BASE_URL)
set(submodule_url "${arg_BASE_URL}${submodule_url}") set(submodule_url "${arg_BASE_URL}${submodule_url}")
qt_ir_normalize_git_url("${submodule_url}" submodule_url)
endif() endif()
qt_ir_get_mirror(mirror_url) qt_ir_get_mirror(mirror_url)
set(mirror "") set(mirror "")
if(NOT has_url_scheme AND mirror_url AND (should_clone OR arg_FETCH)) if(NOT has_url_scheme AND mirror_url AND (should_clone OR arg_FETCH))
set(mirror "${mirror_url}${submodule_base_git_path}") set(mirror "${mirror_url}${submodule_base_git_path}")
qt_ir_normalize_git_url("${mirror}" mirror)
endif() endif()
set(mirror_or_original_url "${submodule_url}") set(mirror_or_original_url "${submodule_url}")

View File

@ -40,16 +40,77 @@ function(qt_ir_get_git_config_contents out_var)
set(${out_var} "${git_output}" PARENT_SCOPE) set(${out_var} "${git_output}" PARENT_SCOPE)
endfunction() endfunction()
# Checks whether the given url has a scheme like https:// or is just a # Parses a git repo url to:
# relative path. # - check if the given url has a scheme like https:// or git:// or is just a
function(qt_ir_has_url_scheme url out_var) # relative path with no scheme (possibly containing '../' segments)
string(REGEX MATCH "^[a-z][a-z0-9+\-.]*://" has_url_scheme "${url}") # - extracts the scheme if it exists
# - extracts the url without the scheme
function(qt_ir_parse_git_url)
set(options "")
set(oneValueArgs
URL
OUT_VAR_HAS_URL_SCHEME
OUT_VAR_SCHEME
OUT_VAR_URL_WITHOUT_SCHEME
)
set(multiValueArgs "")
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
string(REGEX MATCH "^([a-z][a-z0-9+\-.]*://)(.+)" url_scheme_match "${arg_URL}")
if(url_scheme_match)
set(has_url_scheme TRUE)
set(scheme "${CMAKE_MATCH_1}")
set(url_without_scheme "${CMAKE_MATCH_2}")
else()
set(has_url_scheme FALSE)
set(scheme "")
set(url_without_scheme "${url}")
endif()
if(arg_OUT_VAR_HAS_URL_SCHEME)
set(${arg_OUT_VAR_HAS_URL_SCHEME} "${has_url_scheme}" PARENT_SCOPE)
endif()
if(arg_OUT_VAR_SCHEME)
set(${arg_OUT_VAR_SCHEME} "${scheme}" PARENT_SCOPE)
endif()
if(arg_OUT_VAR_URL_WITHOUT_SCHEME)
set(${arg_OUT_VAR_URL_WITHOUT_SCHEME} "${url_without_scheme}" PARENT_SCOPE)
endif()
endfunction()
# Normalizes a url that contains '../' path segments.
# Removes the '../' segments and the directories that they precede.
# Example:
# git://code.qt.io/qt/../playground/qlitehtml.git
# will be normalized to:
# git://code.qt.io/playground/qlitehtml.git
function(qt_ir_normalize_git_url url out_var)
# The exact perl code was while ($base =~ s,(?!\.\./)[^/]+/\.\./,,g) {}
# That got rid of ../ and ../../ in the path, but it broke down
# when more than two '../' segments were present.
#
# In CMake, we instead parse the url to get the non-scheme suffix,
# use get_filename_component(ABSOLUTE) to resolve the url as if it was a relative path
# and then re-add the scheme if it was present.
qt_ir_parse_git_url(
URL "${url}"
OUT_VAR_HAS_URL_SCHEME has_url_scheme
OUT_VAR_SCHEME url_scheme
OUT_VAR_URL_WITHOUT_SCHEME url_without_scheme
)
# Note the empty BASE_DIR is important, otherwise the path is relative to
# ${CMAKE_CURRENT_SOURCE_DIR}.
get_filename_component(normalized_url "${url_without_scheme}" ABSOLUTE BASE_DIR "")
if(has_url_scheme) if(has_url_scheme)
set(${out_var} TRUE PARENT_SCOPE) string(PREPEND normalized_url "${url_scheme}")
else()
set(${out_var} FALSE PARENT_SCOPE)
endif() endif()
set(${out_var} "${normalized_url}" PARENT_SCOPE)
endfunction() endfunction()
# Parses a key-value line from a .git/config or .gitmodules file # Parses a key-value line from a .git/config or .gitmodules file
@ -79,14 +140,22 @@ endmacro()
# url_value # url_value
# the url where to clone a repo from # the url where to clone a repo from
# in perl script it was called $base # in perl script it was called $base
# e.g. '../qtbase.git', 'https://code.qt.io/playground/qlitehtml.git' # Examples:
# - '../qtbase.git'
# - 'https://code.qt.io/playground/qlitehtml.git'
# - '../../playground/qlitehtml.git'
# parent_repo_base_git_path # parent_repo_base_git_path
# the base git path of the parent of the submodule # the base git path of the parent of the submodule
# it is either a relative dir or a full url # it is either a relative dir or a full url
# in the perl script it was called $my_repo_base, # in the perl script it was called $my_repo_base,
# it was passed as first arg to git_clone_all_submodules, # it was passed as first arg to git_clone_all_submodules,
# it was passed the value of $subbases{$module} when doing recursive submodule cloning # it was passed the value of $subbases{$module} when doing recursive submodule cloning
# e.g. 'qt5', 'tqtc-qt5', 'qtdeclarative.git', 'https://code.qt.io/playground/qlitehtml.git' # Examples:
# - 'qt5'
# - 'tqtc-qt5'
# - 'qtdeclarative.git'
# - 'qttools.git'
# - 'https://code.qt.io/playground/qlitehtml.git'
# #
# Outputs # Outputs
# #
@ -94,21 +163,21 @@ endmacro()
# just the value of ${url_value} # just the value of ${url_value}
# ${out_var_prefix}_${submodule_name}_base_git_path # ${out_var_prefix}_${submodule_name}_base_git_path
# the whole url if it has a scheme, otherwise it's the value of # the whole url if it has a scheme, otherwise it's the value of
# ${url_value} relative to ${parent_repo_base_git_path}, so all the ../ are collapsed # ${url_value} relative to ${parent_repo_base_git_path}, so some of the '../' segments
# e.g. 'qtdeclarative.git' # are collapsed depending on how many path segments are available in
# 'https://code.qt.io/playground/qlitehtml.git', # ${parent_repo_base_git_path}.
# Examples:
# - 'qtdeclarative.git'
# - 'https://code.qt.io/playground/qlitehtml.git'
# - '../playground/qlitehtml.git'
macro(qt_ir_parse_git_url_key out_var_prefix submodule_name url_value parent_repo_base_git_path) macro(qt_ir_parse_git_url_key out_var_prefix submodule_name url_value parent_repo_base_git_path)
qt_ir_has_url_scheme("${url_value}" has_url_scheme) qt_ir_parse_git_url(
URL "${url_value}"
OUT_VAR_HAS_URL_SCHEME has_url_scheme
)
if(NOT has_url_scheme) if(NOT has_url_scheme)
set(base_git_path "${parent_repo_base_git_path}/${url_value}") set(base_git_path "${parent_repo_base_git_path}/${url_value}")
qt_ir_normalize_git_url("${base_git_path}" base_git_path)
# The exact code perl code was while ($base =~ s,(?!\.\./)[^/]+/\.\./,,g) {}
# That got rid of ../ and ../../ in the path, but it broke down
# when more than two ../ were present.
# We just use ABSOLUTE to resolve the path and get rid of all ../
# Note the empty BASE_DIR is important, otherwise the path is relative to
# ${CMAKE_CURRENT_SOURCE_DIR}.
get_filename_component(base_git_path "${base_git_path}" ABSOLUTE BASE_DIR "")
else() else()
set(base_git_path "${url_value}") set(base_git_path "${url_value}")
endif() endif()