diff --git a/util/cmake/README.md b/util/cmake/README.md new file mode 100644 index 0000000000..e1699d5283 --- /dev/null +++ b/util/cmake/README.md @@ -0,0 +1,54 @@ +# CMake Utils + +This directory holds scripts to help the porting process from `qmake` to `cmake` for Qt6. + +# Requirements + +* [Python 3.7](https://www.python.org/downloads/), +* `pipenv` or `pip` to manage the modules. + +## Python modules + +Since Python has many ways of handling projects, you have a couple of options to +install the dependencies of the scripts: + +### Using `pipenv` + +The dependencies are specified on the `Pipfile`, so you just need to run +`pipenv install` and that will automatically create a virtual environment +that you can activate with a `pipenv shell`. + +### Using `pip` + +It's highly recommended to use a [virtualenvironment](https://virtualenv.pypa.io/en/latest/) +to avoid conflict with other packages that are already installed: `pip install virtualenv`. + +* Create an environment: `virtualenv env`, +* Activate the environment: `source env/bin/activate` + (on Windows: `source env\Scripts\activate.bat`) +* Install the requirements: `pip install -r requirements.txt` + +# Contributing to the scripts + +You can verify if the styling of a script complaint with PEP8, with a couple of exceptions: + +Install [flake8](http://flake8.pycqa.org/en/latest/) (`pip install flake8`) and run it +on the script you want to test: + +``` +flake8 .py --ignore=E501,E266,W503 +``` + +* `E501`: Line too long (82>79 characters), +* `E266`: Too many leading '#' for block comment, +* `W503`: Line break occurred before a binary operator) + +You can also modify the file with an automatic formatter, +like [black](https://black.readthedocs.io/en/stable/) (`pip install black`), +and execute it: + +``` +black -l 100 .py +``` + +Using Qt's maximum line length, 100. diff --git a/util/cmake/cmakeconversionrate.py b/util/cmake/cmakeconversionrate.py index 3496ed1b91..b87957df6c 100755 --- a/util/cmake/cmakeconversionrate.py +++ b/util/cmake/cmakeconversionrate.py @@ -32,27 +32,37 @@ from argparse import ArgumentParser import os import re import subprocess -import sys import typing def _parse_commandline(): - parser = ArgumentParser(description='Calculate the conversion rate to cmake.') - parser.add_argument('--debug', dest='debug', action='store_true', - help='Turn on debug output') - parser.add_argument('source_directory', metavar='', type=str, - help='The Qt module source directory') - parser.add_argument('binary_directory', metavar='', type=str, - help='The CMake build directory (might be empty)') + parser = ArgumentParser(description="Calculate the conversion rate to cmake.") + parser.add_argument("--debug", dest="debug", action="store_true", help="Turn on debug output") + parser.add_argument( + "source_directory", + metavar="", + type=str, + help="The Qt module source directory", + ) + parser.add_argument( + "binary_directory", + metavar="", + type=str, + help="The CMake build directory (might be empty)", + ) return parser.parse_args() -def calculate_baseline(source_directory: str, *, debug: bool=False) -> int: +def calculate_baseline(source_directory: str, *, debug: bool = False) -> int: if debug: - print('Scanning "{}" for qmake-based tests.'.format(source_directory)) - result = subprocess.run('/usr/bin/git grep -E "^\\s*CONFIG\\s*\\+?=.*\\btestcase\\b" | sort -u | wc -l', - shell=True, capture_output=True, cwd=source_directory) + print(f'Scanning "{source_directory}" for qmake-based tests.') + result = subprocess.run( + '/usr/bin/git grep -E "^\\s*CONFIG\\s*\\+?=.*\\btestcase\\b" | sort -u | wc -l', + shell=True, + capture_output=True, + cwd=source_directory, + ) return int(result.stdout) @@ -60,41 +70,45 @@ def build(source_directory: str, binary_directory: str, *, debug=False) -> None: abs_source = os.path.abspath(source_directory) if not os.path.isdir(binary_directory): os.makedirs(binary_directory) - if not os.path.exists(os.path.join(binary_directory, 'CMakeCache.txt')): + if not os.path.exists(os.path.join(binary_directory, "CMakeCache.txt")): if debug: - print('Running cmake in "{}".'.format(binary_directory)) - result = subprocess.run(['/usr/bin/cmake', '-GNinja', abs_source], cwd=binary_directory) + print(f'Running cmake in "{binary_directory}"') + result = subprocess.run(["/usr/bin/cmake", "-GNinja", abs_source], cwd=binary_directory) if debug: - print('CMake return code: {}.'.format(result.returncode)) + print(f"CMake return code: {result.returncode}.") assert result.returncode == 0 if debug: - print('Running ninja in "{}".'.format(binary_directory)) - result = subprocess.run('/usr/bin/ninja', cwd=binary_directory) + print(f'Running ninja in "{binary_directory}".') + result = subprocess.run("/usr/bin/ninja", cwd=binary_directory) if debug: - print('Ninja return code: {}.'.format(result.returncode)) + print(f"Ninja return code: {result.returncode}.") assert result.returncode == 0 def test(binary_directory: str, *, debug=False) -> typing.Tuple[int, int]: if debug: - print('Running ctest in "{}".'.format(binary_directory)) - result = subprocess.run('/usr/bin/ctest -j 250 | grep "tests passed, "', - shell=True, capture_output=True, cwd=binary_directory) - summary = result.stdout.decode('utf-8').replace('\n', '') + print(f'Running ctest in "{binary_directory}".') + result = subprocess.run( + '/usr/bin/ctest -j 250 | grep "tests passed, "', + shell=True, + capture_output=True, + cwd=binary_directory, + ) + summary = result.stdout.decode("utf-8").replace("\n", "") if debug: - print('Test summary: {} ({}).'.format(summary, result.returncode)) + print(f"Test summary: {summary} ({result.returncode}).") - matches = re.fullmatch(r'\d+% tests passed, (\d+) tests failed out of (\d+)', summary) + matches = re.fullmatch(r"\d+% tests passed, (\d+) tests failed out of (\d+)", summary) if matches: if debug: - print('Matches: failed {}, total {}.'.format(matches.group(1), matches.group(2))) - return (int(matches.group(2)), int(matches.group(2)) - int(matches.group(1)), ) + print(f"Matches: failed {matches.group(1)}, total {matches.group(2)}.") + return (int(matches.group(2)), int(matches.group(2)) - int(matches.group(1))) - return (0, 0,) + return (0, 0) def main() -> int: @@ -102,26 +116,26 @@ def main() -> int: base_line = calculate_baseline(args.source_directory, debug=args.debug) if base_line <= 0: - print('Could not find the qmake baseline in {}.'.format(args.source_directory)) + print(f"Could not find the qmake baseline in {args.source_directory}.") return 1 if args.debug: - print('qmake baseline: {} test binaries.'.format(base_line)) + print(f"qmake baseline: {base_line} test binaries.") cmake_total = 0 cmake_success = 0 try: build(args.source_directory, args.binary_directory, debug=args.debug) - (cmake_total, cmake_success, ) = test(args.binary_directory, debug=args.debug) + (cmake_total, cmake_success) = test(args.binary_directory, debug=args.debug) finally: if cmake_total == 0: - print('\n\n\nCould not calculate the cmake state.') + print("\n\n\nCould not calculate the cmake state.") return 2 else: - print('\n\n\nCMake test conversion rate: {:.2%}.'.format(cmake_total / base_line)) - print('CMake test success rate : {:.2%}.'.format(cmake_success / base_line)) + print(f"\n\n\nCMake test conversion rate: {cmake_total/base_line:.2f}.") + print(f"CMake test success rate : {cmake_success/base_line:.2f}.") return 0 -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 31e062580b..db56f26eda 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -31,11 +31,16 @@ import json_parser import posixpath import re import sys -from typing import Set, Union, List, Dict +from typing import Set from textwrap import dedent -from helper import map_qt_library, featureName, map_platform, \ - find_3rd_party_library_mapping, generate_find_package_info +from helper import ( + map_qt_library, + featureName, + map_platform, + find_3rd_party_library_mapping, + generate_find_package_info, +) knownTests = set() # type: Set[str] @@ -46,114 +51,105 @@ class LibraryMapping: self.resultVariable = resultVariable self.appendFoundSuffix = appendFoundSuffix + def map_tests(test: str) -> str: testmap = { - 'c++11': '$', - 'c++14': '$', - 'c++1z': '$', - 'c99': '$', - 'c11': '$', - - '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', + "c++11": "$", + "c++14": "$", + "c++1z": "$", + "c99": "$", + "c11": "$", + "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", } if test in testmap: return testmap.get(test, None) if test in knownTests: - return 'TEST_{}'.format(featureName(test)) + 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) + txt = ctx["output"] + if txt != "" and not txt.endswith("\n"): + txt += "\n" + txt += "\n".join(output) - ctx['output'] = txt + ctx["output"] = txt return ctx def readJsonFromDir(dir): - path = posixpath.join(dir, 'configure.json') + path = posixpath.join(dir, "configure.json") - print('Reading {}...'.format(path)) + print("Reading {}...".format(path)) assert posixpath.exists(path) parser = json_parser.QMakeSpecificJSONParser() @@ -161,12 +157,13 @@ def readJsonFromDir(dir): def processFiles(ctx, data): - print(' files:') - if 'files' in data: - for (k, v) in data['files'].items(): + 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: @@ -177,7 +174,7 @@ def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): print(' **** Skipping library "{}" -- was masked.'.format(lib)) return - print(' mapped library {} to {}.'.format(lib, newlib.targetName)) + print(" mapped library {} to {}.".format(lib, newlib.targetName)) # Avoid duplicate find_package calls. if newlib.targetName in cmake_find_packages_set: @@ -191,13 +188,15 @@ def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): # 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 \ - 'libs.{}'.format(lib) in feature_data['condition'] and \ - 'emitIf' in feature_data and \ - 'config.' in feature_data['emitIf']: - emit_if = feature_data['emitIf'] + for feature in data["features"]: + feature_data = data["features"][feature] + if ( + "condition" in feature_data + and "libs.{}".format(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: @@ -212,45 +211,46 @@ def lineify(label, value, quote=True): if value: if quote: return ' {} "{}"\n'.format(label, value.replace('"', '\\"')) - return ' {} {}\n'.format(label, value) - return '' + return " {} {}\n".format(label, value) + return "" + def map_condition(condition): # Handle NOT: if isinstance(condition, list): - condition = '(' + ') AND ('.join(condition) + ')' + condition = "(" + ") AND (".join(condition) + ")" if isinstance(condition, bool): if condition: - return 'ON' + return "ON" else: - return 'OFF' + return "OFF" assert isinstance(condition, str) mapped_features = { - 'gbm': 'gbm_FOUND', + "gbm": "gbm_FOUND", "system-xcb": "ON", "system-freetype": "ON", - 'system-pcre2': 'ON', + "system-pcre2": "ON", } # Turn foo != "bar" into (NOT foo STREQUAL 'bar') - condition = re.sub(r"(.+)\s*!=\s*('.+')", '(! \\1 == \\2)', condition) + condition = re.sub(r"(.+)\s*!=\s*('.+')", "(! \\1 == \\2)", condition) - condition = condition.replace('!', 'NOT ') - condition = condition.replace('&&', ' AND ') - condition = condition.replace('||', ' OR ') - condition = condition.replace('==', ' STREQUAL ') + 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) + condition = re.sub(r"input\.sdk\s*==\s*''", "NOT INPUT_SDK", condition) last_pos = 0 - mapped_condition = '' + mapped_condition = "" has_failed = False - for match in re.finditer(r'([a-zA-Z0-9_]+)\.([a-zA-Z0-9_+-]+)', condition): + for match in re.finditer(r"([a-zA-Z0-9_]+)\.([a-zA-Z0-9_+-]+)", condition): substitution = None - appendFoundSuffix = True - if match.group(1) == 'libs': + # appendFoundSuffix = True + if match.group(1) == "libs": libmapping = find_3rd_party_library_mapping(match.group(2)) if libmapping and libmapping.packageName: @@ -258,167 +258,164 @@ def map_condition(condition): if libmapping.resultVariable: substitution = libmapping.resultVariable if libmapping.appendFoundSuffix: - substitution += '_FOUND' + substitution += "_FOUND" - elif match.group(1) == 'features': + 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))) + substitution = "QT_FEATURE_{}".format(featureName(match.group(2))) - elif match.group(1) == 'subarch': - substitution = 'TEST_arch_{}_subarch_{}'.format("${TEST_architecture_arch}", - match.group(2)) + elif match.group(1) == "subarch": + substitution = "TEST_arch_{}_subarch_{}".format( + "${TEST_architecture_arch}", match.group(2) + ) - elif match.group(1) == 'call': - if match.group(2) == 'crossCompile': - substitution = 'CMAKE_CROSSCOMPILING' + elif match.group(1) == "call": + if match.group(2) == "crossCompile": + substitution = "CMAKE_CROSSCOMPILING" - elif match.group(1) == 'tests': + 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) == "input": + substitution = "INPUT_{}".format(featureName(match.group(2))) - elif match.group(1) == 'config': + elif match.group(1) == "config": substitution = map_platform(match.group(2)) - elif match.group(1) == 'module': - substitution = 'TARGET {}'.format(map_qt_library(match.group(2))) + elif match.group(1) == "module": + substitution = "TARGET {}".format(map_qt_library(match.group(2))) - elif match.group(1) == 'arch': - if match.group(2) == 'i386': + 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': + 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': + 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': + substitution = "(TEST_architecture_arch STREQUAL arm64)" + elif match.group(2) == "mips": # FIXME: Does this make sense? - substitution = '(TEST_architecture_arch STREQUAL mips)' + 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 + 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(')', ' ) ') + mapped_condition = mapped_condition.replace("(", " ( ") + mapped_condition = mapped_condition.replace(")", " ) ") # Prettify: - condition = re.sub('\\s+', ' ', mapped_condition) + condition = re.sub("\\s+", " ", mapped_condition) condition = condition.strip() if has_failed: - condition += ' OR FIXME' + condition += " OR FIXME" return condition -def parseInput(ctx, input, data, cm_fh): +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", - + "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", + "avx", + "avx2", + "avx512", + "c++std", + "ccache", + "commercial", + "compile-examples", + "confirm-license", "dbus", "dbus-runtime", - - "debug", "debug-and-release", - + "debug", + "debug-and-release", "developer-build", - - "device", "device-option", - + "device", + "device-option", "f16c", - - "force-asserts", "force-debug-info", "force-pkg-config", + "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", - + "optimize-debug", + "optimize-size", + "optimized-qmake", + "optimized-tools", "pch", - "pkg-config", - "platform", - "plugin-manifests", "profile", "qreal", - - "reduce-exports", "reduce-relocations", - + "reduce-exports", + "reduce-relocations", "release", - "rpath", - "sanitize", - "sdk", - "separate-debug-info", - "shared", - "silent", - "qdbus", - "sse2", "sse3", "sse4.1", @@ -436,9 +433,7 @@ def parseInput(ctx, input, data, cm_fh): "widgets", "xplatform", "zlib", - "doubleconversion", - "eventfd", "glib", "icu", @@ -449,29 +444,31 @@ def parseInput(ctx, input, data, cm_fh): "pps", "slog2", "syslog", - "sqlite", } - if input in skip_inputs: - print(' **** Skipping input {}: masked.'.format(input)) + if sinput in skip_inputs: + print(f" **** Skipping input {sinput}: masked.") return - type = data + dtype = data if isinstance(data, dict): - type = data["type"] + dtype = data["type"] - if type == "boolean": - print(' **** Skipping boolean input {}: masked.'.format(input)) + if dtype == "boolean": + print(f" **** Skipping boolean input {sinput}: masked.") 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"]))) + 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(' XXXX UNHANDLED INPUT TYPE {} in input description'.format(type)) + print(f" XXXX UNHANDLED INPUT TYPE {dtype} in input description") return @@ -490,21 +487,26 @@ def parseInput(ctx, input, data, cm_fh): # }, def parseTest(ctx, test, data, cm_fh): skip_tests = { - 'c++11', 'c++14', 'c++1y', 'c++1z', - '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', + "c++11", + "c++14", + "c++1y", + "c++1z", + "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(' **** Skipping features {}: masked.'.format(test)) + print(f" **** Skipping features {test}: masked.") return if data["type"] == "compile": @@ -513,28 +515,28 @@ def parseTest(ctx, test, data, cm_fh): details = data["test"] if isinstance(details, str): - print(' XXXX UNHANDLED TEST SUB-TYPE {} in test description'.format(details)) + print(f" XXXX UNHANDLED TEST SUB-TYPE {details} in test description") return head = details.get("head", "") if isinstance(head, list): head = "\n".join(head) - sourceCode = head + '\n' + sourceCode = head + "\n" include = details.get("include", "") if isinstance(include, list): - include = '#include <' + '>\n#include <'.join(include) + '>' + include = "#include <" + ">\n#include <".join(include) + ">" elif include: - include = '#include <{}>'.format(include) + include = f"#include <{include}>" - sourceCode += include + '\n' + sourceCode += include + "\n" tail = details.get("tail", "") if isinstance(tail, list): tail = "\n".join(tail) - sourceCode += tail + '\n' + sourceCode += tail + "\n" sourceCode += "int main(int argc, char **argv)\n" sourceCode += "{\n" @@ -545,7 +547,7 @@ def parseTest(ctx, test, data, cm_fh): if isinstance(main, list): main = "\n".join(main) - sourceCode += main + '\n' + sourceCode += main + "\n" sourceCode += " /* END TEST: */\n" sourceCode += " return 0;\n" @@ -556,8 +558,8 @@ def parseTest(ctx, test, data, cm_fh): 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 + 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") @@ -572,7 +574,7 @@ def parseTest(ctx, test, data, cm_fh): # do nothing we're always in c++11 mode pass else: - qmakeFixme = "# FIXME: qmake: {}\n".format(details["qmake"]) + qmakeFixme = f"# FIXME: qmake: {details['qmake']}\n" if "use" in data: if data["use"] == "egl xcb_xlib": @@ -581,12 +583,12 @@ def parseTest(ctx, test, data, cm_fh): cm_fh.write(" set(" + librariesCmakeName + " EGL::EGL X11::X11 X11::XCB)\n") cm_fh.write("endif()\n") else: - qmakeFixme += "# FIXME: use: {}\n".format(data["use"]) + qmakeFixme += f"# FIXME: use: {data['use']}\n" - cm_fh.write("qt_config_compile_test({}\n".format(featureName(test))) + cm_fh.write(f"qt_config_compile_test({featureName(test)}\n") cm_fh.write(lineify("LABEL", data.get("label", ""))) if librariesCmakeName != "": - cm_fh.write(lineify("LIBRARIES", "${"+librariesCmakeName+"}")) + cm_fh.write(lineify("LIBRARIES", "${" + librariesCmakeName + "}")) cm_fh.write(" CODE\n") cm_fh.write('"' + sourceCode + '"') if qmakeFixme != "": @@ -596,14 +598,18 @@ def parseTest(ctx, test, data, cm_fh): elif data["type"] == "libclang": knownTests.add(test) - cm_fh.write("# {}\n".format(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(""" + 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": @@ -611,230 +617,238 @@ def parseTest(ctx, test, data, cm_fh): label = data["label"] - cm_fh.write("# {}\n".format(test)) - cm_fh.write("qt_config_compile_test_x86simd({} \"{}\")\n".format(test, 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." -# }, + # "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"])) + 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', + "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", }, - 'doubleconversion': None, - 'enable_gdb_index': None, - 'enable_new_dtags': None, - 'force_debug_info': None, - 'framework': { - 'condition': 'APPLE AND BUILD_SHARED_LIBS', + "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 )" }, - '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, + "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': '' - }, + "opengl-desktop": {"autoDetect": ""}, # special case to disable implicit feature on WIN32, until ANGLE is ported - 'opengl-dynamic': { - 'autoDetect': 'OFF' + "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 )" }, - '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", }, - '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", }, - '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 + "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(' **** Skipping features {}: masked.'.format(feature)) + 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', ''))) + 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)) + 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 + 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'] + outputType = o["type"] outputArgs = o - if outputType in ['varAssign', 'varAppend', 'varRemove', 'publicQtConfig', 'privateConfig', 'publicConfig']: + if outputType in [ + "varAssign", + "varAppend", + "varRemove", + "publicQtConfig", + "privateConfig", + "publicConfig", + ]: continue - elif outputType == 'define': + elif outputType == "define": publicDefine = True - elif outputType == 'feature': + elif outputType == "feature": negativeFeature = True - elif outputType == 'publicFeature': + elif outputType == "publicFeature": publicFeature = True - elif outputType == 'privateFeature': + elif outputType == "privateFeature": privateFeature = True - elif outputType == 'internalFeature': + elif outputType == "internalFeature": internalFeature = True else: - print(' XXXX UNHANDLED OUTPUT TYPE {} in feature {}.'.format(outputType, feature)) + print(f" XXXX UNHANDLED OUTPUT TYPE {outputType} in feature {feature}.") continue if not any([publicFeature, privateFeature, internalFeature, publicDefine, negativeFeature]): - print(' **** Skipping feature {}: Not relevant for C++.'.format(feature)) + print(f" **** Skipping feature {feature}: Not relevant for C++.") return cxxFeature = featureName(feature) - def writeFeature(name, publicFeature=False, privateFeature=False, labelAppend='', superFeature=None, autoDetect=''): + def writeFeature( + name, + publicFeature=False, + privateFeature=False, + labelAppend="", + superFeature=None, + autoDetect="", + ): if comment: - cm_fh.write('# {}\n'.format(comment)) + cm_fh.write(f"# {comment}\n") - cm_fh.write('qt_feature("{}"'.format(name)) + cm_fh.write(f'qt_feature("{name}"') if publicFeature: - cm_fh.write(' PUBLIC') + cm_fh.write(" PUBLIC") if privateFeature: - cm_fh.write(' PRIVATE') - cm_fh.write('\n') + cm_fh.write(" PRIVATE") + cm_fh.write("\n") - cm_fh.write(lineify('SECTION', section)) - cm_fh.write(lineify('LABEL', label + labelAppend)) + 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)) + cm_fh.write(lineify("PURPOSE", purpose)) + cm_fh.write(lineify("AUTODETECT", autoDetect, quote=False)) if superFeature: - feature_condition = "QT_FEATURE_{}".format(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') + 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} + 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: @@ -843,26 +857,26 @@ def parseFeature(ctx, feature, data, cm_fh): # The label append is to provide a unique label for features that have more than one output # with different names. - labelAppend = '' + labelAppend = "" if isinstance(o, dict): - outputType = o['type'] - if 'name' in o: - name = o['name'] - labelAppend = ': {}'.format(o['name']) + outputType = o["type"] + if "name" in o: + name = o["name"] + labelAppend = f": {o['name']}" - if outputType not in ['feature', 'publicFeature', 'privateFeature']: + if outputType not in ["feature", "publicFeature", "privateFeature"]: continue if name not in featureCalls: - featureCalls[name] = {'name': name, 'labelAppend': labelAppend} + featureCalls[name] = {"name": name, "labelAppend": labelAppend} if name != cxxFeature: - featureCalls[name]['superFeature'] = cxxFeature + featureCalls[name]["superFeature"] = cxxFeature - if outputType in ['feature', 'publicFeature']: - featureCalls[name]['publicFeature'] = True - elif outputType == 'privateFeature': - featureCalls[name]['privateFeature'] = True + 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(): @@ -873,77 +887,80 @@ def parseFeature(ctx, feature, data, cm_fh): outputType = o outputArgs = {} if isinstance(o, dict): - outputType = o['type'] + outputType = o["type"] outputArgs = o # Map negative feature to define: - if outputType == 'feature': - outputType = 'define' - outputArgs = {'name': 'QT_NO_{}'.format(cxxFeature.upper()), - 'negative': True, - 'value': 1, - 'type': 'define'} + if outputType == "feature": + outputType = "define" + outputArgs = { + "name": f"QT_NO_{cxxFeature.upper()}", + "negative": True, + "value": 1, + "type": "define", + } - if outputType != 'define': + if outputType != "define": continue - if outputArgs.get('name') is None: - print(' XXXX DEFINE output without name in feature {}.'.format(feature)) + if outputArgs.get("name") is None: + print(f" XXXX DEFINE output without name in feature {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') + 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(' VALUE "{}"'.format(outputArgs.get("value"))) + cm_fh.write(")\n") def processInputs(ctx, data, cm_fh): - print(' inputs:') - if 'commandline' not in data: + print(" inputs:") + if "commandline" not in data: return - commandLine = data['commandline'] + commandLine = data["commandline"] if "options" not in commandLine: return - for input in commandLine['options']: - parseInput(ctx, input, commandLine['options'][input], cm_fh) + 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: + print(" tests:") + if "tests" not in data: return - for test in data['tests']: - parseTest(ctx, test, data['tests'][test], cm_fh) + 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: + print(" features:") + if "features" not in data: return - for feature in data['features']: - parseFeature(ctx, feature, data['features'][feature], cm_fh) + 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: + print(" libraries:") + if "libraries" not in data: return - for lib in data['libraries']: + for lib in data["libraries"]: parseLib(ctx, lib, data, 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']: + if "subconfigs" in data: + for subconf in data["subconfigs"]: subconfDir = posixpath.join(dir, subconf) subconfData = readJsonFromDir(subconfDir) subconfCtx = ctx @@ -951,11 +968,11 @@ def processSubconfigs(dir, ctx, data): def processJson(dir, ctx, data): - ctx['module'] = data.get('module', 'global') + ctx["module"] = data.get("module", "global") ctx = processFiles(ctx, data) - with open(posixpath.join(dir, "configure.cmake"), 'w') as cm_fh: + with open(posixpath.join(dir, "configure.cmake"), "w") as cm_fh: cm_fh.write("\n\n#### Inputs\n\n") processInputs(ctx, data, cm_fh) @@ -972,8 +989,10 @@ def processJson(dir, ctx, data): processFeatures(ctx, data, cm_fh) - if ctx.get('module') == 'global': - cm_fh.write('\nqt_extra_definition("QT_VERSION_STR" "\\\"${PROJECT_VERSION}\\\"" PUBLIC)\n') + 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') @@ -984,16 +1003,16 @@ def processJson(dir, ctx, data): def main(): if len(sys.argv) != 2: - print("This scripts needs one directory to process!") - quit(1) + print("This scripts needs one directory to process!") + quit(1) - dir = sys.argv[1] + directory = sys.argv[1] - print("Processing: {}.".format(dir)) + print("Processing: {}.".format(directory)) - data = readJsonFromDir(dir) - processJson(dir, {}, data) + data = readJsonFromDir(directory) + processJson(directory, {}, data) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 6ad468485c..ab4f05113b 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -29,14 +29,19 @@ import re import typing + class LibraryMapping: - def __init__(self, soName: str, - packageName: typing.Optional[str], - targetName: typing.Optional[str], *, - resultVariable: typing.Optional[str] = None, - extra: typing.List[str] = [], - appendFoundSuffix: bool = True, - emit_if: str = '') -> None: + def __init__( + self, + soName: str, + packageName: typing.Optional[str], + targetName: typing.Optional[str], + *, + resultVariable: typing.Optional[str] = None, + extra: typing.List[str] = [], + appendFoundSuffix: bool = True, + emit_if: str = "" + ) -> None: self.soName = soName self.packageName = packageName self.resultVariable = resultVariable @@ -49,232 +54,448 @@ class LibraryMapping: self.emit_if = emit_if def is_qt(self) -> bool: - return self.packageName == 'Qt' \ - or self.packageName == 'Qt5' \ - or self.packageName == 'Qt6' + return self.packageName == "Qt" or self.packageName == "Qt5" or self.packageName == "Qt6" + _qt_library_map = [ # Qt: - LibraryMapping('accessibility_support', 'Qt6', 'Qt::AccessibilitySupport', extra = ['COMPONENTS', 'AccessibilitySupport']), - LibraryMapping('androidextras', 'Qt6', 'Qt::AndroidExtras', extra = ['COMPONENTS', 'AndroidExtras']), - LibraryMapping('animation', 'Qt6', 'Qt::3DAnimation', extra = ['COMPONENTS', '3DAnimation']), - LibraryMapping('application-lib', 'Qt6', 'Qt::AppManApplication', extra = ['COMPONENTS', 'AppManApplication']), - LibraryMapping('bluetooth', 'Qt6', 'Qt::Bluetooth', extra = ['COMPONENTS', 'Bluetooth']), - LibraryMapping('bootstrap', 'Qt6', 'Qt::Bootstrap', extra = ['COMPONENTS', 'Bootstrap']), + LibraryMapping( + "accessibility_support", + "Qt6", + "Qt::AccessibilitySupport", + extra=["COMPONENTS", "AccessibilitySupport"], + ), + LibraryMapping( + "androidextras", "Qt6", "Qt::AndroidExtras", extra=["COMPONENTS", "AndroidExtras"] + ), + LibraryMapping("animation", "Qt6", "Qt::3DAnimation", extra=["COMPONENTS", "3DAnimation"]), + LibraryMapping( + "application-lib", "Qt6", "Qt::AppManApplication", extra=["COMPONENTS", "AppManApplication"] + ), + LibraryMapping("bluetooth", "Qt6", "Qt::Bluetooth", extra=["COMPONENTS", "Bluetooth"]), + LibraryMapping("bootstrap", "Qt6", "Qt::Bootstrap", extra=["COMPONENTS", "Bootstrap"]), # bootstrap-dbus: Not needed in Qt6! - LibraryMapping('client', 'Qt6', 'Qt::WaylandClient', extra = ['COMPONENTS', 'WaylandClient']), - LibraryMapping('clipboard_support', 'Qt6', 'Qt::ClipboardSupport', extra = ['COMPONENTS', 'ClipboardSupport']), - LibraryMapping('coap', 'Qt6', 'Qt::Coap', extra = ['COMPONENTS', 'Coap']), - LibraryMapping('common-lib', 'Qt6', 'Qt::AppManCommon', extra = ['COMPONENTS', 'AppManCommon']), - LibraryMapping('compositor', 'Qt6', 'Qt::WaylandCompositor', extra = ['COMPONENTS', 'WaylandCompositor']), - LibraryMapping('concurrent', 'Qt6', 'Qt::Concurrent', extra = ['COMPONENTS', 'Concurrent']), - LibraryMapping('container', 'Qt6', 'Qt::AxContainer', extra = ['COMPONENTS', 'AxContainer']), - LibraryMapping('control', 'Qt6', 'Qt::AxServer', extra = ['COMPONENTS', 'AxServer']), - LibraryMapping('core_headers', 'Qt6', 'Qt::WebEngineCore', extra = ['COMPONENTS', 'WebEngineCore']), - LibraryMapping('core', 'Qt6', 'Qt::Core', extra = ['COMPONENTS', 'Core']), - LibraryMapping('coretest', 'Qt6', 'Qt::3DCoreTest', extra = ['COMPONENTS', '3DCoreTest']), - LibraryMapping('crypto-lib', 'Qt6', 'Qt::AppManCrypto', extra = ['COMPONENTS', 'AppManCrypto']), - LibraryMapping('dbus', 'Qt6', 'Qt::DBus', extra = ['COMPONENTS', 'DBus']), - LibraryMapping('designer', 'Qt6', 'Qt::Designer', extra = ['COMPONENTS', 'Designer']), - LibraryMapping('designercomponents', 'Qt6', 'Qt::DesignerComponents', extra = ['COMPONENTS', 'DesignerComponents']), - LibraryMapping('devicediscovery', 'Qt6', 'Qt::DeviceDiscoverySupport', extra = ['COMPONENTS', 'DeviceDiscoverySupport']), - LibraryMapping('devicediscovery_support', 'Qt6', 'Qt::DeviceDiscoverySupport', extra = ['COMPONENTS', 'DeviceDiscoverySupport']), - LibraryMapping('edid', 'Qt6', 'Qt::EdidSupport', extra = ['COMPONENTS', 'EdidSupport']), - LibraryMapping('edid_support', 'Qt6', 'Qt::EdidSupport', extra = ['COMPONENTS', 'EdidSupport']), - LibraryMapping('eglconvenience', 'Qt6', 'Qt::EglSupport', extra = ['COMPONENTS', 'EglSupport']), - LibraryMapping('eglfsdeviceintegration', 'Qt6', 'Qt::EglFSDeviceIntegration', extra = ['COMPONENTS', 'EglFSDeviceIntegration']), - LibraryMapping('eglfs_kms_support', 'Qt6', 'Qt::EglFsKmsSupport', extra = ['COMPONENTS', 'EglFsKmsSupport']), - LibraryMapping('egl_support', 'Qt6', 'Qt::EglSupport', extra = ['COMPONENTS', 'EglSupport']), + LibraryMapping("client", "Qt6", "Qt::WaylandClient", extra=["COMPONENTS", "WaylandClient"]), + LibraryMapping( + "clipboard_support", "Qt6", "Qt::ClipboardSupport", extra=["COMPONENTS", "ClipboardSupport"] + ), + LibraryMapping("coap", "Qt6", "Qt::Coap", extra=["COMPONENTS", "Coap"]), + LibraryMapping("common-lib", "Qt6", "Qt::AppManCommon", extra=["COMPONENTS", "AppManCommon"]), + LibraryMapping( + "compositor", "Qt6", "Qt::WaylandCompositor", extra=["COMPONENTS", "WaylandCompositor"] + ), + LibraryMapping("concurrent", "Qt6", "Qt::Concurrent", extra=["COMPONENTS", "Concurrent"]), + LibraryMapping("container", "Qt6", "Qt::AxContainer", extra=["COMPONENTS", "AxContainer"]), + LibraryMapping("control", "Qt6", "Qt::AxServer", extra=["COMPONENTS", "AxServer"]), + LibraryMapping( + "core_headers", "Qt6", "Qt::WebEngineCore", extra=["COMPONENTS", "WebEngineCore"] + ), + LibraryMapping("core", "Qt6", "Qt::Core", extra=["COMPONENTS", "Core"]), + LibraryMapping("coretest", "Qt6", "Qt::3DCoreTest", extra=["COMPONENTS", "3DCoreTest"]), + LibraryMapping("crypto-lib", "Qt6", "Qt::AppManCrypto", extra=["COMPONENTS", "AppManCrypto"]), + LibraryMapping("dbus", "Qt6", "Qt::DBus", extra=["COMPONENTS", "DBus"]), + LibraryMapping("designer", "Qt6", "Qt::Designer", extra=["COMPONENTS", "Designer"]), + LibraryMapping( + "designercomponents", + "Qt6", + "Qt::DesignerComponents", + extra=["COMPONENTS", "DesignerComponents"], + ), + LibraryMapping( + "devicediscovery", + "Qt6", + "Qt::DeviceDiscoverySupport", + extra=["COMPONENTS", "DeviceDiscoverySupport"], + ), + LibraryMapping( + "devicediscovery_support", + "Qt6", + "Qt::DeviceDiscoverySupport", + extra=["COMPONENTS", "DeviceDiscoverySupport"], + ), + LibraryMapping("edid", "Qt6", "Qt::EdidSupport", extra=["COMPONENTS", "EdidSupport"]), + LibraryMapping("edid_support", "Qt6", "Qt::EdidSupport", extra=["COMPONENTS", "EdidSupport"]), + LibraryMapping("eglconvenience", "Qt6", "Qt::EglSupport", extra=["COMPONENTS", "EglSupport"]), + LibraryMapping( + "eglfsdeviceintegration", + "Qt6", + "Qt::EglFSDeviceIntegration", + extra=["COMPONENTS", "EglFSDeviceIntegration"], + ), + LibraryMapping( + "eglfs_kms_support", "Qt6", "Qt::EglFsKmsSupport", extra=["COMPONENTS", "EglFsKmsSupport"] + ), + LibraryMapping("egl_support", "Qt6", "Qt::EglSupport", extra=["COMPONENTS", "EglSupport"]), # enginio: Not needed in Qt6! - LibraryMapping('eventdispatchers', 'Qt6', 'Qt::EventDispatcherSupport', extra = ['COMPONENTS', 'EventDispatcherSupport']), - LibraryMapping('eventdispatcher_support', 'Qt6', 'Qt::EventDispatcherSupport', extra = ['COMPONENTS', 'EventDispatcherSupport']), - LibraryMapping('extras', 'Qt6', 'Qt::3DExtras', extra = ['COMPONENTS', '3DExtras']), - LibraryMapping('fbconvenience', 'Qt6', 'Qt::FbSupport', extra = ['COMPONENTS', 'FbSupport']), - LibraryMapping('fb_support', 'Qt6', 'Qt::FbSupport', extra = ['COMPONENTS', 'FbSupport']), - LibraryMapping('fontdatabase_support', 'Qt6', 'Qt::FontDatabaseSupport', extra = ['COMPONENTS', 'FontDatabaseSupport']), - LibraryMapping('gamepad', 'Qt6', 'Qt::Gamepad', extra = ['COMPONENTS', 'Gamepad']), - LibraryMapping('global', 'Qt6', 'Qt::Core', extra = ['COMPONENTS', 'Core']), # manually added special case - LibraryMapping('glx_support', 'Qt6', 'Qt::GlxSupport', extra = ['COMPONENTS', 'GlxSupport']), - LibraryMapping('graphics_support', 'Qt6', 'Qt::GraphicsSupport', extra = ['COMPONENTS', 'GraphicsSupport']), - LibraryMapping('gsttools', 'Qt6', 'Qt::MultimediaGstTools', extra = ['COMPONENTS', 'MultimediaGstTools']), - LibraryMapping('gui', 'Qt6', 'Qt::Gui', extra = ['COMPONENTS', 'Gui']), - LibraryMapping('help', 'Qt6', 'Qt::Help', extra = ['COMPONENTS', 'Help']), - LibraryMapping('hunspellinputmethod', 'Qt6', 'Qt::HunspellInputMethod', extra = ['COMPONENTS', 'HunspellInputMethod']), - LibraryMapping('input', 'Qt6', 'Qt::InputSupport', extra = ['COMPONENTS', 'InputSupport']), - LibraryMapping('input_support', 'Qt6', 'Qt::InputSupport', extra = ['COMPONENTS', 'InputSupport']), - LibraryMapping('installer-lib', 'Qt6', 'Qt::AppManInstaller', extra = ['COMPONENTS', 'AppManInstaller']), - LibraryMapping('knx', 'Qt6', 'Qt::Knx', extra = ['COMPONENTS', 'Knx']), - LibraryMapping('kmsconvenience', 'Qt6', 'Qt::KmsSupport', extra = ['COMPONENTS', 'KmsSupport']), - LibraryMapping('kms_support', 'Qt6', 'Qt::KmsSupport', extra = ['COMPONENTS', 'KmsSupport']), - LibraryMapping('launcher-lib', 'Qt6', 'Qt::AppManLauncher', extra = ['COMPONENTS', 'AppManLauncher']), - LibraryMapping('lib', 'Qt6', 'Qt::Designer', extra = ['COMPONENTS', 'Designer']), - LibraryMapping('linuxaccessibility_support', 'Qt6', 'Qt::LinuxAccessibilitySupport', extra = ['COMPONENTS', 'LinuxAccessibilitySupport']), - LibraryMapping('location', 'Qt6', 'Qt::Location', extra = ['COMPONENTS', 'Location']), - LibraryMapping('logic', 'Qt6', 'Qt::3DLogic', extra = ['COMPONENTS', '3DLogic']), - LibraryMapping('macextras', 'Qt6', 'Qt::MacExtras', extra = ['COMPONENTS', 'MacExtras']), - LibraryMapping('main-lib', 'Qt6', 'Qt::AppManMain', extra = ['COMPONENTS', 'AppManMain']), - LibraryMapping('manager-lib', 'Qt6', 'Qt::AppManManager', extra = ['COMPONENTS', 'AppManManager']), - LibraryMapping('monitor-lib', 'Qt6', 'Qt::AppManMonitor', extra = ['COMPONENTS', 'AppManMonitor']), - LibraryMapping('mqtt', 'Qt6', 'Qt::Mqtt', extra = ['COMPONENTS', 'Mqtt']), - LibraryMapping('multimedia', 'Qt6', 'Qt::Multimedia', extra = ['COMPONENTS', 'Multimedia']), - LibraryMapping('multimediawidgets', 'Qt6', 'Qt::MultimediaWidgets', extra = ['COMPONENTS', 'MultimediaWidgets']), - LibraryMapping('network', 'Qt6', 'Qt::Network', extra = ['COMPONENTS', 'Network']), - LibraryMapping('networkauth', 'Qt6', 'Qt::NetworkAuth', extra = ['COMPONENTS', 'NetworkAuth']), - LibraryMapping('nfc', 'Qt6', 'Qt::Nfc', extra = ['COMPONENTS', 'Nfc']), - LibraryMapping('oauth', 'Qt6', 'Qt::NetworkAuth', extra = ['COMPONENTS', 'NetworkAuth']), - LibraryMapping('openglextensions', 'Qt6', 'Qt::OpenGLExtensions', extra = ['COMPONENTS', 'OpenGLExtensions']), - LibraryMapping('opengl', 'Qt6', 'Qt::OpenGL', extra = ['COMPONENTS', 'OpenGL']), - LibraryMapping('package-lib', 'Qt6', 'Qt::AppManPackage', extra = ['COMPONENTS', 'AppManPackage']), - LibraryMapping('packetprotocol', 'Qt6', 'Qt::PacketProtocol', extra = ['COMPONENTS', 'PacketProtocol']), - LibraryMapping('particles', 'Qt6', 'Qt::QuickParticles', extra = ['COMPONENTS', 'QuickParticles']), - LibraryMapping('platformcompositor', 'Qt6', 'Qt::PlatformCompositorSupport', extra = ['COMPONENTS', 'PlatformCompositorSupport']), - LibraryMapping('platformcompositor_support', 'Qt6', 'Qt::PlatformCompositorSupport', extra = ['COMPONENTS', 'PlatformCompositorSupport']), - LibraryMapping('plugin-interfaces', 'Qt6', 'Qt::AppManPluginInterfaces', extra = ['COMPONENTS', 'AppManPluginInterfaces']), - LibraryMapping('positioning', 'Qt6', 'Qt::Positioning', extra = ['COMPONENTS', 'Positioning']), - LibraryMapping('positioningquick', 'Qt6', 'Qt::PositioningQuick', extra = ['COMPONENTS', 'PositioningQuick']), - LibraryMapping('printsupport', 'Qt6', 'Qt::PrintSupport', extra = ['COMPONENTS', 'PrintSupport']), - LibraryMapping('purchasing', 'Qt6', 'Qt::Purchasing', extra = ['COMPONENTS', 'Purchasing']), - LibraryMapping('qmldebug', 'Qt6', 'Qt::QmlDebug', extra = ['COMPONENTS', 'QmlDebug']), - LibraryMapping('qmldevtools', 'Qt6', 'Qt::QmlDevTools', extra = ['COMPONENTS', 'QmlDevTools']), - LibraryMapping('qml', 'Qt6', 'Qt::Qml', extra = ['COMPONENTS', 'Qml']), - LibraryMapping('qmlmodels', 'Qt6', 'Qt::QmlModels', extra = ['COMPONENTS', 'QmlModels']), - LibraryMapping('qmltest', 'Qt6', 'Qt::QuickTest', extra = ['COMPONENTS', 'QuickTest']), - LibraryMapping('qtmultimediaquicktools', 'Qt6', 'Qt::MultimediaQuick', extra = ['COMPONENTS', 'MultimediaQuick']), - LibraryMapping('quick3danimation', 'Qt6', 'Qt::3DQuickAnimation', extra = ['COMPONENTS', '3DQuickAnimation']), - LibraryMapping('quick3dextras', 'Qt6', 'Qt::3DQuickExtras', extra = ['COMPONENTS', '3DQuickExtras']), - LibraryMapping('quick3dinput', 'Qt6', 'Qt::3DQuickInput', extra = ['COMPONENTS', '3DQuickInput']), - LibraryMapping('quick3d', 'Qt6', 'Qt::3DQuick', extra = ['COMPONENTS', '3DQuick']), - LibraryMapping('quick3drender', 'Qt6', 'Qt::3DQuickRender', extra = ['COMPONENTS', '3DQuickRender']), - LibraryMapping('quick3dscene2d', 'Qt6', 'Qt::3DQuickScene2D', extra = ['COMPONENTS', '3DQuickScene2D']), - LibraryMapping('quickcontrols2', 'Qt6', 'Qt::QuickControls2', extra = ['COMPONENTS', 'QuickControls2']), - LibraryMapping('quick', 'Qt6', 'Qt::Quick', extra = ['COMPONENTS', 'Quick']), - LibraryMapping('quickshapes', 'Qt6', 'Qt::QuickShapes', extra = ['COMPONENTS', 'QuickShapes']), - LibraryMapping('quicktemplates2', 'Qt6', 'Qt::QuickTemplates2', extra = ['COMPONENTS', 'QuickTemplates2']), - LibraryMapping('quickwidgets', 'Qt6', 'Qt::QuickWidgets', extra = ['COMPONENTS', 'QuickWidgets']), - LibraryMapping('render', 'Qt6', 'Qt::3DRender', extra = ['COMPONENTS', '3DRender']), - LibraryMapping('script', 'Qt6', 'Qt::Script', extra = ['COMPONENTS', 'Script']), - LibraryMapping('scripttools', 'Qt6', 'Qt::ScriptTools', extra = ['COMPONENTS', 'ScriptTools']), - LibraryMapping('sensors', 'Qt6', 'Qt::Sensors', extra = ['COMPONENTS', 'Sensors']), - LibraryMapping('serialport', 'Qt6', 'Qt::SerialPort', extra = ['COMPONENTS', 'SerialPort']), - LibraryMapping('serialbus', 'Qt6', 'Qt::SerialBus', extra = ['COMPONENTS', 'SerialBus']), - LibraryMapping('services', 'Qt6', 'Qt::ServiceSupport', extra = ['COMPONENTS', 'ServiceSupport']), - LibraryMapping('service_support', 'Qt6', 'Qt::ServiceSupport', extra = ['COMPONENTS', 'ServiceSupport']), - LibraryMapping('sql', 'Qt6', 'Qt::Sql', extra = ['COMPONENTS', 'Sql']), - LibraryMapping('svg', 'Qt6', 'Qt::Svg', extra = ['COMPONENTS', 'Svg']), - LibraryMapping('testlib', 'Qt6', 'Qt::Test', extra = ['COMPONENTS', 'Test']), - LibraryMapping('texttospeech', 'Qt6', 'Qt::TextToSpeech', extra = ['COMPONENTS', 'TextToSpeech']), - LibraryMapping('theme_support', 'Qt6', 'Qt::ThemeSupport', extra = ['COMPONENTS', 'ThemeSupport']), - LibraryMapping('tts', 'Qt6', 'Qt::TextToSpeech', extra = ['COMPONENTS', 'TextToSpeech']), - LibraryMapping('uiplugin', 'Qt6', 'Qt::UiPlugin', extra = ['COMPONENTS', 'UiPlugin']), - LibraryMapping('uitools', 'Qt6', 'Qt::UiTools', extra = ['COMPONENTS', 'UiTools']), - LibraryMapping('virtualkeyboard', 'Qt6', 'Qt::VirtualKeyboard', extra = ['COMPONENTS', 'VirtualKeyboard']), - LibraryMapping('vulkan_support', 'Qt6', 'Qt::VulkanSupport', extra = ['COMPONENTS', 'VulkanSupport']), - LibraryMapping('waylandclient', 'Qt6', 'Qt::WaylandClient', extra = ['COMPONENTS', 'WaylandClient']), - LibraryMapping('webchannel', 'Qt6', 'Qt::WebChannel', extra = ['COMPONENTS', 'WebChannel']), - LibraryMapping('webengine', 'Qt6', 'Qt::WebEngine', extra = ['COMPONENTS', 'WebEngine']), - LibraryMapping('webenginewidgets', 'Qt6', 'Qt::WebEngineWidgets', extra = ['COMPONENTS', 'WebEngineWidgets']), - LibraryMapping('websockets', 'Qt6', 'Qt::WebSockets', extra = ['COMPONENTS', 'WebSockets']), - LibraryMapping('webview', 'Qt6', 'Qt::WebView', extra = ['COMPONENTS', 'WebView']), - LibraryMapping('widgets', 'Qt6', 'Qt::Widgets', extra = ['COMPONENTS', 'Widgets']), - LibraryMapping('window-lib', 'Qt6', 'Qt::AppManWindow', extra = ['COMPONENTS', 'AppManWindow']), - LibraryMapping('windowsuiautomation_support', 'Qt6', 'Qt::WindowsUIAutomationSupport', extra = ['COMPONENTS', 'WindowsUIAutomationSupport']), - LibraryMapping('winextras', 'Qt6', 'Qt::WinExtras', extra = ['COMPONENTS', 'WinExtras']), - LibraryMapping('x11extras', 'Qt6', 'Qt::X11Extras', extra = ['COMPONENTS', 'X11Extras']), - LibraryMapping('xcb_qpa_lib', 'Qt6', 'Qt::XcbQpa', extra = ['COMPONENTS', 'XcbQpa']), - LibraryMapping('xkbcommon_support', 'Qt6', 'Qt::XkbCommonSupport', extra = ['COMPONENTS', 'XkbCommonSupport']), - LibraryMapping('xmlpatterns', 'Qt6', 'Qt::XmlPatterns', extra = ['COMPONENTS', 'XmlPatterns']), - LibraryMapping('xml', 'Qt6', 'Qt::Xml', extra = ['COMPONENTS', 'Xml']), - LibraryMapping('qmlworkerscript', 'Qt6', 'Qt::QmlWorkerScript', extra = ['COMPONENTS', 'QmlWorkerScript']), - LibraryMapping('quickparticles', 'Qt6', 'Qt::QuickParticles', extra = ['COMPONENTS', 'QuickParticles']) + LibraryMapping( + "eventdispatchers", + "Qt6", + "Qt::EventDispatcherSupport", + extra=["COMPONENTS", "EventDispatcherSupport"], + ), + LibraryMapping( + "eventdispatcher_support", + "Qt6", + "Qt::EventDispatcherSupport", + extra=["COMPONENTS", "EventDispatcherSupport"], + ), + LibraryMapping("extras", "Qt6", "Qt::3DExtras", extra=["COMPONENTS", "3DExtras"]), + LibraryMapping("fbconvenience", "Qt6", "Qt::FbSupport", extra=["COMPONENTS", "FbSupport"]), + LibraryMapping("fb_support", "Qt6", "Qt::FbSupport", extra=["COMPONENTS", "FbSupport"]), + LibraryMapping( + "fontdatabase_support", + "Qt6", + "Qt::FontDatabaseSupport", + extra=["COMPONENTS", "FontDatabaseSupport"], + ), + LibraryMapping("gamepad", "Qt6", "Qt::Gamepad", extra=["COMPONENTS", "Gamepad"]), + LibraryMapping( + "global", "Qt6", "Qt::Core", extra=["COMPONENTS", "Core"] + ), # manually added special case + LibraryMapping("glx_support", "Qt6", "Qt::GlxSupport", extra=["COMPONENTS", "GlxSupport"]), + LibraryMapping( + "graphics_support", "Qt6", "Qt::GraphicsSupport", extra=["COMPONENTS", "GraphicsSupport"] + ), + LibraryMapping( + "gsttools", "Qt6", "Qt::MultimediaGstTools", extra=["COMPONENTS", "MultimediaGstTools"] + ), + LibraryMapping("gui", "Qt6", "Qt::Gui", extra=["COMPONENTS", "Gui"]), + LibraryMapping("help", "Qt6", "Qt::Help", extra=["COMPONENTS", "Help"]), + LibraryMapping( + "hunspellinputmethod", + "Qt6", + "Qt::HunspellInputMethod", + extra=["COMPONENTS", "HunspellInputMethod"], + ), + LibraryMapping("input", "Qt6", "Qt::InputSupport", extra=["COMPONENTS", "InputSupport"]), + LibraryMapping( + "input_support", "Qt6", "Qt::InputSupport", extra=["COMPONENTS", "InputSupport"] + ), + LibraryMapping( + "installer-lib", "Qt6", "Qt::AppManInstaller", extra=["COMPONENTS", "AppManInstaller"] + ), + LibraryMapping("knx", "Qt6", "Qt::Knx", extra=["COMPONENTS", "Knx"]), + LibraryMapping("kmsconvenience", "Qt6", "Qt::KmsSupport", extra=["COMPONENTS", "KmsSupport"]), + LibraryMapping("kms_support", "Qt6", "Qt::KmsSupport", extra=["COMPONENTS", "KmsSupport"]), + LibraryMapping( + "launcher-lib", "Qt6", "Qt::AppManLauncher", extra=["COMPONENTS", "AppManLauncher"] + ), + LibraryMapping("lib", "Qt6", "Qt::Designer", extra=["COMPONENTS", "Designer"]), + LibraryMapping( + "linuxaccessibility_support", + "Qt6", + "Qt::LinuxAccessibilitySupport", + extra=["COMPONENTS", "LinuxAccessibilitySupport"], + ), + LibraryMapping("location", "Qt6", "Qt::Location", extra=["COMPONENTS", "Location"]), + LibraryMapping("logic", "Qt6", "Qt::3DLogic", extra=["COMPONENTS", "3DLogic"]), + LibraryMapping("macextras", "Qt6", "Qt::MacExtras", extra=["COMPONENTS", "MacExtras"]), + LibraryMapping("main-lib", "Qt6", "Qt::AppManMain", extra=["COMPONENTS", "AppManMain"]), + LibraryMapping( + "manager-lib", "Qt6", "Qt::AppManManager", extra=["COMPONENTS", "AppManManager"] + ), + LibraryMapping( + "monitor-lib", "Qt6", "Qt::AppManMonitor", extra=["COMPONENTS", "AppManMonitor"] + ), + LibraryMapping("mqtt", "Qt6", "Qt::Mqtt", extra=["COMPONENTS", "Mqtt"]), + LibraryMapping("multimedia", "Qt6", "Qt::Multimedia", extra=["COMPONENTS", "Multimedia"]), + LibraryMapping( + "multimediawidgets", + "Qt6", + "Qt::MultimediaWidgets", + extra=["COMPONENTS", "MultimediaWidgets"], + ), + LibraryMapping("network", "Qt6", "Qt::Network", extra=["COMPONENTS", "Network"]), + LibraryMapping("networkauth", "Qt6", "Qt::NetworkAuth", extra=["COMPONENTS", "NetworkAuth"]), + LibraryMapping("nfc", "Qt6", "Qt::Nfc", extra=["COMPONENTS", "Nfc"]), + LibraryMapping("oauth", "Qt6", "Qt::NetworkAuth", extra=["COMPONENTS", "NetworkAuth"]), + LibraryMapping( + "openglextensions", "Qt6", "Qt::OpenGLExtensions", extra=["COMPONENTS", "OpenGLExtensions"] + ), + LibraryMapping("opengl", "Qt6", "Qt::OpenGL", extra=["COMPONENTS", "OpenGL"]), + LibraryMapping( + "package-lib", "Qt6", "Qt::AppManPackage", extra=["COMPONENTS", "AppManPackage"] + ), + LibraryMapping( + "packetprotocol", "Qt6", "Qt::PacketProtocol", extra=["COMPONENTS", "PacketProtocol"] + ), + LibraryMapping( + "particles", "Qt6", "Qt::QuickParticles", extra=["COMPONENTS", "QuickParticles"] + ), + LibraryMapping( + "platformcompositor", + "Qt6", + "Qt::PlatformCompositorSupport", + extra=["COMPONENTS", "PlatformCompositorSupport"], + ), + LibraryMapping( + "platformcompositor_support", + "Qt6", + "Qt::PlatformCompositorSupport", + extra=["COMPONENTS", "PlatformCompositorSupport"], + ), + LibraryMapping( + "plugin-interfaces", + "Qt6", + "Qt::AppManPluginInterfaces", + extra=["COMPONENTS", "AppManPluginInterfaces"], + ), + LibraryMapping("positioning", "Qt6", "Qt::Positioning", extra=["COMPONENTS", "Positioning"]), + LibraryMapping( + "positioningquick", "Qt6", "Qt::PositioningQuick", extra=["COMPONENTS", "PositioningQuick"] + ), + LibraryMapping("printsupport", "Qt6", "Qt::PrintSupport", extra=["COMPONENTS", "PrintSupport"]), + LibraryMapping("purchasing", "Qt6", "Qt::Purchasing", extra=["COMPONENTS", "Purchasing"]), + LibraryMapping("qmldebug", "Qt6", "Qt::QmlDebug", extra=["COMPONENTS", "QmlDebug"]), + LibraryMapping("qmldevtools", "Qt6", "Qt::QmlDevTools", extra=["COMPONENTS", "QmlDevTools"]), + LibraryMapping("qml", "Qt6", "Qt::Qml", extra=["COMPONENTS", "Qml"]), + LibraryMapping("qmlmodels", "Qt6", "Qt::QmlModels", extra=["COMPONENTS", "QmlModels"]), + LibraryMapping("qmltest", "Qt6", "Qt::QuickTest", extra=["COMPONENTS", "QuickTest"]), + LibraryMapping( + "qtmultimediaquicktools", + "Qt6", + "Qt::MultimediaQuick", + extra=["COMPONENTS", "MultimediaQuick"], + ), + LibraryMapping( + "quick3danimation", "Qt6", "Qt::3DQuickAnimation", extra=["COMPONENTS", "3DQuickAnimation"] + ), + LibraryMapping( + "quick3dextras", "Qt6", "Qt::3DQuickExtras", extra=["COMPONENTS", "3DQuickExtras"] + ), + LibraryMapping("quick3dinput", "Qt6", "Qt::3DQuickInput", extra=["COMPONENTS", "3DQuickInput"]), + LibraryMapping("quick3d", "Qt6", "Qt::3DQuick", extra=["COMPONENTS", "3DQuick"]), + LibraryMapping( + "quick3drender", "Qt6", "Qt::3DQuickRender", extra=["COMPONENTS", "3DQuickRender"] + ), + LibraryMapping( + "quick3dscene2d", "Qt6", "Qt::3DQuickScene2D", extra=["COMPONENTS", "3DQuickScene2D"] + ), + LibraryMapping( + "quickcontrols2", "Qt6", "Qt::QuickControls2", extra=["COMPONENTS", "QuickControls2"] + ), + LibraryMapping("quick", "Qt6", "Qt::Quick", extra=["COMPONENTS", "Quick"]), + LibraryMapping("quickshapes", "Qt6", "Qt::QuickShapes", extra=["COMPONENTS", "QuickShapes"]), + LibraryMapping( + "quicktemplates2", "Qt6", "Qt::QuickTemplates2", extra=["COMPONENTS", "QuickTemplates2"] + ), + LibraryMapping("quickwidgets", "Qt6", "Qt::QuickWidgets", extra=["COMPONENTS", "QuickWidgets"]), + LibraryMapping("render", "Qt6", "Qt::3DRender", extra=["COMPONENTS", "3DRender"]), + LibraryMapping("script", "Qt6", "Qt::Script", extra=["COMPONENTS", "Script"]), + LibraryMapping("scripttools", "Qt6", "Qt::ScriptTools", extra=["COMPONENTS", "ScriptTools"]), + LibraryMapping("sensors", "Qt6", "Qt::Sensors", extra=["COMPONENTS", "Sensors"]), + LibraryMapping("serialport", "Qt6", "Qt::SerialPort", extra=["COMPONENTS", "SerialPort"]), + LibraryMapping("serialbus", "Qt6", "Qt::SerialBus", extra=["COMPONENTS", "SerialBus"]), + LibraryMapping("services", "Qt6", "Qt::ServiceSupport", extra=["COMPONENTS", "ServiceSupport"]), + LibraryMapping( + "service_support", "Qt6", "Qt::ServiceSupport", extra=["COMPONENTS", "ServiceSupport"] + ), + LibraryMapping("sql", "Qt6", "Qt::Sql", extra=["COMPONENTS", "Sql"]), + LibraryMapping("svg", "Qt6", "Qt::Svg", extra=["COMPONENTS", "Svg"]), + LibraryMapping("testlib", "Qt6", "Qt::Test", extra=["COMPONENTS", "Test"]), + LibraryMapping("texttospeech", "Qt6", "Qt::TextToSpeech", extra=["COMPONENTS", "TextToSpeech"]), + LibraryMapping( + "theme_support", "Qt6", "Qt::ThemeSupport", extra=["COMPONENTS", "ThemeSupport"] + ), + LibraryMapping("tts", "Qt6", "Qt::TextToSpeech", extra=["COMPONENTS", "TextToSpeech"]), + LibraryMapping("uiplugin", "Qt6", "Qt::UiPlugin", extra=["COMPONENTS", "UiPlugin"]), + LibraryMapping("uitools", "Qt6", "Qt::UiTools", extra=["COMPONENTS", "UiTools"]), + LibraryMapping( + "virtualkeyboard", "Qt6", "Qt::VirtualKeyboard", extra=["COMPONENTS", "VirtualKeyboard"] + ), + LibraryMapping( + "vulkan_support", "Qt6", "Qt::VulkanSupport", extra=["COMPONENTS", "VulkanSupport"] + ), + LibraryMapping( + "waylandclient", "Qt6", "Qt::WaylandClient", extra=["COMPONENTS", "WaylandClient"] + ), + LibraryMapping("webchannel", "Qt6", "Qt::WebChannel", extra=["COMPONENTS", "WebChannel"]), + LibraryMapping("webengine", "Qt6", "Qt::WebEngine", extra=["COMPONENTS", "WebEngine"]), + LibraryMapping( + "webenginewidgets", "Qt6", "Qt::WebEngineWidgets", extra=["COMPONENTS", "WebEngineWidgets"] + ), + LibraryMapping("websockets", "Qt6", "Qt::WebSockets", extra=["COMPONENTS", "WebSockets"]), + LibraryMapping("webview", "Qt6", "Qt::WebView", extra=["COMPONENTS", "WebView"]), + LibraryMapping("widgets", "Qt6", "Qt::Widgets", extra=["COMPONENTS", "Widgets"]), + LibraryMapping("window-lib", "Qt6", "Qt::AppManWindow", extra=["COMPONENTS", "AppManWindow"]), + LibraryMapping( + "windowsuiautomation_support", + "Qt6", + "Qt::WindowsUIAutomationSupport", + extra=["COMPONENTS", "WindowsUIAutomationSupport"], + ), + LibraryMapping("winextras", "Qt6", "Qt::WinExtras", extra=["COMPONENTS", "WinExtras"]), + LibraryMapping("x11extras", "Qt6", "Qt::X11Extras", extra=["COMPONENTS", "X11Extras"]), + LibraryMapping("xcb_qpa_lib", "Qt6", "Qt::XcbQpa", extra=["COMPONENTS", "XcbQpa"]), + LibraryMapping( + "xkbcommon_support", "Qt6", "Qt::XkbCommonSupport", extra=["COMPONENTS", "XkbCommonSupport"] + ), + LibraryMapping("xmlpatterns", "Qt6", "Qt::XmlPatterns", extra=["COMPONENTS", "XmlPatterns"]), + LibraryMapping("xml", "Qt6", "Qt::Xml", extra=["COMPONENTS", "Xml"]), + LibraryMapping( + "qmlworkerscript", "Qt6", "Qt::QmlWorkerScript", extra=["COMPONENTS", "QmlWorkerScript"] + ), + LibraryMapping( + "quickparticles", "Qt6", "Qt::QuickParticles", extra=["COMPONENTS", "QuickParticles"] + ) # qtzlib: No longer supported. ] # Note that the library map is adjusted dynamically further down. _library_map = [ # 3rd party: - LibraryMapping('atspi', 'ATSPI2', 'PkgConfig::ATSPI2'), - LibraryMapping('corewlan', None, None), - LibraryMapping('cups', 'Cups', 'Cups::Cups'), - LibraryMapping('db2', 'DB2', 'DB2::DB2'), - LibraryMapping('dbus', 'WrapDBus1', 'dbus-1', resultVariable="DBus1"), - LibraryMapping('doubleconversion', None, None), - LibraryMapping('drm', 'Libdrm', 'Libdrm::Libdrm'), - LibraryMapping('egl', 'EGL', 'EGL::EGL'), - LibraryMapping('flite', 'Flite', 'Flite::Flite'), - LibraryMapping('flite_alsa', 'ALSA', 'ALSA::ALSA'), - LibraryMapping('fontconfig', 'Fontconfig', 'Fontconfig::Fontconfig', resultVariable="FONTCONFIG"), - LibraryMapping('freetype', 'WrapFreetype', 'WrapFreetype::WrapFreetype', extra=['REQUIRED']), - LibraryMapping('gbm', 'gbm', 'gbm::gbm'), - LibraryMapping('glib', 'GLIB2', 'GLIB2::GLIB2'), - LibraryMapping('gnu_iconv', None, None), - LibraryMapping('gtk3', 'GTK3', 'PkgConfig::GTK3'), - LibraryMapping('harfbuzz', 'harfbuzz', 'harfbuzz::harfbuzz'), - LibraryMapping('host_dbus', None, None), - LibraryMapping('icu', 'ICU', 'ICU::i18n ICU::uc ICU::data', extra=['COMPONENTS', 'i18n', 'uc', 'data']), - LibraryMapping('journald', 'Libsystemd', 'PkgConfig::Libsystemd'), - LibraryMapping('jpeg', 'JPEG', 'JPEG::JPEG'), # see also libjpeg - LibraryMapping('libatomic', 'Atomic', 'Atomic'), - LibraryMapping('libclang', 'WrapLibClang', 'WrapLibClang::WrapLibClang'), - LibraryMapping('libdl', None, '${CMAKE_DL_LIBS}'), - LibraryMapping('libinput', 'Libinput', 'Libinput::Libinput'), - LibraryMapping('libjpeg', 'JPEG', 'JPEG::JPEG'), # see also jpeg - LibraryMapping('libpng', 'PNG', 'PNG::PNG'), - LibraryMapping('libproxy', 'Libproxy', 'PkgConfig::Libproxy'), - LibraryMapping('librt', 'WrapRt','WrapRt'), - LibraryMapping('libudev', 'Libudev', 'PkgConfig::Libudev'), - LibraryMapping('lttng-ust', 'LTTngUST', 'LTTng::UST', resultVariable='LTTNGUST'), - LibraryMapping('mtdev', 'Mtdev', 'PkgConfig::Mtdev'), - LibraryMapping('mysql', 'MySQL', 'MySQL::MySQL'), - LibraryMapping('odbc', 'ODBC', 'ODBC::ODBC'), - LibraryMapping('opengl_es2', 'GLESv2', 'GLESv2::GLESv2'), - LibraryMapping('opengl', 'OpenGL', 'OpenGL::GL', resultVariable='OpenGL_OpenGL'), - LibraryMapping('openssl_headers', 'OpenSSL', 'OpenSSL::SSL_nolink', resultVariable='OPENSSL_INCLUDE_DIR', appendFoundSuffix=False), - LibraryMapping('openssl', 'OpenSSL', 'OpenSSL::SSL'), - LibraryMapping('oci', 'Oracle', 'Oracle::OCI'), - LibraryMapping('pcre2', 'WrapPCRE2', 'WrapPCRE2::WrapPCRE2', extra = ['REQUIRED']), - LibraryMapping('posix_iconv', None, None), - LibraryMapping('pps', 'PPS', 'PPS::PPS'), - LibraryMapping('psql', 'PostgreSQL', 'PostgreSQL::PostgreSQL'), - LibraryMapping('slog2', 'Slog2', 'Slog2::Slog2'), - LibraryMapping('speechd', 'SpeechDispatcher', 'SpeechDispatcher::SpeechDispatcher'), - LibraryMapping('sqlite2', None, None), # No more sqlite2 support in Qt6! - LibraryMapping('sqlite3', 'SQLite3', 'SQLite::SQLite3'), - LibraryMapping('sun_iconv', None, None), - LibraryMapping('tslib', 'Tslib', 'PkgConfig::Tslib'), - LibraryMapping('udev', 'Libudev', 'PkgConfig::Libudev'), - LibraryMapping('udev', 'Libudev', 'PkgConfig::Libudev'), # see also libudev! - LibraryMapping('vulkan', 'Vulkan', 'Vulkan::Vulkan'), - LibraryMapping('wayland-server', 'Wayland', 'Wayland::Server'), - LibraryMapping('wayland-client', 'Wayland', 'Wayland::Client'), - LibraryMapping('wayland-cursor', 'Wayland', 'Wayland::Cursor'), - LibraryMapping('wayland-egl', 'Wayland', 'Wayland::Egl'), - LibraryMapping('x11sm', 'X11', '${X11_SM_LIB} ${X11_ICE_LIB}', resultVariable="X11_SM"), - LibraryMapping('xcb', 'XCB', 'XCB::XCB', extra = ['1.9'], resultVariable='TARGET XCB::XCB', appendFoundSuffix=False), - LibraryMapping('xcb_glx', 'XCB', 'XCB::GLX', extra = ['COMPONENTS', 'GLX'], resultVariable='XCB_GLX'), - LibraryMapping('xcb_icccm', 'XCB', 'XCB::ICCCM', extra = ['COMPONENTS', 'ICCCM'], resultVariable='XCB_ICCCM'), - LibraryMapping('xcb_image', 'XCB', 'XCB::IMAGE', extra = ['COMPONENTS', 'IMAGE'], resultVariable='XCB_IMAGE'), - LibraryMapping('xcb_keysyms', 'XCB', 'XCB::KEYSYMS', extra = ['COMPONENTS', 'KEYSYMS'], resultVariable='XCB_KEYSYMS'), - LibraryMapping('xcb_randr', 'XCB', 'XCB::RANDR', extra = ['COMPONENTS', 'RANDR'], resultVariable='XCB_RANDR'), - LibraryMapping('xcb_render', 'XCB', 'XCB::RENDER', extra = ['COMPONENTS', 'RENDER'], resultVariable='XCB_RENDER'), - LibraryMapping('xcb_renderutil', 'XCB', 'XCB::RENDERUTIL', extra = ['COMPONENTS', 'RENDERUTIL'], resultVariable='XCB_RENDERUTIL'), - LibraryMapping('xcb_shape', 'XCB', 'XCB::SHAPE', extra = ['COMPONENTS', 'SHAPE'], resultVariable='XCB_SHAPE'), - LibraryMapping('xcb_shm', 'XCB', 'XCB::SHM', extra = ['COMPONENTS', 'SHM'], resultVariable='XCB_SHM'), - LibraryMapping('xcb_sync', 'XCB', 'XCB::SYNC', extra = ['COMPONENTS', 'SYNC'], resultVariable='XCB_SYNC'), - LibraryMapping('xcb_xfixes', 'XCB', 'XCB::XFIXES', extra = ['COMPONENTS', 'XFIXES'], resultVariable='XCB_XFIXES'), - LibraryMapping('xcb_xinerama', 'XCB', 'XCB::XINERAMA', extra = ['COMPONENTS', 'XINERAMA'], resultVariable='XCB_XINERAMA'), - LibraryMapping('xcb_xinput', 'XCB', 'XCB::XINPUT', extra = ['COMPONENTS', 'XINPUT'], resultVariable='XCB_XINPUT'), - LibraryMapping('xcb_xkb', 'XCB', 'XCB::XKB', extra = ['COMPONENTS', 'XKB'], resultVariable='XCB_XKB'), - LibraryMapping('xcb_xlib', 'X11_XCB', 'X11::XCB'), - LibraryMapping('xkbcommon_evdev', 'XKB', 'XKB::XKB', extra = ['0.4.1']), # see also xkbcommon - LibraryMapping('xkbcommon_x11', 'XKB', 'XKB::XKB', extra = ['0.4.1']), # see also xkbcommon - LibraryMapping('xkbcommon', 'XKB', 'XKB::XKB', extra = ['0.4.1']), - LibraryMapping('xlib', 'X11', 'X11::XCB'), # FIXME: Is this correct? - LibraryMapping('xrender', 'XRender', 'PkgConfig::XRender'), - LibraryMapping('zlib', 'ZLIB', 'ZLIB::ZLIB', extra=['REQUIRED']), - LibraryMapping('zstd', 'ZSTD', 'ZSTD::ZSTD'), - LibraryMapping('tiff', 'TIFF', 'TIFF::TIFF'), - LibraryMapping('webp', 'WrapWebP', 'WrapWebP::WrapWebP'), - LibraryMapping('jasper', 'WrapJasper', 'WrapJasper::WrapJasper'), + LibraryMapping("atspi", "ATSPI2", "PkgConfig::ATSPI2"), + LibraryMapping("corewlan", None, None), + LibraryMapping("cups", "Cups", "Cups::Cups"), + LibraryMapping("db2", "DB2", "DB2::DB2"), + LibraryMapping("dbus", "WrapDBus1", "dbus-1", resultVariable="DBus1"), + LibraryMapping("doubleconversion", None, None), + LibraryMapping("drm", "Libdrm", "Libdrm::Libdrm"), + LibraryMapping("egl", "EGL", "EGL::EGL"), + LibraryMapping("flite", "Flite", "Flite::Flite"), + LibraryMapping("flite_alsa", "ALSA", "ALSA::ALSA"), + LibraryMapping( + "fontconfig", "Fontconfig", "Fontconfig::Fontconfig", resultVariable="FONTCONFIG" + ), + LibraryMapping("freetype", "WrapFreetype", "WrapFreetype::WrapFreetype", extra=["REQUIRED"]), + LibraryMapping("gbm", "gbm", "gbm::gbm"), + LibraryMapping("glib", "GLIB2", "GLIB2::GLIB2"), + LibraryMapping("gnu_iconv", None, None), + LibraryMapping("gtk3", "GTK3", "PkgConfig::GTK3"), + LibraryMapping("harfbuzz", "harfbuzz", "harfbuzz::harfbuzz"), + LibraryMapping("host_dbus", None, None), + LibraryMapping( + "icu", "ICU", "ICU::i18n ICU::uc ICU::data", extra=["COMPONENTS", "i18n", "uc", "data"] + ), + LibraryMapping("journald", "Libsystemd", "PkgConfig::Libsystemd"), + LibraryMapping("jpeg", "JPEG", "JPEG::JPEG"), # see also libjpeg + LibraryMapping("libatomic", "Atomic", "Atomic"), + LibraryMapping("libclang", "WrapLibClang", "WrapLibClang::WrapLibClang"), + LibraryMapping("libdl", None, "${CMAKE_DL_LIBS}"), + LibraryMapping("libinput", "Libinput", "Libinput::Libinput"), + LibraryMapping("libjpeg", "JPEG", "JPEG::JPEG"), # see also jpeg + LibraryMapping("libpng", "PNG", "PNG::PNG"), + LibraryMapping("libproxy", "Libproxy", "PkgConfig::Libproxy"), + LibraryMapping("librt", "WrapRt", "WrapRt"), + LibraryMapping("libudev", "Libudev", "PkgConfig::Libudev"), + LibraryMapping("lttng-ust", "LTTngUST", "LTTng::UST", resultVariable="LTTNGUST"), + LibraryMapping("mtdev", "Mtdev", "PkgConfig::Mtdev"), + LibraryMapping("mysql", "MySQL", "MySQL::MySQL"), + LibraryMapping("odbc", "ODBC", "ODBC::ODBC"), + LibraryMapping("opengl_es2", "GLESv2", "GLESv2::GLESv2"), + LibraryMapping("opengl", "OpenGL", "OpenGL::GL", resultVariable="OpenGL_OpenGL"), + LibraryMapping( + "openssl_headers", + "OpenSSL", + "OpenSSL::SSL_nolink", + resultVariable="OPENSSL_INCLUDE_DIR", + appendFoundSuffix=False, + ), + LibraryMapping("openssl", "OpenSSL", "OpenSSL::SSL"), + LibraryMapping("oci", "Oracle", "Oracle::OCI"), + LibraryMapping("pcre2", "WrapPCRE2", "WrapPCRE2::WrapPCRE2", extra=["REQUIRED"]), + LibraryMapping("posix_iconv", None, None), + LibraryMapping("pps", "PPS", "PPS::PPS"), + LibraryMapping("psql", "PostgreSQL", "PostgreSQL::PostgreSQL"), + LibraryMapping("slog2", "Slog2", "Slog2::Slog2"), + LibraryMapping("speechd", "SpeechDispatcher", "SpeechDispatcher::SpeechDispatcher"), + LibraryMapping("sqlite2", None, None), # No more sqlite2 support in Qt6! + LibraryMapping("sqlite3", "SQLite3", "SQLite::SQLite3"), + LibraryMapping("sun_iconv", None, None), + LibraryMapping("tslib", "Tslib", "PkgConfig::Tslib"), + LibraryMapping("udev", "Libudev", "PkgConfig::Libudev"), + LibraryMapping("udev", "Libudev", "PkgConfig::Libudev"), # see also libudev! + LibraryMapping("vulkan", "Vulkan", "Vulkan::Vulkan"), + LibraryMapping("wayland-server", "Wayland", "Wayland::Server"), + LibraryMapping("wayland-client", "Wayland", "Wayland::Client"), + LibraryMapping("wayland-cursor", "Wayland", "Wayland::Cursor"), + LibraryMapping("wayland-egl", "Wayland", "Wayland::Egl"), + LibraryMapping("x11sm", "X11", "${X11_SM_LIB} ${X11_ICE_LIB}", resultVariable="X11_SM"), + LibraryMapping( + "xcb", + "XCB", + "XCB::XCB", + extra=["1.9"], + resultVariable="TARGET XCB::XCB", + appendFoundSuffix=False, + ), + LibraryMapping( + "xcb_glx", "XCB", "XCB::GLX", extra=["COMPONENTS", "GLX"], resultVariable="XCB_GLX" + ), + LibraryMapping( + "xcb_icccm", "XCB", "XCB::ICCCM", extra=["COMPONENTS", "ICCCM"], resultVariable="XCB_ICCCM" + ), + LibraryMapping( + "xcb_image", "XCB", "XCB::IMAGE", extra=["COMPONENTS", "IMAGE"], resultVariable="XCB_IMAGE" + ), + LibraryMapping( + "xcb_keysyms", + "XCB", + "XCB::KEYSYMS", + extra=["COMPONENTS", "KEYSYMS"], + resultVariable="XCB_KEYSYMS", + ), + LibraryMapping( + "xcb_randr", "XCB", "XCB::RANDR", extra=["COMPONENTS", "RANDR"], resultVariable="XCB_RANDR" + ), + LibraryMapping( + "xcb_render", + "XCB", + "XCB::RENDER", + extra=["COMPONENTS", "RENDER"], + resultVariable="XCB_RENDER", + ), + LibraryMapping( + "xcb_renderutil", + "XCB", + "XCB::RENDERUTIL", + extra=["COMPONENTS", "RENDERUTIL"], + resultVariable="XCB_RENDERUTIL", + ), + LibraryMapping( + "xcb_shape", "XCB", "XCB::SHAPE", extra=["COMPONENTS", "SHAPE"], resultVariable="XCB_SHAPE" + ), + LibraryMapping( + "xcb_shm", "XCB", "XCB::SHM", extra=["COMPONENTS", "SHM"], resultVariable="XCB_SHM" + ), + LibraryMapping( + "xcb_sync", "XCB", "XCB::SYNC", extra=["COMPONENTS", "SYNC"], resultVariable="XCB_SYNC" + ), + LibraryMapping( + "xcb_xfixes", + "XCB", + "XCB::XFIXES", + extra=["COMPONENTS", "XFIXES"], + resultVariable="XCB_XFIXES", + ), + LibraryMapping( + "xcb_xinerama", + "XCB", + "XCB::XINERAMA", + extra=["COMPONENTS", "XINERAMA"], + resultVariable="XCB_XINERAMA", + ), + LibraryMapping( + "xcb_xinput", + "XCB", + "XCB::XINPUT", + extra=["COMPONENTS", "XINPUT"], + resultVariable="XCB_XINPUT", + ), + LibraryMapping( + "xcb_xkb", "XCB", "XCB::XKB", extra=["COMPONENTS", "XKB"], resultVariable="XCB_XKB" + ), + LibraryMapping("xcb_xlib", "X11_XCB", "X11::XCB"), + LibraryMapping("xkbcommon_evdev", "XKB", "XKB::XKB", extra=["0.4.1"]), # see also xkbcommon + LibraryMapping("xkbcommon_x11", "XKB", "XKB::XKB", extra=["0.4.1"]), # see also xkbcommon + LibraryMapping("xkbcommon", "XKB", "XKB::XKB", extra=["0.4.1"]), + LibraryMapping("xlib", "X11", "X11::XCB"), # FIXME: Is this correct? + LibraryMapping("xrender", "XRender", "PkgConfig::XRender"), + LibraryMapping("zlib", "ZLIB", "ZLIB::ZLIB", extra=["REQUIRED"]), + LibraryMapping("zstd", "ZSTD", "ZSTD::ZSTD"), + LibraryMapping("tiff", "TIFF", "TIFF::TIFF"), + LibraryMapping("webp", "WrapWebP", "WrapWebP::WrapWebP"), + LibraryMapping("jasper", "WrapJasper", "WrapJasper::WrapJasper"), ] @@ -283,10 +504,10 @@ def _adjust_library_map(): # We don't want to get pages of package not found messages on # Windows and macOS, and this also improves configure time on # those platforms. - linux_package_prefixes = ['xcb', 'x11', 'xkb', 'xrender', 'xlib', 'wayland'] + linux_package_prefixes = ["xcb", "x11", "xkb", "xrender", "xlib", "wayland"] for i, _ in enumerate(_library_map): if any([_library_map[i].soName.startswith(p) for p in linux_package_prefixes]): - _library_map[i].emit_if = 'config.linux' + _library_map[i].emit_if = "config.linux" _adjust_library_map() @@ -308,7 +529,7 @@ def find_qt_library_mapping(soName: str) -> typing.Optional[LibraryMapping]: def find_library_info_for_target(targetName: str) -> typing.Optional[LibraryMapping]: qt_target = targetName - if targetName.endswith('Private'): + if targetName.endswith("Private"): qt_target = qt_target[:-7] for i in _qt_library_map: @@ -323,61 +544,61 @@ def find_library_info_for_target(targetName: str) -> typing.Optional[LibraryMapp def featureName(input: str) -> str: - replacement_char = '_' - if input.startswith('c++'): - replacement_char = 'x' - return re.sub(r'[^a-zA-Z0-9_]', replacement_char, input) + replacement_char = "_" + if input.startswith("c++"): + replacement_char = "x" + return re.sub(r"[^a-zA-Z0-9_]", replacement_char, input) def map_qt_library(lib: str) -> str: private = False - if lib.endswith('-private'): + if lib.endswith("-private"): private = True lib = lib[:-8] mapped = find_qt_library_mapping(lib) qt_name = lib if mapped: - assert mapped.targetName # Qt libs must have a target name set + assert mapped.targetName # Qt libs must have a target name set qt_name = mapped.targetName if private: - qt_name += 'Private' + qt_name += "Private" return qt_name platform_mapping = { - 'win32': 'WIN32', - 'win': 'WIN32', - 'unix': 'UNIX', - 'darwin': 'APPLE', - 'linux': 'LINUX', - 'integrity': 'INTEGRITY', - 'qnx': 'QNX', - 'vxworks': 'VXWORKS', - 'hpux': 'HPUX', - 'nacl': 'NACL', - 'android': 'ANDROID', - 'android-embedded': 'ANDROID_EMBEDDED', - 'uikit': 'APPLE_UIKIT', - 'tvos': 'APPLE_TVOS', - 'watchos': 'APPLE_WATCHOS', - 'winrt': 'WINRT', - 'wasm': 'WASM', - 'msvc': 'MSVC', - 'clang': 'CLANG', - 'gcc': 'GCC', - 'icc': 'ICC', - 'intel_icc': 'ICC', - 'osx': 'APPLE_OSX', - 'ios': 'APPLE_IOS', - 'freebsd': 'FREEBSD', - 'openbsd': 'OPENBSD', - 'netbsd': 'NETBSD', - 'haiku': 'HAIKU', - 'netbsd': 'NETBSD', - 'mac': 'APPLE', - 'macx': 'APPLE_OSX', - 'macos': 'APPLE_OSX', - 'macx-icc': '(APPLE_OSX AND ICC)', + "win32": "WIN32", + "win": "WIN32", + "unix": "UNIX", + "darwin": "APPLE", + "linux": "LINUX", + "integrity": "INTEGRITY", + "qnx": "QNX", + "vxworks": "VXWORKS", + "hpux": "HPUX", + "nacl": "NACL", + "android": "ANDROID", + "android-embedded": "ANDROID_EMBEDDED", + "uikit": "APPLE_UIKIT", + "tvos": "APPLE_TVOS", + "watchos": "APPLE_WATCHOS", + "winrt": "WINRT", + "wasm": "WASM", + "msvc": "MSVC", + "clang": "CLANG", + "gcc": "GCC", + "icc": "ICC", + "intel_icc": "ICC", + "osx": "APPLE_OSX", + "ios": "APPLE_IOS", + "freebsd": "FREEBSD", + "openbsd": "OPENBSD", + "netbsd": "NETBSD", + "haiku": "HAIKU", + "netbsd": "NETBSD", + "mac": "APPLE", + "macx": "APPLE_OSX", + "macos": "APPLE_OSX", + "macx-icc": "(APPLE_OSX AND ICC)", } @@ -387,7 +608,7 @@ def map_platform(platform: str) -> str: def is_known_3rd_party_library(lib: str) -> bool: - if lib.endswith('/nolink') or lib.endswith('_nolink'): + if lib.endswith("/nolink") or lib.endswith("_nolink"): lib = lib[:-7] mapping = find_3rd_party_library_mapping(lib) @@ -395,20 +616,19 @@ def is_known_3rd_party_library(lib: str) -> bool: def map_3rd_party_library(lib: str) -> str: - libpostfix = '' - if lib.endswith('/nolink'): + libpostfix = "" + if lib.endswith("/nolink"): lib = lib[:-7] - libpostfix = '_nolink' + libpostfix = "_nolink" mapping = find_3rd_party_library_mapping(lib) if not mapping or not mapping.targetName: return lib return mapping.targetName + libpostfix -def generate_find_package_info(lib: LibraryMapping, - use_qt_find_package: bool=True, *, - indent: int = 0, - emit_if: str = '') -> str: +def generate_find_package_info( + lib: LibraryMapping, use_qt_find_package: bool = True, *, indent: int = 0, emit_if: str = "" +) -> str: isRequired = False extra = lib.extra.copy() @@ -418,40 +638,41 @@ def generate_find_package_info(lib: LibraryMapping, extra.remove("REQUIRED") cmake_target_name = lib.targetName - assert(cmake_target_name); + assert cmake_target_name # _nolink or not does not matter at this point: - if cmake_target_name.endswith('_nolink') or cmake_target_name.endswith('/nolink'): + if cmake_target_name.endswith("_nolink") or cmake_target_name.endswith("/nolink"): cmake_target_name = cmake_target_name[:-7] if cmake_target_name and use_qt_find_package: - extra += ['PROVIDED_TARGETS', cmake_target_name] + extra += ["PROVIDED_TARGETS", cmake_target_name] - result = '' - one_ind = ' ' + result = "" + one_ind = " " ind = one_ind * indent if use_qt_find_package: if extra: - result = '{}qt_find_package({} {})\n'.format(ind, lib.packageName, ' '.join(extra)) + result = "{}qt_find_package({} {})\n".format(ind, lib.packageName, " ".join(extra)) else: - result = '{}qt_find_package({})\n'.format(ind, lib.packageName) + result = "{}qt_find_package({})\n".format(ind, lib.packageName) if isRequired: - result += '{}set_package_properties({} PROPERTIES TYPE REQUIRED)\n'.format(ind, lib.packageName) + result += "{}set_package_properties({} PROPERTIES TYPE REQUIRED)\n".format( + ind, lib.packageName + ) else: if extra: - result = '{}find_package({} {})\n'.format(ind, lib.packageName, ' '.join(extra)) + result = "{}find_package({} {})\n".format(ind, lib.packageName, " ".join(extra)) else: - result = '{}find_package({})\n'.format(ind, lib.packageName) + result = "{}find_package({})\n".format(ind, lib.packageName) # If a package should be found only in certain conditions, wrap # the find_package call within that condition. if emit_if: - result = "if(({emit_if}) OR QT_FIND_ALL_PACKAGES_ALWAYS)\n" \ - "{ind}{result}endif()\n".format(emit_if=emit_if, - result=result, - ind=one_ind) + result = "if(({emit_if}) OR QT_FIND_ALL_PACKAGES_ALWAYS)\n" "{ind}{result}endif()\n".format( + emit_if=emit_if, result=result, ind=one_ind + ) return result diff --git a/util/cmake/json_parser.py b/util/cmake/json_parser.py index 6ead008f08..36e8cee37f 100644 --- a/util/cmake/json_parser.py +++ b/util/cmake/json_parser.py @@ -31,6 +31,7 @@ import pyparsing as pp import json import re from helper import _set_up_py_parsing_nicer_debug_output + _set_up_py_parsing_nicer_debug_output(pp) @@ -41,7 +42,7 @@ class QMakeSpecificJSONParser: def create_py_parsing_grammar(self): # Keep around all whitespace. - pp.ParserElement.setDefaultWhitespaceChars('') + pp.ParserElement.setDefaultWhitespaceChars("") def add_element(name: str, value: pp.ParserElement): nonlocal self @@ -57,44 +58,44 @@ class QMakeSpecificJSONParser: # skip to the next quote, and repeat that until the end of the # file. - EOF = add_element('EOF', pp.StringEnd()) - SkipToQuote = add_element('SkipToQuote', pp.SkipTo('"')) - SkipToEOF = add_element('SkipToEOF', pp.SkipTo(EOF)) + EOF = add_element("EOF", pp.StringEnd()) + SkipToQuote = add_element("SkipToQuote", pp.SkipTo('"')) + SkipToEOF = add_element("SkipToEOF", pp.SkipTo(EOF)) def remove_newlines_and_whitespace_in_quoted_string(tokens): first_string = tokens[0] - replaced_string = re.sub(r'\n[ ]*', ' ', first_string) + replaced_string = re.sub(r"\n[ ]*", " ", first_string) return replaced_string - QuotedString = add_element('QuotedString', pp.QuotedString(quoteChar='"', - multiline=True, - unquoteResults=False)) + QuotedString = add_element( + "QuotedString", pp.QuotedString(quoteChar='"', multiline=True, unquoteResults=False) + ) QuotedString.setParseAction(remove_newlines_and_whitespace_in_quoted_string) - QuotedTerm = add_element('QuotedTerm', pp.Optional(SkipToQuote) + QuotedString) - Grammar = add_element('Grammar', pp.OneOrMore(QuotedTerm) + SkipToEOF) + QuotedTerm = add_element("QuotedTerm", pp.Optional(SkipToQuote) + QuotedString) + Grammar = add_element("Grammar", pp.OneOrMore(QuotedTerm) + SkipToEOF) return Grammar def parse_file_using_py_parsing(self, file: str): - print('Pre processing "{}" using py parsing to remove incorrect newlines.'.format(file)) + print(f'Pre processing "{file}" using py parsing to remove incorrect newlines.') try: - with open(file, 'r') as file_fd: + with open(file, "r") as file_fd: contents = file_fd.read() parser_result = self.grammar.parseString(contents, parseAll=True) token_list = parser_result.asList() - joined_string = ''.join(token_list) + joined_string = "".join(token_list) return joined_string except pp.ParseException as pe: print(pe.line) - print(' '*(pe.col-1) + '^') + print(" " * (pe.col - 1) + "^") print(pe) raise pe def parse(self, file: str): pre_processed_string = self.parse_file_using_py_parsing(file) - print('Parsing "{}" using json.loads().'.format(file)) + print(f'Parsing "{file}" using json.loads().') json_parsed = json.loads(pre_processed_string) return json_parsed diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 28eb70454e..7363ace586 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -30,16 +30,12 @@ from __future__ import annotations -from argparse import ArgumentParser -from textwrap import dedent import copy -import xml.etree.ElementTree as ET -from itertools import chain import os.path import posixpath +import sys import re import io -import typing import glob import collections @@ -48,119 +44,164 @@ try: except AttributeError: collectionsAbc = collections -from sympy.logic import (simplify_logic, And, Or, Not,) import pyparsing as pp -from helper import _set_up_py_parsing_nicer_debug_output -_set_up_py_parsing_nicer_debug_output(pp) - -from helper import map_qt_library, map_3rd_party_library, is_known_3rd_party_library, \ - featureName, map_platform, find_library_info_for_target, generate_find_package_info, \ - LibraryMapping +import xml.etree.ElementTree as ET +from argparse import ArgumentParser +from textwrap import dedent +from itertools import chain from shutil import copyfile +from sympy.logic import simplify_logic, And, Or, Not +from sympy.core.sympify import SympifyError +from typing import List, Optional, Dict, Set, IO, Union, Mapping, Any, Callable, FrozenSet, Tuple from special_case_helper import SpecialCaseHandler +from helper import ( + map_qt_library, + map_3rd_party_library, + is_known_3rd_party_library, + featureName, + map_platform, + find_library_info_for_target, + generate_find_package_info, + LibraryMapping, +) +from helper import _set_up_py_parsing_nicer_debug_output + +_set_up_py_parsing_nicer_debug_output(pp) cmake_version_string = "3.15.0" + def _parse_commandline(): - parser = ArgumentParser(description='Generate CMakeLists.txt files from .' - 'pro files.', - epilog='Requirements: pip install sympy pyparsing') - parser.add_argument('--debug', dest='debug', action='store_true', - help='Turn on all debug output') - parser.add_argument('--debug-parser', dest='debug_parser', - action='store_true', - help='Print debug output from qmake parser.') - parser.add_argument('--debug-parse-result', dest='debug_parse_result', - action='store_true', - help='Dump the qmake parser result.') - parser.add_argument('--debug-parse-dictionary', - dest='debug_parse_dictionary', action='store_true', - help='Dump the qmake parser result as dictionary.') - parser.add_argument('--debug-pro-structure', dest='debug_pro_structure', - action='store_true', - help='Dump the structure of the qmake .pro-file.') - parser.add_argument('--debug-full-pro-structure', - dest='debug_full_pro_structure', action='store_true', - help='Dump the full structure of the qmake .pro-file ' - '(with includes).') - parser.add_argument('--debug-special-case-preservation', - dest='debug_special_case_preservation', action='store_true', - help='Show all git commands and file copies.') + parser = ArgumentParser( + description="Generate CMakeLists.txt files from ." "pro files.", + epilog="Requirements: pip install sympy pyparsing", + ) + parser.add_argument( + "--debug", dest="debug", action="store_true", help="Turn on all debug output" + ) + parser.add_argument( + "--debug-parser", + dest="debug_parser", + action="store_true", + help="Print debug output from qmake parser.", + ) + parser.add_argument( + "--debug-parse-result", + dest="debug_parse_result", + action="store_true", + help="Dump the qmake parser result.", + ) + parser.add_argument( + "--debug-parse-dictionary", + dest="debug_parse_dictionary", + action="store_true", + help="Dump the qmake parser result as dictionary.", + ) + parser.add_argument( + "--debug-pro-structure", + dest="debug_pro_structure", + action="store_true", + help="Dump the structure of the qmake .pro-file.", + ) + parser.add_argument( + "--debug-full-pro-structure", + dest="debug_full_pro_structure", + action="store_true", + help="Dump the full structure of the qmake .pro-file " "(with includes).", + ) + parser.add_argument( + "--debug-special-case-preservation", + dest="debug_special_case_preservation", + action="store_true", + help="Show all git commands and file copies.", + ) - parser.add_argument('--is-example', action='store_true', - dest="is_example", - help='Treat the input .pro file as an example.') - parser.add_argument('-s', '--skip-special-case-preservation', - dest='skip_special_case_preservation', action='store_true', - help='Skips behavior to reapply ' - 'special case modifications (requires git in PATH)') - parser.add_argument('-k', '--keep-temporary-files', - dest='keep_temporary_files', action='store_true', - help='Don\'t automatically remove CMakeLists.gen.txt and other ' - 'intermediate files.') + parser.add_argument( + "--is-example", + action="store_true", + dest="is_example", + help="Treat the input .pro file as an example.", + ) + parser.add_argument( + "-s", + "--skip-special-case-preservation", + dest="skip_special_case_preservation", + action="store_true", + help="Skips behavior to reapply " "special case modifications (requires git in PATH)", + ) + parser.add_argument( + "-k", + "--keep-temporary-files", + dest="keep_temporary_files", + action="store_true", + help="Don't automatically remove CMakeLists.gen.txt and other " "intermediate files.", + ) - parser.add_argument('files', metavar='<.pro/.pri file>', type=str, - nargs='+', help='The .pro/.pri file to process') + parser.add_argument( + "files", + metavar="<.pro/.pri file>", + type=str, + nargs="+", + help="The .pro/.pri file to process", + ) return parser.parse_args() -def is_top_level_repo_project(project_file_path: str = '') -> bool: +def is_top_level_repo_project(project_file_path: str = "") -> bool: qmake_conf_path = find_qmake_conf(project_file_path) qmake_conf_dir_path = os.path.dirname(qmake_conf_path) project_dir_path = os.path.dirname(project_file_path) - if qmake_conf_dir_path == project_dir_path: - return True - return False + return qmake_conf_dir_path == project_dir_path -def is_top_level_repo_tests_project(project_file_path: str = '') -> bool: +def is_top_level_repo_tests_project(project_file_path: str = "") -> bool: qmake_conf_path = find_qmake_conf(project_file_path) qmake_conf_dir_path = os.path.dirname(qmake_conf_path) project_dir_path = os.path.dirname(project_file_path) project_dir_name = os.path.basename(project_dir_path) maybe_same_level_dir_path = os.path.join(project_dir_path, "..") normalized_maybe_same_level_dir_path = os.path.normpath(maybe_same_level_dir_path) - if qmake_conf_dir_path == normalized_maybe_same_level_dir_path and project_dir_name == 'tests': - return True - return False + return ( + qmake_conf_dir_path == normalized_maybe_same_level_dir_path and project_dir_name == "tests" + ) -def is_top_level_repo_examples_project(project_file_path: str = '') -> bool: +def is_top_level_repo_examples_project(project_file_path: str = "") -> bool: qmake_conf_path = find_qmake_conf(project_file_path) qmake_conf_dir_path = os.path.dirname(qmake_conf_path) project_dir_path = os.path.dirname(project_file_path) project_dir_name = os.path.basename(project_dir_path) maybe_same_level_dir_path = os.path.join(project_dir_path, "..") normalized_maybe_same_level_dir_path = os.path.normpath(maybe_same_level_dir_path) - if qmake_conf_dir_path == normalized_maybe_same_level_dir_path \ - and project_dir_name == 'examples': - return True - return False + return ( + qmake_conf_dir_path == normalized_maybe_same_level_dir_path + and project_dir_name == "examples" + ) -def is_example_project(project_file_path: str = '') -> bool: +def is_example_project(project_file_path: str = "") -> bool: qmake_conf_path = find_qmake_conf(project_file_path) qmake_conf_dir_path = os.path.dirname(qmake_conf_path) project_relative_path = os.path.relpath(project_file_path, qmake_conf_dir_path) # If the project file is found in a subdir called 'examples' # relative to the repo source dir, then it must be an example. - if project_relative_path.startswith('examples'): - return True - return False + return project_relative_path.startswith("examples") -def find_qmake_conf(project_file_path: str = '') -> typing.Optional[str]: +def find_qmake_conf(project_file_path: str = "") -> Optional[str]: if not os.path.isabs(project_file_path): - print('Warning: could not find .qmake.conf file, given path is not an absolute path: {}' - .format(project_file_path)) + print( + f"Warning: could not find .qmake.conf file, given path is not an " + f"absolute path: {project_file_path}" + ) return None cwd = os.path.dirname(project_file_path) - file_name = '.qmake.conf' + file_name = ".qmake.conf" while os.path.isdir(cwd): maybe_file = posixpath.join(cwd, file_name) @@ -172,13 +213,20 @@ def find_qmake_conf(project_file_path: str = '') -> typing.Optional[str]: return None -def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_file_path: str = '', skip_qtquick_compiler: bool = False, - retain_qtquick_compiler: bool = False, is_example: bool = False) -> str: - assert(target) +def process_qrc_file( + target: str, + filepath: str, + base_dir: str = "", + project_file_path: str = "", + skip_qtquick_compiler: bool = False, + retain_qtquick_compiler: bool = False, + is_example: bool = False, +) -> str: + assert target # Hack to handle QT_SOURCE_TREE. Assume currently that it's the same # as the qtbase source path. - qt_source_tree_literal = '${QT_SOURCE_TREE}' + qt_source_tree_literal = "${QT_SOURCE_TREE}" if qt_source_tree_literal in filepath: qmake_conf = find_qmake_conf(project_file_path) @@ -186,43 +234,44 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil qt_source_tree = os.path.dirname(qmake_conf) filepath = filepath.replace(qt_source_tree_literal, qt_source_tree) else: - print('Warning, could not determine QT_SOURCE_TREE location while trying to find: {}' - .format(filepath)) - + print( + f"Warning, could not determine QT_SOURCE_TREE location while trying " + f"to find: {filepath}" + ) resource_name = os.path.splitext(os.path.basename(filepath))[0] dir_name = os.path.dirname(filepath) - base_dir = posixpath.join('' if base_dir == '.' else base_dir, dir_name) + base_dir = posixpath.join("" if base_dir == "." else base_dir, dir_name) # Small not very thorough check to see if this a shared qrc resource # pattern is mostly used by the tests. - is_parent_path = dir_name.startswith('..') + is_parent_path = dir_name.startswith("..") if not os.path.isfile(filepath): - raise RuntimeError('Invalid file path given to process_qrc_file: {}'.format(filepath)) + raise RuntimeError(f"Invalid file path given to process_qrc_file: {filepath}") tree = ET.parse(filepath) root = tree.getroot() - assert(root.tag == 'RCC') + assert root.tag == "RCC" - output = '' + output = "" resource_count = 0 for resource in root: - assert(resource.tag == 'qresource') - lang = resource.get('lang', '') - prefix = resource.get('prefix', '/') - if not prefix.startswith('/'): - prefix = '/' + prefix + assert resource.tag == "qresource" + lang = resource.get("lang", "") + prefix = resource.get("prefix", "/") + if not prefix.startswith("/"): + prefix = "/" + prefix - full_resource_name = resource_name + (str(resource_count) if resource_count > 0 else '') + full_resource_name = resource_name + (str(resource_count) if resource_count > 0 else "") - files: typing.Dict[str, str] = {} + files: Dict[str, str] = {} for file in resource: path = file.text assert path # Get alias: - alias = file.get('alias', '') + alias = file.get("alias", "") # In cases where examples use shared resources, we set the alias # too the same name of the file, or the applications won't be # be able to locate the resource @@ -230,58 +279,87 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil alias = path files[path] = alias - output += write_add_qt_resource_call(target, full_resource_name, prefix, base_dir, lang, files, skip_qtquick_compiler, retain_qtquick_compiler, is_example) + output += write_add_qt_resource_call( + target, + full_resource_name, + prefix, + base_dir, + lang, + files, + skip_qtquick_compiler, + retain_qtquick_compiler, + is_example, + ) resource_count += 1 return output -def write_add_qt_resource_call(target: str, resource_name: str, prefix: typing.Optional[str], base_dir: typing.Optional[str], - lang: typing.Optional[str], files: typing.Dict[str, str], skip_qtquick_compiler: bool, retain_qtquick_compiler: bool, is_example :bool) -> str: - output = '' +def write_add_qt_resource_call( + target: str, + resource_name: str, + prefix: Optional[str], + base_dir: Optional[str], + lang: Optional[str], + files: Dict[str, str], + skip_qtquick_compiler: bool, + retain_qtquick_compiler: bool, + is_example: bool, +) -> str: + output = "" sorted_files = sorted(files.keys()) - assert(sorted_files) + assert sorted_files for source in sorted_files: alias = files[source] if alias: full_source = posixpath.join(base_dir, source) - output += 'set_source_files_properties("{}"\n' \ - ' PROPERTIES QT_RESOURCE_ALIAS "{}"\n)\n'.format(full_source, alias) + output += ( + f'set_source_files_properties("{full_source}"\n' + f' PROPERTIES QT_RESOURCE_ALIAS "{alias}"\n)\n' + ) # Quote file paths in case there are spaces. sorted_files_backup = sorted_files sorted_files = [] for source in sorted_files_backup: - if source.startswith('${'): + if source.startswith("${"): sorted_files.append(source) else: - sorted_files.append('"{}"'.format(source)) + sorted_files.append(f'"{source}"') - file_list = '\n '.join(sorted_files) - output += 'set({}_resource_files\n {}\n)\n\n'.format(resource_name, file_list) - file_list = "${{{}_resource_files}}".format(resource_name) + file_list = "\n ".join(sorted_files) + output += f"set({resource_name}_resource_files\n {file_list}\n)\n\n" + file_list = f"${{{resource_name}_resource_files}}" if skip_qtquick_compiler: - output += 'set_source_files_properties(${{{}_resource_files}} PROPERTIES QT_SKIP_QUICKCOMPILER 1)\n\n'.format(resource_name) + output += ( + f"set_source_files_properties(${{{resource_name}_resource_files}}" + " PROPERTIES QT_SKIP_QUICKCOMPILER 1)\n\n" + ) if retain_qtquick_compiler: - output += 'set_source_files_properties(${{{}_resource_files}} PROPERTIES QT_RETAIN_QUICKCOMPILER 1)\n\n'.format(resource_name) + output += ( + f"set_source_files_properties(${{{resource_name}_resource_files}}" + "PROPERTIES QT_RETAIN_QUICKCOMPILER 1)\n\n" + ) - params = '' + params = "" if lang: - params += ' LANG\n "{}"\n'.format(lang) - params += ' PREFIX\n "{}"\n'.format(prefix) + params += f'{spaces(1)}LANG\n{spaces(2)}"{lang}"\n' + params += f'{spaces(1)}PREFIX\n{spaces(2)}"{prefix}"\n' if base_dir: - params += ' BASE\n "{}"\n'.format(base_dir) - add_resource_command = '' + params += f'{spaces(1)}BASE\n{spaces(2)}"{base_dir}"\n' + add_resource_command = "" if is_example: - add_resource_command = 'QT6_ADD_RESOURCES' + add_resource_command = "QT6_ADD_RESOURCES" else: - add_resource_command = 'add_qt_resource' - output += '{}({} "{}"\n{} FILES\n {}\n)\n'.format(add_resource_command, - target, resource_name, params, file_list) + add_resource_command = "add_qt_resource" + output += ( + f'{add_resource_command}({target} "{resource_name}"\n{params}{spaces(1)}FILES\n' + f"{spaces(2)}{file_list}\n)\n" + ) return output @@ -291,8 +369,8 @@ def fixup_linecontinuation(contents: str) -> str: # a newline character with an arbitrary amount of whitespace # between the backslash and the newline. # This greatly simplifies the qmake parsing grammar. - contents = re.sub(r'([^\t ])\\[ \t]*\n', '\\1 ', contents) - contents = re.sub(r'\\[ \t]*\n', '', contents) + contents = re.sub(r"([^\t ])\\[ \t]*\n", "\\1 ", contents) + contents = re.sub(r"\\[ \t]*\n", "", contents) return contents @@ -313,24 +391,24 @@ def fixup_comments(contents: str) -> str: # care of it as well, as if the commented line didn't exist in the # first place. - contents = re.sub(r'\n#[^\n]*?\n', '\n', contents, re.DOTALL) + contents = re.sub(r"\n#[^\n]*?\n", "\n", contents, re.DOTALL) return contents def spaces(indent: int) -> str: - return ' ' * indent + return " " * indent def trim_leading_dot(file: str) -> str: - while file.startswith('./'): + while file.startswith("./"): file = file[2:] return file def map_to_file(f: str, scope: Scope, *, is_include: bool = False) -> str: - assert('$$' not in f) + assert "$$" not in f - if f.startswith('${'): # Some cmake variable is prepended + if f.startswith("${"): # Some cmake variable is prepended return f base_dir = scope.currentdir if is_include else scope.basedir @@ -339,11 +417,11 @@ def map_to_file(f: str, scope: Scope, *, is_include: bool = False) -> str: return trim_leading_dot(f) -def handle_vpath(source: str, base_dir: str, vpath: typing.List[str]) -> str: - assert('$$' not in source) +def handle_vpath(source: str, base_dir: str, vpath: List[str]) -> str: + assert "$$" not in source if not source: - return '' + return "" if not vpath: return source @@ -351,7 +429,7 @@ def handle_vpath(source: str, base_dir: str, vpath: typing.List[str]) -> str: if os.path.exists(os.path.join(base_dir, source)): return source - variable_pattern = re.compile(r'\$\{[A-Za-z0-9_]+\}') + variable_pattern = re.compile(r"\$\{[A-Za-z0-9_]+\}") match = re.match(variable_pattern, source) if match: # a complex, variable based path, skipping validation @@ -363,8 +441,8 @@ def handle_vpath(source: str, base_dir: str, vpath: typing.List[str]) -> str: if os.path.exists(fullpath): return trim_leading_dot(posixpath.relpath(fullpath, base_dir)) - print(' XXXX: Source {}: Not found.'.format(source)) - return '{}-NOTFOUND'.format(source) + print(f" XXXX: Source {source}: Not found.") + return f"{source}-NOTFOUND" def flatten_list(l): @@ -379,74 +457,91 @@ def flatten_list(l): def handle_function_value(group: pp.ParseResults): function_name = group[0] function_args = group[1] - if function_name == 'qtLibraryTarget': + if function_name == "qtLibraryTarget": if len(function_args) > 1: - raise RuntimeError('Don\'t know what to with more than one function argument for $$qtLibraryTarget().') + raise RuntimeError( + "Don't know what to with more than one function argument " + "for $$qtLibraryTarget()." + ) return str(function_args[0]) - if function_name == 'quote': + if function_name == "quote": # Do nothing, just return a string result return str(group) - if function_name == 'files': + if function_name == "files": if len(function_args) > 1: - raise RuntimeError('Don\'t know what to with more than one function argument for $$files().') + raise RuntimeError( + "Don't know what to with more than one function argument for $$files()." + ) return str(function_args[0]) if isinstance(function_args, pp.ParseResults): function_args = list(flatten_list(function_args.asList())) # Return the whole expression as a string. - if function_name in ['join', 'files', 'cmakeRelativePath', 'shell_quote', 'shadowed', 'cmakeTargetPath', - 'shell_path', 'cmakeProcessLibs', 'cmakeTargetPaths', - 'cmakePortablePaths', 'escape_expand', 'member']: - return 'join({})'.format(''.join(function_args)) + if function_name in [ + "join", + "files", + "cmakeRelativePath", + "shell_quote", + "shadowed", + "cmakeTargetPath", + "shell_path", + "cmakeProcessLibs", + "cmakeTargetPaths", + "cmakePortablePaths", + "escape_expand", + "member", + ]: + return f"join({''.join(function_args)})" - raise RuntimeError('No logic to handle function "{}", please add one in handle_function_value().'.format(function_name)) - class Operation: - def __init__(self, value: typing.Union[typing.List[str], str]): + def __init__(self, value: Union[List[str], str]): if isinstance(value, list): self._value = value else: - self._value = [str(value), ] + self._value = [str(value)] - def process(self, key: str, input: typing.List[str], - transformer: typing.Callable[[typing.List[str]], typing.List[str]]) -> typing.List[str]: - assert(False) + def process( + self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + ) -> List[str]: + assert False def __repr__(self): - assert(False) + assert False def _dump(self): if not self._value: - return '' + return "" if not isinstance(self._value, list): - return '' + return "" result = [] for i in self._value: if not i: - result.append('') + result.append("") else: result.append(str(i)) return '"' + '", "'.join(result) + '"' class AddOperation(Operation): - def process(self, key: str, input: typing.List[str], - transformer: typing.Callable[[typing.List[str]], typing.List[str]]) -> typing.List[str]: + def process( + self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + ) -> List[str]: return input + transformer(self._value) def __repr__(self): - return '+({})'.format(self._dump()) + return f"+({self._dump()})" class UniqueAddOperation(Operation): - def process(self, key: str, input: typing.List[str], - transformer: typing.Callable[[typing.List[str]], typing.List[str]]) -> typing.List[str]: + def process( + self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + ) -> List[str]: result = input for v in transformer(self._value): if v not in result: @@ -454,15 +549,16 @@ class UniqueAddOperation(Operation): return result def __repr__(self): - return '*({})'.format(self._dump()) + return f"*({self._dump()})" class SetOperation(Operation): - def process(self, key: str, input: typing.List[str], - transformer: typing.Callable[[typing.List[str]], typing.List[str]]) -> typing.List[str]: - values = [] # typing.List[str] + def process( + self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + ) -> List[str]: + values = [] # List[str] for v in self._value: - if v != '$${}'.format(key): + if v != f"$${key}": values.append(v) else: values += input @@ -473,66 +569,70 @@ class SetOperation(Operation): return values def __repr__(self): - return '=({})'.format(self._dump()) + return f"=({self._dump()})" class RemoveOperation(Operation): def __init__(self, value): super().__init__(value) - def process(self, key: str, input: typing.List[str], - transformer: typing.Callable[[typing.List[str]], typing.List[str]]) -> typing.List[str]: + def process( + self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + ) -> List[str]: input_set = set(input) value_set = set(self._value) - result = [] + result: List[str] = [] # Add everything that is not going to get removed: for v in input: if v not in value_set: - result += [v,] + result += [v] # Add everything else with removal marker: for v in transformer(self._value): if v not in input_set: - result += ['-{}'.format(v), ] + result += [f"-{v}"] return result def __repr__(self): - return '-({})'.format(self._dump()) + return f"-({self._dump()})" class Scope(object): SCOPE_ID: int = 1 - def __init__(self, *, - parent_scope: typing.Optional[Scope], - file: typing.Optional[str] = None, condition: str = '', - base_dir: str = '', - operations: typing.Union[ - typing.Mapping[str, typing.List[Operation]], None] = None) -> None: + def __init__( + self, + *, + parent_scope: Optional[Scope], + file: Optional[str] = None, + condition: str = "", + base_dir: str = "", + operations: Union[Mapping[str, List[Operation]], None] = None, + ) -> None: if operations is None: operations = { - 'QT_SOURCE_TREE': [SetOperation(['${QT_SOURCE_TREE}'])], - 'QT_BUILD_TREE': [SetOperation(['${PROJECT_BUILD_DIR}'])], + "QT_SOURCE_TREE": [SetOperation(["${QT_SOURCE_TREE}"])], + "QT_BUILD_TREE": [SetOperation(["${PROJECT_BUILD_DIR}"])], } self._operations = copy.deepcopy(operations) if parent_scope: parent_scope._add_child(self) else: - self._parent = None # type: typing.Optional[Scope] + self._parent = None # type: Optional[Scope] # Only add the "QT = core gui" Set operation once, on the # very top-level .pro scope, aka it's basedir is empty. if not base_dir: - self._operations['QT'] = [SetOperation(['core', 'gui'])] + self._operations["QT"] = [SetOperation(["core", "gui"])] self._basedir = base_dir if file: self._currentdir = os.path.dirname(file) if not self._currentdir: - self._currentdir = '.' + self._currentdir = "." if not self._basedir: self._basedir = self._currentdir @@ -541,30 +641,31 @@ class Scope(object): self._file = file self._file_absolute_path = os.path.abspath(file) self._condition = map_condition(condition) - self._children = [] # type: typing.List[Scope] - self._included_children = [] # type: typing.List[Scope] - self._visited_keys = set() # type: typing.Set[str] - self._total_condition = None # type: typing.Optional[str] + self._children = [] # type: List[Scope] + self._included_children = [] # type: List[Scope] + self._visited_keys = set() # type: Set[str] + self._total_condition = None # type: Optional[str] def __repr__(self): - return '{}:{}:{}:{}:{}'.format(self._scope_id, - self._basedir, self._currentdir, - self._file, self._condition or '') + return ( + f"{self._scope_id}:{self._basedir}:{self._currentdir}:{self._file}:" + f"{self._condition or ''}" + ) def reset_visited_keys(self): self._visited_keys = set() - def merge(self, other: 'Scope') -> None: + def merge(self, other: "Scope") -> None: assert self != other self._included_children.append(other) @property def scope_debug(self) -> bool: - merge = self.get_string('PRO2CMAKE_SCOPE_DEBUG').lower() - return merge == '1' or merge == 'on' or merge == 'yes' or merge == 'true' + merge = self.get_string("PRO2CMAKE_SCOPE_DEBUG").lower() + return merge == "1" or merge == "on" or merge == "yes" or merge == "true" @property - def parent(self) -> typing.Optional[Scope]: + def parent(self) -> Optional[Scope]: return self._parent @property @@ -576,7 +677,7 @@ class Scope(object): return self._currentdir def can_merge_condition(self): - if self._condition == 'else': + if self._condition == "else": return False if self._operations: return False @@ -584,167 +685,162 @@ class Scope(object): child_count = len(self._children) if child_count == 0 or child_count > 2: return False - assert child_count != 1 or self._children[0]._condition != 'else' - return child_count == 1 or self._children[1]._condition == 'else' + assert child_count != 1 or self._children[0]._condition != "else" + return child_count == 1 or self._children[1]._condition == "else" def settle_condition(self): - new_children: typing.List[Scope] = [] + new_children: List[Scope] = [] for c in self._children: c.settle_condition() if c.can_merge_condition(): child = c._children[0] - child._condition = '({}) AND ({})'.format(c._condition, child._condition) + child._condition = "({c._condition}) AND ({child._condition})" new_children += c._children else: new_children.append(c) self._children = new_children @staticmethod - def FromDict(parent_scope: typing.Optional['Scope'], - file: str, statements, cond: str = '', base_dir: str = '') -> Scope: + def FromDict( + parent_scope: Optional["Scope"], file: str, statements, cond: str = "", base_dir: str = "" + ) -> Scope: scope = Scope(parent_scope=parent_scope, file=file, condition=cond, base_dir=base_dir) for statement in statements: if isinstance(statement, list): # Handle skipped parts... assert not statement continue - operation = statement.get('operation', None) + operation = statement.get("operation", None) if operation: - key = statement.get('key', '') - value = statement.get('value', []) - assert key != '' + key = statement.get("key", "") + value = statement.get("value", []) + assert key != "" - if operation == '=': + if operation == "=": scope._append_operation(key, SetOperation(value)) - elif operation == '-=': + elif operation == "-=": scope._append_operation(key, RemoveOperation(value)) - elif operation == '+=': + elif operation == "+=": scope._append_operation(key, AddOperation(value)) - elif operation == '*=': + elif operation == "*=": scope._append_operation(key, UniqueAddOperation(value)) else: - print('Unexpected operation "{}" in scope "{}".' - .format(operation, scope)) - assert(False) + print(f'Unexpected operation "{operation}" in scope "{scope}".') + assert False continue - condition = statement.get('condition', None) + condition = statement.get("condition", None) if condition: - Scope.FromDict(scope, file, - statement.get('statements'), condition, - scope.basedir) + Scope.FromDict(scope, file, statement.get("statements"), condition, scope.basedir) - else_statements = statement.get('else_statements') + else_statements = statement.get("else_statements") if else_statements: - Scope.FromDict(scope, file, else_statements, - 'else', scope.basedir) + Scope.FromDict(scope, file, else_statements, "else", scope.basedir) continue - loaded = statement.get('loaded') + loaded = statement.get("loaded") if loaded: - scope._append_operation('_LOADED', UniqueAddOperation(loaded)) + scope._append_operation("_LOADED", UniqueAddOperation(loaded)) continue - option = statement.get('option', None) + option = statement.get("option", None) if option: - scope._append_operation('_OPTION', UniqueAddOperation(option)) + scope._append_operation("_OPTION", UniqueAddOperation(option)) continue - included = statement.get('included', None) + included = statement.get("included", None) if included: - scope._append_operation('_INCLUDED', - UniqueAddOperation(included)) + scope._append_operation("_INCLUDED", UniqueAddOperation(included)) continue scope.settle_condition() if scope.scope_debug: - print('..... [SCOPE_DEBUG]: Created scope {}:'.format(scope)) + print(f"..... [SCOPE_DEBUG]: Created scope {scope}:") scope.dump(indent=1) - print('..... [SCOPE_DEBUG]: <>') + print("..... [SCOPE_DEBUG]: <>") return scope def _append_operation(self, key: str, op: Operation) -> None: if key in self._operations: self._operations[key].append(op) else: - self._operations[key] = [op, ] + self._operations[key] = [op] @property def file(self) -> str: - return self._file or '' + return self._file or "" @property def file_absolute_path(self) -> str: - return self._file_absolute_path or '' + return self._file_absolute_path or "" @property def generated_cmake_lists_path(self) -> str: assert self.basedir - return os.path.join(self.basedir, 'CMakeLists.gen.txt') + return os.path.join(self.basedir, "CMakeLists.gen.txt") @property def original_cmake_lists_path(self) -> str: assert self.basedir - return os.path.join(self.basedir, 'CMakeLists.txt') + return os.path.join(self.basedir, "CMakeLists.txt") @property def condition(self) -> str: return self._condition @property - def total_condition(self) -> typing.Optional[str]: + def total_condition(self) -> Optional[str]: return self._total_condition @total_condition.setter def total_condition(self, condition: str) -> None: self._total_condition = condition - def _add_child(self, scope: 'Scope') -> None: + def _add_child(self, scope: "Scope") -> None: scope._parent = self self._children.append(scope) @property - def children(self) -> typing.List['Scope']: + def children(self) -> List["Scope"]: result = list(self._children) for include_scope in self._included_children: result += include_scope.children return result def dump(self, *, indent: int = 0) -> None: - ind = ' ' * indent - print('{}Scope "{}":'.format(ind, self)) + ind = spaces(indent) + print(f'{ind}Scope "{self}":') if self.total_condition: - print('{} Total condition = {}'.format(ind, self.total_condition)) - print('{} Keys:'.format(ind)) + print(f"{ind} Total condition = {self.total_condition}") + print(f"{ind} Keys:") keys = self._operations.keys() if not keys: - print('{} -- NONE --'.format(ind)) + print(f"{ind} -- NONE --") else: for k in sorted(keys): - print('{} {} = "{}"' - .format(ind, k, self._operations.get(k, []))) - print('{} Children:'.format(ind)) + print(f'{ind} {k} = "{self._operations.get(k, [])}"') + print(f"{ind} Children:") if not self._children: - print('{} -- NONE --'.format(ind)) + print(f"{ind} -- NONE --") else: for c in self._children: c.dump(indent=indent + 1) - print('{} Includes:'.format(ind)) + print(f"{ind} Includes:") if not self._included_children: - print('{} -- NONE --'.format(ind)) + print(f"{ind} -- NONE --") else: for c in self._included_children: c.dump(indent=indent + 1) - def dump_structure(self, *, type: str = 'ROOT', indent: int = 0) -> None: - print('{}{}: {}'.format(spaces(indent), type, self)) + def dump_structure(self, *, type: str = "ROOT", indent: int = 0) -> None: + print(f"{spaces(indent)}{type}: {self}") for i in self._included_children: - i.dump_structure(type='INCL', indent=indent + 1) + i.dump_structure(type="INCL", indent=indent + 1) for i in self._children: - i.dump_structure(type='CHLD', indent=indent + 1) + i.dump_structure(type="CHLD", indent=indent + 1) @property def keys(self): @@ -754,10 +850,14 @@ class Scope(object): def visited_keys(self): return self._visited_keys - def _evalOps(self, key: str, - transformer: typing.Optional[typing.Callable[[Scope, typing.List[str]], typing.List[str]]], - result: typing.List[str], *, inherit: bool = False) \ - -> typing.List[str]: + def _evalOps( + self, + key: str, + transformer: Optional[Callable[[Scope, List[str]], List[str]]], + result: List[str], + *, + inherit: bool = False, + ) -> List[str]: self._visited_keys.add(key) # Inherrit values from above: @@ -765,9 +865,14 @@ class Scope(object): result = self._parent._evalOps(key, transformer, result) if transformer: - op_transformer = lambda files: transformer(self, files) + + def op_transformer(files): + return transformer(self, files) + else: - op_transformer = lambda files: files + + def op_transformer(files): + return files for op in self._operations.get(key, []): result = op.process(key, result, op_transformer) @@ -777,68 +882,83 @@ class Scope(object): return result - def get(self, key: str, *, ignore_includes: bool = False, inherit: bool = False) -> typing.List[str]: + def get(self, key: str, *, ignore_includes: bool = False, inherit: bool = False) -> List[str]: is_same_path = self.currentdir == self.basedir + if not is_same_path: + relative_path = os.path.relpath(self.currentdir, self.basedir) - if key == '_PRO_FILE_PWD_': - return ['${CMAKE_CURRENT_SOURCE_DIR}'] - if key == 'PWD': + if key == "_PRO_FILE_PWD_": + return ["${CMAKE_CURRENT_SOURCE_DIR}"] + if key == "PWD": if is_same_path: - return ['${CMAKE_CURRENT_SOURCE_DIR}'] + return ["${CMAKE_CURRENT_SOURCE_DIR}"] else: - return ['${CMAKE_CURRENT_SOURCE_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] - if key == 'OUT_PWD': + return [f"${{CMAKE_CURRENT_SOURCE_DIR}}/{relative_path}"] + if key == "OUT_PWD": if is_same_path: - return ['${CMAKE_CURRENT_BINARY_DIR}'] + return ["${CMAKE_CURRENT_BINARY_DIR}"] else: - return ['${CMAKE_CURRENT_BINARY_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] + return [f"${{CMAKE_CURRENT_BINARY_DIR}}/{relative_path}"] return self._evalOps(key, None, [], inherit=inherit) - def get_string(self, key: str, default: str = '', inherit : bool = False) -> str: - v = self.get(key, inherit = inherit) + def get_string(self, key: str, default: str = "", inherit: bool = False) -> str: + v = self.get(key, inherit=inherit) if len(v) == 0: return default assert len(v) == 1 return v[0] - def _map_files(self, files: typing.List[str], *, - use_vpath: bool = True, is_include: bool = False) -> typing.List[str]: + def _map_files( + self, files: List[str], *, use_vpath: bool = True, is_include: bool = False + ) -> List[str]: - expanded_files = [] # type: typing.List[str] + expanded_files = [] # type: List[str] for f in files: r = self._expand_value(f) expanded_files += r - mapped_files = list(map(lambda f: map_to_file(f, self, is_include=is_include), expanded_files)) + mapped_files = list( + map(lambda f: map_to_file(f, self, is_include=is_include), expanded_files) + ) if use_vpath: - result = list(map(lambda f: handle_vpath(f, self.basedir, self.get('VPATH', inherit=True)), mapped_files)) + result = list( + map( + lambda f: handle_vpath(f, self.basedir, self.get("VPATH", inherit=True)), + mapped_files, + ) + ) else: result = mapped_files # strip ${CMAKE_CURRENT_SOURCE_DIR}: - result = list(map(lambda f: f[28:] if f.startswith('${CMAKE_CURRENT_SOURCE_DIR}/') else f, result)) + result = list( + map(lambda f: f[28:] if f.startswith("${CMAKE_CURRENT_SOURCE_DIR}/") else f, result) + ) # strip leading ./: result = list(map(lambda f: trim_leading_dot(f), result)) return result - def get_files(self, key: str, *, use_vpath: bool = False, - is_include: bool = False) -> typing.List[str]: - transformer = lambda scope, files: scope._map_files(files, use_vpath=use_vpath, is_include=is_include) + def get_files( + self, key: str, *, use_vpath: bool = False, is_include: bool = False + ) -> List[str]: + def transformer(scope, files): + return scope._map_files(files, use_vpath=use_vpath, is_include=is_include) + return list(self._evalOps(key, transformer, [])) - def _expand_value(self, value: str) -> typing.List[str]: + def _expand_value(self, value: str) -> List[str]: result = value - pattern = re.compile(r'\$\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?') + pattern = re.compile(r"\$\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?") match = re.search(pattern, result) while match: old_result = result if match.group(0) == value: - get_result = self.get(match.group(1), inherit = True) + get_result = self.get(match.group(1), inherit=True) if len(get_result) == 1: result = get_result[0] else: @@ -849,21 +969,19 @@ class Scope(object): result_list += self._expand_value(entry_value) return result_list else: - replacement = self.get(match.group(1), inherit = True) - replacement_str = replacement[0] if replacement else '' - result = result[:match.start()] \ - + replacement_str \ - + result[match.end():] + replacement = self.get(match.group(1), inherit=True) + replacement_str = replacement[0] if replacement else "" + result = result[: match.start()] + replacement_str + result[match.end() :] if result == old_result: - return [result,] # Do not go into infinite loop + return [result] # Do not go into infinite loop match = re.search(pattern, result) - return [result,] + return [result] - def expand(self, key: str) -> typing.List[str]: + def expand(self, key: str) -> List[str]: value = self.get(key) - result: typing.List[str] = [] + result: List[str] = [] assert isinstance(value, list) for v in value: result += self._expand_value(v) @@ -876,20 +994,19 @@ class Scope(object): @property def TEMPLATE(self) -> str: - return self.get_string('TEMPLATE', 'app') + return self.get_string("TEMPLATE", "app") def _rawTemplate(self) -> str: - return self.get_string('TEMPLATE') + return self.get_string("TEMPLATE") @property def TARGET(self) -> str: - target = self.expandString('TARGET') \ - or os.path.splitext(os.path.basename(self.file))[0] - return re.sub('\.\./', '', target) + target = self.expandString("TARGET") or os.path.splitext(os.path.basename(self.file))[0] + return re.sub(r"\.\./", "", target) @property - def _INCLUDED(self) -> typing.List[str]: - return self.get('_INCLUDED') + def _INCLUDED(self) -> List[str]: + return self.get("_INCLUDED") class QmakeParser: @@ -899,7 +1016,7 @@ class QmakeParser: def _generate_grammar(self): # Define grammar: - pp.ParserElement.setDefaultWhitespaceChars(' \t') + pp.ParserElement.setDefaultWhitespaceChars(" \t") def add_element(name: str, value: pp.ParserElement): nonlocal self @@ -908,66 +1025,81 @@ class QmakeParser: value.setDebug() return value - EOL = add_element('EOL', pp.Suppress(pp.LineEnd())) - Else = add_element('Else', pp.Keyword('else')) - Identifier = add_element('Identifier', pp.Word(pp.alphas + '_', - bodyChars=pp.alphanums+'_-./')) - BracedValue = add_element('BracedValue', - pp.nestedExpr( - ignoreExpr=pp.quotedString | - pp.QuotedString(quoteChar='$(', - endQuoteChar=')', - escQuote='\\', - unquoteResults=False) - ).setParseAction(lambda s, l, t: ['(', *t[0], ')'])) + EOL = add_element("EOL", pp.Suppress(pp.LineEnd())) + Else = add_element("Else", pp.Keyword("else")) + Identifier = add_element( + "Identifier", pp.Word(f"{pp.alphas}_", bodyChars=pp.alphanums + "_-./") + ) + BracedValue = add_element( + "BracedValue", + pp.nestedExpr( + ignoreExpr=pp.quotedString + | pp.QuotedString( + quoteChar="$(", endQuoteChar=")", escQuote="\\", unquoteResults=False + ) + ).setParseAction(lambda s, l, t: ["(", *t[0], ")"]), + ) - Substitution \ - = add_element('Substitution', - pp.Combine(pp.Literal('$') - + (((pp.Literal('$') + Identifier - + pp.Optional(pp.nestedExpr())) - | (pp.Literal('(') + Identifier + pp.Literal(')')) - | (pp.Literal('{') + Identifier + pp.Literal('}')) - | (pp.Literal('$') + pp.Literal('{') - + Identifier + pp.Optional(pp.nestedExpr()) - + pp.Literal('}')) - | (pp.Literal('$') + pp.Literal('[') + Identifier - + pp.Literal(']')) - )))) - LiteralValuePart = add_element('LiteralValuePart', - pp.Word(pp.printables, excludeChars='$#{}()')) - SubstitutionValue \ - = add_element('SubstitutionValue', - pp.Combine(pp.OneOrMore(Substitution - | LiteralValuePart - | pp.Literal('$')))) - FunctionValue \ - = add_element('FunctionValue', - pp.Group(pp.Suppress(pp.Literal('$') + pp.Literal('$')) + Substitution = add_element( + "Substitution", + pp.Combine( + pp.Literal("$") + + ( + ( + (pp.Literal("$") + Identifier + pp.Optional(pp.nestedExpr())) + | (pp.Literal("(") + Identifier + pp.Literal(")")) + | (pp.Literal("{") + Identifier + pp.Literal("}")) + | ( + pp.Literal("$") + + pp.Literal("{") + Identifier - + pp.nestedExpr() #.setParseAction(lambda s, l, t: ['(', *t[0], ')']) - ).setParseAction(lambda s, l, t: handle_function_value(*t))) - Value \ - = add_element('Value', - pp.NotAny(Else | pp.Literal('}') | EOL) \ - + (pp.QuotedString(quoteChar='"', escChar='\\') - | FunctionValue - | SubstitutionValue - | BracedValue)) + + pp.Optional(pp.nestedExpr()) + + pp.Literal("}") + ) + | (pp.Literal("$") + pp.Literal("[") + Identifier + pp.Literal("]")) + ) + ) + ), + ) + LiteralValuePart = add_element( + "LiteralValuePart", pp.Word(pp.printables, excludeChars="$#{}()") + ) + SubstitutionValue = add_element( + "SubstitutionValue", + pp.Combine(pp.OneOrMore(Substitution | LiteralValuePart | pp.Literal("$"))), + ) + FunctionValue = add_element( + "FunctionValue", + pp.Group( + pp.Suppress(pp.Literal("$") + pp.Literal("$")) + + Identifier + + pp.nestedExpr() # .setParseAction(lambda s, l, t: ['(', *t[0], ')']) + ).setParseAction(lambda s, l, t: handle_function_value(*t)), + ) + Value = add_element( + "Value", + pp.NotAny(Else | pp.Literal("}") | EOL) + + ( + pp.QuotedString(quoteChar='"', escChar="\\") + | FunctionValue + | SubstitutionValue + | BracedValue + ), + ) - Values = add_element('Values', pp.ZeroOrMore(Value)('value')) + Values = add_element("Values", pp.ZeroOrMore(Value)("value")) - Op = add_element('OP', - pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') \ - | pp.Literal('*=')) + Op = add_element( + "OP", pp.Literal("=") | pp.Literal("-=") | pp.Literal("+=") | pp.Literal("*=") + ) - Key = add_element('Key', Identifier) + Key = add_element("Key", Identifier) - Operation = add_element('Operation', Key('key') + Op('operation') + Values('value')) - CallArgs = add_element('CallArgs', pp.nestedExpr()) + Operation = add_element("Operation", Key("key") + Op("operation") + Values("value")) + CallArgs = add_element("CallArgs", pp.nestedExpr()) def parse_call_args(results): - out = '' + out = "" for item in chain(*results): if isinstance(item, str): out += item @@ -977,111 +1109,144 @@ class QmakeParser: CallArgs.setParseAction(parse_call_args) - Load = add_element('Load', pp.Keyword('load') + CallArgs('loaded')) - Include = add_element('Include', pp.Keyword('include') + CallArgs('included')) - Option = add_element('Option', pp.Keyword('option') + CallArgs('option')) + Load = add_element("Load", pp.Keyword("load") + CallArgs("loaded")) + Include = add_element("Include", pp.Keyword("include") + CallArgs("included")) + Option = add_element("Option", pp.Keyword("option") + CallArgs("option")) # ignore the whole thing... DefineTestDefinition = add_element( - 'DefineTestDefinition', - pp.Suppress(pp.Keyword('defineTest') + CallArgs - + pp.nestedExpr(opener='{', closer='}', ignoreExpr=pp.LineEnd()))) + "DefineTestDefinition", + pp.Suppress( + pp.Keyword("defineTest") + + CallArgs + + pp.nestedExpr(opener="{", closer="}", ignoreExpr=pp.LineEnd()) + ), + ) # ignore the whole thing... ForLoop = add_element( - 'ForLoop', - pp.Suppress(pp.Keyword('for') + CallArgs - + pp.nestedExpr(opener='{', closer='}', ignoreExpr=pp.LineEnd()))) + "ForLoop", + pp.Suppress( + pp.Keyword("for") + + CallArgs + + pp.nestedExpr(opener="{", closer="}", ignoreExpr=pp.LineEnd()) + ), + ) # ignore the whole thing... ForLoopSingleLine = add_element( - 'ForLoopSingleLine', - pp.Suppress(pp.Keyword('for') + CallArgs + pp.Literal(':') + pp.SkipTo(EOL))) + "ForLoopSingleLine", + pp.Suppress(pp.Keyword("for") + CallArgs + pp.Literal(":") + pp.SkipTo(EOL)), + ) # ignore the whole thing... - FunctionCall = add_element('FunctionCall', pp.Suppress(Identifier + pp.nestedExpr())) + FunctionCall = add_element("FunctionCall", pp.Suppress(Identifier + pp.nestedExpr())) - Scope = add_element('Scope', pp.Forward()) + Scope = add_element("Scope", pp.Forward()) - Statement = add_element('Statement', - pp.Group(Load | Include | Option | ForLoop | ForLoopSingleLine - | DefineTestDefinition | FunctionCall | Operation)) - StatementLine = add_element('StatementLine', Statement + (EOL | pp.FollowedBy('}'))) - StatementGroup = add_element('StatementGroup', - pp.ZeroOrMore(StatementLine | Scope | pp.Suppress(EOL))) + Statement = add_element( + "Statement", + pp.Group( + Load + | Include + | Option + | ForLoop + | ForLoopSingleLine + | DefineTestDefinition + | FunctionCall + | Operation + ), + ) + StatementLine = add_element("StatementLine", Statement + (EOL | pp.FollowedBy("}"))) + StatementGroup = add_element( + "StatementGroup", pp.ZeroOrMore(StatementLine | Scope | pp.Suppress(EOL)) + ) - Block = add_element('Block', - pp.Suppress('{') + pp.Optional(EOL) - + StatementGroup + pp.Optional(EOL) - + pp.Suppress('}') + pp.Optional(EOL)) + Block = add_element( + "Block", + pp.Suppress("{") + + pp.Optional(EOL) + + StatementGroup + + pp.Optional(EOL) + + pp.Suppress("}") + + pp.Optional(EOL), + ) - ConditionEnd = add_element('ConditionEnd', - pp.FollowedBy((pp.Optional(pp.White()) - + (pp.Literal(':') - | pp.Literal('{') - | pp.Literal('|'))))) + ConditionEnd = add_element( + "ConditionEnd", + pp.FollowedBy( + (pp.Optional(pp.White()) + (pp.Literal(":") | pp.Literal("{") | pp.Literal("|"))) + ), + ) - ConditionPart1 = add_element('ConditionPart1', - (pp.Optional('!') + Identifier + pp.Optional(BracedValue))) - ConditionPart2 = add_element('ConditionPart2', pp.CharsNotIn('#{}|:=\\\n')) + ConditionPart1 = add_element( + "ConditionPart1", (pp.Optional("!") + Identifier + pp.Optional(BracedValue)) + ) + ConditionPart2 = add_element("ConditionPart2", pp.CharsNotIn("#{}|:=\\\n")) ConditionPart = add_element( - 'ConditionPart', - (ConditionPart1 ^ ConditionPart2) + ConditionEnd) + "ConditionPart", (ConditionPart1 ^ ConditionPart2) + ConditionEnd + ) - ConditionOp = add_element('ConditionOp', pp.Literal('|') ^ pp.Literal(':')) - ConditionWhiteSpace = add_element('ConditionWhiteSpace', - pp.Suppress(pp.Optional(pp.White(' ')))) + ConditionOp = add_element("ConditionOp", pp.Literal("|") ^ pp.Literal(":")) + ConditionWhiteSpace = add_element( + "ConditionWhiteSpace", pp.Suppress(pp.Optional(pp.White(" "))) + ) - ConditionRepeated = add_element('ConditionRepeated', - pp.ZeroOrMore(ConditionOp - + ConditionWhiteSpace + ConditionPart)) + ConditionRepeated = add_element( + "ConditionRepeated", pp.ZeroOrMore(ConditionOp + ConditionWhiteSpace + ConditionPart) + ) - Condition = add_element('Condition', pp.Combine(ConditionPart + ConditionRepeated)) - Condition.setParseAction(lambda x: ' '.join(x).strip().replace(':', ' && ').strip(' && ')) + Condition = add_element("Condition", pp.Combine(ConditionPart + ConditionRepeated)) + Condition.setParseAction(lambda x: " ".join(x).strip().replace(":", " && ").strip(" && ")) # Weird thing like write_file(a)|error() where error() is the alternative condition # which happens to be a function call. In this case there is no scope, but our code expects # a scope with a list of statements, so create a fake empty statement. ConditionEndingInFunctionCall = add_element( - 'ConditionEndingInFunctionCall', pp.Suppress(ConditionOp) + FunctionCall - + pp.Empty().setParseAction(lambda x: [[]]) - .setResultsName('statements')) + "ConditionEndingInFunctionCall", + pp.Suppress(ConditionOp) + + FunctionCall + + pp.Empty().setParseAction(lambda x: [[]]).setResultsName("statements"), + ) - SingleLineScope = add_element('SingleLineScope', - pp.Suppress(pp.Literal(':')) - + pp.Group(Block | (Statement + EOL))('statements')) - MultiLineScope = add_element('MultiLineScope', Block('statements')) + SingleLineScope = add_element( + "SingleLineScope", + pp.Suppress(pp.Literal(":")) + pp.Group(Block | (Statement + EOL))("statements"), + ) + MultiLineScope = add_element("MultiLineScope", Block("statements")) - SingleLineElse = add_element('SingleLineElse', - pp.Suppress(pp.Literal(':')) - + (Scope | Block | (Statement + pp.Optional(EOL)))) - MultiLineElse = add_element('MultiLineElse', Block) - ElseBranch = add_element('ElseBranch', pp.Suppress(Else) + (SingleLineElse | MultiLineElse)) + SingleLineElse = add_element( + "SingleLineElse", + pp.Suppress(pp.Literal(":")) + (Scope | Block | (Statement + pp.Optional(EOL))), + ) + MultiLineElse = add_element("MultiLineElse", Block) + ElseBranch = add_element("ElseBranch", pp.Suppress(Else) + (SingleLineElse | MultiLineElse)) # Scope is already add_element'ed in the forward declaration above. - Scope <<= \ - pp.Group(Condition('condition') - + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) - + pp.Optional(ElseBranch)('else_statements')) + Scope <<= pp.Group( + Condition("condition") + + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) + + pp.Optional(ElseBranch)("else_statements") + ) - Grammar = StatementGroup('statements') + Grammar = StatementGroup("statements") Grammar.ignore(pp.pythonStyleComment()) return Grammar def parseFile(self, file: str): - print('Parsing \"{}\"...'.format(file)) + print(f'Parsing "{file}"...') try: - with open(file, 'r') as file_fd: + with open(file, "r") as file_fd: contents = file_fd.read() - old_contents = contents + # old_contents = contents contents = fixup_comments(contents) contents = fixup_linecontinuation(contents) result = self._Grammar.parseString(contents, parseAll=True) except pp.ParseException as pe: print(pe.line) - print(' '*(pe.col-1) + '^') + print(f"{' ' * (pe.col-1)}^") print(pe) raise pe return result @@ -1094,30 +1259,32 @@ def parseProFile(file: str, *, debug=False): def map_condition(condition: str) -> str: # Some hardcoded cases that are too bothersome to generalize. - condition = re.sub(r'qtConfig\(opengl\(es1\|es2\)\?\)', - r'QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3', - condition) - condition = re.sub(r'qtConfig\(opengl\.\*\)', r'QT_FEATURE_opengl', condition) - condition = re.sub(r'^win\*$', r'win', condition) - condition = re.sub(r'^no-png$', r'NOT QT_FEATURE_png', condition) - condition = re.sub(r'contains\(CONFIG, static\)', r'NOT QT_BUILD_SHARED_LIBS', condition) - condition = re.sub(r'contains\(QT_CONFIG,\w*shared\)', r'QT_BUILD_SHARED_LIBS', condition) + condition = re.sub( + r"qtConfig\(opengl\(es1\|es2\)\?\)", + r"QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3", + condition, + ) + condition = re.sub(r"qtConfig\(opengl\.\*\)", r"QT_FEATURE_opengl", condition) + condition = re.sub(r"^win\*$", r"win", condition) + condition = re.sub(r"^no-png$", r"NOT QT_FEATURE_png", condition) + condition = re.sub(r"contains\(CONFIG, static\)", r"NOT QT_BUILD_SHARED_LIBS", condition) + condition = re.sub(r"contains\(QT_CONFIG,\w*shared\)", r"QT_BUILD_SHARED_LIBS", condition) def gcc_version_handler(match_obj: re.Match): operator = match_obj.group(1) version_type = match_obj.group(2) - if operator == 'equals': - operator = 'STREQUAL' - elif operator == 'greaterThan': - operator = 'STRGREATER' - elif operator == 'lessThan': - operator = 'STRLESS' + if operator == "equals": + operator = "STREQUAL" + elif operator == "greaterThan": + operator = "STRGREATER" + elif operator == "lessThan": + operator = "STRLESS" version = match_obj.group(3) - return '(QT_COMPILER_VERSION_{} {} {})'.format(version_type, operator, version) + return f"(QT_COMPILER_VERSION_{version_type} {operator} {version})" # TODO: Possibly fix for other compilers. - pattern = r'(equals|greaterThan|lessThan)\(QT_GCC_([A-Z]+)_VERSION,[ ]*([0-9]+)\)' + pattern = r"(equals|greaterThan|lessThan)\(QT_GCC_([A-Z]+)_VERSION,[ ]*([0-9]+)\)" condition = re.sub(pattern, gcc_version_handler, condition) # TODO: the current if(...) replacement makes the parentheses @@ -1125,108 +1292,104 @@ def map_condition(condition: str) -> str: # Need to fix this either with pypi regex recursive regexps, # using pyparsing, or some other proper means of handling # balanced parentheses. - condition = re.sub(r'\bif\s*\((.*?)\)', r'\1', condition) + condition = re.sub(r"\bif\s*\((.*?)\)", r"\1", condition) - condition = re.sub(r'\bisEmpty\s*\((.*?)\)', r'\1_ISEMPTY', condition) - condition = re.sub(r'\bcontains\s*\((.*?),\s*"?(.*?)"?\)', - r'\1___contains___\2', condition) - condition = re.sub(r'\bequals\s*\((.*?),\s*"?(.*?)"?\)', - r'\1___equals___\2', condition) - condition = re.sub(r'\bisEqual\s*\((.*?),\s*"?(.*?)"?\)', - r'\1___equals___\2', condition) - condition = re.sub(r'\s*==\s*', '___STREQUAL___', condition) - condition = re.sub(r'\bexists\s*\((.*?)\)', r'EXISTS \1', condition) + condition = re.sub(r"\bisEmpty\s*\((.*?)\)", r"\1_ISEMPTY", condition) + condition = re.sub(r'\bcontains\s*\((.*?),\s*"?(.*?)"?\)', r"\1___contains___\2", condition) + condition = re.sub(r'\bequals\s*\((.*?),\s*"?(.*?)"?\)', r"\1___equals___\2", condition) + condition = re.sub(r'\bisEqual\s*\((.*?),\s*"?(.*?)"?\)', r"\1___equals___\2", condition) + condition = re.sub(r"\s*==\s*", "___STREQUAL___", condition) + condition = re.sub(r"\bexists\s*\((.*?)\)", r"EXISTS \1", condition) - pattern = r'CONFIG\((debug|release),debug\|release\)' + pattern = r"CONFIG\((debug|release),debug\|release\)" match_result = re.match(pattern, condition) if match_result: build_type = match_result.group(1) - if build_type == 'debug': - build_type = 'Debug' - elif build_type == 'release': - build_type = 'Release' - condition = re.sub(pattern, '(CMAKE_BUILD_TYPE STREQUAL {})'.format(build_type), condition) + if build_type == "debug": + build_type = "Debug" + elif build_type == "release": + build_type = "Release" + condition = re.sub(pattern, f"(CMAKE_BUILD_TYPE STREQUAL {build_type})", condition) - condition = condition.replace('*', '_x_') - condition = condition.replace('.$$', '__ss_') - condition = condition.replace('$$', '_ss_') + condition = condition.replace("*", "_x_") + condition = condition.replace(".$$", "__ss_") + condition = condition.replace("$$", "_ss_") - condition = condition.replace('!', 'NOT ') - condition = condition.replace('&&', ' AND ') - condition = condition.replace('|', ' OR ') + condition = condition.replace("!", "NOT ") + condition = condition.replace("&&", " AND ") + condition = condition.replace("|", " OR ") - cmake_condition = '' + cmake_condition = "" for part in condition.split(): # some features contain e.g. linux, that should not be # turned upper case - feature = re.match(r"(qtConfig|qtHaveModule)\(([a-zA-Z0-9_-]+)\)", - part) + feature = re.match(r"(qtConfig|qtHaveModule)\(([a-zA-Z0-9_-]+)\)", part) if feature: - if (feature.group(1) == "qtHaveModule"): - part = 'TARGET {}'.format(map_qt_library(feature.group(2))) + if feature.group(1) == "qtHaveModule": + part = f"TARGET {map_qt_library(feature.group(2))}" else: feature_name = featureName(feature.group(2)) - if feature_name.startswith('system_') and is_known_3rd_party_library(feature_name[7:]): - part = 'ON' - elif feature == 'dlopen': - part = 'ON' + if feature_name.startswith("system_") and is_known_3rd_party_library( + feature_name[7:] + ): + part = "ON" + elif feature == "dlopen": + part = "ON" else: - part = 'QT_FEATURE_' + feature_name + part = "QT_FEATURE_" + feature_name else: part = map_platform(part) - part = part.replace('true', 'ON') - part = part.replace('false', 'OFF') - cmake_condition += ' ' + part + part = part.replace("true", "ON") + part = part.replace("false", "OFF") + cmake_condition += " " + part return cmake_condition.strip() -def handle_subdir(scope: Scope, - cm_fh: typing.IO[str], - *, - indent: int = 0, - is_example: bool = False) -> None: +def handle_subdir( + scope: Scope, cm_fh: IO[str], *, indent: int = 0, is_example: bool = False +) -> None: # Global nested dictionary that will contain sub_dir assignments and their conditions. # Declared as a global in order not to pollute the nested function signatures with giant # type hints. - sub_dirs: typing.Dict[str, typing.Dict[str, typing.Set[typing.FrozenSet[str]]]] = {} + sub_dirs: Dict[str, Dict[str, Set[FrozenSet[str]]]] = {} # Collects assignment conditions into global sub_dirs dict. - def collect_subdir_info(sub_dir_assignment: str, - *, - current_conditions: typing.FrozenSet[str] = None): - subtraction = sub_dir_assignment.startswith('-') + def collect_subdir_info(sub_dir_assignment: str, *, current_conditions: FrozenSet[str] = None): + subtraction = sub_dir_assignment.startswith("-") if subtraction: subdir_name = sub_dir_assignment[1:] else: subdir_name = sub_dir_assignment if subdir_name not in sub_dirs: sub_dirs[subdir_name] = {} - additions = sub_dirs[subdir_name].get('additions', set()) - subtractions = sub_dirs[subdir_name].get('subtractions', set()) + additions = sub_dirs[subdir_name].get("additions", set()) + subtractions = sub_dirs[subdir_name].get("subtractions", set()) if current_conditions: if subtraction: subtractions.add(current_conditions) else: additions.add(current_conditions) if additions: - sub_dirs[subdir_name]['additions'] = additions + sub_dirs[subdir_name]["additions"] = additions if subtractions: - sub_dirs[subdir_name]['subtractions'] = subtractions + sub_dirs[subdir_name]["subtractions"] = subtractions # Recursive helper that collects subdir info for given scope, # and the children of the given scope. - def handle_subdir_helper(scope: Scope, - cm_fh: typing.IO[str], - *, - indent: int = 0, - current_conditions: typing.FrozenSet[str] = None, - is_example: bool = False): - for sd in scope.get_files('SUBDIRS'): + def handle_subdir_helper( + scope: Scope, + cm_fh: IO[str], + *, + indent: int = 0, + current_conditions: FrozenSet[str] = None, + is_example: bool = False, + ): + for sd in scope.get_files("SUBDIRS"): # Collect info about conditions and SUBDIR assignments in the # current scope. - if os.path.isdir(sd) or sd.startswith('-'): + if os.path.isdir(sd) or sd.startswith("-"): collect_subdir_info(sd, current_conditions=current_conditions) # For the file case, directly write into the file handle. elif os.path.isfile(sd): @@ -1239,15 +1402,14 @@ def handle_subdir(scope: Scope, collect_subdir_info(dirname, current_conditions=current_conditions) else: subdir_result = parseProFile(sd, debug=False) - subdir_scope \ - = Scope.FromDict(scope, sd, - subdir_result.asDict().get('statements'), - '', scope.basedir) + subdir_scope = Scope.FromDict( + scope, sd, subdir_result.asDict().get("statements"), "", scope.basedir + ) do_include(subdir_scope) cmakeify_scope(subdir_scope, cm_fh, indent=indent, is_example=is_example) else: - print(' XXXX: SUBDIR {} in {}: Not found.'.format(sd, scope)) + print(f" XXXX: SUBDIR {sd} in {scope}: Not found.") # Collect info about conditions and SUBDIR assignments in child # scopes, aka recursively call the same function, but with an @@ -1255,12 +1417,14 @@ def handle_subdir(scope: Scope, for c in scope.children: # Use total_condition for 'else' conditions, otherwise just use the regular value to # simplify the logic. - child_condition = c.total_condition if c.condition == 'else' else c.condition - handle_subdir_helper(c, cm_fh, - indent=indent + 1, - is_example=is_example, - current_conditions=frozenset((*current_conditions, - child_condition))) + child_condition = c.total_condition if c.condition == "else" else c.condition + handle_subdir_helper( + c, + cm_fh, + indent=indent + 1, + is_example=is_example, + current_conditions=frozenset((*current_conditions, child_condition)), + ) def group_and_print_sub_dirs(indent: int = 0): # Simplify conditions, and group @@ -1270,40 +1434,42 @@ def handle_subdir(scope: Scope, # Wraps each element in the given interable with parentheses, # to make sure boolean simplification happens correctly. def wrap_in_parenthesis(iterable): - return ['({})'.format(c) for c in iterable] + return [f"({c})" for c in iterable] def join_all_conditions(set_of_alternatives): # Elements within one frozen set represent one single # alternative whose pieces are ANDed together. # This is repeated for each alternative that would # enable a subdir, and are thus ORed together. - final_str = '' + final_str = "" if set_of_alternatives: - wrapped_set_of_alternatives = [wrap_in_parenthesis(alternative) - for alternative in set_of_alternatives] - alternatives = ['({})'.format(" AND ".join(alternative)) - for alternative in wrapped_set_of_alternatives] - final_str = ' OR '.join(sorted(alternatives)) + wrapped_set_of_alternatives = [ + wrap_in_parenthesis(alternative) for alternative in set_of_alternatives + ] + alternatives = [ + f'({" AND ".join(alternative)})' for alternative in wrapped_set_of_alternatives + ] + final_str = " OR ".join(sorted(alternatives)) return final_str for subdir_name in sub_dirs: - additions = sub_dirs[subdir_name].get('additions', set()) - subtractions = sub_dirs[subdir_name].get('subtractions', set()) + additions = sub_dirs[subdir_name].get("additions", set()) + subtractions = sub_dirs[subdir_name].get("subtractions", set()) # An empty condition key represents the group of sub dirs # that should be added unconditionally. - condition_key = '' + condition_key = "" if additions or subtractions: addition_str = join_all_conditions(additions) if addition_str: - addition_str = '({})'.format(addition_str) + addition_str = f"({addition_str})" subtraction_str = join_all_conditions(subtractions) if subtraction_str: - subtraction_str = 'NOT ({})'.format(subtraction_str) + subtraction_str = f"NOT ({subtraction_str})" condition_str = addition_str if condition_str and subtraction_str: - condition_str += ' AND ' + condition_str += " AND " condition_str += subtraction_str condition_simplified = simplify_condition(condition_str) condition_key = condition_simplified @@ -1313,18 +1479,18 @@ def handle_subdir(scope: Scope, grouped_sub_dirs[condition_key] = sub_dir_list_by_key # Print the groups. - ind = ' ' * indent + ind = spaces(1) for condition_key in grouped_sub_dirs: cond_ind = ind if condition_key: - cm_fh.write(f'{ind}if({condition_key})\n') + cm_fh.write(f"{ind}if({condition_key})\n") cond_ind += " " sub_dir_list_by_key = grouped_sub_dirs.get(condition_key, []) for subdir_name in sub_dir_list_by_key: - cm_fh.write(f'{cond_ind}add_subdirectory({subdir_name})\n') + cm_fh.write(f"{cond_ind}add_subdirectory({subdir_name})\n") if condition_key: - cm_fh.write(f'{ind}endif()\n') + cm_fh.write(f"{ind}endif()\n") # A set of conditions which will be ANDed together. The set is recreated with more conditions # as the scope deepens. @@ -1335,22 +1501,21 @@ def handle_subdir(scope: Scope, recursive_evaluate_scope(scope) # Do the work. - handle_subdir_helper(scope, cm_fh, - indent=indent, - current_conditions=current_conditions, - is_example=is_example) + handle_subdir_helper( + scope, cm_fh, indent=indent, current_conditions=current_conditions, is_example=is_example + ) group_and_print_sub_dirs(indent=indent) -def sort_sources(sources: typing.List[str]) -> typing.List[str]: - to_sort = {} # type: typing.Dict[str, typing.List[str]] +def sort_sources(sources: List[str]) -> List[str]: + to_sort = {} # type: Dict[str, List[str]] for s in sources: if s is None: continue dir = os.path.dirname(s) base = os.path.splitext(os.path.basename(s))[0] - if base.endswith('_p'): + if base.endswith("_p"): base = base[:-2] sort_name = posixpath.join(dir, base) @@ -1361,245 +1526,280 @@ def sort_sources(sources: typing.List[str]) -> typing.List[str]: lines = [] for k in sorted(to_sort.keys()): - lines.append(' '.join(sorted(to_sort[k]))) + lines.append(" ".join(sorted(to_sort[k]))) return lines -def _map_libraries_to_cmake(libraries: typing.List[str], - known_libraries: typing.Set[str]) -> typing.List[str]: - result = [] # type: typing.List[str] +def _map_libraries_to_cmake(libraries: List[str], known_libraries: Set[str]) -> List[str]: + result = [] # type: List[str] is_framework = False - for l in libraries: - if l == '-framework': + for lib in libraries: + if lib == "-framework": is_framework = True continue if is_framework: - l = '${FW%s}' % l - if l.startswith('-l'): - l = l[2:] + lib = f"${{FW{lib}}}" + if lib.startswith("-l"): + lib = lib[2:] - if l.startswith('-'): - l = '# Remove: {}'.format(l[1:]) + if lib.startswith("-"): + lib = f"# Remove: {lib[1:]}" else: - l = map_3rd_party_library(l) + lib = map_3rd_party_library(lib) - if not l or l in result or l in known_libraries: + if not lib or lib in result or lib in known_libraries: continue - result.append(l) + result.append(lib) is_framework = False return result -def extract_cmake_libraries(scope: Scope, *, known_libraries: typing.Set[str]=set()) \ - -> typing.Tuple[typing.List[str], typing.List[str]]: - public_dependencies = [] # type: typing.List[str] - private_dependencies = [] # type: typing.List[str] +def extract_cmake_libraries( + scope: Scope, *, known_libraries: Set[str] = set() +) -> Tuple[List[str], List[str]]: + public_dependencies = [] # type: List[str] + private_dependencies = [] # type: List[str] - for key in ['QMAKE_USE', 'LIBS',]: + for key in ["QMAKE_USE", "LIBS"]: public_dependencies += scope.expand(key) - for key in ['QMAKE_USE_PRIVATE', 'QMAKE_USE_FOR_PRIVATE', 'LIBS_PRIVATE',]: + for key in ["QMAKE_USE_PRIVATE", "QMAKE_USE_FOR_PRIVATE", "LIBS_PRIVATE"]: private_dependencies += scope.expand(key) - for key in ['QT_FOR_PRIVATE','QT_PRIVATE']: + for key in ["QT_FOR_PRIVATE", "QT_PRIVATE"]: private_dependencies += [map_qt_library(q) for q in scope.expand(key)] - for key in ['QT',]: + for key in ["QT"]: # Qt public libs: These may include FooPrivate in which case we get # a private dependency on FooPrivate as well as a public dependency on Foo for lib in scope.expand(key): mapped_lib = map_qt_library(lib) - if mapped_lib.endswith('Private'): + if mapped_lib.endswith("Private"): private_dependencies.append(mapped_lib) public_dependencies.append(mapped_lib[:-7]) else: public_dependencies.append(mapped_lib) - return (_map_libraries_to_cmake(public_dependencies, known_libraries), - _map_libraries_to_cmake(private_dependencies, known_libraries)) + return ( + _map_libraries_to_cmake(public_dependencies, known_libraries), + _map_libraries_to_cmake(private_dependencies, known_libraries), + ) -def write_header(cm_fh: typing.IO[str], name: str, - typename: str, *, indent: int = 0): - cm_fh.write('{}###########################################' - '##########################\n'.format(spaces(indent))) - cm_fh.write('{}## {} {}:\n'.format(spaces(indent), name, typename)) - cm_fh.write('{}###########################################' - '##########################\n\n'.format(spaces(indent))) +def write_header(cm_fh: IO[str], name: str, typename: str, *, indent: int = 0): + ind = spaces(indent) + comment_line = "#" * 69 + cm_fh.write(f"{ind}{comment_line}\n") + cm_fh.write(f"{ind}## {name} {typename}:\n") + cm_fh.write(f"{ind}{comment_line}\n\n") -def write_scope_header(cm_fh: typing.IO[str], *, indent: int = 0): - cm_fh.write('\n{}## Scopes:\n'.format(spaces(indent))) - cm_fh.write('{}###########################################' - '##########################\n'.format(spaces(indent))) +def write_scope_header(cm_fh: IO[str], *, indent: int = 0): + ind = spaces(indent) + comment_line = "#" * 69 + cm_fh.write(f"\n{ind}## Scopes:\n") + cm_fh.write(f"{ind}{comment_line}\n") -def write_list(cm_fh: typing.IO[str], entries: typing.List[str], - cmake_parameter: str, - indent: int = 0, *, - header: str = '', footer: str = ''): +def write_list( + cm_fh: IO[str], + entries: List[str], + cmake_parameter: str, + indent: int = 0, + *, + header: str = "", + footer: str = "", +): if not entries: return ind = spaces(indent) - extra_indent = '' + extra_indent = "" if header: - cm_fh.write('{}{}'.format(ind, header)) - extra_indent += ' ' + cm_fh.write(f"{ind}{header}") + extra_indent += " " if cmake_parameter: - cm_fh.write('{}{}{}\n'.format(ind, extra_indent, cmake_parameter)) - extra_indent += ' ' + cm_fh.write(f"{ind}{extra_indent}{cmake_parameter}\n") + extra_indent += " " for s in sort_sources(entries): - cm_fh.write('{}{}{}\n'.format(ind, extra_indent, s)) + cm_fh.write(f"{ind}{extra_indent}{s}\n") if footer: - cm_fh.write('{}{}\n'.format(ind, footer)) + cm_fh.write(f"{ind}{footer}\n") -def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str, - keys: typing.List[str], indent: int = 0, *, - header: str = '', footer: str = ''): +def write_source_file_list( + cm_fh: IO[str], + scope, + cmake_parameter: str, + keys: List[str], + indent: int = 0, + *, + header: str = "", + footer: str = "", +): # collect sources - sources: typing.List[str] = [] + sources: List[str] = [] for key in keys: sources += scope.get_files(key, use_vpath=True) write_list(cm_fh, sources, cmake_parameter, indent, header=header, footer=footer) -def write_all_source_file_lists(cm_fh: typing.IO[str], scope: Scope, header: str, *, - indent: int = 0, footer: str = '', - extra_keys: typing.Optional[typing.List[str]] = None): +def write_all_source_file_lists( + cm_fh: IO[str], + scope: Scope, + header: str, + *, + indent: int = 0, + footer: str = "", + extra_keys: Optional[List[str]] = None, +): if extra_keys is None: extra_keys = [] - write_source_file_list(cm_fh, scope, header, - ['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'] + extra_keys, - indent, footer=footer) + write_source_file_list( + cm_fh, + scope, + header, + ["SOURCES", "HEADERS", "OBJECTIVE_SOURCES", "NO_PCH_SOURCES", "FORMS"] + extra_keys, + indent, + footer=footer, + ) -def write_defines(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: str, *, - indent: int = 0, footer: str = ''): - defines = scope.expand('DEFINES') - defines += [d[2:] for d in scope.expand('QMAKE_CXXFLAGS') if d.startswith('-D')] - defines = [d.replace('=\\\\\\"$$PWD/\\\\\\"', - '="${CMAKE_CURRENT_SOURCE_DIR}/"') for d in defines] +def write_defines( + cm_fh: IO[str], scope: Scope, cmake_parameter: str, *, indent: int = 0, footer: str = "" +): + defines = scope.expand("DEFINES") + defines += [d[2:] for d in scope.expand("QMAKE_CXXFLAGS") if d.startswith("-D")] + defines = [ + d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"') for d in defines + ] - if 'qml_debug' in scope.get('CONFIG'): - defines.append('QT_QML_DEBUG') + if "qml_debug" in scope.get("CONFIG"): + defines.append("QT_QML_DEBUG") write_list(cm_fh, defines, cmake_parameter, indent, footer=footer) -def write_include_paths(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: str, *, - indent: int = 0, footer: str = ''): - includes = [i.rstrip('/') or ('/') for i in scope.get_files('INCLUDEPATH')] +def write_include_paths( + cm_fh: IO[str], scope: Scope, cmake_parameter: str, *, indent: int = 0, footer: str = "" +): + includes = [i.rstrip("/") or ("/") for i in scope.get_files("INCLUDEPATH")] write_list(cm_fh, includes, cmake_parameter, indent, footer=footer) -def write_compile_options(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: str, *, - indent: int = 0, footer: str = ''): - compile_options = [d for d in scope.expand('QMAKE_CXXFLAGS') if not d.startswith('-D')] +def write_compile_options( + cm_fh: IO[str], scope: Scope, cmake_parameter: str, *, indent: int = 0, footer: str = "" +): + compile_options = [d for d in scope.expand("QMAKE_CXXFLAGS") if not d.startswith("-D")] write_list(cm_fh, compile_options, cmake_parameter, indent, footer=footer) -def write_library_section(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0, known_libraries: typing.Set[str]=set()): - (public_dependencies, private_dependencies) \ - = extract_cmake_libraries(scope, known_libraries=known_libraries) +def write_library_section( + cm_fh: IO[str], scope: Scope, *, indent: int = 0, known_libraries: Set[str] = set() +): + public_dependencies, private_dependencies = extract_cmake_libraries( + scope, known_libraries=known_libraries + ) - write_list(cm_fh, private_dependencies, 'LIBRARIES', indent + 1) - write_list(cm_fh, public_dependencies, 'PUBLIC_LIBRARIES', indent + 1) + write_list(cm_fh, private_dependencies, "LIBRARIES", indent + 1) + write_list(cm_fh, public_dependencies, "PUBLIC_LIBRARIES", indent + 1) -def write_autogen_section(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0): - forms = scope.get_files('FORMS') +def write_autogen_section(cm_fh: IO[str], scope: Scope, *, indent: int = 0): + forms = scope.get_files("FORMS") if forms: - write_list(cm_fh, ['uic'], 'ENABLE_AUTOGEN_TOOLS', indent) + write_list(cm_fh, ["uic"], "ENABLE_AUTOGEN_TOOLS", indent) -def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0, known_libraries=set()): +def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int = 0, known_libraries=set()): ind = spaces(indent) # mark RESOURCES as visited: - scope.get('RESOURCES') + scope.get("RESOURCES") - write_all_source_file_lists(cm_fh, scope, 'SOURCES', indent=indent + 1) + write_all_source_file_lists(cm_fh, scope, "SOURCES", indent=indent + 1) - write_source_file_list(cm_fh, scope, 'DBUS_ADAPTOR_SOURCES', ['DBUS_ADAPTORS',], indent + 1) - dbus_adaptor_flags = scope.expand('QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS') + write_source_file_list(cm_fh, scope, "DBUS_ADAPTOR_SOURCES", ["DBUS_ADAPTORS"], indent + 1) + dbus_adaptor_flags = scope.expand("QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS") if dbus_adaptor_flags: - cm_fh.write('{} DBUS_ADAPTOR_FLAGS\n'.format(ind)) - cm_fh.write('{} "{}"\n'.format(ind, '" "'.join(dbus_adaptor_flags))) + dbus_adaptor_flags_line = '" "'.join(dbus_adaptor_flags) + cm_fh.write(f"{ind} DBUS_ADAPTOR_FLAGS\n") + cm_fh.write(f'{ind} "{dbus_adaptor_flags_line}"\n') - write_source_file_list(cm_fh, scope, 'DBUS_INTERFACE_SOURCES', ['DBUS_INTERFACES',], indent + 1) - dbus_interface_flags = scope.expand('QDBUSXML2CPP_INTERFACE_HEADER_FLAGS') + write_source_file_list(cm_fh, scope, "DBUS_INTERFACE_SOURCES", ["DBUS_INTERFACES"], indent + 1) + dbus_interface_flags = scope.expand("QDBUSXML2CPP_INTERFACE_HEADER_FLAGS") if dbus_interface_flags: - cm_fh.write('{} DBUS_INTERFACE_FLAGS\n'.format(ind)) - cm_fh.write('{} "{}"\n'.format(ind, '" "'.join(dbus_interface_flags))) + dbus_interface_flags_line = '" "'.join(dbus_interface_flags) + cm_fh.write(f"{ind} DBUS_INTERFACE_FLAGS\n") + cm_fh.write(f'{ind} "{dbus_interface_flags_line}"\n') - write_defines(cm_fh, scope, 'DEFINES', indent=indent + 1) + write_defines(cm_fh, scope, "DEFINES", indent=indent + 1) - write_include_paths(cm_fh, scope, 'INCLUDE_DIRECTORIES', indent=indent + 1) + write_include_paths(cm_fh, scope, "INCLUDE_DIRECTORIES", indent=indent + 1) write_library_section(cm_fh, scope, indent=indent, known_libraries=known_libraries) - write_compile_options(cm_fh, scope, 'COMPILE_OPTIONS', indent=indent + 1) + write_compile_options(cm_fh, scope, "COMPILE_OPTIONS", indent=indent + 1) write_autogen_section(cm_fh, scope, indent=indent + 1) - link_options = scope.get('QMAKE_LFLAGS') + link_options = scope.get("QMAKE_LFLAGS") if link_options: - cm_fh.write('{} LINK_OPTIONS\n'.format(ind)) + cm_fh.write(f"{ind} LINK_OPTIONS\n") for lo in link_options: - cm_fh.write('{} "{}"\n'.format(ind, lo)) + cm_fh.write(f'{ind} "{lo}"\n') - moc_options = scope.get('QMAKE_MOC_OPTIONS') + moc_options = scope.get("QMAKE_MOC_OPTIONS") if moc_options: - cm_fh.write('{} MOC_OPTIONS\n'.format(ind)) + cm_fh.write(f"{ind} MOC_OPTIONS\n") for mo in moc_options: - cm_fh.write('{} "{}"\n'.format(ind, mo)) + cm_fh.write(f'{ind} "{mo}"\n') - precompiled_header = scope.get('PRECOMPILED_HEADER') + precompiled_header = scope.get("PRECOMPILED_HEADER") if precompiled_header: - cm_fh.write('{} PRECOMPILED_HEADER\n'.format(ind)) + cm_fh.write(f"{ind} PRECOMPILED_HEADER\n") for header in precompiled_header: - cm_fh.write('{} "{}"\n'.format(ind, header)) + cm_fh.write(f'{ind} "{header}"\n') - no_pch_sources = scope.get('NO_PCH_SOURCES') + no_pch_sources = scope.get("NO_PCH_SOURCES") if no_pch_sources: - cm_fh.write('{} NO_PCH_SOURCES\n'.format(ind)) + cm_fh.write(f"{ind} NO_PCH_SOURCES\n") for source in no_pch_sources: - cm_fh.write('{} "{}"\n'.format(ind, source)) + cm_fh.write(f'{ind} "{source}"\n') def is_simple_condition(condition: str) -> bool: - return ' ' not in condition \ - or (condition.startswith('NOT ') and ' ' not in condition[4:]) + return " " not in condition or (condition.startswith("NOT ") and " " not in condition[4:]) def write_ignored_keys(scope: Scope, indent: str) -> str: - result = '' + result = "" ignored_keys = scope.keys - scope.visited_keys for k in sorted(ignored_keys): - if k == '_INCLUDED' or k == 'TARGET' or k == 'QMAKE_DOCS' or k == 'QT_SOURCE_TREE' \ - or k == 'QT_BUILD_TREE' or k == 'TRACEPOINT_PROVIDER': + if k in { + "_INCLUDED", + "TARGET", + "QMAKE_DOCS", + "QT_SOURCE_TREE", + "QT_BUILD_TREE", + "TRACEPOINT_PROVIDER", + }: # All these keys are actually reported already continue values = scope.get(k) - value_string = '' if not values \ - else '"' + '" "'.join(scope.get(k)) + '"' - result += '{}# {} = {}\n'.format(indent, k, value_string) + value_string = "" if not values else '"' + '" "'.join(scope.get(k)) + '"' + result += f"{indent}# {k} = {value_string}\n" if result: - result = '\n#### Keys ignored in scope {}:\n{}'.format(scope, result) + result = f"\n#### Keys ignored in scope {scope}:\n{result}" return result @@ -1620,8 +1820,7 @@ def _iterate_expr_tree(expr, op, matches): def _simplify_expressions(expr, op, matches, replacement): for arg in expr.args: - expr = expr.subs(arg, _simplify_expressions(arg, op, matches, - replacement)) + expr = expr.subs(arg, _simplify_expressions(arg, op, matches, replacement)) if expr.func == op: (to_match, keepers) = tuple(_iterate_expr_tree(expr, op, matches)) @@ -1644,18 +1843,15 @@ def _simplify_expressions(expr, op, matches, replacement): def _simplify_flavors_in_condition(base: str, flavors, expr): - ''' Simplify conditions based on the knownledge of which flavors - belong to which OS. ''' + """ Simplify conditions based on the knownledge of which flavors + belong to which OS. """ base_expr = simplify_logic(base) - false_expr = simplify_logic('false') + false_expr = simplify_logic("false") for flavor in flavors: flavor_expr = simplify_logic(flavor) - expr = _simplify_expressions(expr, And, (base_expr, flavor_expr,), - flavor_expr) - expr = _simplify_expressions(expr, Or, (base_expr, flavor_expr), - base_expr) - expr = _simplify_expressions(expr, And, (Not(base_expr), flavor_expr,), - false_expr) + expr = _simplify_expressions(expr, And, (base_expr, flavor_expr), flavor_expr) + expr = _simplify_expressions(expr, Or, (base_expr, flavor_expr), base_expr) + expr = _simplify_expressions(expr, And, (Not(base_expr), flavor_expr), false_expr) return expr @@ -1670,50 +1866,59 @@ def _simplify_os_families(expr, family_members, other_family_members): expr = _simplify_expressions(expr, And, (f_expr, Not(o_expr)), f_expr) expr = _simplify_expressions(expr, And, (Not(f_expr), o_expr), o_expr) - expr = _simplify_expressions(expr, And, (f_expr, o_expr), simplify_logic('false')) + expr = _simplify_expressions(expr, And, (f_expr, o_expr), simplify_logic("false")) return expr def _recursive_simplify(expr): - ''' Simplify the expression as much as possible based on - domain knowledge. ''' + """ Simplify the expression as much as possible based on + domain knowledge. """ input_expr = expr # Simplify even further, based on domain knowledge: - windowses = ('WIN32', 'WINRT') - apples = ('APPLE_OSX', 'APPLE_UIKIT', 'APPLE_IOS', - 'APPLE_TVOS', 'APPLE_WATCHOS',) - bsds = ('FREEBSD', 'OPENBSD', 'NETBSD',) - androids = ('ANDROID', 'ANDROID_EMBEDDED') - unixes = ('APPLE', *apples, 'BSD', *bsds, 'LINUX', - *androids, 'HAIKU', - 'INTEGRITY', 'VXWORKS', 'QNX', 'WASM') + # windowses = ('WIN32', 'WINRT') + apples = ("APPLE_OSX", "APPLE_UIKIT", "APPLE_IOS", "APPLE_TVOS", "APPLE_WATCHOS") + bsds = ("FREEBSD", "OPENBSD", "NETBSD") + androids = ("ANDROID", "ANDROID_EMBEDDED") + unixes = ( + "APPLE", + *apples, + "BSD", + *bsds, + "LINUX", + *androids, + "HAIKU", + "INTEGRITY", + "VXWORKS", + "QNX", + "WASM", + ) - unix_expr = simplify_logic('UNIX') - win_expr = simplify_logic('WIN32') - false_expr = simplify_logic('false') - true_expr = simplify_logic('true') + unix_expr = simplify_logic("UNIX") + win_expr = simplify_logic("WIN32") + false_expr = simplify_logic("false") + true_expr = simplify_logic("true") expr = expr.subs(Not(unix_expr), win_expr) # NOT UNIX -> WIN32 expr = expr.subs(Not(win_expr), unix_expr) # NOT WIN32 -> UNIX # UNIX [OR foo ]OR WIN32 -> ON [OR foo] - expr = _simplify_expressions(expr, Or, (unix_expr, win_expr,), true_expr) + expr = _simplify_expressions(expr, Or, (unix_expr, win_expr), true_expr) # UNIX [AND foo ]AND WIN32 -> OFF [AND foo] - expr = _simplify_expressions(expr, And, (unix_expr, win_expr,), false_expr) + expr = _simplify_expressions(expr, And, (unix_expr, win_expr), false_expr) - expr = _simplify_flavors_in_condition('WIN32', ('WINRT',), expr) - expr = _simplify_flavors_in_condition('APPLE', apples, expr) - expr = _simplify_flavors_in_condition('BSD', bsds, expr) - expr = _simplify_flavors_in_condition('UNIX', unixes, expr) - expr = _simplify_flavors_in_condition('ANDROID', ('ANDROID_EMBEDDED',), expr) + expr = _simplify_flavors_in_condition("WIN32", ("WINRT",), expr) + expr = _simplify_flavors_in_condition("APPLE", apples, expr) + expr = _simplify_flavors_in_condition("BSD", bsds, expr) + expr = _simplify_flavors_in_condition("UNIX", unixes, expr) + expr = _simplify_flavors_in_condition("ANDROID", ("ANDROID_EMBEDDED",), expr) # Simplify families of OSes against other families: - expr = _simplify_os_families(expr, ('WIN32', 'WINRT'), unixes) + expr = _simplify_os_families(expr, ("WIN32", "WINRT"), unixes) expr = _simplify_os_families(expr, androids, unixes) - expr = _simplify_os_families(expr, ('BSD', *bsds), unixes) + expr = _simplify_os_families(expr, ("BSD", *bsds), unixes) - for family in ('HAIKU', 'QNX', 'INTEGRITY', 'LINUX', 'VXWORKS'): + for family in ("HAIKU", "QNX", "INTEGRITY", "LINUX", "VXWORKS"): expr = _simplify_os_families(expr, (family,), unixes) # Now simplify further: @@ -1730,21 +1935,21 @@ def simplify_condition(condition: str) -> str: input_condition = condition.strip() # Map to sympy syntax: - condition = ' ' + input_condition + ' ' - condition = condition.replace('(', ' ( ') - condition = condition.replace(')', ' ) ') + condition = " " + input_condition + " " + condition = condition.replace("(", " ( ") + condition = condition.replace(")", " ) ") - tmp = '' + tmp = "" while tmp != condition: tmp = condition - condition = condition.replace(' NOT ', ' ~ ') - condition = condition.replace(' AND ', ' & ') - condition = condition.replace(' OR ', ' | ') - condition = condition.replace(' ON ', ' true ') - condition = condition.replace(' OFF ', ' false ') + condition = condition.replace(" NOT ", " ~ ") + condition = condition.replace(" AND ", " & ") + condition = condition.replace(" OR ", " | ") + condition = condition.replace(" ON ", " true ") + condition = condition.replace(" OFF ", " false ") # Replace dashes with a token - condition = condition.replace('-', '_dash_') + condition = condition.replace("-", "_dash_") # SymPy chokes on expressions that contain two tokens one next to # the other delimited by a space, which are not an operation. @@ -1756,12 +1961,12 @@ def simplify_condition(condition: str) -> str: # token symbols. # Support both target names without double colons, and with double # colons. - pattern = re.compile(r'(TARGET [a-zA-Z]+(?:::[a-zA-Z]+)?)') + pattern = re.compile(r"(TARGET [a-zA-Z]+(?:::[a-zA-Z]+)?)") target_symbol_mapping = {} all_target_conditions = re.findall(pattern, condition) for target_condition in all_target_conditions: # Replace spaces and colons with underscores. - target_condition_symbol_name = re.sub('[ :]', '_', target_condition) + target_condition_symbol_name = re.sub("[ :]", "_", target_condition) target_symbol_mapping[target_condition_symbol_name] = target_condition condition = re.sub(target_condition, target_condition_symbol_name, condition) @@ -1775,101 +1980,131 @@ def simplify_condition(condition: str) -> str: condition = re.sub(symbol_name, target_symbol_mapping[symbol_name], condition) # Map back to CMake syntax: - condition = condition.replace('~', 'NOT ') - condition = condition.replace('&', 'AND') - condition = condition.replace('|', 'OR') - condition = condition.replace('True', 'ON') - condition = condition.replace('False', 'OFF') - condition = condition.replace('_dash_', '-') - except: + condition = condition.replace("~", "NOT ") + condition = condition.replace("&", "AND") + condition = condition.replace("|", "OR") + condition = condition.replace("True", "ON") + condition = condition.replace("False", "OFF") + condition = condition.replace("_dash_", "-") + except (SympifyError, TypeError): # sympy did not like our input, so leave this condition alone: condition = input_condition - return condition or 'ON' + return condition or "ON" -def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', - previous_condition: str = '') -> str: +def recursive_evaluate_scope( + scope: Scope, parent_condition: str = "", previous_condition: str = "" +) -> str: current_condition = scope.condition total_condition = current_condition - if total_condition == 'else': - assert previous_condition, \ - "Else branch without previous condition in: %s" % scope.file - total_condition = 'NOT ({})'.format(previous_condition) + if total_condition == "else": + assert previous_condition, f"Else branch without previous condition in: {scope.file}" + total_condition = f"NOT ({previous_condition})" if parent_condition: if not total_condition: total_condition = parent_condition else: - total_condition = '({}) AND ({})'.format(parent_condition, - total_condition) + total_condition = f"({parent_condition}) AND ({total_condition})" scope.total_condition = simplify_condition(total_condition) - prev_condition = '' + prev_condition = "" for c in scope.children: - prev_condition = recursive_evaluate_scope(c, total_condition, - prev_condition) + prev_condition = recursive_evaluate_scope(c, total_condition, prev_condition) return current_condition -def map_to_cmake_condition(condition: typing.Optional[str]) -> str: +def map_to_cmake_condition(condition: str = "") -> str: condition = condition.replace("QTDIR_build", "QT_BUILDING_QT") - condition = re.sub(r'\bQT_ARCH___equals___([a-zA-Z_0-9]*)', - r'(TEST_architecture_arch STREQUAL "\1")', condition or '') - condition = re.sub(r'\bQT_ARCH___contains___([a-zA-Z_0-9]*)', - r'(TEST_architecture_arch STREQUAL "\1")', condition or '') + condition = re.sub( + r"\bQT_ARCH___equals___([a-zA-Z_0-9]*)", + r'(TEST_architecture_arch STREQUAL "\1")', + condition or "", + ) + condition = re.sub( + r"\bQT_ARCH___contains___([a-zA-Z_0-9]*)", + r'(TEST_architecture_arch STREQUAL "\1")', + condition or "", + ) return condition resource_file_expansion_counter = 0 -def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: int = 0, is_example = False): - vpath = scope.expand('VPATH') + + +def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, is_example=False): + # vpath = scope.expand('VPATH') # Handle QRC files by turning them into add_qt_resource: - resources = scope.get_files('RESOURCES') - qtquickcompiler_skipped = scope.get_files('QTQUICK_COMPILER_SKIPPED_RESOURCES') - qtquickcompiler_retained = scope.get_files('QTQUICK_COMPILER_RETAINED_RESOURCES') - qrc_output = '' + resources = scope.get_files("RESOURCES") + qtquickcompiler_skipped = scope.get_files("QTQUICK_COMPILER_SKIPPED_RESOURCES") + qtquickcompiler_retained = scope.get_files("QTQUICK_COMPILER_RETAINED_RESOURCES") + qrc_output = "" if resources: - standalone_files: typing.List[str] = [] + standalone_files: List[str] = [] for r in resources: skip_qtquick_compiler = r in qtquickcompiler_skipped retain_qtquick_compiler = r in qtquickcompiler_retained - if r.endswith('.qrc'): - qrc_output += process_qrc_file(target, r, scope.basedir, scope.file_absolute_path, - skip_qtquick_compiler, retain_qtquick_compiler, is_example) + if r.endswith(".qrc"): + qrc_output += process_qrc_file( + target, + r, + scope.basedir, + scope.file_absolute_path, + skip_qtquick_compiler, + retain_qtquick_compiler, + is_example, + ) else: - immediate_files = {f:"" for f in scope.get_files(r + ".files")} + immediate_files = {f: "" for f in scope.get_files(f"{r}.files")} if immediate_files: immediate_prefix = scope.get(r + ".prefix") if immediate_prefix: immediate_prefix = immediate_prefix[0] else: immediate_prefix = "/" - immediate_base = scope.get(r + ".base") + immediate_base = scope.get(f"{r}.base") immediate_lang = None - immediate_name = "qmake_" + r - qrc_output += write_add_qt_resource_call(target, immediate_name, immediate_prefix, immediate_base, immediate_lang, - immediate_files, skip_qtquick_compiler, retain_qtquick_compiler, is_example) + immediate_name = f"qmake_{r}" + qrc_output += write_add_qt_resource_call( + target, + immediate_name, + immediate_prefix, + immediate_base, + immediate_lang, + immediate_files, + skip_qtquick_compiler, + retain_qtquick_compiler, + is_example, + ) else: - if '*' in r: + if "*" in r: global resource_file_expansion_counter - r = r.replace('"','') - qrc_output += '\nfile(GLOB resource_glob_{} RELATIVE "${{CMAKE_CURRENT_SOURCE_DIR}}" "{}")\n'.format(resource_file_expansion_counter, r) - qrc_output +='foreach(file IN LISTS resource_glob_{})\n'.format(resource_file_expansion_counter) - qrc_output += ' set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/${file}" PROPERTIES QT_RESOURCE_ALIAS "${file}")\n' - qrc_output +='endforeach()\n' - standalone_files.append('${{resource_glob_{}}}'.format(resource_file_expansion_counter)) + r = r.replace('"', "") + + qrc_output += dedent( + f""" + file(GLOB resource_glob_{resource_file_expansion_counter} RELATIVE "${{CMAKE_CURRENT_SOURCE_DIR}}" "{r}") + foreach(file IN LISTS resource_glob_{resource_file_expansion_counter}) + set_source_files_properties("${{CMAKE_CURRENT_SOURCE_DIR}}/${{file}}" PROPERTIES QT_RESOURCE_ALIAS "${{file}}") + endforeach() + """ + ) + + standalone_files.append( + f"${{resource_glob_{resource_file_expansion_counter}}}" + ) resource_file_expansion_counter += 1 else: # stadalone source file properties need to be set as they # are parsed. if skip_qtquick_compiler: - qrc_output += 'set_source_files_properties("{}" PROPERTIES QT_SKIP_QUICKCOMPILER 1)\n\n'.format(r) + qrc_output += 'set_source_files_properties(f"{r}" PROPERTIES QT_SKIP_QUICKCOMPILER 1)\n\n' if retain_qtquick_compiler: - qrc_output += 'set_source_files_properties("{}" PROPERTIES QT_RETAIN_QUICKCOMPILER 1)\n\n'.format(r) + qrc_output += 'set_source_files_properties(f"{r}" PROPERTIES QT_RETAIN_QUICKCOMPILER 1)\n\n' standalone_files.append(r) if standalone_files: @@ -1877,54 +2112,62 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in prefix = "/" base = None lang = None - files = {f:"" for f in standalone_files} + files = {f: "" for f in standalone_files} skip_qtquick_compiler = False - qrc_output += write_add_qt_resource_call(target, name, prefix, base, lang, files, - skip_qtquick_compiler = False, retain_qtquick_compiler = False, - is_example = is_example) - + qrc_output += write_add_qt_resource_call( + target, + name, + prefix, + base, + lang, + files, + skip_qtquick_compiler=False, + retain_qtquick_compiler=False, + is_example=is_example, + ) if qrc_output: - cm_fh.write('\n# Resources:\n') - for line in qrc_output.split('\n'): - cm_fh.write(' ' * indent + line + '\n') + cm_fh.write("\n# Resources:\n") + for line in qrc_output.split("\n"): + cm_fh.write(f"{' ' * indent}{line}\n") -def write_extend_target(cm_fh: typing.IO[str], target: str, - scope: Scope, indent: int = 0): +def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): ind = spaces(indent) extend_qt_io_string = io.StringIO() write_sources_section(extend_qt_io_string, scope) extend_qt_string = extend_qt_io_string.getvalue() - extend_scope = '\n{}extend_target({} CONDITION {}\n' \ - '{}{})\n'.format(ind, target, - map_to_cmake_condition(scope.total_condition), - extend_qt_string, ind) + extend_scope = ( + f"\n{ind}extend_target({target} CONDITION" + f" {map_to_cmake_condition(scope.total_condition)}\n" + f"{extend_qt_string}{ind})\n" + ) if not extend_qt_string: - extend_scope = '' # Nothing to report, so don't! + extend_scope = "" # Nothing to report, so don't! cm_fh.write(extend_scope) write_resources(cm_fh, target, scope, indent) -def flatten_scopes(scope: Scope) -> typing.List[Scope]: - result = [scope] # type: typing.List[Scope] + +def flatten_scopes(scope: Scope) -> List[Scope]: + result = [scope] # type: List[Scope] for c in scope.children: result += flatten_scopes(c) return result -def merge_scopes(scopes: typing.List[Scope]) -> typing.List[Scope]: - result = [] # type: typing.List[Scope] +def merge_scopes(scopes: List[Scope]) -> List[Scope]: + result = [] # type: List[Scope] # Merge scopes with their parents: - known_scopes = {} # type: typing.Mapping[str, Scope] + known_scopes = {} # type: Mapping[str, Scope] for scope in scopes: total_condition = scope.total_condition assert total_condition - if total_condition == 'OFF': + if total_condition == "OFF": # ignore this scope entirely! pass elif total_condition in known_scopes: @@ -1937,30 +2180,57 @@ def merge_scopes(scopes: typing.List[Scope]) -> typing.List[Scope]: return result -def write_simd_part(cm_fh: typing.IO[str], target: str, scope: Scope, indent: int = 0): - simd_options = [ 'sse2', 'sse3', 'ssse3', 'sse4_1', 'sse4_2', 'aesni', 'shani', 'avx', 'avx2', - 'avx512f', 'avx512cd', 'avx512er', 'avx512pf', 'avx512dq', 'avx512bw', - 'avx512vl', 'avx512ifma', 'avx512vbmi', 'f16c', 'rdrnd', 'neon', 'mips_dsp', - 'mips_dspr2', - 'arch_haswell', 'avx512common', 'avx512core']; +def write_simd_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): + simd_options = [ + "sse2", + "sse3", + "ssse3", + "sse4_1", + "sse4_2", + "aesni", + "shani", + "avx", + "avx2", + "avx512f", + "avx512cd", + "avx512er", + "avx512pf", + "avx512dq", + "avx512bw", + "avx512vl", + "avx512ifma", + "avx512vbmi", + "f16c", + "rdrnd", + "neon", + "mips_dsp", + "mips_dspr2", + "arch_haswell", + "avx512common", + "avx512core", + ] for simd in simd_options: - SIMD = simd.upper(); - write_source_file_list(cm_fh, scope, 'SOURCES', - ['{}_HEADERS'.format(SIMD), - '{}_SOURCES'.format(SIMD), - '{}_C_SOURCES'.format(SIMD), - '{}_ASM'.format(SIMD)], - indent, - header = 'add_qt_simd_part({} SIMD {}\n'.format(target, simd), - footer = ')\n\n') + SIMD = simd.upper() + write_source_file_list( + cm_fh, + scope, + "SOURCES", + [f"{SIMD}_HEADERS", f"{SIMD}_SOURCES", f"{SIMD}_C_SOURCES", f"{SIMD}_ASM"], + indent, + header=f"add_qt_simd_part({target} SIMD {simd}\n", + footer=")\n\n", + ) -def write_android_part(cm_fh: typing.IO[str], target: str, scope:Scope, indent: int = 0): - keys = [ 'ANDROID_BUNDLED_JAR_DEPENDENCIES' - , 'ANDROID_LIB_DEPENDENCIES' - , 'ANDROID_JAR_DEPENDENCIES' - , 'ANDROID_LIB_DEPENDENCY_REPLACEMENTS' - , 'ANDROID_BUNDLED_FILES' - , 'ANDROID_PERMISSIONS' ] + +def write_android_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): + keys = [ + "ANDROID_BUNDLED_JAR_DEPENDENCIES", + "ANDROID_LIB_DEPENDENCIES", + "ANDROID_JAR_DEPENDENCIES", + "ANDROID_LIB_DEPENDENCY_REPLACEMENTS", + "ANDROID_BUNDLED_FILES", + "ANDROID_PERMISSIONS", + ] has_no_values = True for key in keys: @@ -1968,40 +2238,48 @@ def write_android_part(cm_fh: typing.IO[str], target: str, scope:Scope, indent: if len(value) != 0: if has_no_values: if scope.condition: - cm_fh.write('\n{}if(ANDROID AND ({}))\n'.format(spaces(indent), scope.condition)) + cm_fh.write(f"\n{spaces(indent)}if(ANDROID AND ({scope.condition}))\n") else: - cm_fh.write('\n{}if(ANDROID)\n'.format(spaces(indent))) + cm_fh.write(f"\n{spaces(indent)}if(ANDROID)\n") indent += 1 has_no_values = False - cm_fh.write('{}set_property(TARGET {} APPEND PROPERTY QT_{}\n'.format(spaces(indent), target, key)) - write_list(cm_fh, value, '', indent + 1) - cm_fh.write('{})\n'.format(spaces(indent))) + cm_fh.write(f"{spaces(indent)}set_property(TARGET {target} APPEND PROPERTY QT_{key}\n") + write_list(cm_fh, value, "", indent + 1) + cm_fh.write(f"{spaces(indent)})\n") indent -= 1 if not has_no_values: - cm_fh.write('{}endif()\n'.format(spaces(indent))) + cm_fh.write(f"{spaces(indent)}endif()\n") -def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, - cmake_function: str, scope: Scope, *, - extra_lines: typing.List[str] = [], - indent: int = 0, extra_keys: typing.List[str], - **kwargs: typing.Any): + +def write_main_part( + cm_fh: IO[str], + name: str, + typename: str, + cmake_function: str, + scope: Scope, + *, + extra_lines: List[str] = [], + indent: int = 0, + extra_keys: List[str], + **kwargs: Any, +): # Evaluate total condition of all scopes: recursive_evaluate_scope(scope) - is_qml_plugin = any('qml_plugin' == s for s in scope.get('_LOADED')) + is_qml_plugin = any("qml_plugin" == s for s in scope.get("_LOADED")) - if 'exceptions' in scope.get('CONFIG'): - extra_lines.append('EXCEPTIONS') + if "exceptions" in scope.get("CONFIG"): + extra_lines.append("EXCEPTIONS") # Get a flat list of all scopes but the main one: scopes = flatten_scopes(scope) - total_scopes = len(scopes) + # total_scopes = len(scopes) # Merge scopes based on their conditions: scopes = merge_scopes(scopes) assert len(scopes) - assert scopes[0].total_condition == 'ON' + assert scopes[0].total_condition == "ON" scopes[0].reset_visited_keys() for k in extra_keys: @@ -2012,44 +2290,44 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, # collect all testdata and insert globbing commands has_test_data = False - if typename == 'Test': - test_data = scope.expand('TESTDATA') + if typename == "Test": + test_data = scope.expand("TESTDATA") if test_data: has_test_data = True - cm_fh.write('# Collect test data\n') + cm_fh.write("# Collect test data\n") for data in test_data: - if '*' in data: - cm_fh.write(dedent(""" - {indent}file(GLOB_RECURSE test_data_glob - {indent1}RELATIVE ${{CMAKE_CURRENT_SOURCE_DIR}} - {indent1}"{}") - """).format( - data, - indent=spaces(indent), - indent1=spaces(indent + 1) - )) - cm_fh.write('{}list(APPEND test_data ${{test_data_glob}})\n'.format(spaces(indent))) + if "*" in data: + cm_fh.write( + dedent( + """ + {spaces(indent)}file(GLOB_RECURSE test_data_glob + {spaces(indent+1)}RELATIVE ${{CMAKE_CURRENT_SOURCE_DIR}} + {spaces(indent+1)}"{}") + """ + ) + ) + cm_fh.write(f"{spaces(indent)}list(APPEND test_data ${{test_data_glob}})\n") else: - cm_fh.write('{}list(APPEND test_data "{}")\n'.format(spaces(indent), data)) - cm_fh.write('\n') + cm_fh.write(f'{spaces(indent)}list(APPEND test_data "{data}")\n') + cm_fh.write("\n") # Check for DESTDIR override - destdir = scope.get_string('DESTDIR') + destdir = scope.get_string("DESTDIR") if destdir: - if destdir.startswith('./') or destdir.startswith('../'): - destdir = '${CMAKE_CURRENT_BINARY_DIR}/' + destdir - extra_lines.append('OUTPUT_DIRECTORY "{}"'.format(destdir)) + if destdir.startswith("./") or destdir.startswith("../"): + destdir = "${CMAKE_CURRENT_BINARY_DIR}/" + destdir + extra_lines.append(f'OUTPUT_DIRECTORY "{destdir}"') - cm_fh.write('{}{}({}\n'.format(spaces(indent), cmake_function, name)) + cm_fh.write(f"{spaces(indent)}{cmake_function}({name}\n") for extra_line in extra_lines: - cm_fh.write('{} {}\n'.format(spaces(indent), extra_line)) + cm_fh.write(f"{spaces(indent)} {extra_line}\n") write_sources_section(cm_fh, scopes[0], indent=indent, **kwargs) if has_test_data: - cm_fh.write('{} TESTDATA ${{test_data}}\n'.format(spaces(indent))) + cm_fh.write(f"{spaces(indent)} TESTDATA ${{test_data}}\n") # Footer: - cm_fh.write('{})\n'.format(spaces(indent))) + cm_fh.write(f"{spaces(indent)})\n") write_resources(cm_fh, name, scope, indent) @@ -2064,7 +2342,6 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, if ignored_keys_report: cm_fh.write(ignored_keys_report) - # Scopes: if len(scopes) == 1: return @@ -2079,121 +2356,151 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, if ignored_keys_report: cm_fh.write(ignored_keys_report) -def write_module(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0) -> str: + +def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: module_name = scope.TARGET - if not module_name.startswith('Qt'): - print('XXXXXX Module name {} does not start with Qt!'.format(module_name)) + if not module_name.startswith("Qt"): + print(f"XXXXXX Module name {module_name} does not start with Qt!") extra = [] # A module should be static when 'static' is in CONFIG # or when option(host_build) is used, as described in qt_module.prf. - is_static = 'static' in scope.get('CONFIG') or 'host_build' in scope.get('_OPTION') + is_static = "static" in scope.get("CONFIG") or "host_build" in scope.get("_OPTION") if is_static: - extra.append('STATIC') - if 'internal_module' in scope.get('CONFIG'): - extra.append('INTERNAL_MODULE') - if 'no_module_headers' in scope.get('CONFIG'): - extra.append('NO_MODULE_HEADERS') - if 'minimal_syncqt' in scope.get('CONFIG'): - extra.append('NO_SYNC_QT') - if 'no_private_module' in scope.get('CONFIG'): - extra.append('NO_PRIVATE_MODULE') - if 'header_module' in scope.get('CONFIG'): - extra.append('HEADER_MODULE') + extra.append("STATIC") + if "internal_module" in scope.get("CONFIG"): + extra.append("INTERNAL_MODULE") + if "no_module_headers" in scope.get("CONFIG"): + extra.append("NO_MODULE_HEADERS") + if "minimal_syncqt" in scope.get("CONFIG"): + extra.append("NO_SYNC_QT") + if "no_private_module" in scope.get("CONFIG"): + extra.append("NO_PRIVATE_MODULE") + if "header_module" in scope.get("CONFIG"): + extra.append("HEADER_MODULE") module_config = scope.get("MODULE_CONFIG") if len(module_config): - extra.append('QMAKE_MODULE_CONFIG {}'.format(" ".join(module_config))) + extra.append(f'QMAKE_MODULE_CONFIG {" ".join(module_config)}') - module_plugin_types = scope.get_files('MODULE_PLUGIN_TYPES') + module_plugin_types = scope.get_files("MODULE_PLUGIN_TYPES") if module_plugin_types: - extra.append('PLUGIN_TYPES {}'.format(" ".join(module_plugin_types))) + extra.append(f"PLUGIN_TYPES {' '.join(module_plugin_types)}") target_name = module_name[2:] - write_main_part(cm_fh, target_name, 'Module', 'add_qt_module', scope, - extra_lines=extra, indent=indent, - known_libraries={}, extra_keys=[]) + write_main_part( + cm_fh, + target_name, + "Module", + "add_qt_module", + scope, + extra_lines=extra, + indent=indent, + known_libraries={}, + extra_keys=[], + ) - if 'qt_tracepoints' in scope.get('CONFIG'): - tracepoints = scope.get_files('TRACEPOINT_PROVIDER') - cm_fh.write('\n\n{}qt_create_tracepoints({} {})\n' - .format(spaces(indent), module_name[2:], ' '.join(tracepoints))) + if "qt_tracepoints" in scope.get("CONFIG"): + tracepoints = scope.get_files("TRACEPOINT_PROVIDER") + cm_fh.write( + f"\n\n{spaces(indent)}qt_create_tracepoints({module_name[2:]} {' '.join(tracepoints)})\n" + ) return target_name -def write_tool(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0) -> str: +def write_tool(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: tool_name = scope.TARGET - extra = ['BOOTSTRAP'] if 'force_bootstrap' in scope.get('CONFIG') else [] + extra = ["BOOTSTRAP"] if "force_bootstrap" in scope.get("CONFIG") else [] - write_main_part(cm_fh, tool_name, 'Tool', 'add_qt_tool', scope, - indent=indent, known_libraries={'Qt::Core', }, - extra_lines=extra, extra_keys=['CONFIG']) + write_main_part( + cm_fh, + tool_name, + "Tool", + "add_qt_tool", + scope, + indent=indent, + known_libraries={"Qt::Core"}, + extra_lines=extra, + extra_keys=["CONFIG"], + ) return tool_name -def write_test(cm_fh: typing.IO[str], scope: Scope, - gui: bool = False, *, indent: int = 0) -> str: +def write_test(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = 0) -> str: test_name = scope.TARGET assert test_name - extra = ['GUI',] if gui else [] - libraries={'Qt::Core', 'Qt::Test'} + extra = ["GUI"] if gui else [] + libraries = {"Qt::Core", "Qt::Test"} - if 'qmltestcase' in scope.get('CONFIG'): - libraries.add('Qt::QmlTest') - extra.append('QMLTEST') - importpath = scope.expand('IMPORTPATH') + if "qmltestcase" in scope.get("CONFIG"): + libraries.add("Qt::QmlTest") + extra.append("QMLTEST") + importpath = scope.expand("IMPORTPATH") if importpath: - extra.append('QML_IMPORTPATH') + extra.append("QML_IMPORTPATH") for path in importpath: - extra.append(' "{}"'.format(path)) + extra.append(f' "{path}"') - write_main_part(cm_fh, test_name, 'Test', 'add_qt_test', scope, - indent=indent, known_libraries=libraries, - extra_lines=extra, extra_keys=[]) + write_main_part( + cm_fh, + test_name, + "Test", + "add_qt_test", + scope, + indent=indent, + known_libraries=libraries, + extra_lines=extra, + extra_keys=[], + ) return test_name -def write_binary(cm_fh: typing.IO[str], scope: Scope, - gui: bool = False, *, indent: int = 0) -> None: +def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = 0) -> None: binary_name = scope.TARGET assert binary_name - is_qt_test_helper = 'qt_test_helper' in scope.get('_LOADED') + is_qt_test_helper = "qt_test_helper" in scope.get("_LOADED") - extra = ['GUI'] if gui and not is_qt_test_helper else [] - cmake_function_call = 'add_qt_executable' + extra = ["GUI"] if gui and not is_qt_test_helper else [] + cmake_function_call = "add_qt_executable" if is_qt_test_helper: - binary_name += '_helper' - cmake_function_call = 'add_qt_test_helper' + binary_name += "_helper" + cmake_function_call = "add_qt_test_helper" - target_path = scope.get_string('target.path') + target_path = scope.get_string("target.path") if target_path: - target_path = target_path.replace('$$[QT_INSTALL_EXAMPLES]', '${INSTALL_EXAMPLESDIR}') - extra.append('OUTPUT_DIRECTORY "{}"'.format(target_path)) - if 'target' in scope.get('INSTALLS'): - extra.append('INSTALL_DIRECTORY "{}"'.format(target_path)) + target_path = target_path.replace("$$[QT_INSTALL_EXAMPLES]", "${INSTALL_EXAMPLESDIR}") + extra.append(f'OUTPUT_DIRECTORY "{target_path}"') + if "target" in scope.get("INSTALLS"): + extra.append(f'INSTALL_DIRECTORY "{target_path}"') - write_main_part(cm_fh, binary_name, 'Binary', cmake_function_call, scope, - extra_lines=extra, indent=indent, - known_libraries={'Qt::Core', }, extra_keys=['target.path', 'INSTALLS']) + write_main_part( + cm_fh, + binary_name, + "Binary", + cmake_function_call, + scope, + extra_lines=extra, + indent=indent, + known_libraries={"Qt::Core"}, + extra_keys=["target.path", "INSTALLS"], + ) return binary_name -def write_find_package_section(cm_fh: typing.IO[str], - public_libs: typing.List[str], - private_libs: typing.List[str], *, indent: int=0): - packages = [] # type: typing.List[LibraryMapping] +def write_find_package_section( + cm_fh: IO[str], public_libs: List[str], private_libs: List[str], *, indent: int = 0 +): + packages = [] # type: List[LibraryMapping] all_libs = public_libs + private_libs for l in all_libs: @@ -2201,55 +2508,73 @@ def write_find_package_section(cm_fh: typing.IO[str], if info and info not in packages: packages.append(info) - ind = spaces(indent) + # ind = spaces(indent) for p in packages: cm_fh.write(generate_find_package_info(p, use_qt_find_package=False, indent=indent)) if packages: - cm_fh.write('\n') + cm_fh.write("\n") -def write_example(cm_fh: typing.IO[str], scope: Scope, - gui: bool = False, *, indent: int = 0) -> str: +def write_example(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = 0) -> str: binary_name = scope.TARGET assert binary_name - cm_fh.write('cmake_minimum_required(VERSION 3.14)\n' + - 'project({} LANGUAGES CXX)\n\n'.format(binary_name) + - 'set(CMAKE_INCLUDE_CURRENT_DIR ON)\n\n' + - 'set(CMAKE_AUTOMOC ON)\n' + - 'set(CMAKE_AUTORCC ON)\n' + - 'set(CMAKE_AUTOUIC ON)\n\n' + - 'set(INSTALL_EXAMPLEDIR "examples")\n\n') + cm_fh.write( + "cmake_minimum_required(VERSION 3.14)\n" + f"project({binary_name} LANGUAGES CXX)\n\n" + "set(CMAKE_INCLUDE_CURRENT_DIR ON)\n\n" + "set(CMAKE_AUTOMOC ON)\n" + "set(CMAKE_AUTORCC ON)\n" + "set(CMAKE_AUTOUIC ON)\n\n" + 'set(INSTALL_EXAMPLEDIR "examples")\n\n' + ) (public_libs, private_libs) = extract_cmake_libraries(scope) write_find_package_section(cm_fh, public_libs, private_libs, indent=indent) - add_executable = 'add_{}executable({}'.format("qt_gui_" if gui else "", binary_name); + add_executable = f'add_{"qt_gui_" if gui else ""}executable({binary_name}' write_all_source_file_lists(cm_fh, scope, add_executable, indent=0) - cm_fh.write(')\n') + cm_fh.write(")\n") - write_include_paths(cm_fh, scope, 'target_include_directories({} PUBLIC'.format(binary_name), - indent=0, footer=')') - write_defines(cm_fh, scope, 'target_compile_definitions({} PUBLIC'.format(binary_name), - indent=0, footer=')') - write_list(cm_fh, private_libs, '', indent=indent, - header='target_link_libraries({} PRIVATE\n'.format(binary_name), footer=')') - write_list(cm_fh, public_libs, '', indent=indent, - header='target_link_libraries({} PUBLIC\n'.format(binary_name), footer=')') - write_compile_options(cm_fh, scope, 'target_compile_options({}'.format(binary_name), - indent=0, footer=')') + write_include_paths( + cm_fh, scope, f"target_include_directories({binary_name} PUBLIC", indent=0, footer=")" + ) + write_defines( + cm_fh, scope, f"target_compile_definitions({binary_name} PUBLIC", indent=0, footer=")" + ) + write_list( + cm_fh, + private_libs, + "", + indent=indent, + header=f"target_link_libraries({binary_name} PRIVATE\n", + footer=")", + ) + write_list( + cm_fh, + public_libs, + "", + indent=indent, + header=f"target_link_libraries({binary_name} PUBLIC\n", + footer=")", + ) + write_compile_options( + cm_fh, scope, f"target_compile_options({binary_name}", indent=0, footer=")" + ) - write_resources(cm_fh, binary_name, scope, indent = indent, is_example = True) + write_resources(cm_fh, binary_name, scope, indent=indent, is_example=True) - cm_fh.write('\ninstall(TARGETS {}\n'.format(binary_name) + - ' RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + - ' BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + - ' LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + - ')\n') + cm_fh.write( + f"\ninstall(TARGETS {binary_name}\n" + ' RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + ' BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + ' LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + ")\n" + ) return binary_name @@ -2260,123 +2585,130 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: extra = [] - plugin_type = scope.get_string('PLUGIN_TYPE') - is_qml_plugin = any('qml_plugin' == s for s in scope.get('_LOADED')) - plugin_function_name = 'add_qt_plugin' + plugin_type = scope.get_string("PLUGIN_TYPE") + is_qml_plugin = any("qml_plugin" == s for s in scope.get("_LOADED")) + plugin_function_name = "add_qt_plugin" if plugin_type: - extra.append('TYPE {}'.format(plugin_type)) + extra.append(f"TYPE {plugin_type}") elif is_qml_plugin: - plugin_function_name = 'add_qml_module' + plugin_function_name = "add_qml_module" write_qml_plugin(cm_fh, plugin_name, scope, indent=indent, extra_lines=extra) - plugin_class_name = scope.get_string('PLUGIN_CLASS_NAME') + plugin_class_name = scope.get_string("PLUGIN_CLASS_NAME") if plugin_class_name: - extra.append('CLASS_NAME {}'.format(plugin_class_name)) + extra.append(f"CLASS_NAME {plugin_class_name}") - write_main_part(cm_fh, plugin_name, 'Plugin', plugin_function_name, scope, - indent=indent, extra_lines=extra, known_libraries={}, extra_keys=[]) + write_main_part( + cm_fh, + plugin_name, + "Plugin", + plugin_function_name, + scope, + indent=indent, + extra_lines=extra, + known_libraries={}, + extra_keys=[], + ) return plugin_name -def write_qml_plugin(cm_fh: typing.IO[str], - target: str, - scope: Scope, *, - extra_lines: typing.List[str] = [], - indent: int = 0, - **kwargs: typing.Any): +def write_qml_plugin( + cm_fh: IO[str], + target: str, + scope: Scope, + *, + extra_lines: List[str] = [], + indent: int = 0, + **kwargs: Any, +): # Collect other args if available indent += 2 - scope_config = scope.get('CONFIG') - is_embedding_qml_files = False + # scope_config = scope.get('CONFIG') + # is_embedding_qml_files = False - - sources = scope.get_files('SOURCES') + sources = scope.get_files("SOURCES") if len(sources) != 0: - extra_lines.append('CPP_PLUGIN') + extra_lines.append("CPP_PLUGIN") - target_path = scope.get_string('TARGETPATH') + target_path = scope.get_string("TARGETPATH") if target_path: - uri = target_path.replace('/','.') - import_name = scope.get_string('IMPORT_NAME') + uri = target_path.replace("/", ".") + import_name = scope.get_string("IMPORT_NAME") # Catch special cases such as foo.QtQuick.2.bar, which when converted # into a target path via cmake will result in foo/QtQuick/2/bar, which is # not what we want. So we supply the target path override. - target_path_from_uri = uri.replace('.', '/') + target_path_from_uri = uri.replace(".", "/") if target_path != target_path_from_uri: - extra_lines.append('TARGET_PATH "{}"'.format(target_path)) + extra_lines.append(f'TARGET_PATH "{target_path}"') if import_name: - extra_lines.append('URI "{}"'.format(import_name)) + extra_lines.append(f'URI "{import_name}"') else: - uri = re.sub('\\.\\d+', '', uri) - extra_lines.append('URI "{}"'.format(uri)) + uri = re.sub("\\.\\d+", "", uri) + extra_lines.append(f'URI "{uri}"') - import_version = scope.get_string('IMPORT_VERSION') + import_version = scope.get_string("IMPORT_VERSION") if import_version: - import_version = import_version.replace("$$QT_MINOR_VERSION","${CMAKE_PROJECT_VERSION_MINOR}") - extra_lines.append('VERSION "{}"'.format(import_version)) + import_version = import_version.replace( + "$$QT_MINOR_VERSION", "${CMAKE_PROJECT_VERSION_MINOR}" + ) + extra_lines.append(f'VERSION "{import_version}"') - plugindump_dep = scope.get_string('QML_PLUGINDUMP_DEPENDENCIES') + plugindump_dep = scope.get_string("QML_PLUGINDUMP_DEPENDENCIES") if plugindump_dep: - extra_lines.append('QML_PLUGINDUMP_DEPENDENCIES "{}"'.format(plugindump_dep)) + extra_lines.append(f'QML_PLUGINDUMP_DEPENDENCIES "{plugindump_dep}"') -def write_qml_plugin_qml_files(cm_fh: typing.IO[str], - target: str, - scope: Scope, - indent: int = 0): +def write_qml_plugin_qml_files(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): - qml_files = scope.get_files('QML_FILES', use_vpath=True) + qml_files = scope.get_files("QML_FILES", use_vpath=True) if qml_files: # Quote file paths in case there are spaces. - qml_files = ['"{}"'.format(f) for f in qml_files] + qml_files = [f'"{f}"' for f in qml_files] - cm_fh.write('\n{}set(qml_files\n{}{}\n)\n'.format( - spaces(indent), - spaces(indent + 1), - '\n{}'.format(spaces(indent + 1)).join(qml_files))) + qml_files_line = "\n{spaces(indent+1)}".join(qml_files) + cm_fh.write(f"\n{spaces(indent)}set(qml_files\n{spaces(indent+1)}{qml_files_line}\n)\n") - target_path = scope.get_string('TARGETPATH', inherit=True) - target_path_mangled = target_path.replace('/', '_') - target_path_mangled = target_path_mangled.replace('.', '_') - resource_name = 'qmake_' + target_path_mangled - cm_fh.write('\n{}add_qt_resource({} {}\n{}FILES\n{}${{qml_files}}\n)\n'.format( - spaces(indent), - target, - resource_name, - spaces(indent + 1), - spaces(indent + 2))) + target_path = scope.get_string("TARGETPATH", inherit=True) + target_path_mangled = target_path.replace("/", "_") + target_path_mangled = target_path_mangled.replace(".", "_") + resource_name = "qmake_" + target_path_mangled + cm_fh.write( + f"\n{spaces(indent)}add_qt_resource({target} {resource_name}\n" + f"{spaces(indent+1)}FILES\n{spaces(indent+2)}${{qml_files}}\n)\n" + ) - cm_fh.write('\nqt_install_qml_files({}\n FILES ${{qml_files}}\n)\n\n'.format( - target)) + cm_fh.write(f"\nqt_install_qml_files({target}\n FILES ${{qml_files}}\n)\n\n") -def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, - indent: int = 0, is_example: bool=False) -> None: - assert scope.TEMPLATE in ('app', 'lib') +def handle_app_or_lib( + scope: Scope, cm_fh: IO[str], *, indent: int = 0, is_example: bool = False +) -> None: + assert scope.TEMPLATE in ("app", "lib") - config = scope.get('CONFIG') - is_lib = scope.TEMPLATE == 'lib' - is_qml_plugin = any('qml_plugin' == s for s in scope.get('_LOADED')) - is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED')) or is_qml_plugin or 'plugin' in config + config = scope.get("CONFIG") + is_lib = scope.TEMPLATE == "lib" + is_qml_plugin = any("qml_plugin" == s for s in scope.get("_LOADED")) + is_plugin = ( + any("qt_plugin" == s for s in scope.get("_LOADED")) or is_qml_plugin or "plugin" in config + ) target = "" if is_plugin: assert not is_example target = write_plugin(cm_fh, scope, indent=indent) - elif is_lib or 'qt_module' in scope.get('_LOADED'): + elif is_lib or "qt_module" in scope.get("_LOADED"): assert not is_example target = write_module(cm_fh, scope, indent=indent) - elif 'qt_tool' in scope.get('_LOADED'): + elif "qt_tool" in scope.get("_LOADED"): assert not is_example target = write_tool(cm_fh, scope, indent=indent) else: - gui = all(val not in config for val in ['console', 'cmdline']) \ - and 'testlib' not in scope.expand('QT') - if 'testcase' in config \ - or 'testlib' in config \ - or 'qmltestcase' in config: + gui = all( + val not in config for val in ["console", "cmdline"] + ) and "testlib" not in scope.expand("QT") + if "testcase" in config or "testlib" in config or "qmltestcase" in config: assert not is_example target = write_test(cm_fh, scope, gui, indent=indent) else: @@ -2385,15 +2717,13 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, else: target = write_binary(cm_fh, scope, gui, indent=indent) - ind = spaces(indent) - write_source_file_list(cm_fh, scope, '', - ['QMAKE_DOCS',], - indent, - header = f"add_qt_docs({target},\n", - footer = ')\n') + # ind = spaces(indent) + write_source_file_list( + cm_fh, scope, "", ["QMAKE_DOCS"], indent, header=f"add_qt_docs({target},\n", footer=")\n" + ) -def handle_top_level_repo_project(scope: Scope, cm_fh: typing.IO[str]): +def handle_top_level_repo_project(scope: Scope, cm_fh: IO[str]): # qtdeclarative project_file_name = os.path.splitext(os.path.basename(scope.file_absolute_path))[0] @@ -2406,7 +2736,7 @@ def handle_top_level_repo_project(scope: Scope, cm_fh: typing.IO[str]): # Found a mapping, adjust name. if qt_lib != file_name_without_qt_prefix: # QtDeclarative - qt_lib = re.sub(r':', r'', qt_lib) + qt_lib = re.sub(r":", r"", qt_lib) # Declarative qt_lib_no_prefix = qt_lib[2:] @@ -2414,36 +2744,39 @@ def handle_top_level_repo_project(scope: Scope, cm_fh: typing.IO[str]): qt_lib += "_FIXME" qt_lib_no_prefix = qt_lib - content = """cmake_minimum_required(VERSION {}) + content = dedent( + f"""\ + cmake_minimum_required(VERSION {cmake_version_string}) -project({} - VERSION 6.0.0 - DESCRIPTION "Qt {} Libraries" - HOMEPAGE_URL "https://qt.io/" - LANGUAGES CXX C -) + project({qt_lib} + VERSION 6.0.0 + DESCRIPTION "Qt {qt_lib_no_prefix} Libraries" + HOMEPAGE_URL "https://qt.io/" + LANGUAGES CXX C + ) -find_package(Qt6 ${{PROJECT_VERSION}} CONFIG REQUIRED COMPONENTS BuildInternals Core SET_ME_TO_SOMETHING_USEFUL) -find_package(Qt6 ${{PROJECT_VERSION}} CONFIG OPTIONAL_COMPONENTS SET_ME_TO_SOMETHING_USEFUL) -qt_build_repo() -""".format(cmake_version_string, qt_lib, qt_lib_no_prefix) + find_package(Qt6 ${{PROJECT_VERSION}} CONFIG REQUIRED COMPONENTS BuildInternals Core SET_ME_TO_SOMETHING_USEFUL) + find_package(Qt6 ${{PROJECT_VERSION}} CONFIG OPTIONAL_COMPONENTS SET_ME_TO_SOMETHING_USEFUL) + qt_build_repo() + """ + ) - cm_fh.write('{}'.format(content)) + cm_fh.write(f"{content}") -def find_top_level_repo_project_file(project_file_path: str = '') -> typing.Optional[str]: +def find_top_level_repo_project_file(project_file_path: str = "") -> Optional[str]: qmake_conf_path = find_qmake_conf(project_file_path) qmake_dir = os.path.dirname(qmake_conf_path) # Hope to a programming god that there's only one .pro file at the # top level directory of repository. - glob_result = glob.glob(os.path.join(qmake_dir, '*.pro')) + glob_result = glob.glob(os.path.join(qmake_dir, "*.pro")) if len(glob_result) > 0: return glob_result[0] return None -def handle_top_level_repo_tests_project(scope: Scope, cm_fh: typing.IO[str]): +def handle_top_level_repo_tests_project(scope: Scope, cm_fh: IO[str]): top_level_project_path = find_top_level_repo_project_file(scope.file_absolute_path) if top_level_project_path: # qtdeclarative @@ -2458,28 +2791,30 @@ def handle_top_level_repo_tests_project(scope: Scope, cm_fh: typing.IO[str]): # Found a mapping, adjust name. if qt_lib != file_name_without_qt: # QtDeclarative - qt_lib = re.sub(r':', r'', qt_lib) + "Tests" + qt_lib = re.sub(r":", r"", qt_lib) + "Tests" else: qt_lib += "Tests_FIXME" else: qt_lib = "Tests_FIXME" - content = """if(NOT TARGET Qt::Test) - cmake_minimum_required(VERSION {}) - project({} VERSION 6.0.0 LANGUAGES C CXX) - find_package(Qt6 ${{PROJECT_VERSION}} REQUIRED COMPONENTS BuildInternals Core SET_ME_TO_SOMETHING_USEFUL) - find_package(Qt6 ${{PROJECT_VERSION}} OPTIONAL_COMPONENTS SET_ME_TO_SOMETHING_USEFUL) - qt_set_up_standalone_tests_build() -endif() + content = dedent( + f"""\ + if(NOT TARGET Qt::Test) + cmake_minimum_required(VERSION {cmake_version_string}) + project({qt_lib} VERSION 6.0.0 LANGUAGES C CXX) + find_package(Qt6 ${{PROJECT_VERSION}} REQUIRED COMPONENTS BuildInternals Core SET_ME_TO_SOMETHING_USEFUL) + find_package(Qt6 ${{PROJECT_VERSION}} OPTIONAL_COMPONENTS SET_ME_TO_SOMETHING_USEFUL) + qt_set_up_standalone_tests_build() + endif() + qt_build_tests()""" + ) -qt_build_tests() -""".format(cmake_version_string, qt_lib) - - cm_fh.write('{}'.format(content)) + cm_fh.write(f"{content}") -def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, - indent: int = 0, is_example: bool=False) -> None: +def cmakeify_scope( + scope: Scope, cm_fh: IO[str], *, indent: int = 0, is_example: bool = False +) -> None: template = scope.TEMPLATE temp_buffer = io.StringIO() @@ -2490,13 +2825,12 @@ def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, # Same for top-level tests. elif is_top_level_repo_tests_project(scope.file_absolute_path): handle_top_level_repo_tests_project(scope, temp_buffer) - elif template == 'subdirs': + elif template == "subdirs": handle_subdir(scope, temp_buffer, indent=indent, is_example=is_example) - elif template in ('app', 'lib'): + elif template in ("app", "lib"): handle_app_or_lib(scope, temp_buffer, indent=indent, is_example=is_example) else: - print(' XXXX: {}: Template type {} not yet supported.' - .format(scope.file, template)) + print(f" XXXX: {scope.file}: Template type {template} not yet supported.") buffer_value = temp_buffer.getvalue() @@ -2504,21 +2838,23 @@ def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, # Wrap top level examples project with some commands which # are necessary to build examples as part of the overall # build. - buffer_value = """qt_examples_build_begin() + buffer_value = dedent( + """\ + qt_examples_build_begin() -{} -qt_examples_build_end() -""".format(buffer_value) + {buffer_value} + qt_examples_build_end() + """ + ) cm_fh.write(buffer_value) -def generate_new_cmakelists(scope: Scope, *, is_example: bool=False) -> None: - print('Generating CMakeLists.gen.txt') - with open(scope.generated_cmake_lists_path, 'w') as cm_fh: +def generate_new_cmakelists(scope: Scope, *, is_example: bool = False) -> None: + print("Generating CMakeLists.gen.txt") + with open(scope.generated_cmake_lists_path, "w") as cm_fh: assert scope.file - cm_fh.write('# Generated from {}.\n\n' - .format(os.path.basename(scope.file))) + cm_fh.write(f"# Generated from {os.path.basename(scope.file)}.\n\n") is_example_heuristic = is_example_project(scope.file_absolute_path) final_is_example_decision = is_example or is_example_heuristic @@ -2529,18 +2865,17 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: for c in scope.children: do_include(c) - for include_file in scope.get_files('_INCLUDED', is_include=True): + for include_file in scope.get_files("_INCLUDED", is_include=True): if not include_file: continue if not os.path.isfile(include_file): - print(' XXXX: Failed to include {}.'.format(include_file)) + print(f" XXXX: Failed to include {include_file}.") continue include_result = parseProFile(include_file, debug=debug) - include_scope \ - = Scope.FromDict(None, include_file, - include_result.asDict().get('statements'), - '', scope.basedir) # This scope will be merged into scope! + include_scope = Scope.FromDict( + None, include_file, include_result.asDict().get("statements"), "", scope.basedir + ) # This scope will be merged into scope! do_include(include_scope) @@ -2548,31 +2883,33 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: def copy_generated_file_to_final_location(scope: Scope, keep_temporary_files=False) -> None: - print('Copying {} to {}'.format(scope.generated_cmake_lists_path, - scope.original_cmake_lists_path)) + print(f"Copying {scope.generated_cmake_lists_path} to {scope.original_cmake_lists_path}") copyfile(scope.generated_cmake_lists_path, scope.original_cmake_lists_path) if not keep_temporary_files: os.remove(scope.generated_cmake_lists_path) -def should_convert_project(project_file_path: str = '') -> bool: +def should_convert_project(project_file_path: str = "") -> bool: qmake_conf_path = find_qmake_conf(project_file_path) qmake_conf_dir_path = os.path.dirname(qmake_conf_path) project_relative_path = os.path.relpath(project_file_path, qmake_conf_dir_path) # Skip cmake auto tests, they should not be converted. - if project_relative_path.startswith('tests/auto/cmake'): + if project_relative_path.startswith("tests/auto/cmake"): return False # Skip qmake testdata projects. - if project_relative_path.startswith('tests/auto/tools/qmake/testdata'): + if project_relative_path.startswith("tests/auto/tools/qmake/testdata"): return False return True def main() -> None: + # Be sure of proper Python version + assert sys.version_info >= (3, 7) + args = _parse_commandline() debug_parsing = args.debug_parser or args.debug @@ -2587,53 +2924,57 @@ def main() -> None: project_file_absolute_path = os.path.abspath(file_relative_path) if not should_convert_project(project_file_absolute_path): - print('Skipping conversion of project: "{}"'.format(project_file_absolute_path)) + print(f'Skipping conversion of project: "{project_file_absolute_path}"') continue parseresult = parseProFile(file_relative_path, debug=debug_parsing) if args.debug_parse_result or args.debug: - print('\n\n#### Parser result:') + print("\n\n#### Parser result:") print(parseresult) - print('\n#### End of parser result.\n') + print("\n#### End of parser result.\n") if args.debug_parse_dictionary or args.debug: - print('\n\n####Parser result dictionary:') + print("\n\n####Parser result dictionary:") print(parseresult.asDict()) - print('\n#### End of parser result dictionary.\n') + print("\n#### End of parser result dictionary.\n") - file_scope = Scope.FromDict(None, file_relative_path, - parseresult.asDict().get('statements')) + file_scope = Scope.FromDict( + None, file_relative_path, parseresult.asDict().get("statements") + ) if args.debug_pro_structure or args.debug: - print('\n\n#### .pro/.pri file structure:') + print("\n\n#### .pro/.pri file structure:") file_scope.dump() - print('\n#### End of .pro/.pri file structure.\n') + print("\n#### End of .pro/.pri file structure.\n") do_include(file_scope, debug=debug_parsing) if args.debug_full_pro_structure or args.debug: - print('\n\n#### Full .pro/.pri file structure:') + print("\n\n#### Full .pro/.pri file structure:") file_scope.dump() - print('\n#### End of full .pro/.pri file structure.\n') + print("\n#### End of full .pro/.pri file structure.\n") generate_new_cmakelists(file_scope, is_example=args.is_example) copy_generated_file = True if not args.skip_special_case_preservation: debug_special_case = args.debug_special_case_preservation or args.debug - handler = SpecialCaseHandler(file_scope.original_cmake_lists_path, - file_scope.generated_cmake_lists_path, - file_scope.basedir, - keep_temporary_files=args.keep_temporary_files, - debug=debug_special_case) + handler = SpecialCaseHandler( + file_scope.original_cmake_lists_path, + file_scope.generated_cmake_lists_path, + file_scope.basedir, + keep_temporary_files=args.keep_temporary_files, + debug=debug_special_case, + ) copy_generated_file = handler.handle_special_cases() if copy_generated_file: - copy_generated_file_to_final_location(file_scope, - keep_temporary_files=args.keep_temporary_files) + copy_generated_file_to_final_location( + file_scope, keep_temporary_files=args.keep_temporary_files + ) os.chdir(backup_current_dir) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/util/cmake/pro_conversion_rate.py b/util/cmake/pro_conversion_rate.py index 740e834ca5..c2807434a6 100755 --- a/util/cmake/pro_conversion_rate.py +++ b/util/cmake/pro_conversion_rate.py @@ -47,9 +47,10 @@ from timeit import default_timer def _parse_commandline(): - parser = ArgumentParser(description='Find pro files for which there are no CMakeLists.txt.') - parser.add_argument('source_directory', metavar='', type=str, - help='The source directory') + parser = ArgumentParser(description="Find pro files for which there are no CMakeLists.txt.") + parser.add_argument( + "source_directory", metavar="", type=str, help="The source directory" + ) return parser.parse_args() @@ -68,6 +69,7 @@ class Blacklist: try: # If package is available, use Aho-Corasick algorithm, from ahocorapy.keywordtree import KeywordTree + self.tree = KeywordTree(case_insensitive=True) for p in self.path_parts: @@ -117,45 +119,58 @@ def check_for_cmake_project(pro_path: str) -> bool: return os.path.exists(cmake_project_path) -def compute_stats(src_path: str, pros_with_missing_project: typing.List[str], - total_pros: int, existing_pros: int, missing_pros: int) -> dict: +def compute_stats( + src_path: str, + pros_with_missing_project: typing.List[str], + total_pros: int, + existing_pros: int, + missing_pros: int, +) -> dict: stats = {} - stats['total projects'] = {'label': 'Total pro files found', - 'value': total_pros} - stats['existing projects'] = {'label': 'Existing CMakeLists.txt files found', - 'value': existing_pros} - stats['missing projects'] = {'label': 'Missing CMakeLists.txt files found', - 'value': missing_pros} - stats['missing examples'] = {'label': 'Missing examples', 'value': 0} - stats['missing tests'] = {'label': 'Missing tests', 'value': 0} - stats['missing src'] = {'label': 'Missing src/**/**', 'value': 0} - stats['missing plugins'] = {'label': 'Missing plugins', 'value': 0} + stats["total projects"] = {"label": "Total pro files found", "value": total_pros} + stats["existing projects"] = { + "label": "Existing CMakeLists.txt files found", + "value": existing_pros, + } + stats["missing projects"] = { + "label": "Missing CMakeLists.txt files found", + "value": missing_pros, + } + stats["missing examples"] = {"label": "Missing examples", "value": 0} + stats["missing tests"] = {"label": "Missing tests", "value": 0} + stats["missing src"] = {"label": "Missing src/**/**", "value": 0} + stats["missing plugins"] = {"label": "Missing plugins", "value": 0} for p in pros_with_missing_project: rel_path = os.path.relpath(p, src_path) if rel_path.startswith("examples"): - stats['missing examples']['value'] += 1 + stats["missing examples"]["value"] += 1 elif rel_path.startswith("tests"): - stats['missing tests']['value'] += 1 + stats["missing tests"]["value"] += 1 elif rel_path.startswith(os.path.join("src", "plugins")): - stats['missing plugins']['value'] += 1 + stats["missing plugins"]["value"] += 1 elif rel_path.startswith("src"): - stats['missing src']['value'] += 1 + stats["missing src"]["value"] += 1 for stat in stats: - if stats[stat]['value'] > 0: - stats[stat]['percentage'] = round(stats[stat]['value'] * 100 / total_pros, 2) + if stats[stat]["value"] > 0: + stats[stat]["percentage"] = round(stats[stat]["value"] * 100 / total_pros, 2) return stats -def print_stats(src_path: str, pros_with_missing_project: typing.List[str], stats: dict, - scan_time: float, script_time: float): +def print_stats( + src_path: str, + pros_with_missing_project: typing.List[str], + stats: dict, + scan_time: float, + script_time: float, +): - if stats['total projects']['value'] == 0: + if stats["total projects"]["value"] == 0: print("No .pro files found. Did you specify a correct source path?") return - if stats['total projects']['value'] == stats['existing projects']['value']: + if stats["total projects"]["value"] == stats["existing projects"]["value"]: print("All projects were converted.") else: print("Missing CMakeLists.txt files for the following projects: \n") @@ -167,10 +182,12 @@ def print_stats(src_path: str, pros_with_missing_project: typing.List[str], stat print("\nStatistics: \n") for stat in stats: - if stats[stat]['value'] > 0: - print("{:<40}: {} ({}%)".format(stats[stat]['label'], - stats[stat]['value'], - stats[stat]['percentage'])) + if stats[stat]["value"] > 0: + print( + "{:<40}: {} ({}%)".format( + stats[stat]["label"], stats[stat]["value"], stats[stat]["percentage"] + ) + ) print("\n{:<40}: {:.10f} seconds".format("Scan time", scan_time)) print("{:<40}: {:.10f} seconds".format("Total script time", script_time)) @@ -184,9 +201,7 @@ def main(): extension = ".pro" blacklist_names = ["config.tests", "doc", "3rdparty", "angle"] - blacklist_path_parts = [ - os.path.join("util", "cmake") - ] + blacklist_path_parts = [os.path.join("util", "cmake")] script_start_time = default_timer() blacklist = Blacklist(blacklist_names, blacklist_path_parts) @@ -206,13 +221,14 @@ def main(): missing_pros = len(pros_with_missing_project) existing_pros = total_pros - missing_pros - stats = compute_stats(src_path, pros_with_missing_project, total_pros, existing_pros, - missing_pros) + stats = compute_stats( + src_path, pros_with_missing_project, total_pros, existing_pros, missing_pros + ) script_end_time = default_timer() script_time = script_end_time - script_start_time print_stats(src_path, pros_with_missing_project, stats, scan_time, script_time) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/util/cmake/requirements.txt b/util/cmake/requirements.txt new file mode 100644 index 0000000000..3a5d19a044 --- /dev/null +++ b/util/cmake/requirements.txt @@ -0,0 +1,4 @@ +pytest; python_version >= '3.7' +mypy; python_version >= '3.7' +pyparsing; python_version >= '3.7' +sympy; python_version >= '3.7' diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index 0d4cff2d67..5d5f50cc2a 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -38,56 +38,70 @@ from argparse import ArgumentParser def parse_command_line(): - parser = ArgumentParser(description='Run pro2cmake on all .pro files recursively in given path.') - parser.add_argument('--only-existing', dest='only_existing', action='store_true', - help='Run pro2cmake only on .pro files that already have a CMakeLists.txt.') - parser.add_argument('--only-qtbase-main-modules', dest='only_qtbase_main_modules', action='store_true', - help='Run pro2cmake only on the main modules in qtbase.') - parser.add_argument('--is-example', dest='is_example', action='store_true', - help='Run pro2cmake with --is-example flag.') - parser.add_argument('path', metavar='', type=str, - help='The path where to look for .pro files.') + parser = ArgumentParser( + description="Run pro2cmake on all .pro files recursively in given path." + ) + parser.add_argument( + "--only-existing", + dest="only_existing", + action="store_true", + help="Run pro2cmake only on .pro files that already have a CMakeLists.txt.", + ) + parser.add_argument( + "--only-qtbase-main-modules", + dest="only_qtbase_main_modules", + action="store_true", + help="Run pro2cmake only on the main modules in qtbase.", + ) + parser.add_argument( + "--is-example", + dest="is_example", + action="store_true", + help="Run pro2cmake with --is-example flag.", + ) + parser.add_argument( + "path", metavar="", type=str, help="The path where to look for .pro files." + ) return parser.parse_args() def find_all_pro_files(base_path: str, args: argparse.Namespace): - def sorter(pro_file: str) -> str: """ Sorter that tries to prioritize main pro files in a directory. """ - pro_file_without_suffix = pro_file.rsplit('/', 1)[-1][:-4] + pro_file_without_suffix = pro_file.rsplit("/", 1)[-1][:-4] dir_name = os.path.dirname(pro_file) - if dir_name.endswith('/' + pro_file_without_suffix): + if dir_name.endswith("/" + pro_file_without_suffix): return dir_name return dir_name + "/__" + pro_file all_files = [] previous_dir_name: str = None - print('Finding .pro files.') - glob_result = glob.glob(os.path.join(base_path, '**/*.pro'), recursive=True) + print("Finding .pro files.") + glob_result = glob.glob(os.path.join(base_path, "**/*.pro"), recursive=True) def cmake_lists_exists_filter(path): path_dir_name = os.path.dirname(path) - if os.path.exists(os.path.join(path_dir_name, 'CMakeLists.txt')): + if os.path.exists(os.path.join(path_dir_name, "CMakeLists.txt")): return True return False def qtbase_main_modules_filter(path): main_modules = [ - 'corelib', - 'network', - 'gui', - 'widgets', - 'testlib', - 'printsupport', - 'opengl', - 'sql', - 'dbus', - 'concurrent', - 'xml', + "corelib", + "network", + "gui", + "widgets", + "testlib", + "printsupport", + "opengl", + "sql", + "dbus", + "concurrent", + "xml", ] - path_suffixes = ['src/{}/{}.pro'.format(m, m, '.pro') for m in main_modules] + path_suffixes = ["src/{m}/{m}.pro" for m in main_modules] for path_suffix in path_suffixes: if path.endswith(path_suffix): @@ -102,7 +116,7 @@ def find_all_pro_files(base_path: str, args: argparse.Namespace): filter_func = qtbase_main_modules_filter if filter_func: - print('Filtering.') + print("Filtering.") filter_result = [p for p in filter_result if filter_func(p)] for pro_file in sorted(filter_result, key=sorter): @@ -118,14 +132,14 @@ def find_all_pro_files(base_path: str, args: argparse.Namespace): def run(all_files: typing.List[str], pro2cmake: str, args: argparse.Namespace) -> typing.List[str]: failed_files = [] files_count = len(all_files) - workers = (os.cpu_count() or 1) + workers = os.cpu_count() or 1 if args.only_qtbase_main_modules: # qtbase main modules take longer than usual to process. workers = 2 with concurrent.futures.ThreadPoolExecutor(max_workers=workers, initargs=(10,)) as pool: - print('Firing up thread pool executor.') + print("Firing up thread pool executor.") def _process_a_file(data: typing.Tuple[str, int, int]) -> typing.Tuple[int, str, str]: filename, index, total = data @@ -134,18 +148,21 @@ def run(all_files: typing.List[str], pro2cmake: str, args: argparse.Namespace) - pro2cmake_args.append(sys.executable) pro2cmake_args.append(pro2cmake) if args.is_example: - pro2cmake_args.append('--is-example') + pro2cmake_args.append("--is-example") pro2cmake_args.append(os.path.basename(filename)) - result = subprocess.run(pro2cmake_args, - cwd=os.path.dirname(filename), - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - stdout = 'Converted[{}/{}]: {}\n'.format(index, total, filename) + result = subprocess.run( + pro2cmake_args, + cwd=os.path.dirname(filename), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + stdout = f"Converted[{index}/{total}]: {filename}\n" return result.returncode, filename, stdout + result.stdout.decode() - for return_code, filename, stdout in pool.map(_process_a_file, - zip(all_files, - range(1, files_count + 1), - (files_count for _ in all_files))): + for return_code, filename, stdout in pool.map( + _process_a_file, + zip(all_files, range(1, files_count + 1), (files_count for _ in all_files)), + ): if return_code: failed_files.append(filename) print(stdout) @@ -157,21 +174,23 @@ def main() -> None: args = parse_command_line() script_path = os.path.dirname(os.path.abspath(__file__)) - pro2cmake = os.path.join(script_path, 'pro2cmake.py') + pro2cmake = os.path.join(script_path, "pro2cmake.py") base_path = args.path all_files = find_all_pro_files(base_path, args) files_count = len(all_files) failed_files = run(all_files, pro2cmake, args) if len(all_files) == 0: - print('No files found.') + print("No files found.") if failed_files: - print('The following files were not successfully ' - 'converted ({} of {}):'.format(len(failed_files), files_count)) + print( + f"The following files were not successfully " + f"converted ({len(failed_files)} of {files_count}):" + ) for f in failed_files: - print(' "{}"'.format(f)) + print(f' "{f}"') -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/util/cmake/special_case_helper.py b/util/cmake/special_case_helper.py index b9cb93dce0..60443aeb61 100644 --- a/util/cmake/special_case_helper.py +++ b/util/cmake/special_case_helper.py @@ -93,31 +93,34 @@ import stat from shutil import copyfile from shutil import rmtree +from textwrap import dedent def remove_special_cases(original: str) -> str: # Remove content between the following markers # '# special case begin' and '# special case end'. # This also remove the markers. - replaced = re.sub(r'\n[^#\n]*?#[^\n]*?special case begin.*?#[^\n]*special case end[^\n]*?\n', - '\n', - original, - 0, - re.DOTALL) + replaced = re.sub( + r"\n[^#\n]*?#[^\n]*?special case begin.*?#[^\n]*special case end[^\n]*?\n", + "\n", + original, + 0, + re.DOTALL, + ) # Remove individual lines that have the "# special case" marker. - replaced = re.sub(r'\n.*#.*special case[^\n]*\n', '\n', replaced) + replaced = re.sub(r"\n.*#.*special case[^\n]*\n", "\n", replaced) return replaced def read_content_from_file(file_path: str) -> str: - with open(file_path, 'r') as file_fd: + with open(file_path, "r") as file_fd: content = file_fd.read() return content def write_content_to_file(file_path: str, content: str) -> None: - with open(file_path, 'w') as file_fd: + with open(file_path, "w") as file_fd: file_fd.write(content) @@ -126,23 +129,23 @@ def resolve_simple_git_conflicts(file_path: str, debug=False) -> None: # If the conflict represents the addition of a new content hunk, # keep the content and remove the conflict markers. if debug: - print('Resolving simple conflicts automatically.') - replaced = re.sub(r'\n<<<<<<< HEAD\n=======(.+?)>>>>>>> master\n', r'\1', content, 0, re.DOTALL) + print("Resolving simple conflicts automatically.") + replaced = re.sub(r"\n<<<<<<< HEAD\n=======(.+?)>>>>>>> master\n", r"\1", content, 0, re.DOTALL) write_content_to_file(file_path, replaced) def copyfile_log(src: str, dst: str, debug=False): if debug: - print('Copying {} to {}.'.format(src, dst)) + print(f"Copying {src} to {dst}.") copyfile(src, dst) def check_if_git_in_path() -> bool: - is_win = os.name == 'nt' - for path in os.environ['PATH'].split(os.pathsep): - git_path = os.path.join(path, 'git') + is_win = os.name == "nt" + for path in os.environ["PATH"].split(os.pathsep): + git_path = os.path.join(path, "git") if is_win: - git_path += '.exe' + git_path += ".exe" if os.path.isfile(git_path) and os.access(git_path, os.X_OK): return True return False @@ -150,31 +153,38 @@ def check_if_git_in_path() -> bool: def run_process_quiet(args_string: str, debug=False) -> bool: if debug: - print('Running command: "{}\"'.format(args_string)) + print(f'Running command: "{args_string}"') args_list = args_string.split() try: subprocess.run(args_list, check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: # git merge with conflicts returns with exit code 1, but that's not # an error for us. - if 'git merge' not in args_string: - print('Error while running: "{}"\n{}'.format(args_string, e.stdout)) + if "git merge" not in args_string: + print( + dedent( + f"""\ + Error while running: "{args_string}" + {e.stdout}""" + ) + ) return False return True def does_file_have_conflict_markers(file_path: str, debug=False) -> bool: if debug: - print('Checking if {} has no leftover conflict markers.'.format(file_path)) + print(f"Checking if {file_path} has no leftover conflict markers.") content_actual = read_content_from_file(file_path) - if '<<<<<<< HEAD' in content_actual: - print('Conflict markers found in {}. ' - 'Please remove or solve them first.'.format(file_path)) + if "<<<<<<< HEAD" in content_actual: + print(f"Conflict markers found in {file_path}. " "Please remove or solve them first.") return True return False -def create_file_with_no_special_cases(original_file_path: str, no_special_cases_file_path: str, debug=False): +def create_file_with_no_special_cases( + original_file_path: str, no_special_cases_file_path: str, debug=False +): """ Reads content of original CMakeLists.txt, removes all content between "# special case" markers or lines, saves the result into a @@ -182,35 +192,36 @@ def create_file_with_no_special_cases(original_file_path: str, no_special_cases_ """ content_actual = read_content_from_file(original_file_path) if debug: - print('Removing special case blocks from {}.'.format(original_file_path)) + print(f"Removing special case blocks from {original_file_path}.") content_no_special_cases = remove_special_cases(content_actual) if debug: - print('Saving original contents of {} ' - 'with removed special case blocks to {}'.format(original_file_path, - no_special_cases_file_path)) + print( + f"Saving original contents of {original_file_path} " + f"with removed special case blocks to {no_special_cases_file_path}" + ) write_content_to_file(no_special_cases_file_path, content_no_special_cases) -def rm_tree_on_error_handler(func: typing.Callable[..., None], - path: str, exception_info: tuple): +def rm_tree_on_error_handler(func: typing.Callable[..., None], path: str, exception_info: tuple): # If the path is read only, try to make it writable, and try # to remove the path again. if not os.access(path, os.W_OK): os.chmod(path, stat.S_IWRITE) func(path) else: - print('Error while trying to remove path: {}. Exception: {}'.format(path, exception_info)) + print(f"Error while trying to remove path: {path}. Exception: {exception_info}") class SpecialCaseHandler(object): - - def __init__(self, - original_file_path: str, - generated_file_path: str, - base_dir: str, - keep_temporary_files=False, - debug=False) -> None: + def __init__( + self, + original_file_path: str, + generated_file_path: str, + base_dir: str, + keep_temporary_files=False, + debug=False, + ) -> None: self.base_dir = base_dir self.original_file_path = original_file_path self.generated_file_path = generated_file_path @@ -220,29 +231,28 @@ class SpecialCaseHandler(object): @property def prev_file_path(self) -> str: - return os.path.join(self.base_dir, '.prev_CMakeLists.txt') + return os.path.join(self.base_dir, ".prev_CMakeLists.txt") @property def post_merge_file_path(self) -> str: - return os.path.join(self.base_dir, 'CMakeLists-post-merge.txt') + return os.path.join(self.base_dir, "CMakeLists-post-merge.txt") @property def no_special_file_path(self) -> str: - return os.path.join(self.base_dir, 'CMakeLists.no-special.txt') + return os.path.join(self.base_dir, "CMakeLists.no-special.txt") def apply_git_merge_magic(self, no_special_cases_file_path: str) -> None: # Create new folder for temporary repo, and ch dir into it. - repo = os.path.join(self.base_dir, 'tmp_repo') + repo = os.path.join(self.base_dir, "tmp_repo") repo_absolute_path = os.path.abspath(repo) - txt = 'CMakeLists.txt' + txt = "CMakeLists.txt" try: os.mkdir(repo) current_dir = os.getcwd() os.chdir(repo) except Exception as e: - print('Failed to create temporary directory for temporary git repo. Exception: {}' - .format(e)) + print(f"Failed to create temporary directory for temporary git repo. Exception: {e}") raise e generated_file_path = os.path.join("..", self.generated_file_path) @@ -252,34 +262,34 @@ class SpecialCaseHandler(object): try: # Create new repo with the "clean" CMakeLists.txt file. - run_process_quiet('git init .', debug=self.debug) - run_process_quiet('git config user.name fake', debug=self.debug) - run_process_quiet('git config user.email fake@fake', debug=self.debug) + run_process_quiet("git init .", debug=self.debug) + run_process_quiet("git config user.name fake", debug=self.debug) + run_process_quiet("git config user.email fake@fake", debug=self.debug) copyfile_log(no_special_cases_file_path, txt, debug=self.debug) - run_process_quiet('git add {}'.format(txt), debug=self.debug) - run_process_quiet('git commit -m no_special', debug=self.debug) - run_process_quiet('git checkout -b no_special', debug=self.debug) + run_process_quiet(f"git add {txt}", debug=self.debug) + run_process_quiet("git commit -m no_special", debug=self.debug) + run_process_quiet("git checkout -b no_special", debug=self.debug) # Copy the original "modified" file (with the special cases) # and make a new commit. - run_process_quiet('git checkout -b original', debug=self.debug) + run_process_quiet("git checkout -b original", debug=self.debug) copyfile_log(original_file_path, txt, debug=self.debug) - run_process_quiet('git add {}'.format(txt), debug=self.debug) - run_process_quiet('git commit -m original', debug=self.debug) + run_process_quiet(f"git add {txt}", debug=self.debug) + run_process_quiet("git commit -m original", debug=self.debug) # Checkout the commit with "clean" file again, and create a # new branch. - run_process_quiet('git checkout no_special', debug=self.debug) - run_process_quiet('git checkout -b newly_generated', debug=self.debug) + run_process_quiet("git checkout no_special", debug=self.debug) + run_process_quiet("git checkout -b newly_generated", debug=self.debug) # Copy the new "modified" file and make a commit. copyfile_log(generated_file_path, txt, debug=self.debug) - run_process_quiet('git add {}'.format(txt), debug=self.debug) - run_process_quiet('git commit -m newly_generated', debug=self.debug) + run_process_quiet(f"git add {txt}", debug=self.debug) + run_process_quiet("git commit -m newly_generated", debug=self.debug) # Merge the "old" branch with modifications into the "new" # branch with the newly generated file. - run_process_quiet('git merge original', debug=self.debug) + run_process_quiet("git merge original", debug=self.debug) # Resolve some simple conflicts (just remove the markers) # for cases that don't need intervention. @@ -288,7 +298,7 @@ class SpecialCaseHandler(object): # Copy the resulting file from the merge. copyfile_log(txt, post_merge_file_path) except Exception as e: - print('Git merge conflict resolution process failed. Exception: {}'.format(e)) + print(f"Git merge conflict resolution process failed. Exception: {e}") raise e finally: os.chdir(current_dir) @@ -298,7 +308,7 @@ class SpecialCaseHandler(object): if not self.keep_temporary_files: rmtree(repo_absolute_path, onerror=rm_tree_on_error_handler) except Exception as e: - print('Error removing temporary repo. Exception: {}'.format(e)) + print(f"Error removing temporary repo. Exception: {e}") def save_next_clean_file(self): files_are_equivalent = filecmp.cmp(self.generated_file_path, self.post_merge_file_path) @@ -316,20 +326,18 @@ class SpecialCaseHandler(object): failed_once = False i = 0 while not success and i < 20: - success = run_process_quiet("git add {}".format(self.prev_file_path), - debug=self.debug) + success = run_process_quiet(f"git add {self.prev_file_path}", debug=self.debug) if not success: failed_once = True i += 1 time.sleep(0.1) if failed_once and not success: - print('Retrying git add, the index.lock was probably acquired.') + print("Retrying git add, the index.lock was probably acquired.") if failed_once and success: - print('git add succeeded.') + print("git add succeeded.") elif failed_once and not success: - print('git add failed. Make sure to git add {} yourself.'.format( - self.prev_file_path)) + print(f"git add failed. Make sure to git add {self.prev_file_path} yourself.") def handle_special_cases_helper(self) -> bool: """ @@ -348,15 +356,18 @@ class SpecialCaseHandler(object): return False if self.use_heuristic: - create_file_with_no_special_cases(self.original_file_path, - self.no_special_file_path) + create_file_with_no_special_cases( + self.original_file_path, self.no_special_file_path + ) no_special_cases_file_path = self.no_special_file_path else: no_special_cases_file_path = self.prev_file_path if self.debug: - print('Using git to reapply special case modifications to newly generated {} ' - 'file'.format(self.generated_file_path)) + print( + f"Using git to reapply special case modifications to newly " + f"generated {self.generated_file_path} file" + ) self.apply_git_merge_magic(no_special_cases_file_path) self.save_next_clean_file() @@ -365,11 +376,13 @@ class SpecialCaseHandler(object): if not self.keep_temporary_files: os.remove(self.post_merge_file_path) - print('Special case reapplication using git is complete. ' - 'Make sure to fix remaining conflict markers.') + print( + "Special case reapplication using git is complete. " + "Make sure to fix remaining conflict markers." + ) except Exception as e: - print('Error occurred while trying to reapply special case modifications: {}'.format(e)) + print(f"Error occurred while trying to reapply special case modifications: {e}") return False finally: if not self.keep_temporary_files and self.use_heuristic: @@ -386,8 +399,10 @@ class SpecialCaseHandler(object): keep_special_cases = original_file_exists and git_available if not git_available: - print('You need to have git in PATH in order to reapply the special ' - 'case modifications.') + print( + "You need to have git in PATH in order to reapply the special " + "case modifications." + ) copy_generated_file = True