diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-08-28 15:15:50 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-12-03 13:37:56 +0000 |
commit | 461020a86aa5822325edba9ec565db39e5df8092 (patch) | |
tree | bb772bfe8f19397bd24ba9b5558cf5c29ca64d1d | |
parent | 55a15a1c1b93d36d705fc69e44b5c806b807dd55 (diff) |
Export non-private and non-public features and CONFIG values
Before we only exported features that had outputType PUBLIC or PRIVATE
on the various "QT_ENABLED_PUBLIC_FEATURES" target properties.
Now we also export features that have output type privateConfig,
publicConfig and publicQtConfig.
The new properties names are:
- QT_QMAKE_PUBLIC_CONFIG for outputType == publicConfig
- QT_QMAKE_PRIVATE_CONFIG for outputType == privateConfig
- QT_QMAKE_PUBLIC_QT_CONFIG for outputType == publicQtConfig
These need to be exported for 2 reasons:
- other modules that need to check the config values
- in preparation for generating proper qmake .prl and .pri
information for each module
Note that the config values are now considered actual features
when doing condition evaluation. So if there exists a feature "ssse3"
with outputType privateConfig, its enabled state can be checked via
QT_FEATURE_ssse3 in consuming modules (but not in the declaring
module).
These config values are also placed in the respective
QT_ENABLED_PUBLIC_FEATURES, QT_ENABLED_PRIVATE_FEATURES properties
when exporting a target, so the properties will now contain both
features and config values.
In order to make this work, feature name normalization has to happen
at CMake time, rather than done by the python script.
This means that features like "developer-build" need to retain the
dash in the qt_feature(), qt_feature_definition() and
qt_feature_config() calls, rather than generating "developer_build"
as the script did before.
The normalization is done at CMake time. Feature conditions,
CMake code, and -DFEATURE_foo=bar options passed on the command line
should still use the underscore version, but the original name is used
for the QT_QMAKE_PUBLIC_CONFIG properties.
Note that "c++11" like features are normalized to "cxx11".
Implementation wise, the configurejson2cmake script is adjusted to
parse these new output types.
Also QtBuild and QtFeature are adjusted to save the config values
in properties, and re-export them from GlobalConfig to Core.
Task-number: QTBUG-75666
Task-number: QTBUG-78178
Change-Id: Ibd4b152e372bdf2d09ed117644f2f2ac53ec5e75
Reviewed-by: Qt CMake Build Bot
Reviewed-by: Leander Beernaert <leander.beernaert@qt.io>
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
-rw-r--r-- | cmake/QtFeature.cmake | 122 | ||||
-rwxr-xr-x | util/cmake/configurejson2cmake.py | 90 |
2 files changed, 193 insertions, 19 deletions
diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake index b3e5eb9b8a..faca42635c 100644 --- a/cmake/QtFeature.cmake +++ b/cmake/QtFeature.cmake @@ -23,10 +23,26 @@ function(qt_feature_module_begin) set(__QtFeature_private_extra "" PARENT_SCOPE) set(__QtFeature_public_extra "" PARENT_SCOPE) + set(__QtFeature_config_definitions "" PARENT_SCOPE) + set(__QtFeature_define_definitions "" PARENT_SCOPE) endfunction() +function(qt_feature_normalize_name name out_var) + # Normalize the feature name to something CMake can deal with. + if(name MATCHES "c\\+\\+") + string(REGEX REPLACE "[^a-zA-Z0-9_]" "x" name "${name}") + else() + string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" name "${name}") + endif() + set(${out_var} "${name}" PARENT_SCOPE) +endfunction() + function(qt_feature feature) + set(original_name "${feature}") + qt_feature_normalize_name("${feature}" feature) + set_property(GLOBAL PROPERTY QT_FEATURE_ORIGINAL_NAME_${feature} "${original_name}") + qt_parse_all_arguments(arg "qt_feature" "PRIVATE;PUBLIC" "LABEL;PURPOSE;SECTION;" "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF" ${ARGN}) @@ -260,7 +276,63 @@ function(qt_evaluate_feature feature) qt_feature_set_value("${feature}" "${cache}" "${emit_if}" "${condition}" "${arg_LABEL}") endfunction() +function(qt_feature_config feature config_var_name) + qt_feature_normalize_name("${feature}" feature) + qt_parse_all_arguments(arg "qt_feature_config" "NEGATE" "NAME" "" ${ARGN}) + + # Store all the config related info in a unique variable key. + set(key_name "_QT_FEATURE_CONFIG_DEFINITION_${feature}_${config_var_name}") + set(${key_name} "FEATURE;${feature};CONFIG_VAR_NAME;${config_var_name};${ARGN}" PARENT_SCOPE) + + # Store the key for later evaluation. + list(APPEND __QtFeature_config_definitions "${key_name}") + + set(__QtFeature_config_definitions ${__QtFeature_config_definitions} PARENT_SCOPE) +endfunction() + +function(qt_evaluate_qmake_config_values key) + if(NOT DEFINED ${key}) + qt_debug_print_variables(DEDUP MATCH "^_QT_FEATURE_CONFIG_DEFINITION") + message(FATAL_ERROR + "Attempting to evaluate feature config ${key} but its definition is missing. ") + endif() + + cmake_parse_arguments(arg + "NEGATE" + "FEATURE;NAME;CONFIG_VAR_NAME" + "" ${${key}}) + + set(expected "NOT") + if (arg_NEGATE) + set(expected "") + endif() + + # If no custom name is specified, then the config value is the same as the feature name. + if(NOT arg_NAME) + set(arg_NAME "${arg_FEATURE}") + endif() + + # The feature condition is false, there is no need to export any config values. + if(${expected} ${QT_FEATURE_${arg_FEATURE}}) + return() + endif() + + if(arg_CONFIG_VAR_NAME STREQUAL "QMAKE_PUBLIC_CONFIG") + list(APPEND __QtFeature_qmake_public_config "${arg_NAME}") + set(__QtFeature_qmake_public_config "${__QtFeature_qmake_public_config}" PARENT_SCOPE) + endif() + if(arg_CONFIG_VAR_NAME STREQUAL "QMAKE_PRIVATE_CONFIG") + list(APPEND __QtFeature_qmake_private_config "${arg_NAME}") + set(__QtFeature_qmake_private_config "${__QtFeature_qmake_private_config}" PARENT_SCOPE) + endif() + if(arg_CONFIG_VAR_NAME STREQUAL "QMAKE_PUBLIC_QT_CONFIG") + list(APPEND __QtFeature_qmake_public_qt_config "${arg_NAME}") + set(__QtFeature_qmake_public_qt_config "${__QtFeature_qmake_public_qt_config}" PARENT_SCOPE) + endif() +endfunction() + function(qt_feature_definition feature name) + qt_feature_normalize_name("${feature}" feature) qt_parse_all_arguments(arg "qt_feature_definition" "NEGATE" "VALUE" "" ${ARGN}) # Store all the define related info in a unique variable key. @@ -389,6 +461,11 @@ function(qt_feature_module_end) endif() endforeach() + foreach(key ${__QtFeature_config_definitions}) + qt_evaluate_qmake_config_values(${key}) + unset(${key} PARENT_SCOPE) + endforeach() + foreach(key ${__QtFeature_define_definitions}) qt_evaluate_feature_definition(${key}) unset(${key} PARENT_SCOPE) @@ -428,7 +505,7 @@ function(qt_feature_module_end) set(propertyPrefix "INTERFACE_") else() set(propertyPrefix "") - set_target_properties("${target}" PROPERTIES EXPORT_PROPERTIES "QT_ENABLED_PUBLIC_FEATURES;QT_DISABLED_PUBLIC_FEATURES;QT_ENABLED_PRIVATE_FEATURES;QT_DISABLED_PRIVATE_FEATURES;MODULE_PLUGIN_TYPES;QT_PLUGINS") + set_target_properties("${target}" PROPERTIES EXPORT_PROPERTIES "QT_ENABLED_PUBLIC_FEATURES;QT_DISABLED_PUBLIC_FEATURES;QT_ENABLED_PRIVATE_FEATURES;QT_DISABLED_PRIVATE_FEATURES;MODULE_PLUGIN_TYPES;QT_PLUGINS;QT_QMAKE_PUBLIC_CONFIG;QT_QMAKE_PRIVATE_CONFIG;QT_QMAKE_PUBLIC_QT_CONFIG") endif() foreach(visibility public private) string(TOUPPER "${visibility}" capitalVisibility) @@ -438,6 +515,36 @@ function(qt_feature_module_end) set_property(TARGET "${target}" PROPERTY ${propertyPrefix}QT_${capitalState}_${capitalVisibility}_FEATURES "${${state}_${visibility}_features}") endforeach() endforeach() + + set_property(TARGET "${target}" + PROPERTY ${propertyPrefix}QT_QMAKE_PUBLIC_CONFIG + "${__QtFeature_qmake_public_config}") + set_property(TARGET "${target}" + PROPERTY ${propertyPrefix}QT_QMAKE_PRIVATE_CONFIG + "${__QtFeature_qmake_private_config}") + set_property(TARGET "${target}" + PROPERTY ${propertyPrefix}QT_QMAKE_PUBLIC_QT_CONFIG + "${__QtFeature_qmake_public_qt_config}") + + # Config values were the old-school features before actual configure.json features were + # implemented. Therefore "CONFIG+=foo" values should be considered features as well, + # so that CMake can find them when building qtmultimedia for example. + if(__QtFeature_qmake_public_config) + set_property(TARGET "${target}" + APPEND PROPERTY ${propertyPrefix}QT_ENABLED_PUBLIC_FEATURES + ${__QtFeature_qmake_public_config}) + endif() + if(__QtFeature_qmake_private_config) + set_property(TARGET "${target}" + APPEND PROPERTY ${propertyPrefix}QT_ENABLED_PRIVATE_FEATURES + ${__QtFeature_qmake_private_config}) + endif() + if(__QtFeature_qmake_public_qt_config) + set_property(TARGET "${target}" + APPEND PROPERTY ${propertyPrefix}QT_ENABLED_PUBLIC_FEATURES + ${__QtFeature_qmake_public_qt_config}) + endif() + qt_feature_copy_global_config_features_to_core(${target}) endif() @@ -478,6 +585,19 @@ function(qt_feature_copy_global_config_features_to_core target) set_property(TARGET Core PROPERTY ${core_property_name} ${total_values}) endforeach() endforeach() + + set(config_property_names + QT_QMAKE_PUBLIC_CONFIG QT_QMAKE_PRIVATE_CONFIG QT_QMAKE_PUBLIC_QT_CONFIG ) + foreach(property_name ${config_property_names}) + set(core_property_name "${property_name}") + set(global_property_name "INTERFACE_${core_property_name}") + + get_property(core_values TARGET Core PROPERTY ${core_property_name}) + get_property(global_values TARGET GlobalConfig PROPERTY ${global_property_name}) + + set(total_values ${core_values} ${global_values}) + set_property(TARGET Core PROPERTY ${core_property_name} ${total_values}) + endforeach() endif() endfunction() diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 481701eaed..f986b65acb 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -803,22 +803,16 @@ def parseFeature(ctx, feature, data, cm_fh): negativeFeature = False # #define QT_NO_featurename in public header internalFeature = False # No custom or QT_FEATURE_ defines publicDefine = False # #define MY_CUSTOM_DEFINE in public header + publicConfig = False # add to CONFIG in public pri file + privateConfig = False # add to CONFIG in private pri file + publicQtConfig = False # add to QT_CONFIG in public pri file for o in output: outputType = o - outputArgs = {} if isinstance(o, dict): outputType = o["type"] - outputArgs = o - if outputType in [ - "varAssign", - "varAppend", - "varRemove", - "publicQtConfig", - "privateConfig", - "publicConfig", - ]: + if outputType in ["varAssign", "varAppend", "varRemove"]: continue elif outputType == "define": publicDefine = True @@ -830,15 +824,32 @@ def parseFeature(ctx, feature, data, cm_fh): privateFeature = True elif outputType == "internalFeature": internalFeature = True + elif outputType == "publicConfig": + publicConfig = True + elif outputType == "privateConfig": + privateConfig = True + elif outputType == "publicQtConfig": + publicQtConfig = True else: print(f" XXXX UNHANDLED OUTPUT TYPE {outputType} in feature {feature}.") continue - if not any([publicFeature, privateFeature, internalFeature, publicDefine, negativeFeature]): + if not any( + [ + publicFeature, + privateFeature, + internalFeature, + publicDefine, + negativeFeature, + publicConfig, + privateConfig, + publicQtConfig, + ] + ): print(f" **** Skipping feature {feature}: Not relevant for C++.") return - cxxFeature = featureName(feature) + normalized_feature_name = featureName(feature) def writeFeature( name, @@ -877,12 +888,12 @@ def parseFeature(ctx, feature, data, cm_fh): # Default internal feature case. featureCalls = {} - featureCalls[cxxFeature] = {"name": cxxFeature, "labelAppend": "", "autoDetect": autoDetect} + featureCalls[feature] = {"name": feature, "labelAppend": "", "autoDetect": autoDetect} # Go over all outputs to compute the number of features that have to be declared for o in output: outputType = o - name = cxxFeature + name = feature # The label append is to provide a unique label for features that have more than one output # with different names. @@ -899,13 +910,19 @@ def parseFeature(ctx, feature, data, cm_fh): if name not in featureCalls: featureCalls[name] = {"name": name, "labelAppend": labelAppend} - if name != cxxFeature: - featureCalls[name]["superFeature"] = cxxFeature + if name != feature: + featureCalls[name]["superFeature"] = normalized_feature_name if outputType in ["feature", "publicFeature"]: featureCalls[name]["publicFeature"] = True elif outputType == "privateFeature": featureCalls[name]["privateFeature"] = True + elif outputType == "publicConfig": + featureCalls[name]["publicConfig"] = True + elif outputType == "privateConfig": + featureCalls[name]["privateConfig"] = True + elif outputType == "publicQtConfig": + featureCalls[name]["publicQtConfig"] = True # Write the qt_feature() calls from the computed feature map for _, args in featureCalls.items(): @@ -923,7 +940,7 @@ def parseFeature(ctx, feature, data, cm_fh): if outputType == "feature": outputType = "define" outputArgs = { - "name": f"QT_NO_{cxxFeature.upper()}", + "name": f"QT_NO_{normalized_feature_name.upper()}", "negative": True, "value": 1, "type": "define", @@ -937,13 +954,50 @@ def parseFeature(ctx, feature, data, cm_fh): continue out_name = outputArgs.get("name") - cm_fh.write(f'qt_feature_definition("{cxxFeature}" "{out_name}"') + cm_fh.write(f'qt_feature_definition("{feature}" "{out_name}"') if outputArgs.get("negative", False): cm_fh.write(" NEGATE") if outputArgs.get("value") is not None: cm_fh.write(f' VALUE "{outputArgs.get("value")}"') cm_fh.write(")\n") + # Write qt_feature_config() calls + for o in output: + outputType = o + name = feature + modified_name = name + + outputArgs = {} + if isinstance(o, dict): + outputType = o["type"] + outputArgs = o + if "name" in o: + modified_name = o["name"] + + if outputType not in ["publicConfig", "privateConfig", "publicQtConfig"]: + continue + + config_type = "" + if outputType == "publicConfig": + config_type = "QMAKE_PUBLIC_CONFIG" + elif outputType == "privateConfig": + config_type = "QMAKE_PRIVATE_CONFIG" + elif outputType == "publicQtConfig": + config_type = "QMAKE_PUBLIC_QT_CONFIG" + + if not config_type: + print(" XXXX config output without type in feature {}.".format(feature)) + continue + + cm_fh.write('qt_feature_config("{}" {}'.format(name, config_type)) + if outputArgs.get("negative", False): + cm_fh.write("\n NEGATE") + if modified_name != name: + cm_fh.write("\n") + cm_fh.write(lineify("NAME", modified_name, quote=True)) + + cm_fh.write(")\n") + def processInputs(ctx, data, cm_fh): print(" inputs:") |