mirror of
git://code.qt.io/qt/qt5.git
synced 2026-02-01 19:36:04 +08:00
Calling configure will now implicitly run init-repository when appropriate. See further down below for what "appropriate" means. All supported init-repository options can be passed to configure as except for -mirror, -oslo, -berlin. This includes useful options like -submodules, -no-resolve-deps and -no-optional-deps. When running configure on a qt5.git clone without any submodules cloned, configure will exit with a helpful error message suggesting to pass -init-submodules, so it automatically clones missing repositories. This means cloning is opt-in, so that internet access is not done implicitly. The error message also suggests passing the -submodules option. This will affect which submodules will be cloned by init-repository and which submodules will be configured by configure. In this case -submodules is effectively an alias of init-repository's -module-subset for cloning purposes. When calling configure a second time, without -init-submodules, on an already configured repo, init-repository behavior is entirely skipped. -submodules now accepts init-repository-style special values like "essential", "addon", "all", "existing", "-deprecated" for the purpose of cloning submodules. The values are then translated into actual repos that should also be configured or skipped by configure. The default subset of cloned submodules is currently the same one as init-repository, "default", which clones 44 actively maintained repositories as well as deprecated submodules. If configure is called a second time WITH -init-submodules, it's the same as calling init-repository --force to re-initialize submodules. In this case passing something like --submodules existing,<additional-submodules> might make sense to add or remove submodules. As a drive-by this also fixes the bug where you couldn't pass a configure -- -DFOO=0 parameter to configure, because it got treated as '0>', redirecting from a different stream than stdout, leading to empty content in the file. [ChangeLog][General][Build System] The configure script now implicitly calls init-repository when appropriate and accepts init-repository command line options. Fixes: QTBUG-120030 Task-number: QTBUG-122622 Change-Id: Iedbfcbf0a87c8ee89e40d00b6377b68296a65a62 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
1150 lines
45 KiB
CMake
1150 lines
45 KiB
CMake
# Copyright (C) 2024 The Qt Company Ltd.
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
# Returns the git version.
|
|
function(qt_ir_get_git_version out_var)
|
|
qt_ir_get_option_value(perl-identical-output perl_identical_output_for_tests)
|
|
|
|
set(extra_args "")
|
|
if(perl_identical_output_for_tests)
|
|
set(extra_args FORCE_QUIET)
|
|
endif()
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git version ${extra_args}
|
|
OUT_OUTPUT_VAR git_output
|
|
ERROR_MESSAGE "Failed to retrieve git version")
|
|
|
|
string(REGEX REPLACE "^git version ([0-9]+)\\.([0-9]+)\\.([0-9]+).*$" "\\1.\\2.\\3"
|
|
version "${git_output}")
|
|
if(NOT version)
|
|
message(FATAL_ERROR "Failed to parse git version: ${git_output}, expected [d]+.[d]+.[d]+")
|
|
endif()
|
|
|
|
set(${out_var} "${version}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Returns the git version, but caches the result in a global property.
|
|
function(qt_ir_get_git_version_cached out_var)
|
|
get_property(version GLOBAL PROPERTY _qt_git_version)
|
|
if(NOT version)
|
|
qt_ir_get_git_version(version)
|
|
endif()
|
|
|
|
set_property(GLOBAL PROPERTY _qt_git_version "${version}")
|
|
|
|
set(${out_var} "${version}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Returns whether git supports the git submodule --progress option.
|
|
function(qt_ir_is_git_progress_supported out_var)
|
|
qt_ir_get_git_version_cached(version)
|
|
if(version VERSION_GREATER_EQUAL "2.11")
|
|
set(${out_var} TRUE PARENT_SCOPE)
|
|
else()
|
|
set(${out_var} FALSE PARENT_SCOPE)
|
|
endif()
|
|
endfunction()
|
|
|
|
# Get the mirror with trailing slashes removed.
|
|
function(qt_ir_get_mirror out_var)
|
|
qt_ir_get_option_value(mirror mirror)
|
|
qt_ir_get_option_value(berlin berlin)
|
|
qt_ir_get_option_value(oslo oslo)
|
|
|
|
if(berlin)
|
|
set(mirror "git://hegel/qt/")
|
|
elseif(oslo)
|
|
set(mirror "git://qilin/qt/")
|
|
endif()
|
|
|
|
# Replace any double trailing slashes from end of mirror
|
|
string(REGEX REPLACE "//+$" "/" mirror "${mirror}")
|
|
|
|
set(${out_var} "${mirror}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Sets up the commit template for a submodule.
|
|
function(qt_ir_setup_commit_template commit_template_dir working_directory)
|
|
set(template "${commit_template_dir}/.commit-template")
|
|
if(NOT EXISTS "${template}")
|
|
return()
|
|
endif()
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git config commit.template "${template}"
|
|
ERROR_MESSAGE "Failed to setup commit template"
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
endfunction()
|
|
|
|
# Initializes a list of submodules. This does not them, but just
|
|
# sets up the .git/config file submodule.$submodule_name.url based on the .gitmodules template file.
|
|
function(qt_ir_run_git_submodule_init submodules working_directory)
|
|
set(submodule_dirs "")
|
|
foreach(submodule_name IN LISTS submodules)
|
|
set(submodule_path "${${prefix}_${submodule_name}_path}")
|
|
list(APPEND submodule_dirs "${submodule_name}")
|
|
endforeach()
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git submodule init ${submodule_dirs}
|
|
ERROR_MESSAGE "Failed to git submodule init ${submodule_dirs}"
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
|
|
qt_ir_setup_commit_template("${working_directory}" "${working_directory}")
|
|
endfunction()
|
|
|
|
# Add gerrit remotes to the repository.
|
|
function(qt_ir_add_git_remotes repo_name working_directory)
|
|
set(gerrit_ssh_base "ssh://@USER@codereview.qt-project.org@PORT@/qt/")
|
|
set(gerrit_repo_url "${gerrit_ssh_base}")
|
|
|
|
qt_ir_get_option_value(codereview-username username)
|
|
|
|
# If given a username, make a "verbose" remote.
|
|
# Otherwise, rely on proper SSH configuration.
|
|
if(username)
|
|
string(REPLACE "@USER@" "${username}@" gerrit_repo_url "${gerrit_repo_url}")
|
|
string(REPLACE "@PORT@" ":29418" gerrit_repo_url "${gerrit_repo_url}")
|
|
else()
|
|
string(REPLACE "@USER@" "" gerrit_repo_url "${gerrit_repo_url}")
|
|
string(REPLACE "@PORT@" "" gerrit_repo_url "${gerrit_repo_url}")
|
|
endif()
|
|
|
|
string(APPEND gerrit_repo_url "${repo_name}")
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git config remote.gerrit.url "${gerrit_repo_url}"
|
|
ERROR_MESSAGE "Failed to set gerrit repo url"
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS
|
|
git config remote.gerrit.fetch "+refs/heads/*:refs/remotes/gerrit/*" "/heads/"
|
|
ERROR_MESSAGE "Failed to set gerrit repo fetch refspec"
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
endfunction()
|
|
|
|
# Handles the copy-objects option, which is used to detach alternates.
|
|
# A copy of all git objects are made from the alternate repository to the current repository.
|
|
# Then the alternates reference is removed.
|
|
function(qt_ir_handle_detach_alternates working_directory)
|
|
qt_ir_get_option_value(copy-objects should_detach)
|
|
if(NOT should_detach)
|
|
return()
|
|
endif()
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git repack -a
|
|
ERROR_MESSAGE "Failed to repack objects to detach alternates"
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
|
|
set(alternates_path "${working_directory}/.git/objects/info/alternates")
|
|
if(EXISTS "${alternates_path}")
|
|
file(REMOVE "${alternates_path}")
|
|
if(EXISTS "${alternates_path}")
|
|
message(FATAL_ERROR "Failed to remove alternates file: ${alternates_path}")
|
|
endif()
|
|
endif()
|
|
endfunction()
|
|
|
|
# Clones a submodule, unless it was previously cloned.
|
|
# When cloning, checks out a specific branch if requested, otherwise does not
|
|
# checkout any files yet, mimicking a bare repo.
|
|
# Sets up an alternates link if requested.
|
|
# Detaches alternates if requested.
|
|
# Fetches refs if requested.
|
|
# Adds a gerrit git remote.
|
|
# Sets up the commit template for the submodule.
|
|
function(qt_ir_clone_one_submodule submodule_name)
|
|
set(options
|
|
CHECKOUT_BRANCH
|
|
FETCH
|
|
)
|
|
set(oneValueArgs
|
|
ALTERNATES
|
|
BASE_URL
|
|
WORKING_DIRECTORY
|
|
)
|
|
set(multiValueArgs "")
|
|
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
qt_ir_get_working_directory_from_arg(working_directory)
|
|
|
|
set(clone_args "")
|
|
set(submodule_path "${${prefix}_${submodule_name}_path}")
|
|
|
|
if(arg_ALTERNATES)
|
|
# alternates is a qt5 repo, so the submodule will be under that.
|
|
set(alternates_dir "${arg_ALTERNATES}/${submodule_path}/.git")
|
|
if(EXISTS "${alternates_dir}")
|
|
list(APPEND clone_args --reference "${arg_ALTERNATES}/${submodule_path}")
|
|
else()
|
|
message(WARNING "'${arg_ALTERNATES}/${submodule_path}' not found, "
|
|
"ignoring alternate for this submodule")
|
|
endif()
|
|
endif()
|
|
|
|
if(NOT EXISTS "${working_directory}/${submodule_path}/.git")
|
|
set(should_clone TRUE)
|
|
else()
|
|
set(should_clone FALSE)
|
|
endif()
|
|
|
|
set(submodule_base_git_path "${${prefix}_${submodule_name}_base_git_path}")
|
|
|
|
set(submodule_url "${submodule_base_git_path}")
|
|
qt_ir_has_url_scheme("${submodule_url}" has_url_scheme)
|
|
if(NOT has_url_scheme AND arg_BASE_URL)
|
|
set(submodule_url "${arg_BASE_URL}${submodule_url}")
|
|
endif()
|
|
|
|
qt_ir_get_mirror(mirror_url)
|
|
set(mirror "")
|
|
if(NOT has_url_scheme AND mirror_url AND (should_clone OR arg_FETCH))
|
|
set(mirror "${mirror_url}${submodule_base_git_path}")
|
|
endif()
|
|
|
|
set(mirror_or_original_url "${submodule_url}")
|
|
if(mirror)
|
|
# Only use the mirror if it can be reached.
|
|
# Access a non-existing ref so no output is shown. It should still
|
|
# succeed if the mirror is accessible.
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git ls-remote "${mirror}" "test/if/mirror/exists"
|
|
WORKING_DIRECTORY "${working_directory}"
|
|
NO_HANDLE_ERROR
|
|
OUT_RESULT_VAR proc_result)
|
|
if(NOT proc_result EQUAL 0)
|
|
message("mirror [${mirror}] is not accessible; ${submodule_url} will be used")
|
|
set(mirror "")
|
|
else()
|
|
set(mirror_or_original_url "${mirror}")
|
|
endif()
|
|
endif()
|
|
|
|
set(submodule_branch "${${prefix}_${submodule_name}_branch}")
|
|
|
|
qt_ir_is_git_progress_supported(is_git_progress_supported)
|
|
qt_ir_get_option_value(quiet quiet)
|
|
qt_ir_get_option_value(perl-identical-output perl_identical_output_for_tests)
|
|
|
|
set(progress_args "")
|
|
if(is_git_progress_supported AND NOT quiet AND NOT perl_identical_output_for_tests)
|
|
set(progress_args --progress)
|
|
endif()
|
|
|
|
if(should_clone)
|
|
if(arg_CHECKOUT_BRANCH)
|
|
list(APPEND clone_args --branch "${submodule_branch}")
|
|
else()
|
|
list(APPEND clone_args --no-checkout)
|
|
endif()
|
|
list(APPEND clone_args ${progress_args})
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git clone ${clone_args} "${mirror_or_original_url}" "${submodule_path}"
|
|
ERROR_MESSAGE "Failed to clone submodule '${submodule_name}'"
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
endif()
|
|
|
|
set(submodule_working_dir "${working_directory}/${submodule_path}")
|
|
|
|
if(mirror)
|
|
# This is only for the user's convenience - we make no use of it.
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git config "remote.mirror.url" "${mirror}"
|
|
ERROR_MESSAGE "Failed to set git config remote.mirror.url to ${mirror}"
|
|
WORKING_DIRECTORY "${submodule_working_dir}")
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git config "remote.mirror.fetch" "+refs/heads/*:refs/remotes/mirror/*"
|
|
ERROR_MESSAGE "Failed to set git config remote.mirror.fetch"
|
|
WORKING_DIRECTORY "${submodule_working_dir}")
|
|
endif()
|
|
|
|
if(NOT should_clone AND arg_FETCH)
|
|
# If we didn't clone, fetch from the right location. We always update
|
|
# the origin remote, so that submodule update --remote works.
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git config remote.origin.url "${mirror_or_original_url}"
|
|
ERROR_MESSAGE "Failed to set remote origin url"
|
|
WORKING_DIRECTORY "${submodule_working_dir}")
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git fetch origin ${progress_args}
|
|
ERROR_MESSAGE "Failed to fetch origin"
|
|
WORKING_DIRECTORY "${submodule_working_dir}")
|
|
endif()
|
|
|
|
if(NOT (should_clone OR arg_FETCH) OR mirror)
|
|
# Leave the origin configured to the canonical URL. It's already correct
|
|
# if we cloned/fetched without a mirror; otherwise it may be anything.
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git config remote.origin.url "${submodule_url}"
|
|
ERROR_MESSAGE "Failed to set remote origin url"
|
|
WORKING_DIRECTORY "${submodule_working_dir}")
|
|
endif()
|
|
|
|
set(commit_template_dir "${working_directory}")
|
|
qt_ir_setup_commit_template("${commit_template_dir}" "${submodule_working_dir}")
|
|
|
|
if(NOT has_url_scheme)
|
|
qt_ir_add_git_remotes("${submodule_base_git_path}" "${submodule_working_dir}")
|
|
endif()
|
|
|
|
qt_ir_handle_detach_alternates("${submodule_working_dir}")
|
|
endfunction()
|
|
|
|
# Get list of submodules that were previously initialized, by looking at the .git/config file.
|
|
function(qt_ir_get_already_initialized_submodules prefix
|
|
out_var_already_initialized_submodules
|
|
parent_repo_base_git_path
|
|
working_directory
|
|
)
|
|
|
|
qt_ir_parse_git_config_file_contents("${prefix}"
|
|
READ_GIT_CONFIG
|
|
PARENT_REPO_BASE_GIT_PATH "${parent_repo_base_git_path}"
|
|
WORKING_DIRECTORY "${working_directory}"
|
|
)
|
|
|
|
set(${out_var_already_initialized_submodules} "${${prefix}_submodules}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# If init-repository --force is called with a different subset, remove
|
|
# previously initialized submodules from the .git/config file.
|
|
# Also mark submodules as ignored if requested.
|
|
function(qt_ir_handle_submodule_removal_and_ignoring prefix
|
|
included_submodules
|
|
parent_repo_base_git_path
|
|
working_directory
|
|
)
|
|
|
|
qt_ir_get_option_value(ignore-submodules ignore_submodules)
|
|
|
|
qt_ir_get_already_initialized_submodules("${prefix}"
|
|
already_initialized_submodules
|
|
"${parent_repo_base_git_path}"
|
|
"${working_directory}")
|
|
|
|
foreach(submodule_name IN LISTS already_initialized_submodules)
|
|
if(NOT submodule_name IN_LIST included_submodules)
|
|
# If a submodule is not included in the list of submodules to be initialized,
|
|
# and it was previously initialized, then remove it from the config.
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git config --remove-section "submodule.${submodule_name}"
|
|
ERROR_MESSAGE "Failed to deinit submodule '${submodule_name}'"
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
continue()
|
|
endif()
|
|
if(ignore_submodules)
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git config "submodule.${submodule_name}.ignore" all
|
|
ERROR_MESSAGE "Failed to ignore submodule '${submodule_name}'"
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
endif()
|
|
endforeach()
|
|
endfunction()
|
|
|
|
# Checks if the submodule is dirty (has uncommited changes).
|
|
function(qt_ir_check_if_dirty_submodule submodule_name working_directory out_is_dirty)
|
|
set(submodule_path "${working_directory}/${${prefix}_${submodule_name}_path}")
|
|
if(NOT EXISTS "${submodule_path}/.git")
|
|
return()
|
|
endif()
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
FORCE_QUIET
|
|
COMMAND_ARGS git status --porcelain --untracked=no --ignore-submodules=all
|
|
WORKING_DIRECTORY "${submodule_path}"
|
|
ERROR_MESSAGE "Failed to get dirty status for '${submodule_name}'"
|
|
OUT_OUTPUT_VAR git_output)
|
|
|
|
string(STRIP "${git_output}" git_output)
|
|
string(REPLACE "\n" ";" git_lines "${git_output}")
|
|
|
|
# After a git clone --no-checkout, git status reports all files as
|
|
# staged for deletion, but we still want to update the submodule.
|
|
# It's unlikely that a genuinely dirty index would have _only_ this
|
|
# type of modifications, and it doesn't seem like a horribly big deal
|
|
# to lose them anyway, so ignore them.
|
|
# @sts = grep(!/^D /, @sts);
|
|
# Filter list that starts with the regex
|
|
list(FILTER git_lines EXCLUDE REGEX "^D ")
|
|
|
|
if(git_lines)
|
|
message(STATUS "${submodule_name} is dirty.")
|
|
set(is_dirty TRUE)
|
|
else()
|
|
set(is_dirty FALSE)
|
|
endif()
|
|
|
|
set(${out_is_dirty} "${is_dirty}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Checks if any submodules are dirty and exits early if any are.
|
|
function(qt_ir_handle_dirty_submodule submodules working_directory)
|
|
set(any_is_dirty FALSE)
|
|
foreach(submodule_name IN LISTS submodules)
|
|
qt_ir_check_if_dirty_submodule("${submodule_name}" "${working_directory}" is_dirty)
|
|
if(is_dirty)
|
|
set(any_is_dirty TRUE)
|
|
endif()
|
|
endforeach()
|
|
|
|
if(any_is_dirty)
|
|
message(FATAL_ERROR "Dirty submodule(s) present; cannot proceed.")
|
|
endif()
|
|
endfunction()
|
|
|
|
# If the branch option is set, checkout the branch specified in the .gitmodules file.
|
|
function(qt_ir_handle_branch_option prefix submodule_name working_directory)
|
|
set(branch_name "${${prefix}_${submodule_name}_branch}")
|
|
if(NOT branch_name)
|
|
message(FATAL_ERROR "No branch defined for submodule '${submodule_name}'")
|
|
endif()
|
|
|
|
set(repo_dir "${working_directory}/${${prefix}_${submodule_name}_path}")
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
FORCE_QUIET
|
|
COMMAND_ARGS git rev-parse -q --verify ${branch_name}
|
|
WORKING_DIRECTORY "${repo_dir}"
|
|
NO_HANDLE_ERROR
|
|
OUT_RESULT_VAR proc_result)
|
|
|
|
# If the branch exists locally, check it out.
|
|
# Otherwise check it out from origin and create a local branch.
|
|
if(proc_result EQUAL 0)
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git checkout ${branch_name}
|
|
WORKING_DIRECTORY "${repo_dir}"
|
|
ERROR_MESSAGE
|
|
"Failed to checkout branch '${branch_name}' in submodule '${submodule_name}'")
|
|
else()
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git checkout -b ${branch_name} origin/${branch_name}
|
|
WORKING_DIRECTORY "${repo_dir}"
|
|
ERROR_MESSAGE
|
|
"Failed to checkout branch '${branch_name}' in submodule '${submodule_name}'")
|
|
endif()
|
|
endfunction()
|
|
|
|
# If the update option is set, update the submodules, without fetching.
|
|
function(qt_ir_handle_update_option will_checkout_branch working_directory)
|
|
set(extra_args "")
|
|
if(will_checkout_branch)
|
|
list(APPEND extra_args --remote --rebase)
|
|
endif()
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git submodule update --force --no-fetch ${extra_args}
|
|
ERROR_MESSAGE "Failed to update submodule '${submodule_name}'"
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
endfunction()
|
|
|
|
# Looks for the 'default' and 'existing' keys, and replaces them with appropriate
|
|
# values, while making sure to prepend '-' to the values if the original key had it.
|
|
function(qt_ir_handle_dash_in_module_subset_expansion out_var
|
|
module_subset already_initialized_submodules)
|
|
|
|
set(expanded_module_subset "")
|
|
foreach(submodule_name IN LISTS module_subset)
|
|
set(has_dash FALSE)
|
|
string(REGEX REPLACE "^(-)" "" submodule_name "${submodule_name}")
|
|
if(CMAKE_MATCH_1)
|
|
set(has_dash TRUE)
|
|
endif()
|
|
|
|
# Replace the default keyword in the input, with the the list of default submodules types,
|
|
# which will be further replaced.
|
|
if(submodule_name STREQUAL "default")
|
|
set(replacement "preview;essential;addon;deprecated")
|
|
# Replace the existing keyword, with the list of already initialized submodules
|
|
# from a previous run.
|
|
elseif(submodule_name STREQUAL "existing")
|
|
set(replacement "${already_initialized_submodules}")
|
|
|
|
if(has_dash)
|
|
# We can't properly support this with the existing algorithm, because we will
|
|
# then exclude it also after dependency resolution, and it can cause an empty list
|
|
# of submodules in certain situations.
|
|
message(FATAL_ERROR "Excluding existing submodules with '-existing' "
|
|
"is not supported, just don't include them.")
|
|
endif()
|
|
else()
|
|
set(replacement "${submodule_name}")
|
|
endif()
|
|
|
|
# Prepend dash to all expanded values
|
|
if(has_dash)
|
|
list(TRANSFORM replacement PREPEND "-")
|
|
endif()
|
|
|
|
list(APPEND expanded_module_subset "${replacement}")
|
|
endforeach()
|
|
|
|
set(${out_var} "${expanded_module_subset}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Processes the given module subset using values that were set by parsing the .gitmodules file.
|
|
#
|
|
# The module subset is a comma-separated list of module names, with an optional '-' at the start.
|
|
# If a - is present, the module (or special expanded keyword) is excluded from the subset.
|
|
# If the value is empty, the default subset is used on initial runs, or the previously
|
|
# existing submodules are used on subsequent runs.
|
|
# If the value is "all", all known submodules are included.
|
|
# If the value is a status like 'addon' or 'essential', only submodules with that status are
|
|
# included.
|
|
# If the value is 'existing', only submodules that were previously initialized are included.
|
|
# This evaluates to an empty list for the first script run.
|
|
# If the value is a module name, only that module is included.
|
|
# The modules to exclude are also set separately, so they can be excluded even after dependency
|
|
# resolution which is done later.
|
|
function(qt_ir_process_module_subset_values prefix)
|
|
set(options
|
|
PREVIOUSLY_INITIALIZED
|
|
)
|
|
set(oneValueArgs
|
|
OUT_VAR_INCLUDE
|
|
OUT_VAR_EXCLUDE
|
|
)
|
|
set(multiValueArgs
|
|
ALREADY_INITIALIZED_SUBMODULES
|
|
EXTRA_IMPLICIT_SUBMODULES
|
|
MODULE_SUBSET
|
|
)
|
|
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
string(REPLACE "," ";" module_subset "${arg_MODULE_SUBSET}")
|
|
|
|
# If a module subset is not specified, either use the default list for the very first run,
|
|
# or use the previously initialized submodules for a subsequent run.
|
|
#
|
|
# If the are no previously initialized submodules, and 'existing' is specified, default
|
|
# to 'default'. This handles the case when someone runs git submodule deinit --all --force,
|
|
# where git initrepository.initialized config key is still true, and then runs
|
|
# configure -init-submodules. Without defaulting to default, we would end up with an empty
|
|
# subset and configure would fail.
|
|
if(NOT module_subset)
|
|
if(arg_PREVIOUSLY_INITIALIZED)
|
|
if(arg_ALREADY_INITIALIZED_SUBMODULES)
|
|
set(module_subset "existing")
|
|
else()
|
|
message(DEBUG "No previously initialized submodules detected even though "
|
|
"'existing' was specified, defaulting to 'default'")
|
|
set(module_subset "default")
|
|
endif()
|
|
else()
|
|
set(module_subset "default")
|
|
endif()
|
|
endif()
|
|
|
|
|
|
qt_ir_handle_dash_in_module_subset_expansion(
|
|
expanded_module_subset "${module_subset}" "${arg_ALREADY_INITIALIZED_SUBMODULES}")
|
|
|
|
set(include_modules "")
|
|
set(exclude_modules "")
|
|
|
|
if(arg_EXTRA_IMPLICIT_SUBMODULES)
|
|
list(APPEND include_modules ${arg_EXTRA_IMPLICIT_SUBMODULES})
|
|
endif()
|
|
|
|
foreach(value IN LISTS expanded_module_subset ${prefix}_submodules_to_remove)
|
|
# An '-' at the start means we should exclude those modules.
|
|
string(REGEX REPLACE "^(-)" "" value "${value}")
|
|
set(list_op "APPEND")
|
|
if(CMAKE_MATCH_1)
|
|
set(list_op "REMOVE_ITEM")
|
|
endif()
|
|
|
|
if(value STREQUAL "all")
|
|
list(${list_op} include_modules "${${prefix}_submodules}")
|
|
if("${list_op}" STREQUAL "REMOVE_ITEM")
|
|
list(APPEND exclude_modules "${${prefix}_submodules}")
|
|
endif()
|
|
elseif(value IN_LIST ${prefix}_statuses)
|
|
list(${list_op} include_modules "${${prefix}_status_${value}_submodules}")
|
|
if("${list_op}" STREQUAL "REMOVE_ITEM")
|
|
list(APPEND exclude_modules "${${prefix}_status_${value}_submodules}")
|
|
endif()
|
|
elseif(NOT "${${prefix}_${value}_path}" STREQUAL "")
|
|
list(${list_op} include_modules "${value}")
|
|
if("${list_op}" STREQUAL "REMOVE_ITEM")
|
|
list(APPEND exclude_modules "${value}")
|
|
endif()
|
|
else()
|
|
if(list_op STREQUAL "REMOVE_ITEM")
|
|
message(WARNING "Excluding non-existent module: ${value}")
|
|
else()
|
|
message(FATAL_ERROR
|
|
"Invalid module subset specified, module name is non-existent: ${value}")
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
|
|
set(${arg_OUT_VAR_INCLUDE} "${include_modules}" PARENT_SCOPE)
|
|
set(${arg_OUT_VAR_EXCLUDE} "${exclude_modules}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Sort the modules and add dependencies if dependency resolving is enabled.
|
|
function(qt_ir_get_module_subset_including_deps prefix out_var initial_modules)
|
|
qt_ir_get_option_value(resolve-deps resolve_deps)
|
|
qt_ir_get_option_value(optional-deps include_optional_deps)
|
|
if(resolve_deps)
|
|
set(exclude_optional_deps "")
|
|
if(NOT include_optional_deps)
|
|
set(exclude_optional_deps EXCLUDE_OPTIONAL_DEPS)
|
|
endif()
|
|
|
|
qt_internal_sort_module_dependencies("${initial_modules}" out_repos
|
|
${exclude_optional_deps}
|
|
PARSE_GITMODULES
|
|
GITMODULES_PREFIX_VAR "${prefix}"
|
|
)
|
|
else()
|
|
set(out_repos "${initial_modules}")
|
|
endif()
|
|
|
|
qt_ir_get_option_value(perl-identical-output perl_identical_output_for_tests)
|
|
if(NOT perl_identical_output_for_tests)
|
|
message(DEBUG "repos that will be initialized after dependency handling: ${out_repos}")
|
|
endif()
|
|
|
|
set(${out_var} "${out_repos}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Check whether init-repository has been run before, perl style.
|
|
# We assume that if the submodule qtbase has been initialized, then init-repository has been run.
|
|
function(qt_ir_check_if_already_initialized_perl_style out_var_is_initialized working_directory)
|
|
set(cmd git config --get submodule.qtbase.url)
|
|
|
|
set(extra_args "")
|
|
qt_ir_get_option_value(perl-identical-output perl_identical_output_for_tests)
|
|
if(perl_identical_output_for_tests)
|
|
list(APPEND extra_args FORCE_QUIET)
|
|
endif()
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS ${cmd}
|
|
OUT_RESULT_VAR git_result
|
|
OUT_OUTPUT_VAR git_output
|
|
OUT_ERROR_VAR git_error
|
|
${extra_args}
|
|
NO_HANDLE_ERROR
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
|
|
if(git_result EQUAL 1 AND NOT git_output)
|
|
set(is_initialized FALSE)
|
|
elseif(git_result EQUAL 0 AND git_output)
|
|
set(is_initialized TRUE)
|
|
else()
|
|
message(FATAL_ERROR "Failed to get result of ${cmd}: ${git_output}")
|
|
endif()
|
|
|
|
set(${out_var_is_initialized} "${is_initialized}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Check whether init-repository has been run before, cmake style.
|
|
# Check for the presence of the initrepository.initialized git config key.
|
|
function(qt_ir_check_if_already_initialized_cmake_style out_var_is_initialized working_directory)
|
|
set(options
|
|
FORCE_QUIET
|
|
)
|
|
set(oneValueArgs "")
|
|
set(multiValueArgs "")
|
|
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
set(cmd git config --get initrepository.initialized)
|
|
|
|
set(extra_args "")
|
|
if(arg_FORCE_QUIET)
|
|
list(APPEND extra_args FORCE_QUIET)
|
|
endif()
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS ${cmd}
|
|
OUT_RESULT_VAR git_result
|
|
OUT_OUTPUT_VAR git_output
|
|
OUT_ERROR_VAR git_error
|
|
${extra_args}
|
|
NO_HANDLE_ERROR
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
|
|
if(git_result EQUAL 1 AND NOT git_output)
|
|
set(is_initialized FALSE)
|
|
elseif(git_result EQUAL 0 AND git_output)
|
|
set(is_initialized TRUE)
|
|
else()
|
|
message(FATAL_ERROR "Failed to get result of ${cmd}: ${git_output}")
|
|
endif()
|
|
|
|
set(${out_var_is_initialized} "${is_initialized}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Check whether init-repository has been run before.
|
|
# The CMake and perl script do it differently, choose which way to do it based
|
|
# on the active options.
|
|
function(qt_ir_check_if_already_initialized out_var_is_initialized working_directory)
|
|
qt_ir_get_option_value(perl-init-check perl_init_check)
|
|
if(perl_init_check)
|
|
qt_ir_check_if_already_initialized_perl_style(is_initialized "${working_directory}")
|
|
else()
|
|
qt_ir_check_if_already_initialized_cmake_style(is_initialized "${working_directory}")
|
|
endif()
|
|
|
|
set(${out_var_is_initialized} "${is_initialized}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Marks the repository as initialized.
|
|
# The perl script used to determine this by checking whether the qtbase submodule was initialized.
|
|
# In the CMake script, we instead opt to set an explicit marker in the repository.
|
|
function(qt_ir_set_is_initialized working_directory)
|
|
# If emulating perl style initialization check, don't set the marker and exit early.
|
|
qt_ir_get_option_value(perl-init-check perl_init_check)
|
|
if(perl_init_check)
|
|
return()
|
|
endif()
|
|
|
|
set(cmd git config initrepository.initialized true)
|
|
|
|
set(extra_args "")
|
|
qt_ir_get_option_value(perl-identical-output perl_identical_output_for_tests)
|
|
if(perl_identical_output_for_tests)
|
|
list(APPEND extra_args FORCE_QUIET)
|
|
endif()
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS ${cmd}
|
|
ERROR_MESSAGE "Failed to mark repository as initialized"
|
|
${extra_args}
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
endfunction()
|
|
|
|
# If the repository has already been initialized, exit early.
|
|
function(qt_ir_handle_if_already_initialized out_var_should_exit working_directory)
|
|
set(should_exit FALSE)
|
|
|
|
qt_ir_check_if_already_initialized(is_initialized "${working_directory}")
|
|
qt_ir_get_option_value(force force)
|
|
qt_ir_get_option_value(quiet quiet)
|
|
qt_ir_is_called_from_configure(is_called_from_configure)
|
|
|
|
if(is_initialized)
|
|
if(NOT force)
|
|
set(should_exit TRUE)
|
|
if(NOT quiet AND NOT is_called_from_configure)
|
|
message(
|
|
"Will not reinitialize already initialized repository (use -f to force)!")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
set(${out_var_should_exit} ${should_exit} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Parses git remote.origin.url and extracts the base url and the repository name.
|
|
#
|
|
# base_url example: git://code.qt.io/qt
|
|
# repo name example: qt5 or tqtc-qt5
|
|
function(qt_ir_get_qt5_repo_name_and_base_url)
|
|
set(options "")
|
|
set(oneValueArgs
|
|
OUT_VAR_QT5_REPO_NAME
|
|
OUT_VAR_BASE_URL
|
|
WORKING_DIRECTORY
|
|
)
|
|
set(multiValueArgs "")
|
|
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
if(NOT arg_WORKING_DIRECTORY)
|
|
message(FATAL_ERROR "qt_ir_get_qt5_repo_name_and_base_url: No working directory specified")
|
|
endif()
|
|
set(working_directory "${arg_WORKING_DIRECTORY}")
|
|
|
|
qt_ir_get_option_value(perl-identical-output perl_identical_output_for_tests)
|
|
|
|
set(extra_args "")
|
|
if(perl_identical_output_for_tests)
|
|
set(extra_args FORCE_QUIET)
|
|
endif()
|
|
|
|
qt_ir_execute_process_and_log_and_handle_error(
|
|
COMMAND_ARGS git config remote.origin.url ${extra_args}
|
|
ERROR_MESSAGE "No origin remote found for qt5 repository"
|
|
OUT_OUTPUT_VAR git_output
|
|
WORKING_DIRECTORY "${working_directory}")
|
|
|
|
string(STRIP "${git_output}" git_output)
|
|
|
|
# Remove the .git at the end, with an optional slash
|
|
string(REGEX REPLACE ".git/?$" "" qt5_repo_name "${git_output}")
|
|
|
|
# Remove the tqtc- prefix, if it exists, and the qt5 suffix and that will be the base_url
|
|
# The qt5_repo_name is qt5 or tqtc-qt5.
|
|
string(REGEX REPLACE "((tqtc-)?qt5)$" "" base_url "${qt5_repo_name}")
|
|
set(qt5_repo_name "${CMAKE_MATCH_1}")
|
|
|
|
if(NOT qt5_repo_name)
|
|
set(qt5_repo_name "qt5")
|
|
endif()
|
|
|
|
if(NOT base_url)
|
|
message(FATAL_ERROR "Failed to parse base url from origin remote: ${git_output}")
|
|
endif()
|
|
|
|
set(${arg_OUT_VAR_QT5_REPO_NAME} "${qt5_repo_name}" PARENT_SCOPE)
|
|
set(${arg_OUT_VAR_BASE_URL} "${base_url}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Creates a symlink or a forwarding script to the target path.
|
|
# Use for setting up git hooks.
|
|
function(qt_ir_ensure_link source_path target_path)
|
|
qt_ir_get_option_value(force-hooks force_hooks)
|
|
if(EXISTS "${target_path}" AND NOT force_hooks)
|
|
return()
|
|
endif()
|
|
|
|
# In case we have a dead symlink or pre-existing hook
|
|
file(REMOVE "${target_path}")
|
|
|
|
qt_ir_get_option_value(quiet quiet)
|
|
if(NOT quiet)
|
|
message("Aliasing ${source_path}\n as ${target_path} ...")
|
|
endif()
|
|
|
|
if(NOT CMAKE_HOST_WIN32)
|
|
file(CREATE_LINK "${source_path}" "${target_path}" RESULT result SYMBOLIC)
|
|
# Don't continue upon success. If symlinking failed, fallthrough to creating
|
|
# a forwarding script.
|
|
if(result EQUAL 0)
|
|
return()
|
|
endif()
|
|
endif()
|
|
|
|
# Windows doesn't do (proper) symlinks. As the post_commit script needs
|
|
# them to locate itself, we write a forwarding script instead.
|
|
|
|
# Make the path palatable for MSYS.
|
|
string(REGEX REPLACE "^(.):/" "/\\1/" source_path "${source_path}")
|
|
|
|
set(contents "#!/bin/sh\nexec ${source_path} \"$@\"\n")
|
|
file(WRITE "${target_path}" "${contents}")
|
|
endfunction()
|
|
|
|
# Installs the git hooks from the qtrepotools module.
|
|
function(qt_ir_install_git_hooks)
|
|
set(options "")
|
|
set(oneValueArgs
|
|
PARENT_REPO_BASE_GIT_PATH
|
|
TOP_LEVEL_SRC_PATH
|
|
WORKING_DIRECTORY
|
|
)
|
|
set(multiValueArgs "")
|
|
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
if(NOT arg_WORKING_DIRECTORY)
|
|
message(FATAL_ERROR "qt_ir_install_git_hooks: No working directory specified")
|
|
endif()
|
|
set(working_directory "${arg_WORKING_DIRECTORY}")
|
|
|
|
if(NOT arg_PARENT_REPO_BASE_GIT_PATH)
|
|
message(FATAL_ERROR "qt_ir_install_git_hooks: No PARENT_REPO_BASE_GIT_PATH specified")
|
|
endif()
|
|
set(parent_repo_base_git_path "${arg_PARENT_REPO_BASE_GIT_PATH}")
|
|
|
|
if(NOT arg_TOP_LEVEL_SRC_PATH)
|
|
message(FATAL_ERROR "qt_ir_install_git_hooks: No TOP_LEVEL_SRC_PATH specified")
|
|
endif()
|
|
set(top_level_src_path "${arg_TOP_LEVEL_SRC_PATH}")
|
|
|
|
set(hooks_dir "${top_level_src_path}/qtrepotools/git-hooks")
|
|
if(NOT EXISTS "${hooks_dir}")
|
|
message("Warning: cannot find Git hooks, qtrepotools module might be absent")
|
|
return()
|
|
endif()
|
|
|
|
set(prefix ir_hooks)
|
|
qt_ir_parse_git_config_file_contents("${prefix}"
|
|
READ_GIT_CONFIG_LOCAL
|
|
PARENT_REPO_BASE_GIT_PATH "${parent_repo_base_git_path}"
|
|
WORKING_DIRECTORY "${working_directory}"
|
|
)
|
|
|
|
foreach(submodule_name IN LISTS ${prefix}_submodules)
|
|
set(submodule_git_dir "${working_directory}/${submodule_name}/.git")
|
|
if(NOT IS_DIRECTORY "${submodule_git_dir}")
|
|
# Get first line
|
|
file(STRINGS "${submodule_git_dir}" submodule_git_dir_contents LIMIT_COUNT 1)
|
|
|
|
# Remove the gitdir: prefix
|
|
string(REGEX REPLACE "^(gitdir: )" "" submodule_git_dir
|
|
"${submodule_git_dir_contents}")
|
|
if("${CMAKE_MATCH_1}" STREQUAL "")
|
|
message(FATAL_ERROR "Malformed .git file ${submodule_git_dir}")
|
|
endif()
|
|
|
|
# Make it an absolute path, because gitdir: is usually relative to the submodule
|
|
get_filename_component(submodule_git_dir "${submodule_git_dir}"
|
|
ABSOLUTE BASE_DIR "${working_directory}/${submodule_name}")
|
|
|
|
# Untested
|
|
set(common_dir "${submodule_git_dir}/commondir")
|
|
if(EXISTS "${common_dir}")
|
|
file(STRINGS "${common_dir}" common_dir_contents LIMIT_COUNT 1)
|
|
string(STRIP "${common_dir_contents}" common_dir_path)
|
|
set(submodule_git_dir "${submodule_git_dir}/${common_dir_path}")
|
|
get_filename_component(submodule_git_dir "${submodule_git_dir}" ABSOLUTE)
|
|
endif()
|
|
endif()
|
|
qt_ir_ensure_link("${hooks_dir}/gerrit_commit_msg_hook"
|
|
"${submodule_git_dir}/hooks/commit-msg")
|
|
qt_ir_ensure_link("${hooks_dir}/git_post_commit_hook"
|
|
"${submodule_git_dir}/hooks/post-commit")
|
|
qt_ir_ensure_link("${hooks_dir}/clang-format-pre-commit"
|
|
"${submodule_git_dir}/hooks/pre-commit")
|
|
endforeach()
|
|
endfunction()
|
|
|
|
# Saves the list of top-level submodules that should be included and excluded.
|
|
# Will be used to pass these values to the top-level configure script.
|
|
function(qt_ir_set_top_level_submodules included_submodules excluded_submodules)
|
|
set_property(GLOBAL PROPERTY _qt_ir_top_level_included_submodules "${included_submodules}")
|
|
set_property(GLOBAL PROPERTY _qt_ir_top_level_excluded_submodules "${excluded_submodules}")
|
|
endfunction()
|
|
|
|
# Gets the list of top-level submodules that should be included and excluded.
|
|
function(qt_ir_get_top_level_submodules out_included_submodules out_excluded_submodules)
|
|
get_property(included GLOBAL PROPERTY _qt_ir_top_level_included_submodules)
|
|
get_property(excluded GLOBAL PROPERTY _qt_ir_top_level_excluded_submodules)
|
|
|
|
set(${out_included_submodules} "${included}" PARENT_SCOPE)
|
|
set(${out_excluded_submodules} "${excluded}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Parses the .gitmodules file and proceses the submodules based on the module-subset option
|
|
# or the given SUBMODULES argument.
|
|
# Also adds dependencies if requested.
|
|
#
|
|
# This is a macro because we want the variables set by
|
|
# qt_ir_parse_gitmodules_file_contents to be available in the calling scope, because it's
|
|
# essentially setting a dictionarty, and we don't want to propagate all the variables manually.
|
|
macro(qt_ir_get_submodules prefix out_var_submodules)
|
|
set(options
|
|
PREVIOUSLY_INITIALIZED
|
|
PROCESS_SUBMODULES_FROM_COMMAND_LINE
|
|
)
|
|
set(oneValueArgs
|
|
PARENT_REPO_BASE_GIT_PATH
|
|
WORKING_DIRECTORY
|
|
)
|
|
set(multiValueArgs
|
|
ALREADY_INITIALIZED_SUBMODULES
|
|
SUBMODULES
|
|
)
|
|
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
qt_ir_get_working_directory_from_arg(working_directory)
|
|
|
|
# Parse the .gitmodules content here, so the parsed data is available downstream
|
|
# in other functions and recursive calls of the same function.
|
|
qt_ir_parse_git_config_file_contents("${prefix}"
|
|
READ_GITMODULES
|
|
PARENT_REPO_BASE_GIT_PATH "${arg_PARENT_REPO_BASE_GIT_PATH}"
|
|
WORKING_DIRECTORY "${working_directory}"
|
|
)
|
|
|
|
qt_ir_get_option_value(perl-identical-output perl_identical_output_for_tests)
|
|
set(extra_implict_submodules "")
|
|
|
|
# Get which modules should be initialized, based on the module-subset option.
|
|
if(arg_PROCESS_SUBMODULES_FROM_COMMAND_LINE)
|
|
qt_ir_get_option_value(module-subset initial_module_subset)
|
|
|
|
# Implicitly add qtrepotools, so we can install git hooks and don't get the
|
|
# missing qtrepotools warning.
|
|
if(NOT perl_identical_output_for_tests)
|
|
list(APPEND extra_implict_submodules "qtrepotools")
|
|
qt_ir_is_verbose(verbose)
|
|
if(verbose)
|
|
message("Implicitly adding qtrepotools to the list of submodules "
|
|
"to initialize for access to git commit hooks, etc. "
|
|
"(use --module-subset=<values>,-qtrepotools to exclude it)")
|
|
endif()
|
|
endif()
|
|
|
|
if(NOT perl_identical_output_for_tests)
|
|
message(DEBUG "module-subset from command line: ${initial_module_subset}")
|
|
endif()
|
|
elseif(arg_SUBMODULES)
|
|
set(initial_module_subset "${arg_SUBMODULES}")
|
|
if(NOT perl_identical_output_for_tests)
|
|
message(DEBUG "module-subset from args: ${initial_module_subset}")
|
|
endif()
|
|
else()
|
|
message(FATAL_ERROR "No submodules specified")
|
|
endif()
|
|
|
|
qt_ir_get_cmake_flag(PREVIOUSLY_INITIALIZED previously_initialized_opt)
|
|
qt_ir_process_module_subset_values("${prefix}"
|
|
${previously_initialized_opt}
|
|
${perl_identical_output_opt}
|
|
ALREADY_INITIALIZED_SUBMODULES ${arg_ALREADY_INITIALIZED_SUBMODULES}
|
|
EXTRA_IMPLICIT_SUBMODULES ${extra_implict_submodules}
|
|
MODULE_SUBSET "${initial_module_subset}"
|
|
OUT_VAR_INCLUDE processed_module_subset
|
|
OUT_VAR_EXCLUDE modules_to_exclude
|
|
)
|
|
if(NOT perl_identical_output_for_tests)
|
|
message(DEBUG "Processed module subset: ${processed_module_subset}")
|
|
endif()
|
|
|
|
# We only resolve dependencies for the top-level call, not for recursive calls.
|
|
if(arg_PROCESS_SUBMODULES_FROM_COMMAND_LINE)
|
|
# Resolve which submodules should be initialized, including dependencies.
|
|
qt_ir_get_module_subset_including_deps("${prefix}"
|
|
submodules_with_deps "${processed_module_subset}")
|
|
|
|
# Then remove any explicitly specified submodules.
|
|
set(submodules_with_deps_and_excluded "${submodules_with_deps}")
|
|
list(REMOVE_ITEM submodules_with_deps_and_excluded ${modules_to_exclude})
|
|
|
|
if(NOT perl_identical_output_for_tests AND modules_to_exclude)
|
|
message(DEBUG "Repos that will be excluded after dependency handling: ${modules_to_exclude}")
|
|
endif()
|
|
|
|
set(submodules "${submodules_with_deps_and_excluded}")
|
|
qt_ir_set_top_level_submodules("${submodules}" "${modules_to_exclude}")
|
|
else()
|
|
set(submodules "${processed_module_subset}")
|
|
endif()
|
|
|
|
# Remove duplicates
|
|
set(submodules_maybe_duplicates "${submodules}")
|
|
list(REMOVE_DUPLICATES submodules)
|
|
if(NOT perl_identical_output_for_tests AND NOT submodules STREQUAL submodules_maybe_duplicates)
|
|
message(DEBUG "Removed duplicates from submodules, final list: ${submodules}")
|
|
endif()
|
|
|
|
set(${out_var_submodules} "${submodules}" PARENT_SCOPE)
|
|
endmacro()
|
|
|
|
# Recursively initialize submodules starting from the given current working directory.
|
|
# This is the equivalent of the perl script's git_clone_all_submodules function.
|
|
function(qt_ir_handle_init_submodules prefix)
|
|
set(options
|
|
CHECKOUT_BRANCH
|
|
PREVIOUSLY_INITIALIZED
|
|
PROCESS_SUBMODULES_FROM_COMMAND_LINE
|
|
)
|
|
set(oneValueArgs
|
|
ALTERNATES
|
|
BASE_URL
|
|
PARENT_REPO_BASE_GIT_PATH
|
|
WORKING_DIRECTORY
|
|
)
|
|
set(multiValueArgs
|
|
ALREADY_INITIALIZED_SUBMODULES
|
|
SUBMODULES
|
|
)
|
|
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
qt_ir_get_working_directory_from_arg(working_directory)
|
|
|
|
# Get the submodules that should be initialized.
|
|
qt_ir_get_cmake_flag(PROCESS_SUBMODULES_FROM_COMMAND_LINE
|
|
process_submodules_from_command_line_opt)
|
|
qt_ir_get_cmake_flag(PREVIOUSLY_INITIALIZED
|
|
previously_initialized_opt)
|
|
qt_ir_get_submodules(${prefix} submodules
|
|
${process_submodules_from_command_line_opt}
|
|
${previously_initialized_opt}
|
|
ALREADY_INITIALIZED_SUBMODULES ${arg_ALREADY_INITIALIZED_SUBMODULES}
|
|
PARENT_REPO_BASE_GIT_PATH "${arg_PARENT_REPO_BASE_GIT_PATH}"
|
|
SUBMODULES "${arg_SUBMODULES}"
|
|
WORKING_DIRECTORY "${working_directory}"
|
|
)
|
|
|
|
qt_ir_get_option_value(perl-identical-output perl_identical_output_for_tests)
|
|
if(NOT submodules AND NOT perl_identical_output_for_tests)
|
|
message("No submodules were given to initialize or they were all excluded.")
|
|
return()
|
|
endif()
|
|
|
|
# Initialize the submodules, but don't clone them yet.
|
|
qt_ir_run_git_submodule_init("${submodules}" "${working_directory}")
|
|
|
|
# Deinit submodules that are not in the list of submodules to be initialized.
|
|
qt_ir_handle_submodule_removal_and_ignoring("${prefix}"
|
|
"${submodules}" "${arg_PARENT_REPO_BASE_GIT_PATH}" "${working_directory}")
|
|
|
|
# Check for dirty submodules.
|
|
qt_ir_handle_dirty_submodule("${submodules}" "${working_directory}")
|
|
|
|
qt_ir_get_cmake_flag(CHECKOUT_BRANCH branch_flag)
|
|
qt_ir_get_option_as_cmake_flag_option(fetch "FETCH" fetch_flag)
|
|
|
|
# Manually clone each submodule if it was not previously cloned, so we can easily
|
|
# use reference (alternates) repos, mirrors, etc.
|
|
# If already cloned, just fetch new data.
|
|
#
|
|
# Note that manually cloning the submodules, as opposed to running git submodule update,
|
|
# places the .git directories inside the submodule directories, but latest git versions
|
|
# expect it in $super_repo/.git/modules.
|
|
# When de-initializing submodules manually, git will absorb the .git directories into the super
|
|
# repo.
|
|
# In case if the super repo already has a copy of the submodule .git dir, git will fail
|
|
# to absorb the .git dir and error out. In that case the already existing .git dir needs to be
|
|
# removed manually, there is no git command to do it afaik.
|
|
foreach(submodule_name IN LISTS submodules)
|
|
qt_ir_clone_one_submodule(${submodule_name}
|
|
ALTERNATES ${arg_ALTERNATES}
|
|
BASE_URL ${arg_BASE_URL}
|
|
WORKING_DIRECTORY "${working_directory}"
|
|
${branch_flag}
|
|
${fetch_flag}
|
|
)
|
|
endforeach()
|
|
|
|
# Checkout branches instead of the default detached HEAD.
|
|
if(branch_flag)
|
|
foreach(submodule_name IN LISTS submodules)
|
|
qt_ir_handle_branch_option("${prefix}" ${submodule_name} "${working_directory}")
|
|
endforeach()
|
|
endif()
|
|
|
|
qt_ir_get_option_value(update will_update)
|
|
if(will_update)
|
|
|
|
# Update the checked out refs without fetching.
|
|
qt_ir_handle_update_option("${branch_flag}" "${working_directory}")
|
|
|
|
# Recursively initialize submodules of submodules.
|
|
foreach(submodule_name IN LISTS submodules)
|
|
set(submodule_path "${${prefix}_${submodule_name}_path}")
|
|
set(submodule_gitmodules_path "${working_directory}/${submodule_path}/.gitmodules")
|
|
|
|
if(EXISTS "${submodule_gitmodules_path}")
|
|
set(alternates_option "")
|
|
if(arg_ALTERNATES)
|
|
set(alternates_option ALTERNATES "${arg_ALTERNATES}/${submodule_name}")
|
|
endif()
|
|
|
|
set(submodule_base_git_path "${${prefix}_${submodule_name}_base_git_path}")
|
|
|
|
qt_ir_handle_init_submodules(
|
|
# Use a different prefix to store new gitmodules data
|
|
ir_sub_${submodule_name}
|
|
|
|
# Check out all submodules recursively
|
|
SUBMODULES "all"
|
|
|
|
BASE_URL "${base_url}"
|
|
PARENT_REPO_BASE_GIT_PATH "${submodule_base_git_path}"
|
|
WORKING_DIRECTORY "${working_directory}/${submodule_name}"
|
|
|
|
# The CHECKOUT_BRANCH option is not propagated on purpose
|
|
${alternates_option}
|
|
)
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
endfunction()
|