diff options
Diffstat (limited to 'util/cmake/configurejson2cmake.py')
| -rwxr-xr-x | util/cmake/configurejson2cmake.py | 1559 |
1 files changed, 0 insertions, 1559 deletions
diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py deleted file mode 100755 index 50a40f61121..00000000000 --- a/util/cmake/configurejson2cmake.py +++ /dev/null @@ -1,1559 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2018 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import json_parser -import posixpath -import re -import sys -from typing import Optional, Set -from textwrap import dedent -import os - -from special_case_helper import SpecialCaseHandler -from helper import ( - map_qt_library, - featureName, - map_platform, - find_3rd_party_library_mapping, - generate_find_package_info, - get_compile_test_dependent_library_mapping, -) - -knownTests = set() # type: Set[str] - - -class LibraryMapping: - def __init__(self, package: str, resultVariable: str, appendFoundSuffix: bool = True) -> None: - self.package = package - self.resultVariable = resultVariable - self.appendFoundSuffix = appendFoundSuffix - - -def map_tests(test: str) -> Optional[str]: - testmap = { - "c99": "c_std_99 IN_LIST CMAKE_C_COMPILE_FEATURES", - "c11": "c_std_11 IN_LIST CMAKE_C_COMPILE_FEATURES", - "x86SimdAlways": "ON", # FIXME: Make this actually do a compile test. - "aesni": "TEST_subarch_aesni", - "avx": "TEST_subarch_avx", - "avx2": "TEST_subarch_avx2", - "avx512f": "TEST_subarch_avx512f", - "avx512cd": "TEST_subarch_avx512cd", - "avx512dq": "TEST_subarch_avx512dq", - "avx512bw": "TEST_subarch_avx512bw", - "avx512er": "TEST_subarch_avx512er", - "avx512pf": "TEST_subarch_avx512pf", - "avx512vl": "TEST_subarch_avx512vl", - "avx512ifma": "TEST_subarch_avx512ifma", - "avx512vbmi": "TEST_subarch_avx512vbmi", - "avx512vbmi2": "TEST_subarch_avx512vbmi2", - "avx512vpopcntdq": "TEST_subarch_avx512vpopcntdq", - "avx5124fmaps": "TEST_subarch_avx5124fmaps", - "avx5124vnniw": "TEST_subarch_avx5124vnniw", - "bmi": "TEST_subarch_bmi", - "bmi2": "TEST_subarch_bmi2", - "cx16": "TEST_subarch_cx16", - "f16c": "TEST_subarch_f16c", - "fma": "TEST_subarch_fma", - "fma4": "TEST_subarch_fma4", - "fsgsbase": "TEST_subarch_fsgsbase", - "gfni": "TEST_subarch_gfni", - "ibt": "TEST_subarch_ibt", - "libclang": "TEST_libclang", - "lwp": "TEST_subarch_lwp", - "lzcnt": "TEST_subarch_lzcnt", - "mmx": "TEST_subarch_mmx", - "movbe": "TEST_subarch_movbe", - "mpx": "TEST_subarch_mpx", - "no-sahf": "TEST_subarch_no_shaf", - "pclmul": "TEST_subarch_pclmul", - "popcnt": "TEST_subarch_popcnt", - "prefetchwt1": "TEST_subarch_prefetchwt1", - "prfchw": "TEST_subarch_prfchw", - "pdpid": "TEST_subarch_rdpid", - "rdpid": "TEST_subarch_rdpid", - "rdseed": "TEST_subarch_rdseed", - "rdrnd": "TEST_subarch_rdrnd", - "rtm": "TEST_subarch_rtm", - "shani": "TEST_subarch_shani", - "shstk": "TEST_subarch_shstk", - "sse2": "TEST_subarch_sse2", - "sse3": "TEST_subarch_sse3", - "ssse3": "TEST_subarch_ssse3", - "sse4a": "TEST_subarch_sse4a", - "sse4_1": "TEST_subarch_sse4_1", - "sse4_2": "TEST_subarch_sse4_2", - "tbm": "TEST_subarch_tbm", - "xop": "TEST_subarch_xop", - "neon": "TEST_subarch_neon", - "iwmmxt": "TEST_subarch_iwmmxt", - "crc32": "TEST_subarch_crc32", - "vis": "TEST_subarch_vis", - "vis2": "TEST_subarch_vis2", - "vis3": "TEST_subarch_vis3", - "dsp": "TEST_subarch_dsp", - "dspr2": "TEST_subarch_dspr2", - "altivec": "TEST_subarch_altivec", - "spe": "TEST_subarch_spe", - "vsx": "TEST_subarch_vsx", - "openssl11": '(OPENSSL_VERSION VERSION_GREATER_EQUAL "1.1.0")', - "libinput_axis_api": "ON", - "xlib": "X11_FOUND", - "wayland-scanner": "WaylandScanner_FOUND", - "3rdparty-hunspell": "VKB_HAVE_3RDPARTY_HUNSPELL", - "t9write-alphabetic": "VKB_HAVE_T9WRITE_ALPHA", - "t9write-cjk": "VKB_HAVE_T9WRITE_CJK", - } - if test in testmap: - return testmap.get(test, None) - if test in knownTests: - return f"TEST_{featureName(test)}" - return None - - -def cm(ctx, *output): - txt = ctx["output"] - if txt != "" and not txt.endswith("\n"): - txt += "\n" - txt += "\n".join(output) - - ctx["output"] = txt - return ctx - - -def readJsonFromDir(path: str) -> str: - path = posixpath.join(path, "configure.json") - - print(f"Reading {path}...") - assert posixpath.exists(path) - - parser = json_parser.QMakeSpecificJSONParser() - return parser.parse(path) - - -def processFiles(ctx, data): - print(" files:") - if "files" in data: - for (k, v) in data["files"].items(): - ctx[k] = v - return ctx - - -def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): - newlib = find_3rd_party_library_mapping(lib) - if not newlib: - print(f' XXXX Unknown library "{lib}".') - return - - if newlib.packageName is None: - print(f' **** Skipping library "{lib}" -- was masked.') - return - - print(f" mapped library {lib} to {newlib.targetName}.") - - # Avoid duplicate find_package calls. - if newlib.targetName in cmake_find_packages_set: - return - - # If certain libraries are used within a feature, but the feature - # is only emitted conditionally with a simple condition (like - # 'on Windows' or 'on Linux'), we should enclose the find_package - # call for the library into the same condition. - emit_if = newlib.emit_if - - # Only look through features if a custom emit_if wasn't provided. - if not emit_if: - for feature in data["features"]: - feature_data = data["features"][feature] - if ( - "condition" in feature_data - and f"libs.{lib}" in feature_data["condition"] - and "emitIf" in feature_data - and "config." in feature_data["emitIf"] - ): - emit_if = feature_data["emitIf"] - break - - if emit_if: - emit_if = map_condition(emit_if) - - cmake_find_packages_set.add(newlib.targetName) - - find_package_kwargs = {"emit_if": emit_if} - if newlib.is_bundled_with_qt: - # If a library is bundled with Qt, it has 2 FindFoo.cmake - # modules: WrapFoo and WrapSystemFoo. - # FindWrapSystemFoo.cmake will try to find the 'Foo' library in - # the usual CMake locations, and will create a - # WrapSystemFoo::WrapSystemFoo target pointing to the library. - # - # FindWrapFoo.cmake will create a WrapFoo::WrapFoo target which - # will link either against the WrapSystemFoo or QtBundledFoo - # target depending on certain feature values. - # - # Because the following qt_find_package call is for - # configure.cmake consumption, we make the assumption that - # configure.cmake is interested in finding the system library - # for the purpose of enabling or disabling a system_foo feature. - find_package_kwargs["use_system_package_name"] = True - find_package_kwargs["module"] = ctx["module"] - - cm_fh.write(generate_find_package_info(newlib, **find_package_kwargs)) - - if "use" in data["libraries"][lib]: - use_entry = data["libraries"][lib]["use"] - if isinstance(use_entry, str): - print(f"1use: {use_entry}") - cm_fh.write(f"qt_add_qmake_lib_dependency({newlib.soName} {use_entry})\n") - else: - for use in use_entry: - print(f"2use: {use}") - indentation = "" - has_condition = False - if "condition" in use: - has_condition = True - indentation = " " - condition = map_condition(use["condition"]) - cm_fh.write(f"if({condition})\n") - cm_fh.write( - f"{indentation}qt_add_qmake_lib_dependency({newlib.soName} {use['lib']})\n" - ) - if has_condition: - cm_fh.write("endif()\n") - - run_library_test = False - mapped_library = find_3rd_party_library_mapping(lib) - if mapped_library: - run_library_test = mapped_library.run_library_test - - if run_library_test and "test" in data["libraries"][lib]: - test = data["libraries"][lib]["test"] - write_compile_test( - ctx, lib, test, data, cm_fh, manual_library_list=[lib], is_library_test=True - ) - - -def lineify(label, value, quote=True): - if value: - if quote: - escaped_value = value.replace('"', '\\"') - return f' {label} "{escaped_value}"\n' - return f" {label} {value}\n" - return "" - - -def map_condition(condition): - # Handle NOT: - if isinstance(condition, list): - condition = "(" + ") AND (".join(condition) + ")" - if isinstance(condition, bool): - if condition: - return "ON" - else: - return "OFF" - assert isinstance(condition, str) - - mapped_features = {"gbm": "gbm_FOUND"} - - # Turn foo != "bar" into (NOT foo STREQUAL 'bar') - condition = re.sub(r"([^ ]+)\s*!=\s*('.*?')", "(! \\1 == \\2)", condition) - # Turn foo != 156 into (NOT foo EQUAL 156) - condition = re.sub(r"([^ ]+)\s*!=\s*([0-9]?)", "(! \\1 EQUAL \\2)", condition) - - condition = condition.replace("!", "NOT ") - condition = condition.replace("&&", " AND ") - condition = condition.replace("||", " OR ") - condition = condition.replace("==", " STREQUAL ") - - # explicitly handle input.sdk == '': - condition = re.sub(r"input\.sdk\s*==\s*''", "NOT INPUT_SDK", condition) - - last_pos = 0 - mapped_condition = "" - has_failed = False - for match in re.finditer(r"([a-zA-Z0-9_]+)\.([a-zA-Z0-9_+-]+)", condition): - substitution = None - # appendFoundSuffix = True - if match.group(1) == "libs": - libmapping = find_3rd_party_library_mapping(match.group(2)) - - if libmapping and libmapping.packageName: - substitution = libmapping.packageName - if libmapping.resultVariable: - substitution = libmapping.resultVariable - if libmapping.appendFoundSuffix: - substitution += "_FOUND" - - # Assume that feature conditions are interested whether - # a system library is found, rather than the bundled one - # which we always know we can build. - if libmapping.is_bundled_with_qt: - substitution = substitution.replace("Wrap", "WrapSystem") - - elif match.group(1) == "features": - feature = match.group(2) - if feature in mapped_features: - substitution = mapped_features.get(feature) - else: - substitution = f"QT_FEATURE_{featureName(match.group(2))}" - - elif match.group(1) == "subarch": - substitution = f"TEST_arch_{'${TEST_architecture_arch}'}_subarch_{match.group(2)}" - - elif match.group(1) == "call": - if match.group(2) == "crossCompile": - substitution = "CMAKE_CROSSCOMPILING" - - elif match.group(1) == "tests": - substitution = map_tests(match.group(2)) - - elif match.group(1) == "input": - substitution = f"INPUT_{featureName(match.group(2))}" - - elif match.group(1) == "config": - substitution = map_platform(match.group(2)) - elif match.group(1) == "module": - substitution = f"TARGET {map_qt_library(match.group(2))}" - - elif match.group(1) == "arch": - if match.group(2) == "i386": - # FIXME: Does this make sense? - substitution = "(TEST_architecture_arch STREQUAL i386)" - elif match.group(2) == "x86_64": - substitution = "(TEST_architecture_arch STREQUAL x86_64)" - elif match.group(2) == "arm": - # FIXME: Does this make sense? - substitution = "(TEST_architecture_arch STREQUAL arm)" - elif match.group(2) == "arm64": - # FIXME: Does this make sense? - substitution = "(TEST_architecture_arch STREQUAL arm64)" - elif match.group(2) == "mips": - # FIXME: Does this make sense? - substitution = "(TEST_architecture_arch STREQUAL mips)" - - if substitution is None: - print(f' XXXX Unknown condition "{match.group(0)}"') - has_failed = True - else: - mapped_condition += condition[last_pos : match.start(1)] + substitution - last_pos = match.end(2) - - mapped_condition += condition[last_pos:] - - # Space out '(' and ')': - mapped_condition = mapped_condition.replace("(", " ( ") - mapped_condition = mapped_condition.replace(")", " ) ") - - # Prettify: - condition = re.sub("\\s+", " ", mapped_condition) - condition = condition.strip() - - # Special case for WrapLibClang in qttools - condition = condition.replace("TEST_libclang.has_clangcpp", "TEST_libclang") - - if has_failed: - condition += " OR FIXME" - - return condition - - -def parseInput(ctx, sinput, data, cm_fh): - skip_inputs = { - "prefix", - "hostprefix", - "extprefix", - "archdatadir", - "bindir", - "datadir", - "docdir", - "examplesdir", - "external-hostbindir", - "headerdir", - "hostbindir", - "hostdatadir", - "hostlibdir", - "importdir", - "libdir", - "libexecdir", - "plugindir", - "qmldir", - "settingsdir", - "sysconfdir", - "testsdir", - "translationdir", - "android-arch", - "android-ndk", - "android-ndk-host", - "android-ndk-platform", - "android-sdk", - "android-toolchain-version", - "android-style-assets", - "appstore-compliant", - "avx", - "avx2", - "avx512", - "c++std", - "ccache", - "commercial", - "confirm-license", - "dbus", - "dbus-runtime", - "debug", - "debug-and-release", - "developer-build", - "device", - "device-option", - "f16c", - "force-asserts", - "force-debug-info", - "force-pkg-config", - "framework", - "gc-binaries", - "gdb-index", - "gcc-sysroot", - "gcov", - "gnumake", - "gui", - "headersclean", - "incredibuild-xge", - "libudev", - "ltcg", - "make", - "make-tool", - "mips_dsp", - "mips_dspr2", - "mp", - "nomake", - "opensource", - "optimize-debug", - "optimize-size", - "optimized-qmake", - "optimized-tools", - "pch", - "pkg-config", - "platform", - "plugin-manifests", - "profile", - "qreal", - "reduce-exports", - "reduce-relocations", - "release", - "rpath", - "sanitize", - "sdk", - "separate-debug-info", - "shared", - "silent", - "qdbus", - "sse2", - "sse3", - "sse4.1", - "sse4.2", - "ssse3", - "static", - "static-runtime", - "strip", - "syncqt", - "sysroot", - "testcocoon", - "use-gold-linker", - "warnings-are-errors", - "Werror", - "widgets", - "xplatform", - "zlib", - "eventfd", - "glib", - "icu", - "inotify", - "journald", - "pcre", - "posix-ipc", - "pps", - "slog2", - "syslog", - } - - if sinput in skip_inputs: - print(f" **** Skipping input {sinput}: masked.") - return - - dtype = data - if isinstance(data, dict): - dtype = data["type"] - - if dtype == "boolean": - print(f" **** Skipping boolean input {sinput}: masked.") - return - - if dtype == "enum": - values_line = " ".join(data["values"]) - cm_fh.write(f"# input {sinput}\n") - cm_fh.write(f'set(INPUT_{featureName(sinput)} "undefined" CACHE STRING "")\n') - cm_fh.write( - f"set_property(CACHE INPUT_{featureName(sinput)} PROPERTY STRINGS undefined {values_line})\n\n" - ) - return - - print(f" XXXX UNHANDLED INPUT TYPE {dtype} in input description") - return - - -def get_library_usage_for_compile_test(library): - result = {} - mapped_library = find_3rd_party_library_mapping(library) - if not mapped_library: - result["fixme"] = f"# FIXME: use: unmapped library: {library}\n" - return result - - if mapped_library.test_library_overwrite: - target_name = mapped_library.test_library_overwrite - else: - target_name = mapped_library.targetName - result["target_name"] = target_name - result["package_name"] = mapped_library.packageName - result["extra"] = mapped_library.extra - return result - - -# Handles config.test/foo/foo.pro projects. -def write_standalone_compile_test(cm_fh, ctx, data, config_test_name, is_library_test): - rel_test_project_path = f"{ctx['test_dir']}/{config_test_name}" - if posixpath.exists(f"{ctx['project_dir']}/{rel_test_project_path}/CMakeLists.txt"): - label = "" - libraries = [] - packages = [] - - if "label" in data: - label = data["label"] - - if is_library_test and config_test_name in data["libraries"]: - if "label" in data["libraries"][config_test_name]: - label = data["libraries"][config_test_name]["label"] - - # If a library entry in configure.json has a test, and - # the test uses a config.tests standalone project, we - # need to get the package and target info for the - # library, and pass it to the test so compiling and - # linking succeeds. - library_usage = get_library_usage_for_compile_test(config_test_name) - if "target_name" in library_usage: - libraries.append(library_usage["target_name"]) - if "package_name" in library_usage: - find_package_arguments = [] - find_package_arguments.append(library_usage["package_name"]) - if "extra" in library_usage: - find_package_arguments.extend(library_usage["extra"]) - package_line = "PACKAGE " + " ".join(find_package_arguments) - packages.append(package_line) - - cm_fh.write( - f""" -qt_config_compile_test("{config_test_name}" - LABEL "{label}" - PROJECT_PATH "${{CMAKE_CURRENT_SOURCE_DIR}}/{rel_test_project_path}" -""" - ) - if libraries: - libraries_string = " ".join(libraries) - cm_fh.write(f" LIBRARIES {libraries_string}\n") - if packages: - packages_string = " ".join(packages) - cm_fh.write(f" PACKAGES {packages_string}") - cm_fh.write(")\n") - - -def write_compile_test( - ctx, name, details, data, cm_fh, manual_library_list=None, is_library_test=False -): - - if manual_library_list is None: - manual_library_list = [] - - inherited_test_name = details["inherit"] if "inherit" in details else None - inherit_details = None - if inherited_test_name and is_library_test: - inherit_details = data["libraries"][inherited_test_name]["test"] - if not inherit_details: - print(f" XXXX Failed to locate inherited library test {inherited_test_name}") - - if isinstance(details, str): - write_standalone_compile_test(cm_fh, ctx, data, details, is_library_test) - return - - def resolve_head(detail): - head = detail.get("head") - if isinstance(head, list): - head = "\n".join(head) - return head + "\n" if head else "" - - head = "" - if inherit_details: - head += resolve_head(inherit_details) - head += resolve_head(details) - - sourceCode = head - - def resolve_include(detail, keyword): - include = detail.get(keyword, "") - if isinstance(include, list): - include = "#include <" + ">\n#include <".join(include) + ">\n" - elif include: - include = f"#include <{include}>\n" - return include - - include = "" - if is_library_test: - if inherit_details: - inherited_lib_data = data["libraries"][inherited_test_name] - include += resolve_include(inherited_lib_data, "headers") - this_lib_data = data["libraries"][name] - include += resolve_include(this_lib_data, "headers") - else: - if inherit_details: - include += resolve_include(inherit_details, "include") - include += resolve_include(details, "include") - - sourceCode += include - - def resolve_tail(detail): - tail = detail.get("tail") - if isinstance(tail, list): - tail = "\n".join(tail) - return tail + "\n" if tail else "" - - tail = "" - if inherit_details: - tail += resolve_tail(inherit_details) - tail += resolve_tail(details) - - sourceCode += tail - - if sourceCode: # blank line before main - sourceCode += "\n" - sourceCode += "int main(void)\n" - sourceCode += "{\n" - sourceCode += " /* BEGIN TEST: */\n" - - def resolve_main(detail): - main = detail.get("main") - if isinstance(main, list): - main = "\n".join(main) - return main + "\n" if main else "" - - main = "" - if inherit_details: - main += resolve_main(inherit_details) - main += resolve_main(details) - - sourceCode += main - - sourceCode += " /* END TEST: */\n" - sourceCode += " return 0;\n" - sourceCode += "}\n" - - sourceCode = sourceCode.replace('"', '\\"') - - librariesCmakeName = "" - languageStandard = "" - compileOptions = "" - qmakeFixme = "" - - cm_fh.write(f"# {name}\n") - - if "qmake" in details: # We don't really have many so we can just enumerate them all - if details["qmake"] == "unix:LIBS += -lpthread": - librariesCmakeName = format(featureName(name)) + "_TEST_LIBRARIES" - cm_fh.write("if (UNIX)\n") - cm_fh.write(" set(" + librariesCmakeName + " pthread)\n") - cm_fh.write("endif()\n") - elif details["qmake"] == "linux: LIBS += -lpthread -lrt": - librariesCmakeName = format(featureName(name)) + "_TEST_LIBRARIES" - cm_fh.write("if (LINUX)\n") - cm_fh.write(" set(" + librariesCmakeName + " pthread rt)\n") - cm_fh.write("endif()\n") - elif details["qmake"] == "!winrt: LIBS += runtimeobject.lib": - librariesCmakeName = format(featureName(name)) + "_TEST_LIBRARIES" - cm_fh.write("if (NOT WINRT)\n") - cm_fh.write(" set(" + librariesCmakeName + " runtimeobject)\n") - cm_fh.write("endif()\n") - elif details["qmake"] == "CONFIG += c++11": - # do nothing we're always in c++11 mode - pass - elif details["qmake"] == "CONFIG += c++11 c++14": - languageStandard = "CXX_STANDARD 14" - elif details["qmake"] == "CONFIG += c++11 c++14 c++17": - languageStandard = "CXX_STANDARD 17" - elif details["qmake"] == "CONFIG += c++11 c++14 c++17 c++20": - languageStandard = "CXX_STANDARD 20" - elif details["qmake"] == "QMAKE_CXXFLAGS += -fstack-protector-strong": - compileOptions = details["qmake"][18:] - else: - qmakeFixme = f"# FIXME: qmake: {details['qmake']}\n" - - library_list = [] - test_libraries = manual_library_list - - if "use" in data: - test_libraries += data["use"].split(" ") - - for library in test_libraries: - if len(library) == 0: - continue - - adjusted_library = get_compile_test_dependent_library_mapping(name, library) - library_usage = get_library_usage_for_compile_test(adjusted_library) - if "fixme" in library_usage: - qmakeFixme += library_usage["fixme"] - continue - else: - library_list.append(library_usage["target_name"]) - - cm_fh.write(f"qt_config_compile_test({featureName(name)}\n") - cm_fh.write(lineify("LABEL", data.get("label", ""))) - if librariesCmakeName != "" or len(library_list) != 0: - cm_fh.write(" LIBRARIES\n") - if librariesCmakeName != "": - cm_fh.write(lineify("", "${" + librariesCmakeName + "}")) - if len(library_list) != 0: - cm_fh.write(" ") - cm_fh.write("\n ".join(library_list)) - cm_fh.write("\n") - if compileOptions != "": - cm_fh.write(f" COMPILE_OPTIONS {compileOptions}\n") - cm_fh.write(" CODE\n") - cm_fh.write('"' + sourceCode + '"') - if qmakeFixme != "": - cm_fh.write(qmakeFixme) - if languageStandard != "": - cm_fh.write(f"\n {languageStandard}\n") - cm_fh.write(")\n\n") - - -# "tests": { -# "cxx11_future": { -# "label": "C++11 <future>", -# "type": "compile", -# "test": { -# "include": "future", -# "main": [ -# "std::future<int> f = std::async([]() { return 42; });", -# "(void)f.get();" -# ], -# "qmake": "unix:LIBS += -lpthread" -# } -# }, - - -def write_compiler_supports_flag_test( - ctx, name, details, data, cm_fh, manual_library_list=None, is_library_test=False -): - cm_fh.write(f"qt_config_compiler_supports_flag_test({featureName(name)}\n") - cm_fh.write(lineify("LABEL", data.get("label", ""))) - cm_fh.write(lineify("FLAG", data.get("flag", ""))) - cm_fh.write(")\n\n") - - -def write_linker_supports_flag_test( - ctx, name, details, data, cm_fh, manual_library_list=None, is_library_test=False -): - cm_fh.write(f"qt_config_linker_supports_flag_test({featureName(name)}\n") - cm_fh.write(lineify("LABEL", data.get("label", ""))) - cm_fh.write(lineify("FLAG", data.get("flag", ""))) - cm_fh.write(")\n\n") - - -def parseTest(ctx, test, data, cm_fh): - skip_tests = { - "c11", - "c99", - "gc_binaries", - "precomile_header", - "reduce_exports", - "gc_binaries", - "libinput_axis_api", - "wayland-scanner", - "xlib", - } - - if test in skip_tests: - print(f" **** Skipping features {test}: masked.") - return - - if data["type"] == "compile": - knownTests.add(test) - - if "test" in data: - details = data["test"] - else: - details = test - - write_compile_test(ctx, test, details, data, cm_fh) - - if data["type"] == "compilerSupportsFlag": - knownTests.add(test) - - if "test" in data: - details = data["test"] - else: - details = test - - write_compiler_supports_flag_test(ctx, test, details, data, cm_fh) - - if data["type"] == "linkerSupportsFlag": - knownTests.add(test) - - if "test" in data: - details = data["test"] - else: - details = test - - write_linker_supports_flag_test(ctx, test, details, data, cm_fh) - - elif data["type"] == "libclang": - knownTests.add(test) - - cm_fh.write(f"# {test}\n") - lib_clang_lib = find_3rd_party_library_mapping("libclang") - cm_fh.write(generate_find_package_info(lib_clang_lib)) - cm_fh.write( - dedent( - """ - if(TARGET WrapLibClang::WrapLibClang) - set(TEST_libclang "ON" CACHE BOOL "Required libclang version found." FORCE) - endif() - """ - ) - ) - cm_fh.write("\n") - - elif data["type"] == "x86Simd": - knownTests.add(test) - - label = data["label"] - - cm_fh.write(f"# {test}\n") - cm_fh.write(f'qt_config_compile_test_x86simd({test} "{label}")\n') - cm_fh.write("\n") - - elif data["type"] == "machineTuple": - knownTests.add(test) - - label = data["label"] - - cm_fh.write(f"# {test}\n") - cm_fh.write(f'qt_config_compile_test_machine_tuple("{label}")\n') - cm_fh.write("\n") - - # "features": { - # "android-style-assets": { - # "label": "Android Style Assets", - # "condition": "config.android", - # "output": [ "privateFeature" ], - # "comment": "This belongs into gui, but the license check needs it here already." - # }, - else: - print(f" XXXX UNHANDLED TEST TYPE {data['type']} in test description") - - -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 - "alloc_malloc_h": None, - "alloc_stdlib_h": None, - "build_all": None, - "ccache": {"autoDetect": "1", "condition": "QT_USE_CCACHE"}, - "compiler-flags": None, - "cross_compile": {"condition": "CMAKE_CROSSCOMPILING"}, - "debug_and_release": { - "autoDetect": "1", # Setting this to None has weird effects... - "condition": "QT_GENERATOR_IS_MULTI_CONFIG", - }, - "debug": { - "autoDetect": "ON", - "condition": "CMAKE_BUILD_TYPE STREQUAL Debug OR Debug IN_LIST CMAKE_CONFIGURATION_TYPES", - }, - "dlopen": {"condition": "UNIX"}, - "force_debug_info": { - "autoDetect": "CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo OR RelWithDebInfo IN_LIST CMAKE_CONFIGURATION_TYPES" - }, - "framework": { - "condition": "APPLE AND BUILD_SHARED_LIBS AND NOT CMAKE_BUILD_TYPE STREQUAL Debug" - }, - "gc_binaries": {"condition": "NOT QT_FEATURE_shared"}, - "gcc-sysroot": None, - "gcov": None, - "GNUmake": None, - "host-dbus": None, - "iconv": { - "condition": "NOT QT_FEATURE_icu AND QT_FEATURE_textcodec AND NOT WIN32 AND NOT QNX AND NOT ANDROID AND NOT APPLE AND WrapIconv_FOUND", - }, - "incredibuild_xge": None, - "ltcg": { - "autoDetect": "ON", - "cmakePrelude": """set(__qt_ltcg_detected FALSE) -if(CMAKE_INTERPROCEDURAL_OPTIMIZATION) - set(__qt_ltcg_detected TRUE) -else() - foreach(config ${CMAKE_BUILD_TYPE} ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER "${config}" __qt_uc_config) - if(CMAKE_INTERPROCEDURAL_OPTIMIZATION_${__qt_uc_config}) - set(__qt_ltcg_detected TRUE) - break() - endif() - endforeach() - unset(__qt_uc_config) -endif()""", - "condition": "__qt_ltcg_detected", - }, - "msvc_mp": None, - "simulator_and_device": {"condition": "UIKIT AND NOT QT_APPLE_SDK"}, - "pkg-config": {"condition": "PKG_CONFIG_FOUND"}, - "precompile_header": {"condition": "BUILD_WITH_PCH"}, - "profile": None, - "qmakeargs": None, - "qpa_default_platform": None, # Not a bool! - "qreal": { - "condition": 'DEFINED QT_COORD_TYPE AND NOT QT_COORD_TYPE STREQUAL "double"', - "output": [ - { - "type": "define", - "name": "QT_COORD_TYPE", - "value": "${QT_COORD_TYPE}", - }, - { - "type": "define", - "name": "QT_COORD_TYPE_STRING", - "value": '\\"${QT_COORD_TYPE}\\"', - }, - ], - }, - "reduce_exports": { - "condition": "NOT MSVC", - }, - "release": None, - "release_tools": None, - "rpath": { - "autoDetect": "1", - "condition": "BUILD_SHARED_LIBS AND UNIX AND NOT WIN32 AND NOT ANDROID", - }, - "shared": { - "condition": "BUILD_SHARED_LIBS", - "output": [ - "publicFeature", - "publicQtConfig", - "publicConfig", - { - "type": "define", - "name": "QT_STATIC", - "prerequisite": "!defined(QT_SHARED) && !defined(QT_STATIC)", - "negative": True, - }, - ], - }, - "silent": None, - "sql-sqlite": {"condition": "QT_FEATURE_datestring"}, - "stl": None, # Do we really need to test for this in 2018?! - "strip": None, - "verifyspec": None, # qmake specific... - "warnings_are_errors": None, # FIXME: Do we need these? - "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: - print(f" **** Skipping features {feature}: masked.") - return - - handled = { - "autoDetect", - "comment", - "condition", - "description", - "disable", - "emitIf", - "enable", - "label", - "output", - "purpose", - "section", - } - label = mapping.get("label", data.get("label", "")) - purpose = mapping.get("purpose", data.get("purpose", data.get("description", label))) - autoDetect = map_condition(mapping.get("autoDetect", data.get("autoDetect", ""))) - condition = map_condition(mapping.get("condition", data.get("condition", ""))) - output = mapping.get("output", data.get("output", [])) - comment = mapping.get("comment", data.get("comment", "")) - section = mapping.get("section", data.get("section", "")) - enable = map_condition(mapping.get("enable", data.get("enable", ""))) - disable = map_condition(mapping.get("disable", data.get("disable", ""))) - emitIf = map_condition(mapping.get("emitIf", data.get("emitIf", ""))) - cmakePrelude = mapping.get("cmakePrelude", None) - cmakeEpilogue = mapping.get("cmakeEpilogue", None) - - for k in [k for k in data.keys() if k not in handled]: - print(f" XXXX UNHANDLED KEY {k} in feature description") - - if not output: - # feature that is only used in the conditions of other features - output = ["internalFeature"] - - publicFeature = False # #define QT_FEATURE_featurename in public header - privateFeature = False # #define QT_FEATURE_featurename in private header - 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 - if isinstance(o, dict): - outputType = o["type"] - - if outputType in [ - "varAssign", - "varAppend", - "varRemove", - "useBFDLinker", - "useGoldLinker", - "useLLDLinker", - ]: - continue - elif outputType == "define": - publicDefine = True - elif outputType == "feature": - negativeFeature = True - elif outputType == "publicFeature": - publicFeature = True - elif outputType == "privateFeature": - 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, - publicConfig, - privateConfig, - publicQtConfig, - ] - ): - print(f" **** Skipping feature {feature}: Not relevant for C++.") - return - - normalized_feature_name = featureName(feature) - - def writeFeature( - name, - publicFeature=False, - privateFeature=False, - labelAppend="", - superFeature=None, - autoDetect="", - cmakePrelude=None, - cmakeEpilogue=None, - ): - if comment: - cm_fh.write(f"# {comment}\n") - - if cmakePrelude is not None: - cm_fh.write(cmakePrelude) - cm_fh.write("\n") - - cm_fh.write(f'qt_feature("{name}"') - if publicFeature: - cm_fh.write(" PUBLIC") - if privateFeature: - cm_fh.write(" PRIVATE") - cm_fh.write("\n") - - cm_fh.write(lineify("SECTION", section)) - cm_fh.write(lineify("LABEL", label + labelAppend)) - if purpose != label: - cm_fh.write(lineify("PURPOSE", purpose)) - cm_fh.write(lineify("AUTODETECT", autoDetect, quote=False)) - if superFeature: - feature_condition = f"QT_FEATURE_{superFeature}" - else: - feature_condition = condition - cm_fh.write(lineify("CONDITION", feature_condition, quote=False)) - cm_fh.write(lineify("ENABLE", enable, quote=False)) - cm_fh.write(lineify("DISABLE", disable, quote=False)) - cm_fh.write(lineify("EMIT_IF", emitIf, quote=False)) - cm_fh.write(")\n") - - if cmakeEpilogue is not None: - cm_fh.write(cmakeEpilogue) - cm_fh.write("\n") - - # Write qt_feature() calls before any qt_feature_definition() calls - - # Default internal feature case. - featureCalls = {} - featureCalls[feature] = { - "name": feature, - "labelAppend": "", - "autoDetect": autoDetect, - "cmakePrelude": cmakePrelude, - "cmakeEpilogue": cmakeEpilogue, - } - - # Go over all outputs to compute the number of features that have to be declared - for o in output: - outputType = o - name = feature - - # The label append is to provide a unique label for features that have more than one output - # with different names. - labelAppend = "" - - if isinstance(o, dict): - outputType = o["type"] - if "name" in o: - name = o["name"] - labelAppend = f": {o['name']}" - - if outputType not in ["feature", "publicFeature", "privateFeature"]: - continue - if name not in featureCalls: - featureCalls[name] = {"name": name, "labelAppend": labelAppend} - - 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(): - writeFeature(**args) - - # Write qt_feature_definition() calls - for o in output: - outputType = o - outputArgs = {} - if isinstance(o, dict): - outputType = o["type"] - outputArgs = o - - # Map negative feature to define: - if outputType == "feature": - outputType = "define" - outputArgs = { - "name": f"QT_NO_{normalized_feature_name.upper()}", - "negative": True, - "value": 1, - "type": "define", - } - - if outputType != "define": - continue - - if outputArgs.get("name") is None: - print(f" XXXX DEFINE output without name in feature {feature}.") - continue - - out_name = outputArgs.get("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")}"') - if outputArgs.get("prerequisite") is not None: - cm_fh.write(f' PREREQUISITE "{outputArgs.get("prerequisite")}"') - 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 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("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 "type" in entry and entry["type"] == "buildParts": - message = entry["message"] - cm_fh.write(f'qt_configure_add_summary_build_parts("{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}.") - - -report_condition_mapping = { - "(features.rpath || features.rpath_dir) && !features.shared": "(features.rpath || QT_EXTRA_RPATHS) && !features.shared", - "(features.rpath || features.rpath_dir) && var.QMAKE_LFLAGS_RPATH == ''": None, -} - - -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 - - if isinstance(condition, str) and condition in report_condition_mapping: - new_condition = report_condition_mapping[condition] - if new_condition is None: - continue - else: - condition = new_condition - 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 parseCommandLineCustomHandler(ctx, data, cm_fh): - cm_fh.write(f"qt_commandline_custom({data})\n") - - -def parseCommandLineOptions(ctx, data, cm_fh): - for key in data: - args = [key] - option = data[key] - if isinstance(option, str): - args += ["TYPE", option] - else: - if "type" in option: - args += ["TYPE", option["type"]] - if "name" in option: - args += ["NAME", option["name"]] - if "value" in option: - args += ["VALUE", option["value"]] - if "values" in option: - values = option["values"] - if isinstance(values, list): - args += ["VALUES", " ".join(option["values"])] - else: - args += ["MAPPING"] - for lhs in values: - args += [lhs, values[lhs]] - - cm_fh.write(f"qt_commandline_option({' '.join(args)})\n") - - -def parseCommandLinePrefixes(ctx, data, cm_fh): - for key in data: - cm_fh.write(f"qt_commandline_prefix({key} {data[key]})\n") - - -def processCommandLine(ctx, data, cm_fh): - print(" commandline:") - - if "subconfigs" in data: - for subconf in data["subconfigs"]: - cm_fh.write(f"qt_commandline_subconfig({subconf})\n") - - if "commandline" not in data: - return - - commandLine = data["commandline"] - if "custom" in commandLine: - print(" custom:") - parseCommandLineCustomHandler(ctx, commandLine["custom"], cm_fh) - if "options" in commandLine: - print(" options:") - parseCommandLineOptions(ctx, commandLine["options"], cm_fh) - if "prefix" in commandLine: - print(" prefix:") - parseCommandLinePrefixes(ctx, commandLine["prefix"], cm_fh) - if "assignments" in commandLine: - print(" assignments are ignored") - - -def processInputs(ctx, data, cm_fh): - print(" inputs:") - if "commandline" not in data: - return - - commandLine = data["commandline"] - if "options" not in commandLine: - return - - for input_option in commandLine["options"]: - parseInput(ctx, input_option, commandLine["options"][input_option], cm_fh) - - -def processTests(ctx, data, cm_fh): - print(" tests:") - if "tests" not in data: - return - - for test in data["tests"]: - parseTest(ctx, test, data["tests"][test], cm_fh) - - -def processFeatures(ctx, data, cm_fh): - print(" features:") - if "features" not in data: - return - - for feature in data["features"]: - parseFeature(ctx, feature, data["features"][feature], cm_fh) - - -def processLibraries(ctx, data, cm_fh): - cmake_find_packages_set = set() - print(" libraries:") - if "libraries" not in data: - return - - for lib in data["libraries"]: - 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: - for subconf in data["subconfigs"]: - subconfDir = posixpath.join(path, subconf) - subconfData = readJsonFromDir(subconfDir) - subconfCtx = ctx - processJson(subconfDir, subconfCtx, subconfData) - - -class special_cased_file: - def __init__(self, base_dir: str, file_name: str, skip_special_case_preservation: bool): - self.base_dir = base_dir - self.file_path = posixpath.join(base_dir, file_name) - self.gen_file_path = self.file_path + ".gen" - self.preserve_special_cases = not skip_special_case_preservation - - def __enter__(self): - self.file = open(self.gen_file_path, "w") - if self.preserve_special_cases: - self.sc_handler = SpecialCaseHandler( - os.path.abspath(self.file_path), - os.path.abspath(self.gen_file_path), - os.path.abspath(self.base_dir), - debug=False, - ) - return self.file - - def __exit__(self, type, value, trace_back): - self.file.close() - if self.preserve_special_cases: - self.sc_handler.handle_special_cases() - os.replace(self.gen_file_path, self.file_path) - - -def processJson(path, ctx, data, skip_special_case_preservation=False): - ctx["project_dir"] = path - ctx["module"] = data.get("module", "global") - ctx["test_dir"] = data.get("testDir", "config.tests") - - ctx = processFiles(ctx, data) - - with special_cased_file(path, "qt_cmdline.cmake", skip_special_case_preservation) as cm_fh: - processCommandLine(ctx, data, cm_fh) - - with special_cased_file(path, "configure.cmake", skip_special_case_preservation) as cm_fh: - cm_fh.write("\n\n#### Inputs\n\n") - - processInputs(ctx, data, cm_fh) - - cm_fh.write("\n\n#### Libraries\n\n") - - processLibraries(ctx, data, cm_fh) - - cm_fh.write("\n\n#### Tests\n\n") - - processTests(ctx, data, cm_fh) - - cm_fh.write("\n\n#### Features\n\n") - - 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' - ) - cm_fh.write('qt_extra_definition("QT_VERSION_MAJOR" ${PROJECT_VERSION_MAJOR} PUBLIC)\n') - cm_fh.write('qt_extra_definition("QT_VERSION_MINOR" ${PROJECT_VERSION_MINOR} PUBLIC)\n') - cm_fh.write('qt_extra_definition("QT_VERSION_PATCH" ${PROJECT_VERSION_PATCH} PUBLIC)\n') - - # do this late: - processSubconfigs(path, ctx, data) - - -def main(): - if len(sys.argv) < 2: - print("This scripts needs one directory to process!") - quit(1) - - directory = sys.argv[1] - skip_special_case_preservation = "-s" in sys.argv[2:] - - print(f"Processing: {directory}.") - - data = readJsonFromDir(directory) - processJson(directory, {}, data, skip_special_case_preservation=skip_special_case_preservation) - - -if __name__ == "__main__": - main() |
