qt5base-lts/cmake/QtProcessConfigureArgs.cmake
Joerg Bornemann 584d4be174 CMake: Re-implement configure/qmake's command line handling in CMake
We extend configurejson2cmake to read the "commandline"
information from configure.json. This data is then translated to CMake function
calls and written it into commandline.cmake files.

We extend QtProcessConfigureArgs.cmake to pick up those commandline.cmake
files to feed our command line handling code, which is a
re-implementation of the command line handling in qt_configure.prf.

The command line handler sets INPUT_xxx variables, similar to
configure/qmake's config.input.xxx variables. The INPUT_xxx values are
translated
- to -DFEATURE_xxx=ON/OFF arguments if the input represents a feature,
- to corresponding CMake variables if such a variable is known,
- or to -DINPUT_xxx=yyy CMake arguments.

Configure arguments that have an entry in
cmake/configure-cmake-mapping.md are actually implemented. Other
arguments are likely to need more work.

Task-number: QTBUG-85373
Change-Id: Ia96baa673fc1fb88e73ba05a1afb473aa074b37d
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
2020-08-17 08:08:20 +02:00

647 lines
21 KiB
CMake

# This script reads Qt configure arguments from config.opt,
# translates the arguments to CMake arguments and calls CMake.
#
# This file is to be used in CMake script mode with the following variables set:
# OPTFILE: A text file containing the options that were passed to configure
# with one option per line.
include(${CMAKE_CURRENT_LIST_DIR}/QtFeatureCommon.cmake)
cmake_policy(SET CMP0007 NEW)
cmake_policy(SET CMP0057 NEW)
set(cmake_args "")
macro(push)
list(APPEND cmake_args ${ARGN})
endmacro()
macro(pop_path_argument)
list(POP_FRONT configure_args path)
string(REGEX REPLACE "^\"(.*)\"$" "\\1" path "${path}")
file(TO_CMAKE_PATH "${path}" path)
endmacro()
get_filename_component(qtbase_source_dir ".." ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
set(configure_filename "configure.cmake")
set(commandline_filename "qt_cmdline.cmake")
set(source_dir "${qtbase_source_dir}")
file(STRINGS "${OPTFILE}" configure_args)
list(FILTER configure_args EXCLUDE REGEX "^[ \t]*$")
list(TRANSFORM configure_args STRIP)
unset(generator)
set(auto_detect_generator TRUE)
unset(device_options)
set_property(GLOBAL PROPERTY UNHANDLED_ARGS "")
while(configure_args)
list(POP_FRONT configure_args arg)
if(arg STREQUAL "-cmake")
# ignore
elseif(arg STREQUAL "-cmake-generator")
list(POP_FRONT configure_args generator)
elseif(arg STREQUAL "-cmake-use-default-generator")
set(auto_detect_generator FALSE)
elseif(arg STREQUAL "-top-level")
get_filename_component(source_dir "../.." ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
file(GLOB commandline_files "${source_dir}/*/${commandline_filename}")
if(EXISTS "${source_dir}/${commandline_filename}")
list(PREPEND commandline_files "${source_dir}/${commandline_filename}")
endif()
elseif(arg STREQUAL "-skip")
list(POP_FRONT configure_args qtrepo)
push("-DBUILD_${qtrepo}=OFF")
elseif(arg STREQUAL "-hostprefix")
message(FATAL_ERROR "${arg} is not supported in the CMake build.")
elseif(arg STREQUAL "-external-hostbindir")
# This points to the bin directory of the Qt installation.
# This can be multiple levels deep and we cannot deduce the QT_HOST_PATH safely.
message(FATAL_ERROR "${arg} is not supported anymore. Use -qt-host-path <dir> instead.")
elseif(arg STREQUAL "-qt-host-path")
pop_path_argument()
push("-DQT_HOST_PATH=${path}")
elseif(arg MATCHES "^-host.*dir")
message(FATAL_ERROR "${arg} is not supported anymore.")
elseif(arg STREQUAL "--")
# Everything after this argument will be passed to CMake verbatim.
push(${configure_args})
break()
else()
set_property(GLOBAL APPEND PROPERTY UNHANDLED_ARGS "${arg}")
endif()
endwhile()
if(NOT commandline_files)
set(commandline_files "${qtbase_source_dir}/${commandline_filename}")
endif()
####################################################################################################
# Define functions/macros that are called in configure.cmake files
#
# Every function that's called in a configure.cmake file must be defined here.
# Most are empty stubs.
####################################################################################################
set_property(GLOBAL PROPERTY COMMANDLINE_KNOWN_FEATURES "")
function(qt_feature feature)
set_property(GLOBAL APPEND PROPERTY COMMANDLINE_KNOWN_FEATURES "${feature}")
endfunction()
macro(defstub name)
function(${name})
endfunction()
endmacro()
defstub(qt_add_qmake_lib_dependency)
defstub(qt_config_compile_test)
defstub(qt_config_compile_test_machine_tuple)
defstub(qt_config_compile_test_x86simd)
defstub(qt_configure_add_report_entry)
defstub(qt_configure_add_summary_build_mode)
defstub(qt_configure_add_summary_build_parts)
defstub(qt_configure_add_summary_build_type_and_config)
defstub(qt_configure_add_summary_entry)
defstub(qt_configure_add_summary_section)
defstub(qt_configure_end_summary_section)
defstub(qt_extra_definition)
defstub(qt_feature_config)
defstub(qt_feature_definition)
defstub(qt_find_package)
defstub(set_package_properties)
####################################################################################################
# Define functions/macros that are called in qt_cmdline.cmake files
####################################################################################################
unset(commandline_custom_handlers)
set(commandline_nr_of_prefixes 0)
set(commandline_nr_of_assignments 0)
macro(qt_commandline_subconfig subconfig)
list(APPEND commandline_subconfigs "${subconfig}")
endmacro()
macro(qt_commandline_custom handler)
list(APPEND commandline_custom_handlers ${handler})
endmacro()
function(qt_commandline_option name)
set(options)
set(oneValueArgs TYPE NAME VALUE)
set(multiValueArgs VALUES MAPPING)
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(commandline_option_${name} "${arg_TYPE}" PARENT_SCOPE)
if(NOT "${arg_NAME}" STREQUAL "")
set(commandline_option_${name}_variable "${arg_NAME}" PARENT_SCOPE)
endif()
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()
endfunction()
function(qt_commandline_prefix arg var)
set(idx ${commandline_nr_of_prefixes})
set(commandline_prefix_${idx} "${arg}" "${var}" PARENT_SCOPE)
math(EXPR n "${commandline_nr_of_prefixes} + 1")
set(commandline_nr_of_prefixes ${n} PARENT_SCOPE)
endfunction()
function(qt_commandline_assignment var internal_var)
set(idx ${commandline_nr_of_assignments})
set(commandline_assignment_${idx} "${var}" "${internal_var}" PARENT_SCOPE)
math(EXPR n "${commandline_nr_of_assignments} + 1")
set(commandline_nr_of_assignments ${n} PARENT_SCOPE)
endfunction()
####################################################################################################
# Load qt_cmdline.cmake files
####################################################################################################
while(commandline_files)
list(POP_FRONT commandline_files commandline_file)
get_filename_component(commandline_file_directory "${commandline_file}" DIRECTORY)
set(configure_file "${commandline_file_directory}/${configure_filename}")
unset(commandline_subconfigs)
include(${configure_file})
include(${commandline_file})
if(commandline_subconfigs)
list(TRANSFORM commandline_subconfigs PREPEND "${commandline_file_directory}/")
list(TRANSFORM commandline_subconfigs APPEND "/${commandline_filename}")
list(PREPEND commandline_files "${commandline_subconfigs}")
endif()
endwhile()
get_property(commandline_known_features GLOBAL PROPERTY COMMANDLINE_KNOWN_FEATURES)
####################################################################################################
# Process the data from the qt_cmdline.cmake files
####################################################################################################
function(qtConfAddNote msg)
message(WARNING "${msg}")
endfunction()
function(qtConfAddError msg)
message(FATAL_ERROR "${msg}")
endfunction()
set_property(GLOBAL PROPERTY CONFIG_INPUTS "")
function(qtConfCommandlineSetInput name val)
if(NOT "${commandline_option_${name}_variable}" STREQUAL "")
set(name "${commandline_option_${name}_variable}")
endif()
if(NOT "${INPUT_${name}}" STREQUAL "")
set(oldval "${INPUT_${name}}")
if("${oldval}" STREQUAL "${val}")
qtConfAddNote("Option '${name}' with value '${val}' was specified twice")
else()
qtConfAddNote("Overriding option '${name}' with '${val}' (was: '${oldval}')")
endif()
endif()
set_property(GLOBAL PROPERTY INPUT_${name} "${val}")
set_property(GLOBAL APPEND PROPERTY CONFIG_INPUTS ${name})
endfunction()
function(qtConfValidateValue 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)
qtConfAddError("Invalid value '${val}' supplied to command line option '${opt}'.")
endfunction()
function(qt_commandline_mapped_enum_value opt key out_var)
unset(result)
set(mapping ${commandline_option_${opt}_mapping})
while(mapping)
list(POP_FRONT mapping mapping_key)
list(POP_FRONT mapping mapping_value)
if(mapping_key STREQUAL key)
set(result "${mapping_value}")
break()
endif()
endwhile()
set(${out_var} "${result}" PARENT_SCOPE)
endfunction()
function(qtConfHasNextCommandlineArg out_var)
get_property(args GLOBAL PROPERTY UNHANDLED_ARGS)
list(LENGTH args n)
if(n GREATER 0)
set(result TRUE)
else()
set(result FALSE)
endif()
set(${out_var} ${result} PARENT_SCOPE)
endfunction()
function(qtConfPeekNextCommandlineArg out_var)
get_property(args GLOBAL PROPERTY UNHANDLED_ARGS)
list(GET args 0 result)
set(${out_var} ${result} PARENT_SCOPE)
endfunction()
function(qtConfGetNextCommandlineArg out_var)
get_property(args GLOBAL PROPERTY UNHANDLED_ARGS)
list(POP_FRONT args result)
set_property(GLOBAL PROPERTY UNHANDLED_ARGS ${args})
set(${out_var} ${result} PARENT_SCOPE)
endfunction()
function(qt_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()
qtConfCommandlineSetInput("${arg}" "${val}")
endfunction()
function(qt_commandline_string arg val nextok)
if(nextok)
qtConfGetNextCommandlineArg(val)
if("${val}" MATCHES "^-")
qtConfAddError("No value supplied to command line options '${opt}'.")
endif()
endif()
qtConfValidateValue("${opt}" "${val}" success)
if(success)
qtConfCommandlineSetInput("${opt}" "${val}")
endif()
endfunction()
function(qt_commandline_optionalString arg val nextok)
if("${val}" STREQUAL "")
if(nextok)
qtConfPeekNextCommandlineArg(val)
endif()
if(val MATCHES "^-.*|[A-Z0-9_+]=.*" OR val STREQUAL "")
set(val "yes")
else()
qtConfGetNextCommandlineArg(val)
endif()
endif()
qtConfValidateValue("${arg}" "${val}" success)
if(success)
qtConfCommandlineSetInput("${arg}" "${val}")
endif()
endfunction()
function(qt_commandline_addString arg val nextok)
if("${val}" STREQUAL "" AND nextok)
qtConfGetNextCommandlineArg(val)
endif()
if(val MATCHES "^-.*" OR val STREQUAL "")
qtConfAddError("No value supplied to command line option '${arg}'.")
endif()
qtConfValidateValue("${arg}" "${val}" success)
if(success)
if(DEFINED command_line_option_${arg}_variable)
set(opt ${command_line_option_${arg}_variable)
endif()
set_property(GLOBAL APPEND PROPERTY "INPUT_${arg}" "${val}")
set_property(GLOBAL APPEND PROPERTY CONFIG_INPUTS ${arg})
endif()
endfunction()
function(qt_commandline_enum arg val nextok)
if("${val}" STREQUAL "")
set(val "yes")
endif()
unset(mapped)
if(DEFINED "commandline_option_${arg}_mapping")
qt_commandline_mapped_enum_value("${arg}" "${val}" mapped)
elseif(DEFINED "commandline_option_${arg}_values")
if(val IN_LIST commandline_option_${arg}_values)
set(mapped ${val})
endif()
endif()
if("${mapped}" STREQUAL "")
qtConfAddError("Invalid value '${val}' supplied to command line option '${arg}'.")
endif()
qtConfCommandlineSetInput("${arg}" "${mapped}")
endfunction()
function(qt_commandline_void arg val nextok)
if(NOT "${val}" STREQUAL "")
qtConfAddError("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()
qtConfCommandlineSetInput("${arg}" "${val}")
endfunction()
function(qt_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 ")")
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()
while(1)
qtConfHasNextCommandlineArg(has_next)
if(NOT has_next)
break()
endif()
qtConfGetNextCommandlineArg(arg)
set(handled FALSE)
foreach(func ${commandline_custom_handlers})
qt_call_function("qt_commandline_${func}" handled "${arg}")
if(handled)
break()
endif()
endforeach()
if(handled)
continue()
endif()
if(arg MATCHES "^([A-Z0-9_]+)=(.*)")
set(lhs "${CMAKE_MATCH_1}")
set(rhs "${CMAKE_MATCH_2}")
math(EXPR n "${commandline_nr_of_assignments} - 1")
foreach(i RANGE ${n})
list(GET commandline_assignment_${i} 0 var)
list(GET commandline_assignment_${i} 1 internal_var)
if(lhs STREQUAL var)
set(handled TRUE)
qtConfCommandlineSetInput("${internal_var}" "${rhs}")
break()
endif()
endforeach()
if(NOT handled)
message(FATAL_ERROR "Assigning unknown variable '${lhs}' on command line.")
endif()
endif()
if(handled)
continue()
endif()
# parse out opt and val
set(nextok FALSE)
if(arg MATCHES "^--?enable-(.*)")
set(opt "${CMAKE_MATCH_1}")
set(val "yes")
elseif(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(opt "${CMAKE_MATCH_1}")
unset(val)
elseif(arg MATCHES "^-(.*)")
set(nextok TRUE)
set(opt "${CMAKE_MATCH_1}")
unset(val)
if(NOT DEFINED commandline_option_${opt} AND opt MATCHES "(qt|system)-(.*)")
set(opt "${CMAKE_MATCH_2}")
set(val "${CMAKE_MATCH_1}")
message("opt: ${opt} val: ${val}")
endif()
else()
qtConfAddError("Invalid command line parameter '${arg}'.")
endif()
set(type ${commandline_option_${opt}})
if("${type}" STREQUAL "")
# No match in the regular options, try matching the prefixes
math(EXPR n "${commandline_nr_of_prefixes} - 1")
foreach(i RANGE ${n})
list(GET commandline_prefix_${i} 0 pfx)
if(arg MATCHES "^-${pfx}(.*)")
list(GET commandline_prefix_${i} 1 opt)
set(val "${CMAKE_MATCH_1}")
set(type addString)
break()
endif()
endforeach()
endif()
# Handle builtin [-no]-feature-xxx
if("${type}" STREQUAL "" AND opt MATCHES "^feature-(.*)")
set(opt "${CMAKE_MATCH_1}")
if(NOT opt IN_LIST commandline_known_features)
qtConfAddError("Enabling/Disabling unknown feature '${opt}'.")
endif()
set(type boolean)
endif()
if("${type}" STREQUAL "")
qtConfAddError("Unknown command line option '${arg}'.")
endif()
if(NOT COMMAND "qt_commandline_${type}")
qtConfAddError("Unknown type '${type}' for command line option '${opt}'.")
endif()
qt_call_function("qt_commandline_${type}" "${opt}" "${val}" "${nextok}")
endwhile()
####################################################################################################
# Translate some of the INPUT_xxx values to CMake arguments
####################################################################################################
# Turn the global properties into proper variables
get_property(config_inputs GLOBAL PROPERTY CONFIG_INPUTS)
list(REMOVE_DUPLICATES config_inputs)
foreach(var ${config_inputs})
get_property(INPUT_${var} GLOBAL PROPERTY INPUT_${var})
endforeach()
macro(drop_input name)
list(REMOVE_ITEM config_inputs ${name})
endmacro()
macro(translate_boolean_input name cmake_var)
if("${INPUT_${name}}" STREQUAL "yes")
push("-D${cmake_var}=ON")
drop_input(${name})
elseif("${INPUT_${name}}" STREQUAL "no")
push("-D${cmake_var}=OFF")
drop_input(${name})
endif()
endmacro()
macro(translate_string_input name cmake_var)
if(DEFINED INPUT_${name})
push("-D${cmake_var}=${INPUT_${name}}")
drop_input(${name})
endif()
endmacro()
macro(translate_path_input name cmake_var)
if(DEFINED INPUT_${name})
set(path "${INPUT_${name}}")
string(REGEX REPLACE "^\"(.*)\"$" "\\1" path "${path}")
file(TO_CMAKE_PATH "${path}" path)
push("-D${cmake_var}=${path}")
drop_input(${name})
endif()
endmacro()
foreach(feature ${commandline_known_features})
qt_feature_normalize_name("${feature}" cmake_feature)
translate_boolean_input(${feature} FEATURE_${cmake_feature})
endforeach()
translate_boolean_input(precompile_header BUILD_WITH_PCH)
translate_boolean_input(ccache QT_USE_CCACHE)
translate_boolean_input(shared BUILD_SHARED_LIBS)
translate_string_input(qt_namespace QT_NAMESPACE)
translate_path_input(prefix CMAKE_INSTALL_PREFIX)
translate_path_input(extprefix CMAKE_STAGING_PREFIX)
foreach(kind bin lib archdata libexec qml data doc translation sysconf examples tests)
string(TOUPPER ${kind} uc_kind)
translate_path_input(${kind}dir INSTALL_${uc_kind}DIR)
endforeach()
translate_path_input(headerdir INSTALL_INCLUDEDIR)
translate_path_input(plugindir INSTALL_PLUGINSDIR)
if(NOT "${INPUT_device}" STREQUAL "")
push("-DQT_QMAKE_TARGET_MKSPEC=devices/${INPUT_device}")
drop_input(device)
endif()
translate_string_input(platform QT_QMAKE_TARGET_MKSPEC)
translate_string_input(xplatform QT_QMAKE_TARGET_MKSPEC)
drop_input(make)
drop_input(nomake)
set(build_parts libs tests examples tools ${INPUT_make})
list(REMOVE_DUPLICATES build_parts)
foreach(part ${INPUT_nomake})
list(REMOVE_ITEM build_parts ${part})
endforeach()
if("tests" IN_LIST build_parts)
push("-DBUILD_TESTING=ON")
else()
push("-DBUILD_TESTING=OFF")
endif()
if("examples" IN_LIST build_parts)
push("-DBUILD_EXAMPLES=ON")
else()
push("-DBUILD_EXAMPLES=OFF")
endif()
if(NOT "tools" IN_LIST build_parts)
qtConfAddWarning("'-nomake tools' is not implemented yet.")
endif()
drop_input(debug)
drop_input(release)
drop_input(debug_and_release)
drop_input(force_debug_info)
unset(build_configs)
if(INPUT_debug)
set(build_configs Debug)
elseif("${INPUT_debug}" STREQUAL "no")
set(build_configs Release)
elseif(INPUT_debug_and_release)
set(build_configs Debug Release)
endif()
if(INPUT_force_debug_info)
list(TRANSFORM build_configs REPLACE "^Release$" "RelWithDebInfo")
endif()
list(LENGTH build_configs nr_of_build_configs)
if(nr_of_build_configs EQUAL 1)
push("-DCMAKE_BUILD_TYPE=${build_configs}")
elseif(nr_of_build_configs GREATER 1)
set(multi_config ON)
string(REPLACE ";" "\\;" escaped_build_configs "${build_configs}")
# We must not use the push macro here to avoid variable expansion.
# That would destroy our escaping.
list(APPEND cmake_args "-DCMAKE_CONFIGURATION_TYPES=${escaped_build_configs}")
endif()
drop_input(device-option)
if(INPUT_device-option)
unset(escaped_device_options)
list(JOIN INPUT_device-option "\\;" escaped_device_options)
list(APPEND cmake_args "-DQT_QMAKE_DEVICE_OPTIONS=${escaped_device_options}")
endif()
drop_input(rpaths)
if(INPUT_rpaths)
unset(escaped_paths)
list(JOIN INPUT_rpaths "\\;" escaped_paths)
list(APPEND cmake_args "-DQT_EXTRA_RPATHS=${escaped_paths}")
endif()
foreach(input ${config_inputs})
push("-DINPUT_${input}=${INPUT_${input}}")
endforeach()
if(NOT generator AND auto_detect_generator)
find_program(ninja ninja)
if(ninja)
set(generator Ninja)
if(multi_config)
string(APPEND generator " Multi-Config")
endif()
else()
if(CMAKE_HOST_UNIX)
set(generator "Unix Makefiles")
elseif(CMAKE_HOST_WINDOWS)
find_program(msvc_compiler cl.exe)
if(msvc_compiler)
set(generator "NMake Makefiles")
find_program(jom jom)
if(jom)
string(APPEND generator " JOM")
endif()
else()
set(generator "MinGW Makefiles")
endif()
endif()
endif()
endif()
if(generator)
push(-G "${generator}")
endif()
push("${source_dir}")
execute_process(COMMAND "${CMAKE_COMMAND}" ${cmake_args}
COMMAND_ECHO STDOUT
RESULT_VARIABLE exit_code)
if(NOT exit_code EQUAL 0)
message(FATAL_ERROR "CMake exited with code ${exit_code}.")
endif()