aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2024-01-29 18:10:42 +0100
committerAlexandru Croitor <alexandru.croitor@qt.io>2024-02-28 06:24:02 +0100
commit2c9664ca33d2ed3d8236c865a489c9e7c5885f68 (patch)
tree8c7e57fd3565ad18864bd826d4d58e455b53f1f1
parent915b8d2f54ec06eb97a6319012d719f42fb0ce87 (diff)
CMake: Integrate init-repository with the configure script
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>
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt5
-rw-r--r--cmake/QtIRCommandLineHelpers.cmake51
-rw-r--r--cmake/QtIRGitHelpers.cmake42
-rw-r--r--cmake/QtIRHelp.txt4
-rw-r--r--cmake/QtIRHelpers.cmake213
-rw-r--r--cmake/QtIROptionsHelpers.cmake14
-rw-r--r--cmake/QtIRScript.cmake2
-rw-r--r--cmake/QtTopLevelConfigureScript.cmake17
-rw-r--r--cmake/QtTopLevelHelpers.cmake52
-rwxr-xr-xconfigure28
-rw-r--r--configure.bat36
12 files changed, 412 insertions, 53 deletions
diff --git a/.gitignore b/.gitignore
index 30b03896..a0f1959d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ build-*
.DS_Store
init-repository.opt
init-repository.opt.in
+config.tl.opt
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad0b001e..00d66f0b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -43,8 +43,9 @@ foreach(module IN LISTS QT_BUILD_SUBMODULES)
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${module}/CMakeLists.txt)
message(FATAL_ERROR
"Module '${module}' cannot be found. Please double-check the "
- "spelling and try again. Or run `./init-repository` to get "
- "the submodules.")
+ "spelling and try again. Or run\n"
+ "`./configure -init-submodules -submodules ${module}` "
+ "to clone the submodule and its dependencies.")
endif()
endforeach()
diff --git a/cmake/QtIRCommandLineHelpers.cmake b/cmake/QtIRCommandLineHelpers.cmake
index 5cc0caed..465a994b 100644
--- a/cmake/QtIRCommandLineHelpers.cmake
+++ b/cmake/QtIRCommandLineHelpers.cmake
@@ -247,12 +247,29 @@ function(qt_ir_get_raw_args_from_optfile optfile_path out_var)
set(${out_var} "${args}" PARENT_SCOPE)
endfunction()
- # Reads the optfile_path, iterates over the given command line arguments,
- # sets the input for recongized options.
- #
- # IGNORE_UNKNOWN_ARGS tells the function not to fail if it encounters an unknown
- # option, needed when the script is called from the configure script with
- # configure-only-known options.
+# Reads the optfile_path, iterates over the given command line arguments,
+# sets the input for recongized options.
+#
+# Handles the following styles of CLI arguments:
+# --no-foo / --disable-foo
+# -no-foo / -disable-foo
+# --foo=<values>
+# --foo <values>
+# -foo <values>
+# --foo
+# -foo
+# --f
+# -f
+#
+# Currently handles the following types of CLI arguments:
+# string
+# boolean
+# void
+#
+# IGNORE_UNKNOWN_ARGS tells the function not to fail if it encounters an unknown
+# option, and instead append it to a global list of unknown options.
+# It is needed when the script is called from the configure script with
+# configure-only-known options.
function(qt_ir_process_args_from_optfile optfile_path)
set(options IGNORE_UNKNOWN_ARGS)
set(oneValueArgs "")
@@ -286,7 +303,13 @@ function(qt_ir_process_args_from_optfile optfile_path)
set(opt "${CMAKE_MATCH_1}")
unset(val)
else()
- qt_ir_add_error("Invalid command line parameter '${arg}'.")
+ if(NOT arg_IGNORE_UNKNOWN_ARGS)
+ qt_ir_add_error("Invalid command line parameter '${arg}'.")
+ else()
+ message(DEBUG "Unknown command line parameter '${arg}'. Collecting.")
+ qt_ir_append_unknown_args("${arg}")
+ continue()
+ endif()
endif()
set(type "${commandline_option_${opt}_type}")
@@ -295,7 +318,8 @@ function(qt_ir_process_args_from_optfile optfile_path)
if(NOT arg_IGNORE_UNKNOWN_ARGS)
qt_ir_add_error("Unknown command line option '${arg}'.")
else()
- message(DEBUG "Unknown command line option '${arg}'. Ignoring.")
+ message(DEBUG "Unknown command line option '${arg}'. Collecting.")
+ qt_ir_append_unknown_args("${arg}")
continue()
endif()
endif()
@@ -332,6 +356,17 @@ function(qt_ir_set_unhandled_args args)
set_property(GLOBAL PROPERTY _qt_ir_unhandled_args "${args}")
endfunction()
+# Adds to the unknown command line args.
+function(qt_ir_append_unknown_args args)
+ set_property(GLOBAL APPEND PROPERTY _qt_ir_unknown_args ${args})
+endfunction()
+
+# Gets the unhandled command line args.
+function(qt_ir_get_unknown_args out_var)
+ get_property(args GLOBAL PROPERTY _qt_ir_unknown_args)
+ set(${out_var} "${args}" PARENT_SCOPE)
+endfunction()
+
# Gets the unsupported options that init-repository.pl supports, but the cmake port does
# not support.
function(qt_ir_get_unsupported_options out_var)
diff --git a/cmake/QtIRGitHelpers.cmake b/cmake/QtIRGitHelpers.cmake
index 0797ed35..c8f64b49 100644
--- a/cmake/QtIRGitHelpers.cmake
+++ b/cmake/QtIRGitHelpers.cmake
@@ -516,9 +516,21 @@ function(qt_ir_process_module_subset_values prefix)
# 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)
- set(module_subset "existing")
+ 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()
@@ -713,11 +725,12 @@ function(qt_ir_handle_if_already_initialized out_var_should_exit working_directo
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)
+ if(NOT quiet AND NOT is_called_from_configure)
message(
"Will not reinitialize already initialized repository (use -f to force)!")
endif()
@@ -821,6 +834,7 @@ function(qt_ir_install_git_hooks)
set(options "")
set(oneValueArgs
PARENT_REPO_BASE_GIT_PATH
+ TOP_LEVEL_SRC_PATH
WORKING_DIRECTORY
)
set(multiValueArgs "")
@@ -836,7 +850,12 @@ function(qt_ir_install_git_hooks)
endif()
set(parent_repo_base_git_path "${arg_PARENT_REPO_BASE_GIT_PATH}")
- set(hooks_dir "${CMAKE_CURRENT_SOURCE_DIR}/qtrepotools/git-hooks")
+ 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()
@@ -884,6 +903,22 @@ function(qt_ir_install_git_hooks)
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.
@@ -976,6 +1011,7 @@ macro(qt_ir_get_submodules prefix out_var_submodules)
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()
diff --git a/cmake/QtIRHelp.txt b/cmake/QtIRHelp.txt
index 03b92548..9d6f5749 100644
--- a/cmake/QtIRHelp.txt
+++ b/cmake/QtIRHelp.txt
@@ -38,7 +38,7 @@ Options:
Module options:
- --module-subset=<module1>,<module2>...
+ --module-subset=<module1>,<module2>... / -submodules <module1>,<module2>...
Only initialize the specified subset of modules given as the
argument. Specified modules must already exist in .gitmodules. The
string "all" results in cloning all known modules. The strings
@@ -57,6 +57,8 @@ Options:
the default is to initialize the "existing" submodules, rather than the
default subset. Entries may be prefixed with a dash to exclude them
from a bigger set, e.g. "all,-ignore" or "existing,-qttools".
+ For compatibility with qt's configure script, -submodules is an alias
+ of --module-subset. Note the difference in dashes and the equal sign.
--no-update
Skip the `git submodule update' command.
diff --git a/cmake/QtIRHelpers.cmake b/cmake/QtIRHelpers.cmake
index e1073efc..3b20fa1e 100644
--- a/cmake/QtIRHelpers.cmake
+++ b/cmake/QtIRHelpers.cmake
@@ -32,6 +32,167 @@ function(qt_ir_get_cmake_flag flag_name out_var)
endif()
endfunction()
+# There are some init-repository options that we do not want to allow when called from
+# configure. Make sure we error out when they are set by the user.
+function(qt_ir_validate_options_for_configure)
+ set(disallowed_options
+ # Disallow mirror options, because users should set up a proper git mirror manually,
+ # not via configure.
+ mirror
+ oslo
+ berlin
+ )
+ foreach(disallowed_option IN LISTS disallowed_options)
+ qt_ir_get_option_value(${disallowed_option} value)
+ if(value)
+ set(msg
+ "Initialization option '${disallowed_option}' is not supported by configure. "
+ "If you think this option should be supported, please let us know at "
+ "https://bugreports.qt.io/"
+ )
+ message(FATAL_ERROR ${msg})
+ endif()
+ endforeach()
+endfunction()
+
+# Handle the case when init-repository is called from the configure script.
+function(qt_ir_handle_called_from_configure top_level_src_path out_var_exit_reason)
+ # Nothing special to do if we're not called from configure.
+ qt_ir_is_called_from_configure(is_called_from_configure)
+ if(NOT is_called_from_configure)
+ set(${out_var_exit_reason} FALSE PARENT_SCOPE)
+ return()
+ endif()
+
+ # Check whether qtbase was cloned, if not, tell the user how to initialize
+ # the repos as part of the configure script.
+ qt_ir_get_option_value(init-submodules init_submodules)
+ set(configure_script "${top_level_src_path}/qtbase/configure")
+ if(NOT EXISTS "${configure_script}" AND NOT init_submodules)
+ set(msg "Oops. It looks like you didn't initialize any submodules yet.\nCall configure "
+ "with the -init-submodules option to automatically clone a default set of "
+ "submodules before configuring Qt.\nYou can also pass "
+ "-submodules submodule2,submodule3 to clone a particular set of submodules "
+ "and their dependencies. See ./init-repository --help for more information on values "
+ "accepted by --module-subset (which gets its values from -submodules).")
+ message(${msg})
+ set(${out_var_exit_reason} NEED_INIT_SUBMODULES PARENT_SCOPE)
+ return()
+ endif()
+
+ # Don't do init-repository things when called from configure, qtbase exists and the
+ # -init-submodules option is not passed. We assume the repo was already
+ # initialized.
+ if(NOT init_submodules)
+ set(${out_var_exit_reason} ALREADY_INITIALIZED PARENT_SCOPE)
+ return()
+ endif()
+
+ qt_ir_validate_options_for_configure()
+
+ # -init_submodules implies --force
+ qt_ir_set_option_value(force TRUE)
+
+ set(${out_var_exit_reason} FALSE PARENT_SCOPE)
+endfunction()
+
+# Returns a list of command line arguments with the init-repository specific
+# options removed, which are not recognized by configure.
+# It also handles -submodules values like 'essential', 'existing' and '-qtsvg' and transforms them
+# into the final list of submodules to be included and excluded, which are then translated
+# to configure -submodules and -skip options.
+function(qt_ir_get_args_from_optfile_configure_filtered optfile_path out_var)
+ # Get args unknown to init-repository, and pass them to configure as-is.
+ qt_ir_get_unknown_args(unknown_args)
+
+ set(filtered_args ${unknown_args})
+ set(extra_configure_args "")
+ set(extra_cmake_args "")
+
+ # If the -submodules or --module-subset options were specified, transform
+ # the values into something configure understands and pass them to configure.
+ qt_ir_get_option_value(module-subset submodules)
+ if(submodules)
+ qt_ir_get_top_level_submodules(include_submodules exclude_submodules)
+
+ # qtrepotools is always implicitly cloned, but it doesn't actually
+ # have a CMakeLists.txt, so remove it.
+ list(REMOVE_ITEM include_submodules "qtrepotools")
+
+ # Make sure to explicitly pass -DBUILD_<module>=ON, in case they were
+ # skipped before, otherwise configure might fail.
+ if(include_submodules)
+ set(explicit_build_submodules "${include_submodules}")
+ list(TRANSFORM explicit_build_submodules PREPEND "-DBUILD_")
+ list(TRANSFORM explicit_build_submodules APPEND "=ON")
+ list(APPEND extra_cmake_args ${explicit_build_submodules})
+ endif()
+
+ list(JOIN include_submodules "," include_submodules)
+ list(JOIN exclude_submodules "," exclude_submodules)
+
+ # Handle case when the -skip argument is already passed.
+ # In that case read the passed values, merge with new ones,
+ # remove both the -skip and its values, and re-add it later.
+ list(FIND filtered_args "-skip" skip_index)
+ if(exclude_submodules AND skip_index GREATER -1)
+ list(LENGTH filtered_args filtered_args_length)
+ math(EXPR skip_args_index "${skip_index} + 1")
+
+ if(skip_args_index LESS filtered_args_length)
+ list(GET filtered_args "${skip_args_index}" skip_args)
+ string(REPLACE "," ";" skip_args "${skip_args}")
+ list(APPEND skip_args ${exclude_submodules})
+ list(REMOVE_DUPLICATES skip_args)
+ list(JOIN skip_args "," exclude_submodules)
+ list(REMOVE_AT filtered_args "${skip_args_index}")
+ list(REMOVE_AT filtered_args "${skip_index}")
+ endif()
+ endif()
+
+ # Handle case when only '-submodules existing' is passed and the
+ # subset ends up empty.
+ if(include_submodules)
+ list(APPEND extra_configure_args "-submodules" "${include_submodules}")
+ endif()
+ if(exclude_submodules)
+ list(APPEND extra_configure_args "-skip" "${exclude_submodules}")
+ endif()
+ endif()
+
+ # Insert the extra arguments into the proper positions before and after '--'.
+ list(FIND filtered_args "--" cmake_args_index)
+
+ # -- is not found
+ if(cmake_args_index EQUAL -1)
+ # Append extra configure args if present
+ if(extra_configure_args)
+ list(APPEND filtered_args ${extra_configure_args})
+ endif()
+ # Append extra cmake args if present, but make sure to add -- first at the end
+ if(extra_cmake_args)
+ list(APPEND filtered_args "--")
+ list(APPEND filtered_args ${extra_cmake_args})
+ endif()
+ else()
+ # -- is found, that means we probably have cmake args
+ # Insert extra configure args if present, before the -- index.
+ if(extra_configure_args)
+ list(INSERT filtered_args "${cmake_args_index}" ${extra_configure_args})
+ endif()
+ # Find the -- index again, because it might have moved
+ list(FIND filtered_args "--" cmake_args_index)
+ # Compute the index of the argument after the --.
+ math(EXPR cmake_args_index "${cmake_args_index} + 1")
+ # Insert extra cmake args if present, after the -- index.
+ if(extra_cmake_args)
+ list(INSERT filtered_args "${cmake_args_index}" ${extra_cmake_args})
+ endif()
+ endif()
+
+ set(${out_var} "${filtered_args}" PARENT_SCOPE)
+endfunction()
+
# Checks whether any of the arguments passed on the command line are options
# that are marked as unsupported in the cmake port of init-repository.
function(qt_ir_check_if_unsupported_options_used out_var out_var_option_name)
@@ -76,18 +237,29 @@ function(qt_ir_is_verbose out_var)
set(${out_var} "${value}" PARENT_SCOPE)
endfunction()
+function(qt_ir_is_called_from_configure out_var)
+ qt_ir_get_option_value(from-configure value)
+ set(${out_var} "${value}" PARENT_SCOPE)
+endfunction()
+
# Main logic of the script.
-function(qt_ir_run_after_args_parsed)
+function(qt_ir_run_after_args_parsed top_level_src_path out_var_exit_reason)
+ set(${out_var_exit_reason} FALSE PARENT_SCOPE)
+
+ qt_ir_is_called_from_configure(is_called_from_configure)
+
qt_ir_is_help_requested(show_help)
- if(show_help)
+ if(show_help AND NOT is_called_from_configure)
qt_ir_show_help()
+ set(${out_var_exit_reason} SHOWED_HELP PARENT_SCOPE)
return()
endif()
- set(working_directory "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(working_directory "${top_level_src_path}")
- qt_ir_handle_if_already_initialized(should_exit "${working_directory}")
- if(should_exit)
+ qt_ir_handle_if_already_initialized(already_initialized "${working_directory}")
+ if(already_initialized)
+ set(${out_var_exit_reason} ALREADY_INITIALIZED PARENT_SCOPE)
return()
endif()
@@ -137,6 +309,7 @@ function(qt_ir_run_after_args_parsed)
# Install commit and other various hooks.
qt_ir_install_git_hooks(
PARENT_REPO_BASE_GIT_PATH "${qt5_repo_name}"
+ TOP_LEVEL_SRC_PATH "${top_level_src_path}"
WORKING_DIRECTORY "${working_directory}"
)
@@ -145,9 +318,30 @@ function(qt_ir_run_after_args_parsed)
endfunction()
# Entrypoint of the init-repository script.
-function(qt_ir_run_main_script)
+function(qt_ir_run_main_script top_level_src_path out_var_exit_reason)
+ set(${out_var_exit_reason} FALSE PARENT_SCOPE)
+
+ # Windows passes backslash paths.
+ file(TO_CMAKE_PATH "${top_level_src_path}" top_level_src_path)
+
qt_ir_set_known_command_line_options()
- qt_ir_process_args_from_optfile("${OPTFILE}")
+
+ # If called from configure, there might be arguments that init-repository doesn't know about
+ # because they are meant for configure. In that case ignore unknown arguments.
+ qt_ir_get_option_value(from-configure from_configure)
+ if(from_configure)
+ set(ignore_unknown_args "IGNORE_UNKNOWN_ARGS")
+ else()
+ set(ignore_unknown_args "")
+ endif()
+
+ qt_ir_process_args_from_optfile("${OPTFILE}" "${ignore_unknown_args}")
+
+ qt_ir_handle_called_from_configure("${top_level_src_path}" exit_reason)
+ if(exit_reason)
+ set(${out_var_exit_reason} "${exit_reason}" PARENT_SCOPE)
+ return()
+ endif()
qt_ir_check_if_unsupported_options_used(
unsupported_options_used option_name)
@@ -155,5 +349,8 @@ function(qt_ir_run_main_script)
qt_ir_show_error_how_to_run_perl("${OPTFILE}" "${option_name}")
endif()
- qt_ir_run_after_args_parsed()
+ qt_ir_run_after_args_parsed("${top_level_src_path}" exit_reason)
+ set(${out_var_exit_reason} "${exit_reason}" PARENT_SCOPE)
+
+ # TODO: Consider using cmake_language(EXIT <exit-code>) when cmake 3.29 is released.
endfunction()
diff --git a/cmake/QtIROptionsHelpers.cmake b/cmake/QtIROptionsHelpers.cmake
index 877265be..d87c7463 100644
--- a/cmake/QtIROptionsHelpers.cmake
+++ b/cmake/QtIROptionsHelpers.cmake
@@ -30,5 +30,19 @@ macro(qt_ir_set_known_command_line_options)
qt_ir_commandline_option(resolve-deps TYPE boolean DEFAULT_VALUE yes)
qt_ir_commandline_option(update TYPE boolean DEFAULT_VALUE yes)
qt_ir_commandline_option(verbose TYPE boolean)
+
+ # These are used when init-repository is called from configure.
+ qt_ir_commandline_option(from-configure TYPE boolean)
+ # Implies force.
+ qt_ir_commandline_option(init-submodules TYPE boolean)
+ # We alias qtbase's submodules option to init-repository module-subset.
+ qt_ir_commandline_option(submodules ALIAS module-subset TYPE string)
+
+ set_property(GLOBAL PROPERTY _qt_ir_known_command_line_options "${commandline_known_options}")
endmacro()
+# Gets list of known command line options.
+function(qt_ir_get_known_command_line_options out_var)
+ get_property(values GLOBAL PROPERTY _qt_ir_known_command_line_options)
+ set(${out_var} "${values}" PARENT_SCOPE)
+endfunction()
diff --git a/cmake/QtIRScript.cmake b/cmake/QtIRScript.cmake
index 50cf960c..fc5ffba9 100644
--- a/cmake/QtIRScript.cmake
+++ b/cmake/QtIRScript.cmake
@@ -14,4 +14,4 @@ endmacro()
qt_ir_setup_include_paths()
qt_ir_include_all_helpers()
-qt_ir_run_main_script()
+qt_ir_run_main_script("${CMAKE_CURRENT_SOURCE_DIR}" exit_reason)
diff --git a/cmake/QtTopLevelConfigureScript.cmake b/cmake/QtTopLevelConfigureScript.cmake
new file mode 100644
index 00000000..304bf7b7
--- /dev/null
+++ b/cmake/QtTopLevelConfigureScript.cmake
@@ -0,0 +1,17 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+# Sets up the include paths for all the helpers configure uses.
+macro(qt_tl_setup_include_paths)
+ list(APPEND CMAKE_MODULE_PATH
+ "${CMAKE_CURRENT_LIST_DIR}"
+ "${CMAKE_CURRENT_LIST_DIR}/3rdparty/cmake"
+ )
+ include(QtTopLevelHelpers)
+endmacro()
+
+qt_tl_setup_include_paths()
+qt_tl_include_all_helpers()
+qt_tl_run_main_script()
diff --git a/cmake/QtTopLevelHelpers.cmake b/cmake/QtTopLevelHelpers.cmake
index e84ce757..c7058efb 100644
--- a/cmake/QtTopLevelHelpers.cmake
+++ b/cmake/QtTopLevelHelpers.cmake
@@ -1,6 +1,58 @@
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+macro(qt_tl_include_all_helpers)
+ include(QtIRHelpers)
+ qt_ir_include_all_helpers()
+endmacro()
+
+function(qt_tl_run_toplevel_configure top_level_src_path)
+ # Filter out init-repository specific arguments before passing them to
+ # configure.
+ qt_ir_get_args_from_optfile_configure_filtered("${OPTFILE}" configure_args)
+
+ # Get the path to the qtbase configure script.
+ set(qtbase_dir_name "qtbase")
+ set(configure_path "${top_level_src_path}/${qtbase_dir_name}/configure")
+ if(CMAKE_HOST_WIN32)
+ string(APPEND configure_path ".bat")
+ endif()
+
+ if(NOT EXISTS "${configure_path}")
+ message(FATAL_ERROR
+ "The required qtbase/configure script was not found: ${configure_path}\n"
+ "Try re-running configure with --init-submodules")
+ endif()
+
+ # Make a build directory for qtbase in the current build directory.
+ set(qtbase_build_dir "${CMAKE_CURRENT_BINARY_DIR}/${qtbase_dir_name}")
+ file(MAKE_DIRECTORY "${qtbase_build_dir}")
+
+ qt_ir_execute_process_and_log_and_handle_error(
+ COMMAND_ARGS "${configure_path}" -top-level ${configure_args}
+ WORKING_DIRECTORY "${qtbase_build_dir}"
+ FORCE_VERBOSE
+ )
+endfunction()
+
+function(qt_tl_run_main_script)
+ if(NOT TOP_LEVEL_SRC_PATH)
+ message(FATAL_ERROR "Assertion: configure TOP_LEVEL_SRC_PATH is not set")
+ endif()
+
+ # Tell init-repository it is called from configure.
+ qt_ir_set_option_value(from-configure TRUE)
+
+ # Run init-repository in-process.
+ qt_ir_run_main_script("${TOP_LEVEL_SRC_PATH}" exit_reason)
+ if(exit_reason AND NOT exit_reason STREQUAL "ALREADY_INITIALIZED")
+ return()
+ endif()
+
+ # Then run configure out-of-process.
+ qt_tl_run_toplevel_configure("${TOP_LEVEL_SRC_PATH}")
+endfunction()
+
# Populates $out_module_list with all subdirectories that have a CMakeLists.txt file
function(qt_internal_find_modules out_module_list)
set(module_list "")
diff --git a/configure b/configure
index dc0b78a4..aab559b9 100755
--- a/configure
+++ b/configure
@@ -1,18 +1,22 @@
#! /bin/sh
-# Copyright (C) 2020 The Qt Company Ltd.
+# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-srcpath=`dirname $0`
-srcpath=`(cd "$srcpath"; pwd)`
-configure=$srcpath/qtbase/configure
-if [ ! -e "$configure" ]; then
- echo "$configure not found. Did you forget to run \"init-repository\"?" >&2
- exit 1
-fi
+src_path=`dirname $0`
+src_path=`(cd "$src_path"; /bin/pwd)`
-set -ex
+optfile=config.tl.opt
+opttmpfile=config.tl.opt.in
-mkdir -p qtbase
-cd qtbase
+# Posix compatible way to truncate file
+: > "$optfile"
+: > "$opttmpfile"
-exec "$configure" -top-level "$@"
+# For consistency, use QtWriteArgsFile.cmake to write the optfile like we do on Windows.
+# We do the same with the configure script in qtbase.
+for arg in "$@"; do echo \"$arg\" >> "$opttmpfile"; done
+
+cmake -DIN_FILE="${opttmpfile}" -DOUT_FILE="${optfile}" -P "${src_path}/cmake/QtWriteArgsFile.cmake"
+
+cmake_script_path="$src_path/cmake/QtTopLevelConfigureScript.cmake"
+exec cmake -DTOP_LEVEL_SRC_PATH="$src_path" -DOPTFILE="${optfile}" -P "$cmake_script_path"
diff --git a/configure.bat b/configure.bat
index e68301d6..9c0e0e62 100644
--- a/configure.bat
+++ b/configure.bat
@@ -1,23 +1,23 @@
-@echo off
-:: Copyright (C) 2020 The Qt Company Ltd.
+:: Copyright (C) 2024 The Qt Company Ltd.
:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-set "srcpath=%~dp0"
-set "configure=%srcpath%qtbase\configure.bat"
-if not exist "%configure%" (
- echo %configure% not found. Did you forget to run "init-repository"? >&2
- exit /b 1
-)
-
-if not exist qtbase mkdir qtbase || exit /b 1
-
-echo + cd qtbase
-cd qtbase || exit /b 1
+@echo off
+setlocal ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
+set script_dir_path=%~dp0
+set script_dir_path=%script_dir_path:~0,-1%
-echo + %configure% -top-level %*
-call %configure% -top-level %*
-set err=%errorlevel%
+set cmake_scripts_dir=%script_dir_path%\cmake
+:: The '.' in 'echo.%*' ensures we don't print "echo is off" when no arguments are passed
+:: https://devblogs.microsoft.com/oldnewthing/20170802-00/?p=96735
+:: The space before the '>' makes sure that when we have a digit at the end of the args, we
+:: don't accidentally concatenate it with the '>' resulting in '0>' or '2>' which redirects into the
+:: file from a stream different than stdout, leading to broken or empty content.
+echo.%* >config.tl.opt.in
-cd ..
+call cmake -DIN_FILE=config.tl.opt.in -DOUT_FILE=config.tl.opt ^
+ -P "%cmake_scripts_dir%\QtWriteArgsFile.cmake"
+call cmake -DOPTFILE=config.tl.opt -DTOP_LEVEL_SRC_PATH="%script_dir_path%" ^
+ -P "%cmake_scripts_dir%\QtTopLevelConfigureScript.cmake"
-exit /b %err%
+del config.tl.opt.in
+del config.tl.opt