diff options
-rw-r--r-- | cmake/QtBaseConfigureTests.cmake | 2 | ||||
-rw-r--r-- | cmake/QtBuildInformation.cmake | 327 | ||||
-rw-r--r-- | cmake/QtBuildInternals/QtBuildInternalsConfig.cmake | 2 | ||||
-rw-r--r-- | cmake/QtFeature.cmake | 7 | ||||
-rw-r--r-- | cmake/QtSetup.cmake | 1 | ||||
-rwxr-xr-x | util/cmake/configurejson2cmake.py | 126 |
6 files changed, 460 insertions, 5 deletions
diff --git a/cmake/QtBaseConfigureTests.cmake b/cmake/QtBaseConfigureTests.cmake index 0d6bb1407b..0844c8df20 100644 --- a/cmake/QtBaseConfigureTests.cmake +++ b/cmake/QtBaseConfigureTests.cmake @@ -82,7 +82,7 @@ function(qt_run_config_test_architecture) set(QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT ${QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT} CACHE INTERNAL "Test variables that should be exported") list(JOIN _sub_architecture " " subarch_summary) - message(STATUS "Building for: ${QT_QMAKE_TARGET_MKSPEC} (${TEST_architecture_arch}, CPU features: ${subarch_summary})") + set_property(GLOBAL PROPERTY qt_configure_subarch_summary "${subarch_summary}") endfunction() diff --git a/cmake/QtBuildInformation.cmake b/cmake/QtBuildInformation.cmake index 097192b2ab..cf783168f0 100644 --- a/cmake/QtBuildInformation.cmake +++ b/cmake/QtBuildInformation.cmake @@ -1,11 +1,13 @@ function(qt_print_feature_summary) include(FeatureSummary) + # Show which packages were found. feature_summary(WHAT PACKAGES_FOUND REQUIRED_PACKAGES_NOT_FOUND RECOMMENDED_PACKAGES_NOT_FOUND OPTIONAL_PACKAGES_NOT_FOUND RUNTIME_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) + qt_configure_print_summary() endfunction() function(qt_print_build_instructions) @@ -33,3 +35,328 @@ function(qt_print_build_instructions) message("\nIf reconfiguration fails for some reason, try to remove 'CMakeCache.txt' \ from the build directory \n") endfunction() + +function(qt_configure_print_summary) + # Evaluate all recorded commands. + qt_configure_eval_commands() + + # Show Qt-specific configure summary and any notes, wranings, etc. + if(__qt_configure_reports) + message("Configure summary:\n${__qt_configure_reports}") + endif() + if(__qt_configure_notes) + message("${__qt_configure_notes}") + endif() + if(__qt_configure_warnings) + message("${__qt_configure_warnings}") + endif() + if(__qt_configure_errors) + message("${__qt_configure_errors}") + endif() + message("") + if(__qt_configure_an_error_occurred) + message(FATAL_ERROR "Check the configuration messages for an error that has occurred.") + endif() +endfunction() + +# Takes a list of arguments, and saves them to be evaluated at the end of the configuration +# phase when the configuration summary is shown. +function(qt_configure_record_command) + # Don't record commands when only evaluating features of a configure.cmake file. + if(__QtFeature_only_evaluate_features) + return() + endif() + + get_property(command_count GLOBAL PROPERTY qt_configure_command_count) + + if(NOT DEFINED command_count) + set(command_count 0) + else() + math(EXPR command_count "${command_count}+1") + endif() + + set_property(GLOBAL PROPERTY qt_configure_command_${command_count} "${ARGV}") + set_property(GLOBAL PROPERTY qt_configure_command_count "${command_count}") +endfunction() + +function(qt_configure_eval_commands) + get_property(command_count GLOBAL PROPERTY qt_configure_command_count) + if(NOT command_count) + set(command_count 0) + endif() + set(command_index 0) + + while(command_index LESS command_count) + get_property(command_args GLOBAL PROPERTY qt_configure_command_${command_index}) + if(NOT command_args) + message(FATAL_ERROR + "Empty arguments encountered while processing qt configure reports.") + endif() + + list(POP_FRONT command_args command_name) + if(command_name STREQUAL ADD_SUMMARY_SECTION) + qt_configure_process_add_summary_section(${command_args}) + elseif(command_name STREQUAL END_SUMMARY_SECTION) + qt_configure_process_end_summary_section(${command_args}) + elseif(command_name STREQUAL ADD_REPORT_ENTRY) + qt_configure_process_add_report_entry(${command_args}) + elseif(command_name STREQUAL ADD_SUMMARY_ENTRY) + qt_configure_process_add_summary_entry(${command_args}) + elseif(command_name STREQUAL ADD_BUILD_TYPE_AND_CONFIG) + qt_configure_process_add_summary_build_type_and_config(${command_args}) + elseif(command_name STREQUAL ADD_BUILD_MODE) + qt_configure_process_add_summary_build_mode(${command_args}) + endif() + + math(EXPR command_index "${command_index}+1") + endwhile() + + # Propagate content to parent. + set(__qt_configure_reports "${__qt_configure_reports}" PARENT_SCOPE) + set(__qt_configure_notes "${__qt_configure_notes}" PARENT_SCOPE) + set(__qt_configure_warnings "${__qt_configure_warnings}" PARENT_SCOPE) + set(__qt_configure_errors "${__qt_configure_errors}" PARENT_SCOPE) + set(__qt_configure_an_error_occurred "${__qt_configure_an_error_occurred}" PARENT_SCOPE) +endfunction() + +macro(qt_configure_add_report message) + string(APPEND __qt_configure_reports "\n${message}") + set(__qt_configure_reports "${__qt_configure_reports}" PARENT_SCOPE) +endmacro() + +macro(qt_configure_add_report_padded label message) + qt_configure_get_padded_string("${__qt_configure_indent}${label}" "${message}" padded_message) + string(APPEND __qt_configure_reports "\n${padded_message}") + set(__qt_configure_reports "${__qt_configure_reports}" PARENT_SCOPE) +endmacro() + +function(qt_configure_get_padded_string label value out_var) + set(pad_string "........................................") + string(LENGTH "${label}" label_len) + string(LENGTH "${pad_string}" pad_len) + math(EXPR pad_len "${pad_len}-${label_len}") + if(pad_len LESS "0") + set(pad_len "0") + endif() + string(SUBSTRING "${pad_string}" 0 "${pad_len}" pad_string) + set(output "${label} ${pad_string} ${value}") + set("${out_var}" "${output}" PARENT_SCOPE) +endfunction() + +function(qt_configure_add_summary_entry) + qt_configure_record_command(ADD_SUMMARY_ENTRY ${ARGV}) +endfunction() + +function(qt_configure_process_add_summary_entry) + qt_parse_all_arguments(arg "qt_configure_add_summary_entry" + "" + "ARGS;TYPE;MESSAGE" "CONDITION" ${ARGN}) + + if(NOT arg_TYPE) + set(arg_TYPE "feature") + endif() + + if(NOT "${arg_CONDITION}" STREQUAL "") + qt_evaluate_config_expression(condition_result ${arg_CONDITION}) + if(NOT condition_result) + return() + endif() + endif() + + if(arg_TYPE STREQUAL "firstAvailableFeature") + set(first_feature_found FALSE) + set(message "") + string(REPLACE " " ";" args_list "${arg_ARGS}") + foreach(feature ${args_list}) + qt_feature_normalize_name("${feature}" feature) + if(NOT DEFINED QT_FEATURE_${feature}) + message(FATAL_ERROR "Asking for a report on undefined feature ${feature}.") + endif() + if(QT_FEATURE_${feature}) + set(first_feature_found TRUE) + set(message "${QT_FEATURE_LABEL_${feature}}") + break() + endif() + endforeach() + + if(NOT first_feature_found) + set(message "<none>") + endif() + qt_configure_add_report_padded("${arg_MESSAGE}" "${message}") + elseif(arg_TYPE STREQUAL "featureList") + set(available_features "") + string(REPLACE " " ";" args_list "${arg_ARGS}") + + foreach(feature ${args_list}) + qt_feature_normalize_name("${feature}" feature) + if(NOT DEFINED QT_FEATURE_${feature}) + message(FATAL_ERROR "Asking for a report on undefined feature ${feature}.") + endif() + if(QT_FEATURE_${feature}) + list(APPEND available_features "${QT_FEATURE_LABEL_${feature}}") + endif() + endforeach() + + if(NOT available_features) + set(message "<none>") + else() + list(JOIN available_features " " message) + endif() + qt_configure_add_report_padded("${arg_MESSAGE}" "${message}") + elseif(arg_TYPE STREQUAL "feature") + qt_feature_normalize_name("${arg_ARGS}" feature) + + set(label "${QT_FEATURE_LABEL_${feature}}") + + if(NOT label) + set(label "${feature}") + endif() + + if(QT_FEATURE_${feature}) + set(value "yes") + else() + set(value "no") + endif() + + qt_configure_add_report_padded("${label}" "${value}") + endif() +endfunction() + +function(qt_configure_add_summary_build_type_and_config) + qt_configure_record_command(ADD_BUILD_TYPE_AND_CONFIG ${ARGV}) +endfunction() + +function(qt_configure_process_add_summary_build_type_and_config) + get_property(subarch_summary GLOBAL PROPERTY qt_configure_subarch_summary) + set(message + "Building for: ${QT_QMAKE_TARGET_MKSPEC} (${TEST_architecture_arch}, CPU features: ${subarch_summary})") + qt_configure_add_report("${message}") + + set(message "Compiler: ") + if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + string(APPEND message "clang (Apple)") + elseif(CLANG) + string(APPEND message "clang") + elseif(ICC) + string(APPEND message "intel_icc") + elseif(QCC) + string(APPEND message "rim_qcc") + elseif(GCC) + string(APPEND message "gcc") + elseif(MSVC) + string(APPEND message "msvc") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GHS") + string(APPEND message "ghs") + else() + string(APPEND message "unknown (${CMAKE_CXX_COMPILER_ID})") + endif() + string(APPEND message " ${CMAKE_CXX_COMPILER_VERSION}") + qt_configure_add_report("${message}") +endfunction() + +function(qt_configure_add_summary_build_mode) + qt_configure_record_command(ADD_BUILD_MODE ${ARGV}) +endfunction() + +function(qt_configure_process_add_summary_build_mode label) + set(message "") + if(DEFINED CMAKE_BUILD_TYPE) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + string(APPEND message "debug") + elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + string(APPEND message "release") + elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + string(APPEND message "release (with debug info)") + else() + string(APPEND message "${CMAKE_BUILD_TYPE}") + endif() + elseif(DEFINED CMAKE_CONFIGURATION_TYPES) + if("Release" IN_LIST CMAKE_CONFIGURATION_TYPES + AND "Debug" IN_LIST CMAKE_CONFIGURATION_TYPES) + string(APPEND message "debug and release") + elseif("RelWithDebInfo" IN_LIST CMAKE_CONFIGURATION_TYPES + AND "Debug" IN_LIST CMAKE_CONFIGURATION_TYPES) + string(APPEND message "debug and release (with debug info)") + else() + string(APPEND message "${CMAKE_CONFIGURATION_TYPES}") + endif() + endif() + + qt_configure_add_report_padded("${label}" "${message}") +endfunction() + +function(qt_configure_add_summary_section) + qt_configure_record_command(ADD_SUMMARY_SECTION ${ARGV}) +endfunction() + +function(qt_configure_process_add_summary_section) + qt_parse_all_arguments(arg "qt_configure_add_summary_section" + "" "NAME" "" ${ARGN}) + + qt_configure_add_report("${__qt_configure_indent}${arg_NAME}:") + if(NOT DEFINED __qt_configure_indent) + set(__qt_configure_indent " " PARENT_SCOPE) + else() + set(__qt_configure_indent "${__qt_configure_indent} " PARENT_SCOPE) + endif() +endfunction() + +function(qt_configure_end_summary_section) + qt_configure_record_command(END_SUMMARY_SECTION ${ARGV}) +endfunction() + +function(qt_configure_process_end_summary_section) + string(LENGTH "${__qt_configure_indent}" indent_len) + if(indent_len GREATER_EQUAL 2) + string(SUBSTRING "${__qt_configure_indent}" 2 -1 __qt_configure_indent) + set(__qt_configure_indent "${__qt_configure_indent}" PARENT_SCOPE) + endif() +endfunction() + +function(qt_configure_add_report_entry) + qt_configure_record_command(ADD_REPORT_ENTRY ${ARGV}) +endfunction() + +function(qt_configure_process_add_report_entry) + qt_parse_all_arguments(arg "qt_configure_add_report_entry" + "" + "TYPE;MESSAGE" "CONDITION" ${ARGN}) + + set(possible_types NOTE WARNING ERROR FATAL_ERROR) + if(NOT "${arg_TYPE}" IN_LIST possible_types) + message(FATAL_ERROR "qt_configure_add_report_entry: '${arg_TYPE}' is not a valid type.") + endif() + + if(NOT arg_MESSAGE) + message(FATAL_ERROR "qt_configure_add_report_entry: Empty message given.") + endif() + + if(arg_TYPE STREQUAL "NOTE") + set(contents_var "__qt_configure_notes") + set(prefix "Note: ") + elseif(arg_TYPE STREQUAL "WARNING") + set(contents_var "__qt_configure_warnings") + set(prefix "WARNING: ") + elseif(arg_TYPE STREQUAL "ERROR") + set(contents_var "__qt_configure_errors") + set(prefix "ERROR: ") + elseif(arg_TYPE STREQUAL "FATAL_ERROR") + set(contents_var "__qt_configure_errors") + set(prefix "FATAL ERROR: ") + endif() + + if(NOT "${arg_CONDITION}" STREQUAL "") + qt_evaluate_config_expression(condition_result ${arg_CONDITION}) + endif() + + if("${arg_CONDITION}" STREQUAL "" OR condition_result) + set(new_report "${prefix}${arg_MESSAGE}") + string(APPEND "${contents_var}" "\n${new_report}") + + if(arg_TYPE STREQUAL "ERROR" OR arg_TYPE STREQUAL "FATAL_ERROR") + set(__qt_configure_an_error_occurred "TRUE" PARENT_SCOPE) + endif() + endif() + + set("${contents_var}" "${${contents_var}}" PARENT_SCOPE) +endfunction() diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake index b44ec7f435..c945fd124a 100644 --- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake +++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake @@ -94,8 +94,6 @@ macro(qt_build_repo_begin) endmacro() macro(qt_build_repo_end) - include(QtBuildInformation) - if(NOT QT_BUILD_STANDALONE_TESTS) # Delayed actions on some of the Qt targets: include(QtPostProcess) diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake index 7b852b3c2e..34ae83b55d 100644 --- a/cmake/QtFeature.cmake +++ b/cmake/QtFeature.cmake @@ -14,6 +14,9 @@ function(qt_feature_module_begin) if ("${arg_PRIVATE_FILE}" STREQUAL "") message(FATAL_ERROR "qt_feature_begin_module needs a PRIVATE_FILE name!") endif() + set(__QtFeature_only_evaluate_features OFF PARENT_SCOPE) + else() + set(__QtFeature_only_evaluate_features ON PARENT_SCOPE) endif() set(__QtFeature_library "${arg_LIBRARY}" PARENT_SCOPE) @@ -278,6 +281,9 @@ function(qt_evaluate_feature feature) qt_feature_set_cache_value(cache "${feature}" "${emit_if}" "${result}" "${arg_LABEL}") qt_feature_set_value("${feature}" "${cache}" "${emit_if}" "${condition}" "${arg_LABEL}") + + # Store each feature's label for summary info. + set(QT_FEATURE_LABEL_${feature} "${arg_LABEL}" CACHE INTERNAL "") endfunction() function(qt_feature_config feature config_var_name) @@ -581,6 +587,7 @@ function(qt_feature_module_end) unset(__QtFeature_define_definitions PARENT_SCOPE) unset(__QtFeature_custom_enabled_features PARENT_SCOPE) unset(__QtFeature_custom_disabled_features PARENT_SCOPE) + unset(__QtFeature_only_evaluate_features PARENT_SCOPE) endfunction() function(qt_feature_copy_global_config_features_to_core target) diff --git a/cmake/QtSetup.cmake b/cmake/QtSetup.cmake index 4b5e632847..e8f1caf2fa 100644 --- a/cmake/QtSetup.cmake +++ b/cmake/QtSetup.cmake @@ -148,6 +148,7 @@ endif() include(QtBuild) ## Qt Feature support: +include(QtBuildInformation) include(QtFeature) ## Compiler optimization flags: diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index ad183c1906..390042760b 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -255,7 +255,7 @@ def map_condition(condition): mapped_features = {"gbm": "gbm_FOUND", "system-xcb": "ON"} # Turn foo != "bar" into (NOT foo STREQUAL 'bar') - condition = re.sub(r"(.+)\s*!=\s*('.+')", "(! \\1 == \\2)", condition) + condition = re.sub(r"([^ ]+)\s*!=\s*('.*?')", "(! \\1 == \\2)", condition) condition = condition.replace("!", "NOT ") condition = condition.replace("&&", " AND ") @@ -753,7 +753,7 @@ def parseTest(ctx, test, data, cm_fh): print(f" XXXX UNHANDLED TEST TYPE {data['type']} in test description") -def parseFeature(ctx, feature, data, cm_fh): +def get_feature_mapping(): # This is *before* the feature name gets normalized! So keep - and + chars, etc. feature_mapping = { "alloc_h": None, # handled by alloc target @@ -848,7 +848,11 @@ def parseFeature(ctx, feature, data, cm_fh): "webp": {"condition": "QT_FEATURE_imageformatplugin AND WrapWebP_FOUND"}, "xkbcommon-system": None, # another system library, just named a bit different from the rest } + return feature_mapping + +def parseFeature(ctx, feature, data, cm_fh): + feature_mapping = get_feature_mapping() mapping = feature_mapping.get(feature, {}) if mapping is None: @@ -1087,6 +1091,110 @@ def parseFeature(ctx, feature, data, cm_fh): cm_fh.write(")\n") +def processSummaryHelper(ctx, entries, cm_fh): + for entry in entries: + if isinstance(entry, str): + name = entry + cm_fh.write(f'qt_configure_add_summary_entry(ARGS "{name}")\n') + elif "type" in entry \ + and entry["type"] in ["feature", "firstAvailableFeature", "featureList"]: + function_args = [] + entry_type = entry["type"] + + if entry_type in ["firstAvailableFeature", "featureList"]: + feature_mapping = get_feature_mapping() + unhandled_feature = False + for feature_name, value in feature_mapping.items(): + # Skip entries that mention a feature which is + # skipped by configurejson2cmake in the feature + # mapping. This is not ideal, but prevents errors at + # CMake configuration time. + if not value and f"{feature_name}" in entry["args"]: + unhandled_feature = True + break + + if unhandled_feature: + print(f" XXXX UNHANDLED FEATURE in SUMMARY TYPE {entry}.") + continue + + if entry_type != "feature": + function_args.append(lineify("TYPE", entry_type)) + if "args" in entry: + args = entry["args"] + function_args.append(lineify("ARGS", args)) + if "message" in entry: + message = entry["message"] + function_args.append(lineify("MESSAGE", message)) + if "condition" in entry: + condition = map_condition(entry["condition"]) + function_args.append(lineify("CONDITION", condition, quote=False)) + entry_args_string = "".join(function_args) + cm_fh.write(f'qt_configure_add_summary_entry(\n{entry_args_string})\n') + elif "type" in entry and entry["type"] == "buildTypeAndConfig": + cm_fh.write(f'qt_configure_add_summary_build_type_and_config()\n') + elif "type" in entry and entry["type"] == "buildMode": + message = entry["message"] + cm_fh.write(f'qt_configure_add_summary_build_mode({message})\n') + elif "section" in entry: + section = entry["section"] + cm_fh.write(f'qt_configure_add_summary_section(NAME "{section}")\n') + processSummaryHelper(ctx, entry["entries"], cm_fh) + cm_fh.write(f'qt_configure_end_summary_section() # end of "{section}" section\n') + else: + print(f" XXXX UNHANDLED SUMMARY TYPE {entry}.") + + +def processReportHelper(ctx, entries, cm_fh): + feature_mapping = get_feature_mapping() + + for entry in entries: + if isinstance(entry, dict): + entry_args = [] + if "type" not in entry: + print(f" XXXX UNHANDLED REPORT TYPE missing type in {entry}.") + continue + + report_type = entry["type"] + if report_type not in ["note", "warning", "error"]: + print(f" XXXX UNHANDLED REPORT TYPE unknown type in {entry}.") + continue + + report_type = report_type.upper() + entry_args.append(lineify("TYPE", report_type, quote=False)) + message = entry["message"] + + # Replace semicolons, qt_parse_all_arguments can't handle + # them due to an escaping bug in CMake regarding escaping + # macro arguments. + # https://gitlab.kitware.com/cmake/cmake/issues/19972 + message = message.replace(";", ",") + + entry_args.append(lineify("MESSAGE", message)) + # Need to overhaul everything to fix conditions. + if "condition" in entry: + condition = entry["condition"] + + unhandled_condition = False + for feature_name, value in feature_mapping.items(): + # Skip reports that mention a feature which is + # skipped by configurejson2cmake in the feature + # mapping. This is not ideal, but prevents errors at + # CMake configuration time. + if not value and f"features.{feature_name}" in condition: + unhandled_condition = True + break + + if unhandled_condition: + print(f" XXXX UNHANDLED CONDITION in REPORT TYPE {entry}.") + continue + condition = map_condition(condition) + entry_args.append(lineify("CONDITION", condition, quote=False)) + entry_args_string = "".join(entry_args) + cm_fh.write(f'qt_configure_add_report_entry(\n{entry_args_string})\n') + else: + print(f" XXXX UNHANDLED REPORT TYPE {entry}.") + + def processInputs(ctx, data, cm_fh): print(" inputs:") if "commandline" not in data: @@ -1128,6 +1236,18 @@ def processLibraries(ctx, data, cm_fh): parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set) +def processReports(ctx, data, cm_fh): + if "summary" in data: + print(" summary:") + processSummaryHelper(ctx, data["summary"], cm_fh) + if "report" in data: + print(" report:") + processReportHelper(ctx, data["report"], cm_fh) + if "earlyReport" in data: + print(" earlyReport:") + processReportHelper(ctx, data["earlyReport"], cm_fh) + + def processSubconfigs(path, ctx, data): assert ctx is not None if "subconfigs" in data: @@ -1162,6 +1282,8 @@ def processJson(path, ctx, data): processFeatures(ctx, data, cm_fh) + processReports(ctx, data, cm_fh) + if ctx.get("module") == "global": cm_fh.write( '\nqt_extra_definition("QT_VERSION_STR" "\\"${PROJECT_VERSION}\\"" PUBLIC)\n' |