aboutsummaryrefslogtreecommitdiffstats
path: root/cmake/QtIRGitHelpers.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/QtIRGitHelpers.cmake')
-rw-r--r--cmake/QtIRGitHelpers.cmake1151
1 files changed, 1151 insertions, 0 deletions
diff --git a/cmake/QtIRGitHelpers.cmake b/cmake/QtIRGitHelpers.cmake
new file mode 100644
index 00000000..288f4ac7
--- /dev/null
+++ b/cmake/QtIRGitHelpers.cmake
@@ -0,0 +1,1151 @@
+# 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}")
+ if(modules_to_exclude)
+ list(REMOVE_ITEM submodules_with_deps_and_excluded ${modules_to_exclude})
+ endif()
+
+ 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()