diff options
Diffstat (limited to 'cmake/QtIRHelpers.cmake')
-rw-r--r-- | cmake/QtIRHelpers.cmake | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/cmake/QtIRHelpers.cmake b/cmake/QtIRHelpers.cmake new file mode 100644 index 00000000..9f372932 --- /dev/null +++ b/cmake/QtIRHelpers.cmake @@ -0,0 +1,367 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Includes all helper files for access to necessary functions. +macro(qt_ir_include_all_helpers) + include(QtIRCommandLineHelpers) + include(QtIRGitHelpers) + include(QtIROptionsHelpers) + include(QtIRParsingHelpers) + include(QtIRProcessHelpers) + include(QtIRTestHelpers) + include(QtTopLevelHelpers) +endmacro() + +# Convenience macro to get the working directory from the arguments passed to +# cmake_parse_arguments. Saves a few lines and makes reading the code slightly +# easier. +macro(qt_ir_get_working_directory_from_arg out_var) + if(NOT arg_WORKING_DIRECTORY) + message(FATAL_ERROR "No working directory specified") + endif() + set(${out_var} "${arg_WORKING_DIRECTORY}") +endmacro() + +# Convenience function to set the variable to the name of cmake_parse_arguments +# flag option if it is active. +function(qt_ir_get_cmake_flag flag_name out_var) + if(arg_${flag_name}) + set(${out_var} "${flag_name}" PARENT_SCOPE) + else() + set(${out_var} "" PARENT_SCOPE) + 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) + cmake_parse_arguments(arg "ALREADY_INITIALIZED" "" "" ${ARGV}) + + # 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) + if(NOT include_submodules AND arg_ALREADY_INITIALIZED) + set(include_submodules "${submodules}") + endif() + + # 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() + + # Pass --help if it was requested. + qt_ir_is_help_requested(show_help) + if(show_help) + list(APPEND filtered_args "-help") + 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) + qt_ir_get_unsupported_options(unsupported_options) + + set(unsupported_options_used FALSE) + foreach(unsupported_option IN LISTS unsupported_options) + qt_ir_get_option_value(${unsupported_option} value) + if(value) + set(${out_var_option_name} "${unsupported_option}" PARENT_SCOPE) + set(unsupported_options_used TRUE) + break() + endif() + endforeach() + set(${out_var} "${unsupported_options_used}" PARENT_SCOPE) +endfunction() + +# When an unsupported option is used, show an error message and tell the user +# to run the perly script manually. +function(qt_ir_show_error_how_to_run_perl opt_file unsupported_option_name) + qt_ir_get_raw_args_from_optfile("${opt_file}" args) + string(REPLACE ";" " " args "${args}") + + set(perl_cmd "perl ./init-repository.pl ${args}") + + message(FATAL_ERROR + "Option '${unsupported_option_name}' is not implemented in the cmake " + "port of init-repository. Please let us know if this option is really " + "important for you at https://bugreports.qt.io/. Meanwhile, you can " + "still run the perl script directly. \n ${perl_cmd}") +endfunction() + +# Check whether help was requested. +function(qt_ir_is_help_requested out_var) + qt_ir_get_option_value(help value) + set(${out_var} "${value}" PARENT_SCOPE) +endfunction() + +# Check whether the verbose option was used. +function(qt_ir_is_verbose out_var) + qt_ir_get_option_value(verbose value) + 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 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 AND NOT is_called_from_configure) + qt_ir_show_help() + set(${out_var_exit_reason} SHOWED_HELP PARENT_SCOPE) + return() + endif() + + set(working_directory "${top_level_src_path}") + + qt_ir_handle_if_already_initialized(already_initialized "${working_directory}") + if(already_initialized) + set(${out_var_exit_reason} ALREADY_INITIALIZED PARENT_SCOPE) + return() + endif() + + # This will be used by the module subset processing to determine whether we + # should re-initialize the previously initialized (existing) subset. + qt_ir_check_if_already_initialized_cmake_style(is_initialized + "${working_directory}" FORCE_QUIET) + set(previously_initialized_option "") + if(is_initialized) + set(previously_initialized_option PREVIOUSLY_INITIALIZED) + endif() + + + # Ge the name of the qt5 repo (tqtc- or not) and the base url for all other repos + qt_ir_get_qt5_repo_name_and_base_url( + OUT_VAR_QT5_REPO_NAME qt5_repo_name + OUT_VAR_BASE_URL base_url + WORKING_DIRECTORY "${working_directory}") + + qt_ir_get_already_initialized_submodules("${prefix}" + already_initialized_submodules + "${qt5_repo_name}" + "${working_directory}") + + # Get some additional options to pass down. + qt_ir_get_option_value(alternates alternates) + qt_ir_get_option_as_cmake_flag_option(branch "CHECKOUT_BRANCH" checkout_branch_option) + + # The prefix for the cmake-style 'dictionary' that will be used by various functions. + set(prefix "ir_top") + + # Initialize and clone the submodules + qt_ir_handle_init_submodules("${prefix}" + ALTERNATES "${alternates}" + ALREADY_INITIALIZED_SUBMODULES "${already_initialized_submodules}" + BASE_URL "${base_url}" + PARENT_REPO_BASE_GIT_PATH "${qt5_repo_name}" + PROCESS_SUBMODULES_FROM_COMMAND_LINE + WORKING_DIRECTORY "${working_directory}" + ${checkout_branch_option} + ${previously_initialized_option} + ) + + # Add gerrit remotes. + qt_ir_add_git_remotes("${qt5_repo_name}" "${working_directory}") + + # 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}" + ) + + # Mark the repo as being initialized. + qt_ir_set_is_initialized("${working_directory}") +endfunction() + +# Entrypoint of the init-repository 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() + + # 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) + if(unsupported_options_used) + qt_ir_show_error_how_to_run_perl("${OPTFILE}" "${option_name}") + endif() + + 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() |