Files
qt5/cmake/QtIRCommandLineHelpers.cmake
Alexandru Croitor 2c9664ca33 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>
2024-02-28 06:24:02 +01:00

405 lines
14 KiB
CMake

# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# This file contains a modified subset of the qtbase/QtProcessConfigureArgs.cmake commands
# with renamed functions, because we need similar logic for init-repository, but
# we can't access qtbase before we clone it.
# Call a function with the given arguments.
function(qt_ir_call_function func)
set(call_code "${func}(")
math(EXPR n "${ARGC} - 1")
foreach(i RANGE 1 ${n})
string(APPEND call_code "\"${ARGV${i}}\" ")
endforeach()
string(APPEND call_code ")")
string(REPLACE "\\" "\\\\" call_code "${call_code}")
if(${CMAKE_VERSION} VERSION_LESS "3.18.0")
set(incfile qt_tmp_func_call.cmake)
file(WRITE "${incfile}" "${call_code}")
include(${incfile})
file(REMOVE "${incfile}")
else()
cmake_language(EVAL CODE "${call_code}")
endif()
endfunction()
# Show an error.
function(qt_ir_add_error)
message(FATAL_ERROR ${ARGV})
endfunction()
# Check if there are still unhandled command line arguments.
function(qt_ir_args_has_next_command_line_arg out_var)
qt_ir_get_unhandled_args(args)
list(LENGTH args n)
if(n GREATER 0)
set(result TRUE)
else()
set(result FALSE)
endif()
set(${out_var} ${result} PARENT_SCOPE)
endfunction()
# Get the next unhandled command line argument without popping it.
function(qt_ir_args_peek_next_command_line_arg out_var)
qt_ir_get_unhandled_args(args)
list(GET args 0 result)
set(${out_var} ${result} PARENT_SCOPE)
endfunction()
# Get the next unhandled command line argument.
function(qt_ir_args_get_next_command_line_arg out_var)
qt_ir_get_unhandled_args(args)
list(POP_FRONT args result)
qt_ir_set_unhandled_args("${args}")
set(${out_var} ${result} PARENT_SCOPE)
endfunction()
# Helper macro to parse the arguments for the command line options.
macro(qt_ir_commandline_option_parse_arguments)
set(options UNSUPPORTED)
set(oneValueArgs TYPE NAME SHORT_NAME ALIAS VALUE DEFAULT_VALUE)
set(multiValueArgs VALUES MAPPING)
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
endmacro()
# We use this to define the command line options that init-repository accepts.
# Arguments
# name - name of the long form option
# e.g. 'module-subset' will parse '--module-subset'
# UNSUPPORTED - mark the option as unsupported in the cmake port of init-repository,
# which means we will fall back to calling the perl script instead
# TYPE - the type of the option, currently we support boolean, string and void
# VALUE - the value to be set for a 'void' type option
# VALUES - the valid values for an option
# MAPPING - currently unused
# SHORT_NAME - an alternative short name flag,
# e.g. 'f' will parse -f for --force
# ALIAS - mark the option as an alias of another option, both will have the
# same value when retrieved.
# DEFAULT_VALUE - the default value to be set for the option when it's not specified
# on the command line
#
# NOTE: Make sure to update the SHORT_NAME code path when adding new options.
function(qt_ir_commandline_option_helper name)
qt_ir_commandline_option_parse_arguments(${ARGN})
set(unsupported_options "${commandline_known_unsupported_options}")
if(arg_UNSUPPORTED)
set(commandline_option_${name}_unsupported
"${arg_UNSUPPORTED}" PARENT_SCOPE)
list(APPEND unsupported_options "${name}")
endif()
set(commandline_known_unsupported_options "${unsupported_options}" PARENT_SCOPE)
set(commandline_known_options
"${commandline_known_options};${name}" PARENT_SCOPE)
set(commandline_option_${name}_type "${arg_TYPE}" PARENT_SCOPE)
if(NOT "${arg_VALUE}" STREQUAL "")
set(commandline_option_${name}_value "${arg_VALUE}" PARENT_SCOPE)
endif()
if(arg_VALUES)
set(commandline_option_${name}_values ${arg_VALUES} PARENT_SCOPE)
elseif(arg_MAPPING)
set(commandline_option_${name}_mapping ${arg_MAPPING} PARENT_SCOPE)
endif()
if(NOT "${arg_SHORT_NAME}" STREQUAL "")
set(commandline_option_${name}_short_name "${arg_SHORT_NAME}" PARENT_SCOPE)
endif()
if(NOT "${arg_ALIAS}" STREQUAL "")
set(commandline_option_${name}_alias "${arg_ALIAS}" PARENT_SCOPE)
endif()
# Should be last, in case alias was specified
if(NOT "${arg_DEFAULT_VALUE}" STREQUAL "")
set(commandline_option_${name}_default_value "${arg_DEFAULT_VALUE}" PARENT_SCOPE)
qt_ir_command_line_set_input("${name}" "${arg_DEFAULT_VALUE}")
endif()
endfunction()
# Defines an option that init-repository understands.
# Uses qt_ir_commandline_option_helper to define both long and short option names.
macro(qt_ir_commandline_option name)
# Define the main option
qt_ir_commandline_option_helper("${name}" ${ARGN})
qt_ir_commandline_option_parse_arguments(${ARGN})
# Define the short name option if it's requested
if(NOT "${arg_SHORT_NAME}" STREQUAL ""
AND "${commandline_option_${arg_SHORT_NAME}_type}" STREQUAL "")
set(unsupported "")
if(arg_UNSUPPORTED)
set(unsupported "${arg_UNSUPPORTED}")
endif()
qt_ir_commandline_option_helper("${arg_SHORT_NAME}"
TYPE "${arg_TYPE}"
ALIAS "${name}"
VALUE "${arg_VALUE}"
VALUES ${arg_VALUES}
MAPPING ${arg_MAPPING}
DEFAULT_VALUE ${arg_DEFAULT_VALUE}
${unsupported}
)
endif()
endmacro()
# Saves the value of a command line option into a global property.
function(qt_ir_command_line_set_input name val)
if(NOT "${commandline_option_${name}_alias}" STREQUAL "")
set(name "${commandline_option_${name}_alias}")
endif()
set_property(GLOBAL PROPERTY _qt_ir_input_${name} "${val}")
set_property(GLOBAL APPEND PROPERTY _qt_ir_inputs ${name})
endfunction()
# Appends a value of a command line option into a global property.
# Currently unused
function(qt_ir_command_line_append_input name val)
if(NOT "${commandline_option_${name}_alias}" STREQUAL "")
set(name "${commandline_option_${name}_alias}")
endif()
get_property(oldval GLOBAL PROPERTY _qt_ir_input_${name})
if(NOT "${oldval}" STREQUAL "")
string(PREPEND val "${oldval};")
endif()
qt_ir_command_line_set_input(${name} "${val}" )
endfunction()
# Checks if the value of a command line option is valid.
function(qt_ir_validate_value opt val out_var)
set(${out_var} TRUE PARENT_SCOPE)
set(valid_values ${commandline_option_${arg}_values})
list(LENGTH valid_values n)
if(n EQUAL 0)
return()
endif()
foreach(v ${valid_values})
if(val STREQUAL v)
return()
endif()
endforeach()
set(${out_var} FALSE PARENT_SCOPE)
list(JOIN valid_values " " valid_values_str)
qt_ir_add_error("Invalid value '${val}' supplied to command line option '${opt}'."
"\nAllowed values: ${valid_values_str}\n")
endfunction()
# Sets / handles the value of a command line boolean option.
function(qt_ir_commandline_boolean arg val nextok)
if("${val}" STREQUAL "")
set(val "yes")
endif()
if(NOT val STREQUAL "yes" AND NOT val STREQUAL "no")
message(FATAL_ERROR
"Invalid value '${val}' given for boolean command line option '${arg}'.")
endif()
qt_ir_command_line_set_input("${arg}" "${val}")
endfunction()
# Sets / handles the value of a command line string option.
function(qt_ir_commandline_string arg val nextok)
if(nextok)
qt_ir_args_get_next_command_line_arg(val)
if("${val}" MATCHES "^-")
qt_ir_add_error("No value supplied to command line options '${arg}'.")
endif()
endif()
qt_ir_validate_value("${arg}" "${val}" success)
if(success)
qt_ir_command_line_set_input("${arg}" "${val}")
endif()
endfunction()
# Sets / handles the value of a command line void option.
# This is an option like --force, which doesn't take any arguments.
# Currently unused
function(qt_ir_commandline_void arg val nextok)
if(NOT "${val}" STREQUAL "")
qt_i_add_error("Command line option '${arg}' expects no argument ('${val}' given).")
endif()
if(DEFINED commandline_option_${arg}_value)
set(val ${commandline_option_${arg}_value})
endif()
if("${val}" STREQUAL "")
set(val yes)
endif()
qt_ir_command_line_set_input("${arg}" "${val}")
endfunction()
# Reads the command line arguments from the optfile_path.
function(qt_ir_get_raw_args_from_optfile optfile_path out_var)
file(STRINGS "${optfile_path}" args)
set(${out_var} "${args}" PARENT_SCOPE)
endfunction()
# 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 "")
set(multiValueArgs "")
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
qt_ir_get_raw_args_from_optfile("${optfile_path}" configure_args)
qt_ir_set_unhandled_args("${configure_args}")
while(1)
qt_ir_args_has_next_command_line_arg(has_next)
if(NOT has_next)
break()
endif()
qt_ir_args_get_next_command_line_arg(arg)
# parse out opt and val
set(nextok FALSE)
if(arg MATCHES "^--?(disable|no)-(.*)")
set(opt "${CMAKE_MATCH_2}")
set(val "no")
elseif(arg MATCHES "^--([^=]+)=(.*)")
set(opt "${CMAKE_MATCH_1}")
set(val "${CMAKE_MATCH_2}")
elseif(arg MATCHES "^--(.*)")
set(nextok TRUE)
set(opt "${CMAKE_MATCH_1}")
unset(val)
elseif(arg MATCHES "^-(.*)")
set(nextok TRUE)
set(opt "${CMAKE_MATCH_1}")
unset(val)
else()
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}")
if("${type}" STREQUAL "")
if(NOT arg_IGNORE_UNKNOWN_ARGS)
qt_ir_add_error("Unknown command line option '${arg}'.")
else()
message(DEBUG "Unknown command line option '${arg}'. Collecting.")
qt_ir_append_unknown_args("${arg}")
continue()
endif()
endif()
if(NOT COMMAND "qt_ir_commandline_${type}")
qt_ir_add_error("Unknown type '${type}' for command line option '${opt}'.")
endif()
qt_ir_call_function("qt_ir_commandline_${type}" "${opt}" "${val}" "${nextok}")
endwhile()
endfunction()
# Shows help for the command line options.
function(qt_ir_show_help)
set(help_file "${CMAKE_CURRENT_LIST_DIR}/QtIRHelp.txt")
if(EXISTS "${help_file}")
file(READ "${help_file}" content)
message("${content}")
endif()
message([[
General Options:
-help, -h ............ Display this help screen
]])
endfunction()
# Gets the unhandled command line args.
function(qt_ir_get_unhandled_args out_var)
get_property(args GLOBAL PROPERTY _qt_ir_unhandled_args)
set(${out_var} "${args}" PARENT_SCOPE)
endfunction()
# Sets the unhandled command line args.
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)
set(${out_var} "${commandline_known_unsupported_options}" PARENT_SCOPE)
endfunction()
# Get the value of a command line option.
function(qt_ir_get_option_value name out_var)
if(NOT "${commandline_option_${name}_alias}" STREQUAL "")
set(name "${commandline_option_${name}_alias}")
endif()
get_property(value GLOBAL PROPERTY _qt_ir_input_${name})
set(${out_var} "${value}" PARENT_SCOPE)
endfunction()
# Set the value of a command line option manually.
function(qt_ir_set_option_value name value)
if(NOT "${commandline_option_${name}_alias}" STREQUAL "")
set(name "${commandline_option_${name}_alias}")
endif()
qt_ir_command_line_set_input("${name}" "${value}")
endfunction()
# Get the value of a command line option as a cmakke flag option, to be passed
# to functions that use cmake_parse_arguments.
function(qt_ir_get_option_as_cmake_flag_option cli_name cmake_option_name out_var)
qt_ir_get_option_value("${cli_name}" bool_value)
set(cmake_option "")
if(bool_value)
set(cmake_option "${cmake_option_name}")
endif()
set(${out_var} "${cmake_option}" PARENT_SCOPE)
endfunction()