#!/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 import os.path import re import sys from typing import Set, Union, List, Dict from helper import map_qt_library, featureName, substitute_platform 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_library(lib: str) -> Union[str, LibraryMapping, List[str]]: libmap = { 'atspi': 'ATSPI2', 'corewlan': None, # Framework 'cups': 'Cups', 'double-conversion': 'WrapDoubleConversion', 'drm': 'Libdrm', 'egl': 'EGL', 'fontconfig': LibraryMapping(package='Fontconfig', resultVariable="FONTCONFIG"), 'freetype': 'Freetype', 'gbm': 'gbm', 'glib': 'GLIB2', 'gnu_iconv': None, 'gtk3': 'GTK3', 'harfbuzz': 'harfbuzz', 'host_dbus': None, 'icu': ['ICU', 'COMPONENTS', 'i18n', 'uc', 'data'], 'journald': 'Libsystemd', 'libatomic': 'Atomic', 'libdl': None, # handled by CMAKE_DL_LIBS 'libinput': 'Libinput', 'libjpeg': 'JPEG', 'libpng': 'PNG', 'libpng': 'PNG', 'libproxy': 'Libproxy', 'librt': 'WrapRt', 'libudev': 'Libudev', 'lttng-ust': LibraryMapping(package='LTTngUST', resultVariable="LTTNGUST"), 'mtdev': 'Mtdev', 'odbc': 'ODBC', 'opengl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_OpenGL"), 'openssl': 'OpenSSL', 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), 'pcre2': ['PCRE2', 'REQUIRED'], 'posix_iconv': None, 'pps': 'PPS', 'psql': 'PostgreSQL', 'slog2': 'Slog2', 'sqlite3': 'SQLite3', 'sun_iconv': None, 'tslib': 'Tslib', 'udev': 'Libudev', 'vulkan': 'Vulkan', 'wayland_server': 'Wayland', 'x11sm': LibraryMapping(package="X11", resultVariable="X11_SM"), 'xcb_glx': LibraryMapping(package="XCB", resultVariable="XCB_GLX"), 'xcb_render': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), 'xcb': ['XCB', '1.9'], 'xcb_xinput': LibraryMapping(package="XCB", resultVariable="XCB_XINPUT"), 'xcb_xkb': LibraryMapping(package="XCB", resultVariable="XCB_XKB"), 'xcb_xlib': 'X11_XCB', 'xkbcommon': ['XKB', '0.4.1'], 'xlib': 'X11', 'xrender': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), 'zlib': 'ZLIB', 'zstd': 'ZSTD', 'opengl_es2': 'GLESv2', } # type: Dict[str, Union[str, List[str], LibraryMapping]] if lib not in libmap: raise Exception(' XXXX Unknown library "{}".'.format(lib)) return libmap[lib] def map_tests(test: str) -> str: testmap = { 'c++11': '$', 'c++14': '$', 'c++1z': '$', 'c99': '$', 'c11': '$', 'x86SimdAlways': 'ON', # FIXME: Is this the right thing? '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_c16c', 'fma': 'TEST_subarch_fma', 'fma4': 'TEST_subarch_fma4', 'fsgsbase': 'TEST_subarch_fsgsbase', 'gfni': 'TEST_subarch_gfni', 'ibt': 'TEST_subarch_ibt', '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", } if test in testmap: return testmap.get(test, None) if test in knownTests: return 'TEST_{}'.format(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(dir): path = os.path.join(dir, 'configure.json') print('Reading {}...'.format(path)) assert os.path.exists(path) with open(path, 'r') as fh: return json.load(fh) 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): extra = [] try: newlib = map_library(lib) if isinstance(newlib, list): extra = newlib[1:] newlib = newlib[0] elif isinstance(newlib, LibraryMapping): newlib = newlib.package except Exception: return ctx if newlib is None: print(' **** Skipping library "{}" -- was masked.'.format(lib)) return print(' mapped library {} to {}.'.format(lib, newlib)) # Avoid duplicate find_package calls. if newlib in cmake_find_packages_set: return cmake_find_packages_set.add(newlib) isRequired = False if extra: if "REQUIRED" in extra: isRequired = True extra.remove("REQUIRED") if extra: cm_fh.write('find_package({} {})\n'.format(newlib, ' '.join(extra))) else: cm_fh.write('find_package({})\n'.format(newlib)) cm_fh.write('set_package_properties({} PROPERTIES TYPE {})\n' .format(newlib, 'REQUIRED' if isRequired else 'OPTIONAL') ) def lineify(label, value, quote=True): if value: if quote: return ' {} "{}"\n'.format(label, value.replace('"', '\\"')) return ' {} {}\n'.format(label, value) 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 = { "dlopen": "ON", 'gbm': 'gbm_FOUND', "system-xcb": "ON", "system-freetype": "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': try: substitution = map_library(match.group(2)) if isinstance(substitution, list): substitution = substitution[0] elif isinstance(substitution, LibraryMapping): appendFoundSuffix = substitution.appendFoundSuffix substitution = substitution.resultVariable except Exception: substitution = None if substitution is not None and appendFoundSuffix: substitution += '_FOUND' elif match.group(1) == 'features': feature = match.group(2) if feature in mapped_features: substitution = mapped_features.get(feature) else: substitution = 'QT_FEATURE_{}'.format(featureName(match.group(2))) elif match.group(1) == 'subarch': substitution = 'TEST_subarch_{}'.format(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 = 'INPUT_{}'.format(featureName(match.group(2))) elif match.group(1) == 'config': substitution = substitute_platform(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(' XXXX Unknown condition "{}".'.format(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, input, 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", "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 input in skip_inputs: print(' **** Skipping input {}: masked.'.format(input)) return type = data if isinstance(data, dict): type = data["type"] if type == "boolean": print(' **** Skipping boolean input {}: masked.'.format(input)) return if type == "enum": cm_fh.write("# input {}\n".format(input)) cm_fh.write('set(INPUT_{} "undefined" CACHE STRING "")\n'.format(featureName(input))) cm_fh.write('set_property(CACHE INPUT_{} PROPERTY STRINGS undefined {})\n\n'.format(featureName(input), " ".join(data["values"]))) return print(' XXXX UNHANDLED INPUT TYPE {} in input description'.format(type)) return # "tests": { # "cxx11_future": { # "label": "C++11 ", # "type": "compile", # "test": { # "include": "future", # "main": [ # "std::future f = std::async([]() { return 42; });", # "(void)f.get();" # ], # "qmake": "unix:LIBS += -lpthread" # } # }, def parseTest(ctx, test, data, cm_fh): skip_tests = { 'c11', 'c99', 'c++11', 'c++14', 'c++1y', 'c++1z', 'reduce_exports', 'posix-iconv', "sun-iconv", 'separate_debug_info', # FIXME: see if cmake can do this 'gc_binaries', 'libinput_axis_api', 'xlib', } if test in skip_tests: print(' **** Skipping features {}: masked.'.format(test)) return if data["type"] == "compile": knownTests.add(test) details = data["test"] if isinstance(details, str): print(' XXXX UNHANDLED TEST SUB-TYPE {} in test description'.format(details)) 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 = '#include <{}>'.format(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 = "" qmakeFixme = "" cm_fh.write("# {}\n".format(test)) 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(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(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"] == "CONFIG += c++11": # do nothing we're always in c++11 mode pass else: qmakeFixme = "# FIXME: qmake: {}\n".format(details["qmake"]) cm_fh.write("qt_config_compile_test({}\n".format(featureName(test))) cm_fh.write(lineify("LABEL", data.get("label", ""))) if librariesCmakeName != "": cm_fh.write(lineify("LIBRARIES", "${"+librariesCmakeName+"}")) cm_fh.write(" CODE\n") cm_fh.write('"' + sourceCode + '"') if qmakeFixme != "": cm_fh.write(qmakeFixme) if "use" in data: cm_fh.write("# FIXME: use: {}\n".format(data["use"])) cm_fh.write(")\n\n") elif data["type"] == "x86Simd": knownTests.add(test) label = data["label"] cm_fh.write("# {}\n".format(test)) cm_fh.write("qt_config_compile_test_x86simd({} \"{}\")\n".format(test, label)) 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(' XXXX UNHANDLED TEST TYPE {} in test description'.format(data["type"])) 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, 'c++11': None, # C and C++ versions 'c11': None, 'c++14': None, 'c++1y': None, 'c++1z': None, 'c89': None, 'c99': None, 'ccache': None, 'compiler-flags': None, 'cross_compile': None, 'debug_and_release': None, 'debug': None, 'dlopen': None, # handled by CMAKE_DL_LIBS '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, '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, '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-jpeg': None, 'system-pcre2': None, 'system-png': None, 'system-sqlite': None, 'system-xcb': None, 'system-zlib': None, 'use_gold_linker': 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 } mapping = feature_mapping.get(feature, {}) if mapping is None: print(' **** Skipping features {}: masked.'.format(feature)) 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(' XXXX UNHANDLED KEY {} in feature description'.format(k)) if not output: # feature that is only used in the conditions of other features output = ["internalFeature"] publicInfo = False privateInfo = False internalFeature = False 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 in ['feature', 'publicFeature', 'define']: publicInfo = True elif outputType == 'privateFeature': privateInfo = True elif outputType == 'internalFeature': internalFeature = True else: print(' XXXX UNHANDLED OUTPUT TYPE {} in feature {}.'.format(outputType, feature)) continue if not publicInfo and not privateInfo and not internalFeature: print(' **** Skipping feature {}: Not relevant for C++.'.format(feature)) return # write feature: cxxFeature = featureName(feature) if comment: cm_fh.write('# {}\n'.format(comment)) cm_fh.write('qt_feature("{}"'.format(cxxFeature)) if publicInfo: cm_fh.write(' PUBLIC') if privateInfo: cm_fh.write(' PRIVATE') cm_fh.write('\n') cm_fh.write(lineify('SECTION', section)) cm_fh.write(lineify('LABEL', label)) if purpose != label: cm_fh.write(lineify('PURPOSE', purpose)) cm_fh.write(lineify('AUTODETECT', autoDetect, quote=False)) cm_fh.write(lineify('CONDITION', 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') for o in output: outputType = o outputArgs = {} if isinstance(o, dict): outputType = o['type'] outputArgs = o # Map feature to define: if outputType == 'feature': outputType = 'define' outputArgs = {'name': 'QT_NO_{}'.format(cxxFeature.upper()), 'negative': True, 'value': 1, 'type': 'define'} if outputType != 'define': continue if outputArgs.get('name') is None: print(' XXXX DEFINE output without name in feature {}.'.format(feature)) continue cm_fh.write('qt_feature_definition("{}" "{}"'.format(cxxFeature, outputArgs.get('name'))) if outputArgs.get('negative', False): cm_fh.write(' NEGATE') if outputArgs.get('value') is not None: cm_fh.write(' VALUE "{}"'.format(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 in commandLine['options']: parseInput(ctx, input, commandLine['options'][input], 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['libraries'][lib], cm_fh, cmake_find_packages_set) def processSubconfigs(dir, ctx, data): assert ctx is not None if 'subconfigs' in data: for subconf in data['subconfigs']: subconfDir = os.path.join(dir, subconf) subconfData = readJsonFromDir(subconfDir) subconfCtx = ctx processJson(subconfDir, subconfCtx, subconfData) def processJson(dir, ctx, data): ctx['module'] = data.get('module', 'global') ctx = processFiles(ctx, data) with open(os.path.join(dir, "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') if ctx.get('module') == 'gui': cm_fh.write('\nqt_extra_definition("QT_QPA_DEFAULT_PLATFORM" "${QT_QPA_DEFAULT_PLATFORM}" PUBLIC)\n') # do this late: processSubconfigs(dir, ctx, data) def main(): if len(sys.argv) != 2: print("This scripts needs one directory to process!") quit(1) dir = sys.argv[1] print("Processing: {}.".format(dir)) data = readJsonFromDir(dir) processJson(dir, {}, data) if __name__ == '__main__': main()