58964af094
Update configurejson2cmake.py to also write out the libraries compile tests depend on. Change-Id: I22dbc227a33b9b5d39a2198c6ee6062e7e1bf3de Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
1050 lines
34 KiB
Python
Executable File
1050 lines
34 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#############################################################################
|
|
##
|
|
## Copyright (C) 2018 The Qt Company Ltd.
|
|
## Contact: https://www.qt.io/licensing/
|
|
##
|
|
## This file is part of the plugins of the Qt Toolkit.
|
|
##
|
|
## $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
|
## Commercial License Usage
|
|
## Licensees holding valid commercial Qt licenses may use this file in
|
|
## accordance with the commercial license agreement provided with the
|
|
## Software or, alternatively, in accordance with the terms contained in
|
|
## a written agreement between you and The Qt Company. For licensing terms
|
|
## and conditions see https://www.qt.io/terms-conditions. For further
|
|
## information use the contact form at https://www.qt.io/contact-us.
|
|
##
|
|
## GNU General Public License Usage
|
|
## Alternatively, this file may be used under the terms of the GNU
|
|
## General Public License version 3 as published by the Free Software
|
|
## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
## included in the packaging of this file. Please review the following
|
|
## information to ensure the GNU General Public License requirements will
|
|
## be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
##
|
|
## $QT_END_LICENSE$
|
|
##
|
|
#############################################################################
|
|
|
|
import json_parser
|
|
import posixpath
|
|
import re
|
|
import sys
|
|
from typing import Optional, Set
|
|
from textwrap import dedent
|
|
|
|
from helper import (
|
|
map_qt_library,
|
|
featureName,
|
|
map_platform,
|
|
find_3rd_party_library_mapping,
|
|
generate_find_package_info,
|
|
)
|
|
|
|
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_aes",
|
|
"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_rdseed", # FIXME: Is this the right thing?
|
|
"rtm": "TEST_subarch_rtm",
|
|
"shani": "TEST_subarch_sha",
|
|
"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",
|
|
"posix-iconv": "TEST_posix_iconv",
|
|
"sun-iconv": "TEST_sun_iconv",
|
|
"openssl11": '(OPENSSL_VERSION VERSION_GREATER_EQUAL "1.1.0")',
|
|
"reduce_exports": "CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY",
|
|
"libinput_axis_api": "ON",
|
|
"xlib": "X11_FOUND",
|
|
"wayland-scanner": "WaylandScanner_FOUND",
|
|
}
|
|
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)
|
|
|
|
cm_fh.write(generate_find_package_info(newlib, emit_if=emit_if))
|
|
|
|
|
|
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",
|
|
"system-xcb": "ON",
|
|
"system-freetype": "ON",
|
|
"system-pcre2": "ON",
|
|
}
|
|
|
|
# Turn foo != "bar" into (NOT foo STREQUAL 'bar')
|
|
condition = re.sub(r"(.+)\s*!=\s*('.+')", "(! \\1 == \\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"
|
|
|
|
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()
|
|
|
|
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",
|
|
"compile-examples",
|
|
"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",
|
|
"harfbuzz",
|
|
"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",
|
|
"doubleconversion",
|
|
"eventfd",
|
|
"glib",
|
|
"icu",
|
|
"inotify",
|
|
"journald",
|
|
"pcre",
|
|
"posix-ipc",
|
|
"pps",
|
|
"slog2",
|
|
"syslog",
|
|
"sqlite",
|
|
}
|
|
|
|
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
|
|
|
|
|
|
# "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 parseTest(ctx, test, data, cm_fh):
|
|
skip_tests = {
|
|
"c11",
|
|
"c99",
|
|
"gc_binaries",
|
|
"posix-iconv",
|
|
"sun-iconv",
|
|
"precomile_header",
|
|
"reduce_exports",
|
|
"separate_debug_info", # FIXME: see if cmake can do this
|
|
"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)
|
|
|
|
details = data["test"]
|
|
|
|
if isinstance(details, str):
|
|
if not ctx["test_dir"]:
|
|
print(f" XXXX UNHANDLED TEST SUB-TYPE {details} in test description")
|
|
return
|
|
|
|
cm_fh.write(
|
|
f"""
|
|
if(EXISTS "${{CMAKE_CURRENT_SOURCE_DIR}}/{ctx['test_dir']}/{data['test']}/CMakeLists.txt")
|
|
qt_config_compile_test("{data['test']}"
|
|
LABEL "{data['label']}"
|
|
PROJECT_PATH "${{CMAKE_CURRENT_SOURCE_DIR}}/{ctx['test_dir']}/{data['test']}")
|
|
endif()
|
|
"""
|
|
)
|
|
return
|
|
|
|
head = details.get("head", "")
|
|
if isinstance(head, list):
|
|
head = "\n".join(head)
|
|
|
|
sourceCode = head + "\n"
|
|
|
|
include = details.get("include", "")
|
|
if isinstance(include, list):
|
|
include = "#include <" + ">\n#include <".join(include) + ">"
|
|
elif include:
|
|
include = f"#include <{include}>"
|
|
|
|
sourceCode += include + "\n"
|
|
|
|
tail = details.get("tail", "")
|
|
if isinstance(tail, list):
|
|
tail = "\n".join(tail)
|
|
|
|
sourceCode += tail + "\n"
|
|
|
|
sourceCode += "int main(int argc, char **argv)\n"
|
|
sourceCode += "{\n"
|
|
sourceCode += " (void)argc; (void)argv;\n"
|
|
sourceCode += " /* BEGIN TEST: */\n"
|
|
|
|
main = details.get("main", "")
|
|
if isinstance(main, list):
|
|
main = "\n".join(main)
|
|
|
|
sourceCode += main + "\n"
|
|
|
|
sourceCode += " /* END TEST: */\n"
|
|
sourceCode += " return 0;\n"
|
|
sourceCode += "}\n"
|
|
|
|
sourceCode = sourceCode.replace('"', '\\"')
|
|
|
|
librariesCmakeName = ""
|
|
languageStandard = ""
|
|
qmakeFixme = ""
|
|
|
|
cm_fh.write(f"# {test}\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(test)) + "_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(test)) + "_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(test)) + "_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++2a":
|
|
languageStandard = "CXX_STANDARD 20"
|
|
else:
|
|
qmakeFixme = f"# FIXME: qmake: {details['qmake']}\n"
|
|
|
|
library_list = []
|
|
if "use" in data:
|
|
for library in data["use"].split(" "):
|
|
if len(library) == 0:
|
|
continue
|
|
|
|
mapped_library = find_3rd_party_library_mapping(library)
|
|
if not mapped_library:
|
|
qmakeFixme += f"# FIXME: use: unmapped library: {library}\n"
|
|
continue
|
|
library_list.append(mapped_library.targetName)
|
|
|
|
|
|
|
|
cm_fh.write(f"qt_config_compile_test({featureName(test)}\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")
|
|
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")
|
|
|
|
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")
|
|
|
|
# "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 parseFeature(ctx, feature, data, cm_fh):
|
|
# 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,
|
|
"c11": None,
|
|
"c89": None,
|
|
"c99": None,
|
|
"ccache": None,
|
|
"compiler-flags": None,
|
|
"cross_compile": None,
|
|
"debug_and_release": None,
|
|
"debug": None,
|
|
"dlopen": {"condition": "UNIX"},
|
|
"doubleconversion": None,
|
|
"enable_gdb_index": None,
|
|
"enable_new_dtags": None,
|
|
"force_debug_info": None,
|
|
"framework": {"condition": "APPLE AND BUILD_SHARED_LIBS"},
|
|
"gc_binaries": None,
|
|
"gcc-sysroot": None,
|
|
"gcov": None,
|
|
"gnu-libiconv": {
|
|
"condition": "NOT WIN32 AND NOT QNX AND NOT ANDROID AND NOT APPLE AND TEST_posix_iconv AND NOT TEST_iconv_needlib",
|
|
"enable": "TEST_posix_iconv AND NOT TEST_iconv_needlib",
|
|
"disable": "NOT TEST_posix_iconv OR TEST_iconv_needlib",
|
|
},
|
|
"GNUmake": None,
|
|
"harfbuzz": {"condition": "harfbuzz_FOUND"},
|
|
"host-dbus": None,
|
|
"iconv": {
|
|
"condition": "NOT QT_FEATURE_icu AND QT_FEATURE_textcodec AND ( TEST_posix_iconv OR TEST_sun_iconv )"
|
|
},
|
|
"incredibuild_xge": None,
|
|
"jpeg": {"condition": "QT_FEATURE_imageformatplugin AND JPEG_FOUND"},
|
|
"ltcg": None,
|
|
"msvc_mp": None,
|
|
"optimize_debug": None,
|
|
"optimize_size": None,
|
|
# special case to enable implicit feature on WIN32, until ANGLE is ported
|
|
"opengl-desktop": {"autoDetect": ""},
|
|
# special case to disable implicit feature on WIN32, until ANGLE is ported
|
|
"opengl-dynamic": {"autoDetect": "OFF"},
|
|
"opengles2": { # special case to disable implicit feature on WIN32, until ANGLE is ported
|
|
"condition": "NOT WIN32 AND ( NOT APPLE_WATCHOS AND NOT QT_FEATURE_opengl_desktop AND GLESv2_FOUND )"
|
|
},
|
|
"pkg-config": None,
|
|
"posix_fallocate": None, # Only needed for sqlite, which we do not want to build
|
|
"posix-libiconv": {
|
|
"condition": "NOT WIN32 AND NOT QNX AND NOT ANDROID AND NOT APPLE AND TEST_posix_iconv AND TEST_iconv_needlib",
|
|
"enable": "TEST_posix_iconv AND TEST_iconv_needlib",
|
|
"disable": "NOT TEST_posix_iconv OR NOT TEST_iconv_needlib",
|
|
},
|
|
"precompile_header": None,
|
|
"profile": None,
|
|
"qmakeargs": None,
|
|
"qpa_default_platform": None, # Not a bool!
|
|
"reduce_relocations": None,
|
|
"release": None,
|
|
"release_tools": None,
|
|
"rpath_dir": None, # rpath related
|
|
"rpath": None,
|
|
"sanitize_address": None, # sanitizer
|
|
"sanitize_memory": None,
|
|
"sanitizer": None,
|
|
"sanitize_thread": None,
|
|
"sanitize_undefined": None,
|
|
"separate_debug_info": None,
|
|
"shared": None,
|
|
"silent": None,
|
|
"sql-sqlite": {"condition": "QT_FEATURE_datestring AND SQLite3_FOUND"},
|
|
"stack-protector-strong": None,
|
|
"static": None,
|
|
"static_runtime": None,
|
|
"stl": None, # Do we really need to test for this in 2018?!
|
|
"strip": None,
|
|
"sun-libiconv": {
|
|
"condition": "NOT WIN32 AND NOT QNX AND NOT ANDROID AND NOT APPLE AND TEST_sun_iconv",
|
|
"enable": "TEST_sun_iconv",
|
|
"disable": "NOT TEST_sun_iconv",
|
|
},
|
|
"system-doubleconversion": None, # No system libraries anymore!
|
|
"system-freetype": None,
|
|
"system-harfbuzz": None,
|
|
"system-jpeg": None,
|
|
"system-pcre2": None,
|
|
"system-png": None,
|
|
"system-sqlite": None,
|
|
"system-xcb": None,
|
|
"system-zlib": None,
|
|
"tiff": {"condition": "QT_FEATURE_imageformatplugin AND TIFF_FOUND"},
|
|
"use_gold_linker": None,
|
|
"verifyspec": None, # qmake specific...
|
|
"warnings_are_errors": None, # FIXME: Do we need these?
|
|
"webp": {"condition": "QT_FEATURE_imageformatplugin AND WrapWebP_FOUND"},
|
|
"xkbcommon-system": None, # another system library, just named a bit different from the rest
|
|
}
|
|
|
|
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", "")))
|
|
|
|
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
|
|
|
|
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",
|
|
]:
|
|
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
|
|
else:
|
|
print(f" XXXX UNHANDLED OUTPUT TYPE {outputType} in feature {feature}.")
|
|
continue
|
|
|
|
if not any([publicFeature, privateFeature, internalFeature, publicDefine, negativeFeature]):
|
|
print(f" **** Skipping feature {feature}: Not relevant for C++.")
|
|
return
|
|
|
|
cxxFeature = featureName(feature)
|
|
|
|
def writeFeature(
|
|
name,
|
|
publicFeature=False,
|
|
privateFeature=False,
|
|
labelAppend="",
|
|
superFeature=None,
|
|
autoDetect="",
|
|
):
|
|
if comment:
|
|
cm_fh.write(f"# {comment}\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")
|
|
|
|
# Write qt_feature() calls before any qt_feature_definition() calls
|
|
|
|
# Default internal feature case.
|
|
featureCalls = {}
|
|
featureCalls[cxxFeature] = {"name": cxxFeature, "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
|
|
|
|
# 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 != cxxFeature:
|
|
featureCalls[name]["superFeature"] = cxxFeature
|
|
|
|
if outputType in ["feature", "publicFeature"]:
|
|
featureCalls[name]["publicFeature"] = True
|
|
elif outputType == "privateFeature":
|
|
featureCalls[name]["privateFeature"] = 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_{cxxFeature.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("{cxxFeature}" "{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")
|
|
|
|
|
|
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 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)
|
|
|
|
|
|
def processJson(path, ctx, data):
|
|
ctx["module"] = data.get("module", "global")
|
|
ctx["test_dir"] = data.get("testDir", "")
|
|
|
|
ctx = processFiles(ctx, data)
|
|
|
|
with open(posixpath.join(path, "configure.cmake"), "w") 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)
|
|
|
|
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]
|
|
|
|
print(f"Processing: {directory}.")
|
|
|
|
data = readJsonFromDir(directory)
|
|
processJson(directory, {}, data)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|