From e9c45bbdddd4df005bdaa5eea9740d351e6eaea2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 24 Oct 2018 15:20:27 +0200 Subject: Begin port of qtbase to CMake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Done-by: Alexandru Croitor Done-by: Frederik Gladhorn Done-by: Kevin Funk Done-by: Mikhail Svetkin Done-by: Simon Hausmann Done-by: Tobias Hunger Done-by: Tor Arne Vestbø Done-by: Volker Krause Change-Id: Ida4f8bd190f9a4849a1af7b5b7981337a5df5310 Reviewed-by: Simon Hausmann Reviewed-by: Tobias Hunger Reviewed-by: Mikhail Svetkin --- util/cmake/Pipfile | 14 + util/cmake/cmakeconversionrate.py | 127 +++++ util/cmake/configurejson2cmake.py | 860 +++++++++++++++++++++++++++++++ util/cmake/generate_module_map.sh | 38 ++ util/cmake/helper.py | 206 ++++++++ util/cmake/pro2cmake.py | 736 ++++++++++++++++++++++++++ util/cmake/run_pro2cmake.py | 44 ++ util/cmake/tests/__init__.py | 0 util/cmake/tests/data/complex_values.pro | 22 + util/cmake/tests/data/definetest.pro | 6 + util/cmake/tests/data/else.pro | 6 + util/cmake/tests/data/else2.pro | 4 + util/cmake/tests/data/else3.pro | 7 + util/cmake/tests/data/else4.pro | 6 + util/cmake/tests/data/else5.pro | 10 + util/cmake/tests/data/else6.pro | 11 + util/cmake/tests/data/else7.pro | 2 + util/cmake/tests/data/else8.pro | 5 + util/cmake/tests/data/function_if.pro | 4 + util/cmake/tests/data/include.pro | 3 + util/cmake/tests/data/load.pro | 3 + util/cmake/tests/data/quoted.pro | 5 + util/cmake/tests/data/unset.pro | 2 + util/cmake/tests/test_parsing.py | 162 ++++++ 24 files changed, 2283 insertions(+) create mode 100644 util/cmake/Pipfile create mode 100755 util/cmake/cmakeconversionrate.py create mode 100755 util/cmake/configurejson2cmake.py create mode 100755 util/cmake/generate_module_map.sh create mode 100644 util/cmake/helper.py create mode 100755 util/cmake/pro2cmake.py create mode 100755 util/cmake/run_pro2cmake.py create mode 100644 util/cmake/tests/__init__.py create mode 100644 util/cmake/tests/data/complex_values.pro create mode 100644 util/cmake/tests/data/definetest.pro create mode 100644 util/cmake/tests/data/else.pro create mode 100644 util/cmake/tests/data/else2.pro create mode 100644 util/cmake/tests/data/else3.pro create mode 100644 util/cmake/tests/data/else4.pro create mode 100644 util/cmake/tests/data/else5.pro create mode 100644 util/cmake/tests/data/else6.pro create mode 100644 util/cmake/tests/data/else7.pro create mode 100644 util/cmake/tests/data/else8.pro create mode 100644 util/cmake/tests/data/function_if.pro create mode 100644 util/cmake/tests/data/include.pro create mode 100644 util/cmake/tests/data/load.pro create mode 100644 util/cmake/tests/data/quoted.pro create mode 100644 util/cmake/tests/data/unset.pro create mode 100755 util/cmake/tests/test_parsing.py (limited to 'util/cmake') diff --git a/util/cmake/Pipfile b/util/cmake/Pipfile new file mode 100644 index 0000000000..d7e1905378 --- /dev/null +++ b/util/cmake/Pipfile @@ -0,0 +1,14 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pytest = "*" +mypy = "*" +pyparsing = "*" + +[dev-packages] + +[requires] +python_version = "3.7" diff --git a/util/cmake/cmakeconversionrate.py b/util/cmake/cmakeconversionrate.py new file mode 100755 index 0000000000..3496ed1b91 --- /dev/null +++ b/util/cmake/cmakeconversionrate.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +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)') + + return parser.parse_args() + + +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) + return int(result.stdout) + + +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 debug: + print('Running cmake in "{}".'.format(binary_directory)) + result = subprocess.run(['/usr/bin/cmake', '-GNinja', abs_source], cwd=binary_directory) + if debug: + print('CMake return code: {}.'.format(result.returncode)) + + assert result.returncode == 0 + + if debug: + print('Running ninja in "{}".'.format(binary_directory)) + result = subprocess.run('/usr/bin/ninja', cwd=binary_directory) + if debug: + print('Ninja return code: {}.'.format(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', '') + if debug: + print('Test summary: {} ({}).'.format(summary, result.returncode)) + + 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)), ) + + return (0, 0,) + + +def main() -> int: + args = _parse_commandline() + + 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)) + return 1 + + if args.debug: + print('qmake baseline: {} test binaries.'.format(base_line)) + + 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) + finally: + if cmake_total == 0: + 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)) + return 0 + + +if __name__ == '__main__': + main() diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py new file mode 100755 index 0000000000..4623e8d874 --- /dev/null +++ b/util/cmake/configurejson2cmake.py @@ -0,0 +1,860 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import json +import os.path +import re +import sys +from typing import Set, Union, List, Dict + +from helper import map_qt_library, featureName, substitute_platform + +knownTests = set() # type: Set[str] + + +class LibraryMapping: + def __init__(self, package: str, resultVariable: str, appendFoundSuffix: bool = True) -> None: + self.package = package + self.resultVariable = resultVariable + self.appendFoundSuffix = appendFoundSuffix + + +def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: + libmap = { + 'zlib': 'ZLIB', + 'gbm': 'gbm', + 'host_dbus': None, + 'libdl': None, # handled by CMAKE_DL_LIBS + 'libatomic': 'Atomic', + 'double-conversion': 'WrapDoubleConversion', + 'gnu_iconv': None, + 'sun_iconv': None, + 'posix_iconv': None, + 'icu': ['ICU', 'COMPONENTS', 'i18n', 'uc', 'data'], + 'pcre2': ['PCRE2', 'REQUIRED'], + 'libpng': 'PNG', + 'libudev': 'Libudev', + 'udev': 'Libudev', + 'journald': 'Libsystemd', + 'vulkan': 'Vulkan', + 'glib': 'GLib', + 'harfbuzz': 'harfbuzz', + 'opengl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_OpenGL"), + 'egl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_EGL"), + 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), + 'libpng': 'PNG', + 'libjpeg': 'JPEG', + 'freetype': 'Freetype', + 'fontconfig': LibraryMapping(package='Fontconfig', resultVariable="FONTCONFIG"), + 'libinput': 'Libinput', + 'xcb': ['XCB', '1.9'], + 'libproxy': 'libproxy', + 'drm': 'Libdrm', + 'xkbcommon': ['XKB', '0.4.1'], + 'xlib': 'X11', + 'xcb_xlib': 'X11_XCB', + 'xrender': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), + 'xcb_render': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), + 'xcb_glx': LibraryMapping(package="XCB", resultVariable="XCB_GLX"), + 'xcb_xkb': LibraryMapping(package="XCB", resultVariable="XCB_XKB"), + 'xcb_xinput': LibraryMapping(package="XCB", resultVariable="XCB_XINPUT"), + 'x11sm': LibraryMapping(package="X11", resultVariable="X11_SM"), + 'wayland_server': 'Wayland', + } # type: Dict[str, Union[str, List[str], LibraryMapping]] + if lib not in libmap: + raise Exception(' XXXX Unknown library "{}".'.format(lib)) + + return libmap[lib] + + +def map_tests(test: str) -> str: + testmap = { + 'c++11': '$', + 'c++14': '$', + 'c++1z': '$', + 'c99': '$', + 'c11': '$', + + 'x86SimdAlways': 'ON', # FIXME: Is this the right thing? + + 'aesni': 'TEST_subarch_aes', + 'avx': 'TEST_subarch_avx', + 'avx2': 'TEST_subarch_avx2', + 'avx512f': 'TEST_subarch_avx512f', + 'avx512cd': 'TEST_subarch_avx512cd', + 'avx512dq': 'TEST_subarch_avx512dq', + 'avx512bw': 'TEST_subarch_avx512bw', + 'avx512er': 'TEST_subarch_avx512er', + 'avx512pf': 'TEST_subarch_avx512pf', + 'avx512vl': 'TEST_subarch_avx512vl', + 'avx512ifma': 'TEST_subarch_avx512ifma', + 'avx512vbmi': 'TEST_subarch_avx512vbmi', + 'avx512vbmi2': 'TEST_subarch_avx512vbmi2', + 'avx512vpopcntdq': 'TEST_subarch_avx512vpopcntdq', + 'avx5124fmaps': 'TEST_subarch_avx5124fmaps', + 'avx5124vnniw': 'TEST_subarch_avx5124vnniw', + 'bmi': 'TEST_subarch_bmi', + 'bmi2': 'TEST_subarch_bmi2', + 'cx16': 'TEST_subarch_cx16', + 'f16c': 'TEST_subarch_c16c', + 'fma': 'TEST_subarch_fma', + 'fma4': 'TEST_subarch_fma4', + 'fsgsbase': 'TEST_subarch_fsgsbase', + 'gfni': 'TEST_subarch_gfni', + 'ibt': 'TEST_subarch_ibt', + 'lwp': 'TEST_subarch_lwp', + 'lzcnt': 'TEST_subarch_lzcnt', + 'mmx': 'TEST_subarch_mmx', + 'movbe': 'TEST_subarch_movbe', + 'mpx': 'TEST_subarch_mpx', + 'no-sahf': 'TEST_subarch_no_shaf', + 'pclmul': 'TEST_subarch_pclmul', + 'popcnt': 'TEST_subarch_popcnt', + 'prefetchwt1': 'TEST_subarch_prefetchwt1', + 'prfchw': 'TEST_subarch_prfchw', + 'pdpid': 'TEST_subarch_rdpid', + 'rdpid': 'TEST_subarch_rdpid', + 'rdseed': 'TEST_subarch_rdseed', + 'rdrnd': 'TEST_subarch_rdseed', # FIXME: Is this the right thing? + 'rtm': 'TEST_subarch_rtm', + 'shani': 'TEST_subarch_sha', + 'shstk': 'TEST_subarch_shstk', + 'sse2': 'TEST_subarch_sse2', + 'sse3': 'TEST_subarch_sse3', + 'ssse3': 'TEST_subarch_ssse3', + 'sse4a': 'TEST_subarch_sse4a', + 'sse4_1': 'TEST_subarch_sse4_1', + 'sse4_2': 'TEST_subarch_sse4_2', + 'tbm': 'TEST_subarch_tbm', + 'xop': 'TEST_subarch_xop', + + 'neon': 'TEST_subarch_neon', + 'iwmmxt': 'TEST_subarch_iwmmxt', + 'crc32': 'TEST_subarch_crc32', + + 'vis': 'TEST_subarch_vis', + 'vis2': 'TEST_subarch_vis2', + 'vis3': 'TEST_subarch_vis3', + + 'dsp': 'TEST_subarch_dsp', + 'dspr2': 'TEST_subarch_dspr2', + + 'altivec': 'TEST_subarch_altivec', + 'spe': 'TEST_subarch_spe', + 'vsx': 'TEST_subarch_vsx', + + 'posix-iconv': 'TEST_posix_iconv', + 'sun-iconv': 'TEST_sun_iconv', + + 'openssl11': '(OPENSSL_VERSION VERSION_GREATER_EQUAL "1.1.0")', + + 'reduce_exports': 'CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY', + } + if test in testmap: + return testmap.get(test, None) + if test in knownTests: + return 'TEST_{}'.format(featureName(test)) + return None + + +def cm(ctx, *output): + txt = ctx['output'] + if txt != '' and not txt.endswith('\n'): + txt += '\n' + txt += '\n'.join(output) + + ctx['output'] = txt + return ctx + + +def readJsonFromDir(dir): + path = os.path.join(dir, 'configure.json') + + print('Reading {}...'.format(path)) + assert os.path.exists(path) + + with open(path, 'r') as fh: + return json.load(fh) + + +def processFiles(ctx, data): + print(' files:') + if 'files' in data: + for (k, v) in data['files'].items(): + ctx[k] = v + return ctx + +def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): + extra = [] + try: + newlib = map_library(lib) + if isinstance(newlib, list): + extra = newlib[1:] + newlib = newlib[0] + elif isinstance(newlib, LibraryMapping): + newlib = newlib.package + except Exception: + return ctx + + if newlib is None: + print(' **** Skipping library "{}" -- was masked.'.format(lib)) + return + + print(' mapped library {} to {}.'.format(lib, newlib)) + + # Avoid duplicate find_package calls. + if newlib in cmake_find_packages_set: + return + + cmake_find_packages_set.add(newlib) + + isRequired = False + + if extra: + if "REQUIRED" in extra: + isRequired = True + extra.remove("REQUIRED") + + if extra: + cm_fh.write('find_package({} {})\n'.format(newlib, ' '.join(extra))) + else: + cm_fh.write('find_package({})\n'.format(newlib)) + + cm_fh.write('set_package_properties({} PROPERTIES TYPE {})\n' + .format(newlib, 'REQUIRED' if isRequired else 'OPTIONAL') + ) + +def lineify(label, value, quote=True): + if value: + if quote: + return ' {} "{}"\n'.format(label, value.replace('"', '\\"')) + return ' {} {}\n'.format(label, value) + return '' + +def map_condition(condition): + # Handle NOT: + if isinstance(condition, list): + condition = '(' + ') AND ('.join(condition) + ')' + if isinstance(condition, bool): + if condition: + return 'ON' + else: + return 'OFF' + assert isinstance(condition, str) + + mapped_features = { + "dlopen": "UNIX", + 'gbm': 'gbm_FOUND', + "sun-libiconv": "TEST_sun_iconv", + "system-xcb": "ON", + "system-freetype": "ON", + } + + # Turn foo != "bar" into (NOT foo STREQUAL 'bar') + condition = re.sub(r"(.+)\s*!=\s*('.+')", '(! \\1 == \\2)', condition) + + condition = condition.replace('!', 'NOT ') + condition = condition.replace('&&', ' AND ') + condition = condition.replace('||', ' OR ') + condition = condition.replace('==', ' STREQUAL ') + + # explicitly handle input.sdk == '': + condition = re.sub(r"input\.sdk\s*==\s*''", 'NOT INPUT_SDK', condition) + + last_pos = 0 + mapped_condition = '' + has_failed = False + for match in re.finditer(r'([a-zA-Z0-9_]+)\.([a-zA-Z0-9_+-]+)', condition): + substitution = None + appendFoundSuffix = True + if match.group(1) == 'libs': + try: + substitution = map_library(match.group(2)) + if isinstance(substitution, list): + substitution = substitution[0] + elif isinstance(substitution, LibraryMapping): + appendFoundSuffix = substitution.appendFoundSuffix + substitution = substitution.resultVariable + except Exception: + substitution = None + + if substitution is not None and appendFoundSuffix: + substitution += '_FOUND' + + elif match.group(1) == 'features': + feature = match.group(2) + if feature in mapped_features: + substitution = mapped_features.get(feature) + else: + substitution = 'QT_FEATURE_{}'.format(featureName(match.group(2))) + + elif match.group(1) == 'subarch': + substitution = 'TEST_subarch_{}'.format(match.group(2)) + + elif match.group(1) == 'call': + if match.group(2) == 'crossCompile': + substitution = 'CMAKE_CROSSCOMPILING' + + elif match.group(1) == 'tests': + substitution = map_tests(match.group(2)) + + elif match.group(1) == 'input': + substitution = 'INPUT_{}'.format(featureName(match.group(2))) + + elif match.group(1) == 'config': + substitution = substitute_platform(match.group(2)) + + elif match.group(1) == 'arch': + if match.group(2) == 'i386': + # FIXME: Does this make sense? + substitution = '(TEST_architecture_arch STREQUAL i386)' + elif match.group(2) == 'x86_64': + substitution = '(TEST_architecture_arch STREQUAL x86_64)' + elif match.group(2) == 'arm': + # FIXME: Does this make sense? + substitution = '(TEST_architecture_arch STREQUAL arm)' + elif match.group(2) == 'arm64': + # FIXME: Does this make sense? + substitution = '(TEST_architecture_arch STREQUAL arm64)' + elif match.group(2) == 'mips': + # FIXME: Does this make sense? + substitution = '(TEST_architecture_arch STREQUAL mips)' + + if substitution is None: + print(' XXXX Unknown condition "{}".'.format(match.group(0))) + has_failed = True + else: + mapped_condition += condition[last_pos:match.start(1)] + substitution + last_pos = match.end(2) + + mapped_condition += condition[last_pos:] + + # Space out '(' and ')': + mapped_condition = mapped_condition.replace('(', ' ( ') + mapped_condition = mapped_condition.replace(')', ' ) ') + + # Prettify: + condition = re.sub('\\s+', ' ', mapped_condition) + condition = condition.strip() + + if has_failed: + condition += ' OR FIXME' + + return condition + + +def parseInput(ctx, input, data, cm_fh): + skip_inputs = { + "prefix", "hostprefix", "extprefix", + + "archdatadir", "bindir", "datadir", "docdir", + "examplesdir", "external-hostbindir", "headerdir", + "hostbindir", "hostdatadir", "hostlibdir", + "importdir", "libdir", "libexecdir", + "plugindir", "qmldir", "settingsdir", + "sysconfdir", "testsdir", "translationdir", + + "android-arch", "android-ndk", "android-ndk-host", + "android-ndk-platform", "android-sdk", + "android-toolchain-version", "android-style-assets", + + "appstore-compliant", + + "avx", "avx2", "avx512", "c++std", "ccache", "commercial", + "compile-examples", "confirm-license", + "dbus", + "dbus-runtime", + + "debug", "debug-and-release", + + "developer-build", + + "device", "device-option", + + "f16c", + + "force-asserts", "force-debug-info", "force-pkg-config", + "framework", + + "gc-binaries", + + "gdb-index", + + "gcc-sysroot", + + "gcov", + + "gnumake", + + "gui", + + "headersclean", + + "incredibuild-xge", + + "libudev", + "ltcg", + "make", + "make-tool", + + "mips_dsp", + "mips_dspr2", + "mp", + + "nomake", + + "opensource", + + "optimize-debug", "optimize-size", "optimized-qmake", "optimized-tools", + + "pch", + + "pkg-config", + + "platform", + + "plugin-manifests", + "profile", + "qreal", + + "reduce-exports", "reduce-relocations", + + "release", + + "rpath", + + "sanitize", + + "sdk", + + "separate-debug-info", + + "shared", + + "silent", + + "qdbus", + + "sse2", + "sse3", + "sse4.1", + "sse4.2", + "ssse3", + "static", + "static-runtime", + "strip", + "syncqt", + "sysroot", + "testcocoon", + "use-gold-linker", + "warnings-are-errors", + "Werror", + "widgets", + "xplatform", + "zlib", + + "doubleconversion", + + "eventfd", + "glib", + "icu", + "inotify", + "journald", + "pcre", + "posix-ipc", + "pps", + "slog2", + "syslog", + + "sqlite", + } + + if input in skip_inputs: + print(' **** Skipping input {}: masked.'.format(input)) + return + + type = data + if isinstance(data, dict): + type = data["type"] + + if type == "boolean": + print(' **** Skipping boolean input {}: masked.'.format(input)) + return + + if type == "enum": + cm_fh.write("# input {}\n".format(input)) + cm_fh.write('set(INPUT_{} "undefined" CACHE STRING "")\n'.format(featureName(input))) + cm_fh.write('set_property(CACHE INPUT_{} PROPERTY STRINGS undefined {})\n\n'.format(featureName(input), " ".join(data["values"]))) + return + + print(' XXXX UNHANDLED INPUT TYPE {} in input description'.format(type)) + return + + +# "tests": { +# "cxx11_future": { +# "label": "C++11 ", +# "type": "compile", +# "test": { +# "include": "future", +# "main": [ +# "std::future f = std::async([]() { return 42; });", +# "(void)f.get();" +# ], +# "qmake": "unix:LIBS += -lpthread" +# } +# }, +def parseTest(ctx, test, data, cm_fh): + skip_tests = { + 'c11', 'c99', + 'c++11', 'c++14', 'c++1y', 'c++1z', + 'reduce_exports', + 'posix-iconv', "sun-iconv", + 'separate_debug_info', # FIXME: see if cmake can do this + 'gc_binaries', + } + + if test in skip_tests: + print(' **** Skipping features {}: masked.'.format(test)) + return + + if data["type"] == "compile": + knownTests.add(test) + + details = data["test"] + + if isinstance(details, str): + print(' XXXX UNHANDLED TEST SUB-TYPE {} in test description'.format(details)) + return + + head = details.get("head", "") + if isinstance(head, list): + head = "\n".join(head) + + sourceCode = head + '\n' + + include = details.get("include", "") + if isinstance(include, list): + include = '#include <' + '>\n#include <'.join(include) + '>' + elif include: + include = '#include <{}>'.format(include) + + sourceCode += include + '\n' + + tail = details.get("tail", "") + if isinstance(tail, list): + tail = "\n".join(tail) + + sourceCode += tail + '\n' + + sourceCode += "int main(int argc, char **argv)\n" + sourceCode += "{\n" + sourceCode += " (void)argc; (void)argv;\n" + sourceCode += " /* BEGIN TEST: */\n" + + main = details.get("main", "") + if isinstance(main, list): + main = "\n".join(main) + + sourceCode += main + '\n' + + sourceCode += " /* END TEST: */\n" + sourceCode += " return 0;\n" + sourceCode += "}\n" + + sourceCode = sourceCode.replace('"', '\\"') + + cm_fh.write("# {}\n".format(test)) + cm_fh.write("qt_config_compile_test({}\n".format(featureName(test))) + cm_fh.write(lineify("LABEL", data.get("label", ""))) + cm_fh.write('"' + sourceCode + '"') + if "qmake" in details: + cm_fh.write("# FIXME: qmake: {}\n".format(details["qmake"])) + cm_fh.write(")\n\n") + + elif data["type"] == "x86Simd": + knownTests.add(test) + + label = data["label"] + + cm_fh.write("# {}\n".format(test)) + cm_fh.write("qt_config_compile_test_x86simd({} \"{}\")\n".format(test, label)) + cm_fh.write("\n") + +# "features": { +# "android-style-assets": { +# "label": "Android Style Assets", +# "condition": "config.android", +# "output": [ "privateFeature" ], +# "comment": "This belongs into gui, but the license check needs it here already." +# }, + else: + print(' XXXX UNHANDLED TEST TYPE {} in test description'.format(data["type"])) + + +def parseFeature(ctx, feature, data, cm_fh): + skip_features = { + 'c++11', 'c++14', 'c++1y', 'c++1z', # C++ versions + 'c89', 'c99', 'c11', # C versions + 'stl', # Do we really need to test for this in 2018?! + 'rpath', 'rpath_dir', # rpath related + 'static', 'shared', # static/shared libs + 'debug', 'release', 'debug_and_release', 'build_all', 'optimize_debug', 'optimize_size', # build types + 'release_tools', 'gcov', 'silent', 'profile', + 'msvc_mp', 'static_runtime', 'incredibuild_xge', 'ccache', # compiler specific stuff + 'sanitize_address', 'sanitize_thread', 'sanitize_memory', # sanitizer + 'sanitize_undefined', 'sanitizer', + 'force_debug_info', 'separate_debug_info', 'warnings_are_errors', # FIXME: Do we need these? + 'strip', 'precompile_header', 'ltcg', 'enable_new_dtags', + 'enable_gdb_index', 'reduce_relocations', + 'stack-protector-strong', + 'host-dbus', # dbus related + 'cross_compile', 'gcc-sysroot', # cross compile related + 'gc_binaries', 'qmakeargs', 'use_gold_linker', 'pkg-config', 'verifyspec', # qmake stuff... + 'GNUmake', 'compiler-flags', + 'system-doubleconversion', 'system-pcre2', 'system-zlib', 'system-png', 'system-jpeg', 'system-freetype', 'system-xcb', 'xkbcommon-system', # system libraries + 'doubleconversion', + 'dlopen', # handled by CMAKE_DL_LIBS + 'alloc_stdlib_h', 'alloc_h', 'alloc_malloc_h', # handled by alloc target + 'posix_fallocate', # Only needed for sqlite, which we do not want to build + 'qpa_default_platform', # Not a bool! + 'sun-libiconv', # internal feature but not referenced in our system + } + if feature in skip_features: + print(' **** Skipping features {}: masked.'.format(feature)) + return + + disabled_features = set() + + override_condition = {} + + handled = { 'autoDetect', 'comment', 'condition', 'description', 'disable', 'emitIf', 'enable', 'label', 'output', 'purpose', 'section' } + label = data.get('label', '') + purpose = data.get('purpose', data.get('description', label)) + autoDetect = map_condition(data.get('autoDetect', '')) + condition = override_condition.get(feature, map_condition(data.get('condition', ''))) + output = data.get('output', []) + comment = data.get('comment', '') + section = data.get('section', '') + enable = map_condition(data.get('enable', '')) + disable = map_condition(data.get('disable', '')) + emitIf = map_condition(data.get('emitIf', '')) + + if feature in disabled_features: + condition = "FALSE" + + for k in [k for k in data.keys() if k not in handled]: + print(' XXXX UNHANDLED KEY {} in feature description'.format(k)) + + if not output: + # feature that is only used in the conditions of other features + output = ["internalFeature"] + + publicInfo = False + privateInfo = False + internalFeature = False + + for o in output: + outputType = o + outputArgs = {} + if isinstance(o, dict): + outputType = o['type'] + outputArgs = o + + if outputType in ['varAssign', 'varAppend', 'varRemove', 'publicQtConfig', 'privateConfig', 'publicConfig']: + continue + + elif outputType in ['feature', 'publicFeature', 'define']: + publicInfo = True + elif outputType == 'privateFeature': + privateInfo = True + elif outputType == 'internalFeature': + internalFeature = True + else: + print(' XXXX UNHANDLED OUTPUT TYPE {} in feature {}.'.format(outputType, feature)) + continue + + if not publicInfo and not privateInfo and not internalFeature: + print(' **** Skipping feature {}: Not relevant for C++.'.format(feature)) + return + + # write feature: + cxxFeature = featureName(feature) + if comment: + cm_fh.write('# {}\n'.format(comment)) + + cm_fh.write('qt_feature("{}"'.format(cxxFeature)) + if publicInfo: + cm_fh.write(' PUBLIC') + if privateInfo: + cm_fh.write(' PRIVATE') + cm_fh.write('\n') + + cm_fh.write(lineify('SECTION', section)) + cm_fh.write(lineify('LABEL', label)) + if purpose != label: + cm_fh.write(lineify('PURPOSE', purpose)) + cm_fh.write(lineify('AUTODETECT', autoDetect, quote=False)) + cm_fh.write(lineify('CONDITION', condition, quote=False)) + cm_fh.write(lineify('ENABLE', enable, quote=False)) + cm_fh.write(lineify('DISABLE', disable, quote=False)) + cm_fh.write(lineify('EMIT_IF', emitIf, quote=False)) + cm_fh.write(')\n') + + for o in output: + outputType = o + outputArgs = {} + if isinstance(o, dict): + outputType = o['type'] + outputArgs = o + + # Map feature to define: + if outputType == 'feature': + outputType = 'define' + outputArgs = {'name': 'QT_NO_{}'.format(cxxFeature.upper()), + 'negative': True, + 'value': 1, + 'type': 'define'} + + if outputType != 'define': + continue + + if outputArgs.get('name') is None: + print(' XXXX DEFINE output without name in feature {}.'.format(feature)) + continue + + cm_fh.write('qt_feature_definition("{}" "{}"'.format(cxxFeature, outputArgs.get('name'))) + if outputArgs.get('negative', False): + cm_fh.write(' NEGATE') + if outputArgs.get('value') is not None: + cm_fh.write(' VALUE "{}"'.format(outputArgs.get('value'))) + cm_fh.write(')\n') + + +def processInputs(ctx, data, cm_fh): + print(' inputs:') + if 'commandline' not in data: + return + + commandLine = data['commandline'] + if "options" not in commandLine: + return + + for input in commandLine['options']: + parseInput(ctx, input, commandLine['options'][input], cm_fh) + + +def processTests(ctx, data, cm_fh): + print(' tests:') + if 'tests' not in data: + return + + for test in data['tests']: + parseTest(ctx, test, data['tests'][test], cm_fh) + + +def processFeatures(ctx, data, cm_fh): + print(' features:') + if 'features' not in data: + return + + for feature in data['features']: + parseFeature(ctx, feature, data['features'][feature], cm_fh) + + +def processLibraries(ctx, data, cm_fh): + cmake_find_packages_set = set() + print(' libraries:') + if 'libraries' not in data: + return + + for lib in data['libraries']: + parseLib(ctx, lib, data['libraries'][lib], cm_fh, cmake_find_packages_set) + + +def processSubconfigs(dir, ctx, data): + assert ctx is not None + if 'subconfigs' in data: + for subconf in data['subconfigs']: + subconfDir = os.path.join(dir, subconf) + subconfData = readJsonFromDir(subconfDir) + subconfCtx = ctx + processJson(subconfDir, subconfCtx, subconfData) + + +def processJson(dir, ctx, data): + ctx['module'] = data.get('module', 'global') + + ctx = processFiles(ctx, data) + + with open(os.path.join(dir, "configure.cmake"), 'w') as cm_fh: + cm_fh.write("\n\n#### Inputs\n\n") + + processInputs(ctx, data, cm_fh) + + cm_fh.write("\n\n#### Libraries\n\n") + + processLibraries(ctx, data, cm_fh) + + cm_fh.write("\n\n#### Tests\n\n") + + processTests(ctx, data, cm_fh) + + cm_fh.write("\n\n#### Features\n\n") + + processFeatures(ctx, data, cm_fh) + + if ctx.get('module') == 'global': + cm_fh.write('\nqt_extra_definition("QT_VERSION_STR" "\\\"${PROJECT_VERSION}\\\"" PUBLIC)\n') + cm_fh.write('qt_extra_definition("QT_VERSION_MAJOR" ${PROJECT_VERSION_MAJOR} PUBLIC)\n') + cm_fh.write('qt_extra_definition("QT_VERSION_MINOR" ${PROJECT_VERSION_MINOR} PUBLIC)\n') + cm_fh.write('qt_extra_definition("QT_VERSION_PATCH" ${PROJECT_VERSION_PATCH} PUBLIC)\n') + + if ctx.get('module') == 'gui': + cm_fh.write('\nqt_extra_definition("QT_QPA_DEFAULT_PLATFORM" "${QT_QPA_DEFAULT_PLATFORM}" PUBLIC)\n') + + # do this late: + processSubconfigs(dir, ctx, data) + + +def main(): + if len(sys.argv) != 2: + print("This scripts needs one directory to process!") + quit(1) + + dir = sys.argv[1] + + print("Processing: {}.".format(dir)) + + data = readJsonFromDir(dir) + processJson(dir, {}, data) + + +if __name__ == '__main__': + main() diff --git a/util/cmake/generate_module_map.sh b/util/cmake/generate_module_map.sh new file mode 100755 index 0000000000..1ca0bfc43c --- /dev/null +++ b/util/cmake/generate_module_map.sh @@ -0,0 +1,38 @@ +#!/usr/bin/bash +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +pro_files=$(find . -name \*.pro) + +for f in ${pro_files}; do + if grep "^load(qt_module)" "${f}" > /dev/null ; then + target=$(grep "TARGET" "${f}" | cut -d'=' -f2 | sed -e "s/\s*//g") + module=$(basename ${f}) + echo "'${module%.pro}': '${target}'," + fi +done diff --git a/util/cmake/helper.py b/util/cmake/helper.py new file mode 100644 index 0000000000..2ddeee98c1 --- /dev/null +++ b/util/cmake/helper.py @@ -0,0 +1,206 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import re + + +def featureName(input: str) -> str: + return re.sub(r'[^a-zA-Z0-9_]', '_', input) + + +def map_qt_base_library(lib: str) -> str: + library_map = { + 'global': 'Qt::Core', # manually added special case + 'accessibility_support': 'Qt::AccessibilitySupport', + 'androidextras': 'Qt::AndroidExtras', + 'animation': 'Qt::3DAnimation', + 'application-lib': 'Qt::AppManApplication', + 'bluetooth': 'Qt::Bluetooth', + 'bootstrap-dbus': 'Qt::BootstrapDBus', + 'bootstrap': 'Qt::Bootstrap', + 'client': 'Qt::WaylandClient', + 'clipboard_support': 'Qt::ClipboardSupport', + 'common-lib': 'Qt::AppManCommon', + 'compositor': 'Qt::WaylandCompositor', + 'concurrent': 'Qt::Concurrent', + 'container': 'Qt::AxContainer', + 'control': 'Qt::AxServer', + 'core_headers': 'Qt::WebEngineCore', + 'core': 'Qt::Core', + 'coretest': 'Qt::3DCoreTest', + 'crypto-lib': 'Qt::AppManCrypto', + 'dbus': 'Qt::DBus', + 'devicediscovery': 'Qt::DeviceDiscoverySupport', + 'edid': 'Qt::EdidSupport', + 'eglconvenience': 'Qt::EglSupport', + 'eglfsdeviceintegration': 'Qt::EglFSDeviceIntegration', + 'eglfs_kms_support': 'Qt::EglFsKmsSupport', + 'enginio_client': 'Enginio', + 'eventdispatchers': 'Qt::EventDispatcherSupport', + 'extras': 'Qt::3DExtras', + 'fbconvenience': 'Qt::FbSupport', + 'fontdatabase_support': 'Qt::FontDatabaseSupport', + 'gamepad': 'Qt::Gamepad', + 'glxconvenience': 'Qt::GlxSupport', + 'graphics_support': 'Qt::GraphicsSupport', + 'gsttools': 'Qt::MultimediaGstTools', + 'gui': 'Qt::Gui', + 'help': 'Qt::Help', + 'hunspellinputmethod': 'Qt::HunspellInputMethod', + 'input': 'Qt::InputSupport', + 'installer-lib': 'Qt::AppManInstaller', + 'kmsconvenience': 'Qt::KmsSupport', + 'launcher-lib': 'Qt::AppManLauncher', + 'lib': 'Qt::Designer', + 'linuxaccessibility': 'Qt::LinuxAccessibilitySupport', + 'location': 'Qt::Location', + 'logic': 'Qt::3DLogic', + 'macextras': 'Qt::MacExtras', + 'main-lib': 'Qt::AppManMain', + 'manager-lib': 'Qt::AppManManager', + 'monitor-lib': 'Qt::AppManMonitor', + 'multimedia': 'Qt::Multimedia', + 'multimediawidgets': 'Qt::MultimediaWidgets', + 'network': 'Qt::Network', + 'nfc': 'Qt::Nfc', + 'oauth': 'Qt::NetworkAuth', + 'openglextensions': 'Qt::OpenGLExtensions', + 'opengl': 'Qt::OpenGL', + 'package-lib': 'Qt::AppManPackage', + 'packetprotocol': 'Qt::PacketProtocol', + 'particles': 'Qt::QuickParticles', + 'platformcompositor': 'Qt::PlatformCompositorSupport', + 'plugin-interfaces': 'Qt::AppManPluginInterfaces', + 'positioning': 'Qt::Positioning', + 'positioningquick': 'Qt::PositioningQuick', + 'printsupport': 'Qt::PrintSupport', + 'purchasing': 'Qt::Purchasing', + 'qmldebug': 'Qt::QmlDebug', + 'qmldevtools': 'Qt::QmlDevTools', + 'qml': 'Qt::Qml', + 'qmltest': 'Qt::QuickTest', + 'qtmultimediaquicktools': 'Qt::MultimediaQuick', + 'qtzlib': 'Qt::Zlib', + 'quick3danimation': 'Qt::3DQuickAnimation', + 'quick3dextras': 'Qt::3DQuickExtras', + 'quick3dinput': 'Qt::3DQuickInput', + 'quick3d': 'Qt::3DQuick', + 'quick3drender': 'Qt::3DQuickRender', + 'quick3dscene2d': 'Qt::3DQuickScene2D', + 'quickcontrols2': 'Qt::QuickControls2', + 'quick': 'Qt::Quick', + 'quickshapes': 'Qt::QuickShapes', + 'quicktemplates2': 'Qt::QuickTemplates2', + 'quickwidgets': 'Qt::QuickWidgets', + 'render': 'Qt::3DRender', + 'script': 'Qt::Script', + 'scripttools': 'Qt::ScriptTools', + 'sensors': 'Qt::Sensors', + 'serialport': 'Qt::SerialPort', + 'services': 'Qt::ServiceSupport', + 'sql': 'Qt::Sql', + 'svg': 'Qt::Svg', + 'testlib': 'Qt::Test', + 'theme_support': 'Qt::ThemeSupport', + 'service_support': 'Qt::ServiceSupport', + 'eventdispatcher_support': 'Qt::EventDispatcherSupport', + 'edid_support': 'Qt::EdidSupport', + 'tts': 'Qt::TextToSpeech', + 'uiplugin': 'Qt::UiPlugin', + 'uitools': 'Qt::UiTools', + 'virtualkeyboard': 'Qt::VirtualKeyboard', + 'vkconvenience': 'Qt::VulkanSupport', + 'webchannel': 'Qt::WebChannel', + 'webengine': 'Qt::WebEngine', + 'webenginewidgets': 'Qt::WebEngineWidgets', + 'websockets': 'Qt::WebSockets', + 'webview': 'Qt::WebView', + 'widgets': 'Qt::Widgets', + 'window-lib': 'Qt::AppManWindow', + 'windowsuiautomation': 'Qt::WindowsUIAutomationSupport', + 'winextras': 'Qt::WinExtras', + 'x11extras': 'Qt::X11Extras', + 'xcb_qpa_lib': 'Qt::XcbQpa', + 'xmlpatterns': 'Qt::XmlPatterns', + 'xml': 'Qt::Xml', + } + return library_map.get(lib, lib) + + +def map_qt_library(lib: str) -> str: + private = False + if lib.endswith('-private'): + private = True + lib = lib[:-8] + mapped = map_qt_base_library(lib) + if private: + mapped += 'Private' + return mapped + + +platform_mapping = { + 'win32': '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', + 'osx': 'APPLE_OSX', + 'freebsd': 'FREEBSD', + 'haiku': 'HAIKU', + 'netbsd': 'NETBSD', + 'mac': 'APPLE_OSX', +} + + +def substitute_platform(platform: str) -> str: + """ Return the qmake platform as cmake platform or the unchanged string. """ + return platform_mapping.get(platform, platform) + + +libray_mapping = { + 'zlib': 'ZLIB::ZLIB', + 'glib': 'PkgConfig::GLib', +} + + +def substitute_libs(lib: str) -> str: + return libray_mapping.get(lib, lib) diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py new file mode 100755 index 0000000000..37364d63ad --- /dev/null +++ b/util/cmake/pro2cmake.py @@ -0,0 +1,736 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from argparse import ArgumentParser +import os.path +import re +import sys +import io +from typing import IO, List, Dict, Union +import typing + +import pyparsing as pp + +from helper import map_qt_library, featureName, substitute_platform, substitute_libs + + +def _parse_commandline(): + parser = ArgumentParser(description='Generate CMakeLists.txt files from .pro files.') + 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('files', metavar='<.pro/.pri file>', type=str, nargs='+', + help='The .pro/.pri file to process') + + return parser.parse_args() + + +def spaces(indent: int) -> str: + return ' ' * indent + + +def map_to_file(f: str, top_dir: str, current_dir: str, + want_absolute_path: bool = False) -> typing.Optional[str]: + if f == '$$NO_PCH_SOURCES': + return None + if f.startswith('$$PWD/') or f == '$$PWD': # INCLUDEPATH += $$PWD + return os.path.join(os.path.relpath(current_dir, top_dir), f[6:]) + if f.startswith('$$OUT_PWD/'): + return "${CMAKE_CURRENT_BUILD_DIR}/" + f[10:] + if f.startswith('$$QT_SOURCE_TREE'): + return "${PROJECT_SOURCE_DIR}/" + f[17:] + if f.startswith("./"): + return os.path.join(current_dir, f) + if want_absolute_path and not os.path.isabs(f): + return os.path.join(current_dir, f) + return f + + +def map_source_to_cmake(source: str) -> typing.Optional[str]: + if not source or source == '$$NO_PCH_SOURCES': + return None + if source.startswith('$$PWD/'): + return source[6:] + if source == '.': + return "${CMAKE_CURRENT_SOURCE_DIR}" + if source.startswith('$$QT_SOURCE_TREE/'): + return "${PROJECT_SOURCE_DIR}/" + source[17:] + return source + + +def map_source_to_fs(base_dir: str, file: str, source: str) -> typing.Optional[str]: + if source is None or source == '$$NO_PCH_SOURCES': + return None + if source.startswith('$$PWD/'): + return os.path.join(os.path.dirname(file), source[6:]) + if source.startswith('$$QT_SOURCE_TREE/'): + return os.path.join('.', source[17:]) + if source.startswith('${PROJECT_SOURCE_DIR}/'): + return os.path.join('.', source[22:]) + if source.startswith('${CMAKE_CURRENT_SOURCE_DIR}/'): + return os.path.join(base_dir, source[28:]) + return os.path.join(base_dir, source) + + +class Scope: + def __init__(self, file: typing.Optional[str]=None, condition: str='', base_dir: str='') -> None: + self._parent = None # type: Scope + self._basedir = base_dir + if file: + self._currentdir = os.path.dirname(file) + if not self._currentdir: + self._currentdir = '.' + if not self._basedir: + self._basedir = self._currentdir + + self._file = file + self._condition = map_condition(condition) + self._children = [] # type: List[Scope] + self._values = {} # type: Dict[str, List[str]] + + def merge(self, other: 'Scope') -> None: + for c in other._children: + self.add_child(c) + other.set_basedir(self._basedir) + + for k in self._values.keys(): + self.append_value(k, other.get(k, [])) + + for k in other._values.keys(): + if k not in self._values: + self.set_value(k, other.get(k)) + + def set_basedir(self, dir: str) -> None: + self._basedir = dir + for c in self._children: + c.set_basedir(dir) + + def basedir(self) -> str: + return self._basedir + + def currentdir(self) -> str: + return self._currentdir + + @staticmethod + def FromDict(file: str, statements, cond: str = '', base_dir: str = ''): + scope = Scope(file, cond, base_dir) + for statement in statements: + if isinstance(statement, list): # Handle skipped parts... + assert not statement + continue + + operation = statement.get('operation', None) + if operation: + key = statement.get('key', '') + value = statement.get('value', []) + assert key != '' + + if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH') or key.endswith('_HEADERS') or key.endswith('_SOURCES'): + value = [map_to_file(v, scope.basedir(), scope.currentdir()) for v in value] + + if operation == '=': + scope.set_value(key, value) + elif operation == '-=': + scope.substract_value(key, value) + elif operation == '+=' or operation == '*=': + scope.append_value(key, value) + else: + print('Unexpected operation "{}" in scope with condition {}.'.format(operation, cond)) + assert(False) + + continue + + condition = statement.get('condition', None) + if condition: + child = Scope.FromDict(file, statement.get('statements'), condition, scope.basedir()) + scope.add_child(child) + + else_statements = statement.get('else_statements') + if else_statements: + child = Scope.FromDict(file, else_statements, 'NOT ' + condition, scope.basedir()) + scope.add_child(child) + continue + + loaded = statement.get('loaded', None) + if loaded: + scope.append_value('_LOADED', loaded) + continue + + option = statement.get('option', None) + if option: + scope.append_value('_OPTION', option) + continue + + included = statement.get('included', None) + if included: + scope.append_value('_INCLUDED', + map_to_file(included, scope.basedir(), scope.currentdir())) + continue + + return scope + + def file(self) -> str: + return self._file or '' + + def cMakeListsFile(self) -> str: + return os.path.join(self.basedir(), 'CMakeLists.txt') + + def condition(self) -> str: + return self._condition + + def _push_down_TEMPLATE(self, template: str) -> None: + if not self._rawTemplate(): + self.set_value('TEMPLATE', [template, ]) + for c in self._children: + c._push_down_TEMPLATE(template) + + def add_child(self, scope: 'Scope') -> None: + scope._parent = self + if not scope._rawTemplate(): + scope._push_down_TEMPLATE(self.getTemplate()) + self._children.append(scope) + + def set_value(self, key: str, value: List[str]) -> None: + self._values[key] = value + + def append_value(self, key: str, value: Union[str, List[str]]) -> None: + array = self._values.get(key, []) + if isinstance(value, str): + array.append(value) + elif isinstance(value, list): + array += value + else: + assert False + self._values[key] = array + + def substract_value(self, key: str, value: Union[str, List[str]]) -> None: + if isinstance(value, str): + to_remove = [value, ] + if isinstance(value, list): + to_remove = value + + self.append_value(key, ['-{}'.format(v) for v in to_remove]) + + def children(self) -> List['Scope']: + return self._children + + def dump(self, *, indent: int = 0) -> None: + ind = ' ' * indent + if self._condition == '': + print('{}Scope {} in {}.'.format(ind, self._file, self._basedir)) + else: + print('{}Scope {} in {} with condition: {}.'.format(ind, self._file, self._basedir, self._condition)) + print('{}Keys:'.format(ind)) + for k in sorted(self._values.keys()): + print('{} {} = "{}"'.format(ind, k, self._values[k])) + print('{}Children:'.format(ind)) + for c in self._children: + c.dump(indent=indent + 1) + + def get(self, key: str, default=None) -> List[str]: + default = default or [] + return self._values.get(key, default) + + def getString(self, key: str, default: str = '') -> str: + v = self.get(key) + if isinstance(v, list): + if len(v) == 0: + return default + assert len(v) == 1 + return v[0] + elif isinstance(v, str): + return v + else: + assert False + return default + + def getTemplate(self) -> str: + return self.getString('TEMPLATE', 'app') + + def _rawTemplate(self) -> str: + return self.getString('TEMPLATE') + + def getTarget(self) -> str: + return self.getString('TARGET') or os.path.splitext(os.path.basename(self.file()))[0] + + +class QmakeParser: + def __init__(self, *, debug: bool = False) -> None: + self._Grammar = self._generate_grammar(debug) + + def _generate_grammar(self, debug: bool): + # Define grammar: + pp.ParserElement.setDefaultWhitespaceChars(' \t') + + LC = pp.Suppress(pp.Literal('\\') + pp.LineEnd()) + EOL = pp.Suppress(pp.Optional(pp.pythonStyleComment()) + pp.LineEnd()) + + Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_./') + 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(']')) + ))) + # Do not match word ending in '\' since that breaks line continuation:-/ + LiteralValuePart = pp.Word(pp.printables, excludeChars='$#{}()') + SubstitutionValue = pp.Combine(pp.OneOrMore(Substitution | LiteralValuePart | pp.Literal('$'))) + Value = (pp.QuotedString(quoteChar='"', escChar='\\') | SubstitutionValue) + + Values = pp.ZeroOrMore(Value)('value') + + Op = pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') | pp.Literal('*=') + + Operation = Identifier('key') + Op('operation') + Values('value') + Load = pp.Keyword('load') + pp.Suppress('(') + Identifier('loaded') + pp.Suppress(')') + Include = pp.Keyword('include') + pp.Suppress('(') + pp.CharsNotIn(':{=}#)\n')('included') + pp.Suppress(')') + Option = pp.Keyword('option') + pp.Suppress('(') + Identifier('option') + pp.Suppress(')') + DefineTest = pp.Suppress(pp.Keyword('defineTest') + pp.Suppress('(') + Identifier + pp.Suppress(')') + + pp.nestedExpr(opener='{', closer='}') + pp.LineEnd()) # ignore the whole thing... + FunctionCall = pp.Suppress(Identifier + pp.nestedExpr()) + + Scope = pp.Forward() + + Statement = pp.Group(Load | Include | Option | DefineTest | FunctionCall | Operation) + StatementLine = Statement + EOL + StatementGroup = pp.ZeroOrMore(Scope | EOL | StatementLine) + + Block = pp.Suppress('{') + pp.Optional(EOL) \ + + pp.ZeroOrMore(EOL | Statement + EOL | Scope) \ + + pp.Optional(Statement) + pp.Optional(EOL) \ + + pp.Suppress('}') + pp.Optional(EOL) + + Condition = pp.Optional(pp.White()) + pp.CharsNotIn(':{=}#\\\n') + Condition.setParseAction(lambda x: ' '.join(x).strip()) + + SingleLineScope = pp.Suppress(pp.Literal(':')) + pp.Group(Scope | Block | StatementLine)('statements') + MultiLineScope = Block('statements') + + SingleLineElse = pp.Suppress(pp.Literal(':')) + pp.Group(Scope | StatementLine)('else_statements') + MultiLineElse = pp.Group(Block)('else_statements') + Else = pp.Suppress(pp.Keyword('else')) + (SingleLineElse | MultiLineElse) + Scope <<= pp.Group(Condition('condition') + (SingleLineScope | MultiLineScope) + pp.Optional(Else)) + + if debug: + for ename in "EOL Identifier Substitution SubstitutionValue LiteralValuePart Value Values SingleLineScope MultiLineScope Scope SingleLineElse MultiLineElse Else Condition Block StatementGroup Statement Load Include Option DefineTest FunctionCall Operation".split(): + expr = locals()[ename] + expr.setName(ename) + expr.setDebug() + + Grammar = StatementGroup('statements') + Grammar.ignore(LC) + + return Grammar + + def parseFile(self, file: str): + print('Parsing \"{}\"...'.format(file)) + try: + result = self._Grammar.parseFile(file, parseAll=True) + except pp.ParseException as pe: + print(pe.line) + print(' '*(pe.col-1) + '^') + print(pe) + raise pe + return result + + +def parseProFile(file: str, *, debug=False): + parser = QmakeParser(debug=debug) + return parser.parseFile(file) + + +def map_condition(condition: str) -> str: + condition = condition.replace('!', 'NOT ') + condition = condition.replace('&&', ' AND ') + condition = condition.replace('|', ' OR ') + condition = condition.replace('==', ' STREQUAL ') + + 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) + if feature: + part = 'QT_FEATURE_' + featureName(feature.group(2)) + else: + part = substitute_platform(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: IO[str], *, indent: int = 0) -> None: + assert scope.getTemplate() == 'subdirs' + ind = ' ' * indent + for sd in scope.get('SUBDIRS', []): + full_sd = os.path.join(scope.basedir(), sd) + if os.path.isdir(full_sd): + cm_fh.write('{}add_subdirectory({})\n'.format(ind, sd)) + elif os.path.isfile(full_sd): + subdir_result = parseProFile(full_sd, debug=False) + subdir_scope = Scope.FromDict(full_sd, subdir_result.asDict().get('statements'), + '', scope.basedir()) + + cmakeify_scope(subdir_scope, cm_fh, indent=indent + 1) + elif sd.startswith('-'): + cm_fh.write('{}### remove_subdirectory("{}")\n'.format(ind, sd[1:])) + else: + print(' XXXX: SUBDIR {} in {}: Not found.'.format(sd, scope.file())) + + for c in scope.children(): + cond = c.condition() + if cond == 'else': + cm_fh.write('\n{}else()\n'.format(ind)) + elif cond: + cm_fh.write('\n{}if({})\n'.format(ind, cond)) + + handle_subdir(c, cm_fh, indent=indent + 1) + + if cond: + cm_fh.write('{}endif()\n'.format(ind)) + + +def sort_sources(sources) -> 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'): + base = base[:-2] + sort_name = os.path.join(dir, base) + + array = to_sort.get(sort_name, []) + array.append(s) + + to_sort[sort_name] = array + + lines = [] + for k in sorted(to_sort.keys()): + lines.append(' '.join(sorted(to_sort[k]))) + + return lines + + +def write_header(cm_fh: 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_scope_header(cm_fh: IO[str], *, indent: int=0): + cm_fh.write('\n{}## Scopes:\n'.format(spaces(indent))) + cm_fh.write('{}#####################################################################\n'.format(spaces(indent))) + + +def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0, + known_libraries=set()) -> None: + ind = spaces(indent) + + plugin_type = scope.get('PLUGIN_TYPE') + if plugin_type: + cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0])) + + sources = scope.get('SOURCES') + scope.get('HEADERS') + scope.get('OBJECTIVE_SOURCES') + scope.get('NO_PCH_SOURCES') + scope.get('FORMS') + resources = scope.get('RESOURCES') + if resources: + qrc_only = True + for r in resources: + if not r.endswith('.qrc'): + qrc_only = False + break + + if not qrc_only: + print(' XXXX Ignoring non-QRC file resources.') + else: + sources += resources + + sources = [map_source_to_cmake(s) for s in sources] + if sources: + cm_fh.write('{} SOURCES\n'.format(ind)) + for l in sort_sources(sources): + cm_fh.write('{} {}\n'.format(ind, l)) + + if scope.get('DEFINES'): + cm_fh.write('{} DEFINES\n'.format(ind)) + for d in scope.get('DEFINES'): + d = d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"') + cm_fh.write('{} {}\n'.format(ind, d)) + if scope.get('INCLUDEPATH'): + cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind)) + for i in scope.get('INCLUDEPATH'): + cm_fh.write('{} {}\n'.format(ind, i)) + + dependencies = [map_qt_library(q) for q in scope.get('QT') if map_qt_library(q) not in known_libraries] + dependencies += [map_qt_library(q) for q in scope.get('QT_FOR_PRIVATE') if map_qt_library(q) not in known_libraries] + dependencies += scope.get('QMAKE_USE_PRIVATE') + scope.get('LIBS_PRIVATE') + scope.get('LIBS') + if dependencies: + cm_fh.write('{} LIBRARIES\n'.format(ind)) + is_framework = False + for d in dependencies: + if d == '-framework': + is_framework = True + continue + if is_framework: + d = '${FW%s}' % d + if d.startswith('-l'): + d = d[2:] + d = substitute_libs(d) + cm_fh.write('{} {}\n'.format(ind, d)) + is_framework = False + + +def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, parent_condition: str='', + previous_conditon: str='', *, indent: int=0) -> str: + total_condition = scope.condition() + if total_condition == 'else': + assert previous_conditon, "Else branch without previous condition in: %s" % scope.file() + total_condition = 'NOT ({})'.format(previous_conditon) + if parent_condition: + total_condition = '({}) AND ({})'.format(parent_condition, total_condition) + + 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(spaces(indent), target, total_condition, extend_qt_string) + + if not extend_qt_string: + # Comment out the generated extend_target call because there no sources were found, but keep it commented + # for informational purposes. + extend_scope = ''.join(['#' + line for line in extend_scope.splitlines(keepends=True)]) + cm_fh.write(extend_scope) + + children = scope.children() + if children: + prev_condition = '' + for c in children: + prev_condition = write_extend_target(cm_fh, target, c, total_condition, prev_condition) + + return total_condition + + +def write_main_part(cm_fh: IO[str], name: str, typename: str, + cmake_function: str, scope: Scope, *, + extra_lines: typing.List[str] = [], + indent: int=0, + **kwargs: typing.Any): + write_header(cm_fh, name, typename, indent=indent) + + cm_fh.write('{}{}({}\n'.format(spaces(indent), cmake_function, name)) + for extra_line in extra_lines: + cm_fh.write('{} {}\n'.format(spaces(indent), extra_line)) + + write_sources_section(cm_fh, scope, indent=indent, **kwargs) + + # Footer: + cm_fh.write('{})\n'.format(spaces(indent))) + + # Scopes: + if not scope.children(): + return + + write_scope_header(cm_fh, indent=indent) + + for c in scope.children(): + write_extend_target(cm_fh, name, c, '', indent=indent) + + + +def write_module(cm_fh: IO[str], scope: Scope, *, indent: int=0) -> None: + module_name = scope.getTarget() + assert module_name.startswith('Qt') + + extra = [] + if 'static' in scope.get('CONFIG'): + extra.append('STATIC') + if 'no_module_headers' in scope.get('CONFIG'): + extra.append('NO_MODULE_HEADERS') + + write_main_part(cm_fh, module_name[2:], 'Module', 'add_qt_module', scope, + extra_lines=extra, indent=indent, known_libraries={'Qt::Core', }) + + + if 'qt_tracepoints' in scope.get('CONFIG'): + tracepoints = map_to_file(scope.getString('TRACEPOINT_PROVIDER'), scope.basedir(), scope.currentdir()) + cm_fh.write('\n\n{}qt_create_tracepoints({} {})\n'.format(spaces(indent), module_name[2:], tracepoints)) + + +def write_tool(cm_fh: IO[str], scope: Scope, *, indent: int=0) -> None: + tool_name = scope.getTarget() + + write_main_part(cm_fh, tool_name, 'Tool', 'add_qt_tool', scope, + indent=indent, known_libraries={'Qt::Core', }) + + +def write_test(cm_fh: IO[str], scope: Scope, *, indent: int=0) -> None: + test_name = scope.getTarget() + assert test_name + + write_main_part(cm_fh, test_name, 'Test', 'add_qt_test', scope, + indent=indent, known_libraries={'Qt::Core', 'Qt::Test', }) + + +def write_binary(cm_fh: IO[str], scope: Scope, gui: bool=False, *, indent: int=0) -> None: + binary_name = scope.getTarget() + assert binary_name + + extra = ['GUI',] if gui else [] + write_main_part(cm_fh, binary_name, 'Binary', 'add_qt_executable', scope, + extra_lines=extra, indent=indent, known_libraries={'Qt::Core', }) + + +def write_plugin(cm_fh, scope, *, indent: int=0): + plugin_name = scope.getTarget() + assert plugin_name + + write_main_part(cm_fh, plugin_name, 'Plugin', 'add_qt_plugin', scope, + indent=indent, known_libraries={'QtCore', }) + + +def handle_app_or_lib(scope: Scope, cm_fh: IO[str], *, indent=0) -> None: + assert scope.getTemplate() in ('app', 'lib', None) + + is_lib = scope.getTemplate() == 'lib' + is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED', [])) + + if is_lib or 'qt_module' in scope.get('_LOADED', []): + write_module(cm_fh, scope, indent=indent) + elif is_plugin: + write_plugin(cm_fh, scope, indent=indent) + elif 'qt_tool' in scope.get('_LOADED', []): + write_tool(cm_fh, scope, indent=indent) + else: + if 'testcase' in scope.get('CONFIG') or 'testlib' in scope.get('CONFIG'): + write_test(cm_fh, scope, indent=indent) + else: + gui = 'console' not in scope.get('CONFIG') + write_binary(cm_fh, scope, gui, indent=indent) + + docs = scope.getString("QMAKE_DOCS") + if docs: + cm_fh.write("\n{}add_qt_docs({})\n".format(spaces(indent), map_to_file(docs, scope.basedir(), scope.currentdir()))) + + +def handle_qt_for_config(scope: Scope, cm_fh: IO[str], *, indent: int=0) -> None: + for config in scope.get("QT_FOR_CONFIG") or []: + lib = map_qt_library(config) + if lib.endswith("Private"): + cm_fh.write('{}qt_pull_features_into_current_scope(PRIVATE_FEATURES {})\n'.format(spaces(indent), lib[:-len("Private")])) + else: + cm_fh.write('{}qt_pull_features_into_current_scope(PUBLIC_FEATURES {})\n'.format(spaces(indent), lib)) + + +def cmakeify_scope(scope: Scope, cm_fh: IO[str], *, indent: int=0) -> None: + template = scope.getTemplate() + handle_qt_for_config(scope, cm_fh) + if template == 'subdirs': + handle_subdir(scope, cm_fh, indent=indent) + elif template in ('app', 'lib', None): + handle_app_or_lib(scope, cm_fh, indent=indent) + else: + print(' XXXX: {}: Template type {} not yet supported.' + .format(scope.file(), template)) + + +def generate_cmakelists(scope: Scope) -> None: + with open(scope.cMakeListsFile(), 'w') as cm_fh: + assert scope.file() + cm_fh.write('# Generated from {}.\n\n'.format(os.path.basename(scope.file()))) + cmakeify_scope(scope, cm_fh) + + +def do_include(scope: Scope, *, debug: bool=False) -> None: + for i in scope.get('_INCLUDED', []): + dir = scope.basedir() + include_file = map_to_file(i, dir, scope.currentdir(), want_absolute_path=True) + if not os.path.isfile(include_file): + print(' XXXX: Failed to include {}.'.format(include_file)) + continue + + include_result = parseProFile(include_file, debug=debug) + include_scope = Scope.FromDict(include_file, include_result.asDict().get('statements'), + '', dir) + + do_include(include_scope) + + scope.merge(include_scope) + + for c in scope.children(): + do_include(c) + + +def main() -> None: + args = _parse_commandline() + + for file in args.files: + parseresult = parseProFile(file, debug=args.debug_parser or args.debug) + + if args.debug_parse_result or args.debug: + print('\n\n#### Parser result:') + print(parseresult) + print('\n#### End of parser result.\n') + if args.debug_parse_dictionary or args.debug: + print('\n\n####Parser result dictionary:') + print(parseresult.asDict()) + print('\n#### End of parser result dictionary.\n') + + file_scope = Scope.FromDict(file, parseresult.asDict().get('statements')) + + if args.debug_pro_structure or args.debug: + print('\n\n#### .pro/.pri file structure:') + print(file_scope.dump()) + print('\n#### End of .pro/.pri file structure.\n') + + do_include(file_scope) + + if args.debug_full_pro_structure or args.debug: + print('\n\n#### Full .pro/.pri file structure:') + print(file_scope.dump()) + print('\n#### End of full .pro/.pri file structure.\n') + + generate_cmakelists(file_scope) + + +if __name__ == '__main__': + main() diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py new file mode 100755 index 0000000000..4340eab094 --- /dev/null +++ b/util/cmake/run_pro2cmake.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import glob +import os +import subprocess +import sys + +script_path = os.path.dirname(os.path.abspath(__file__)) +base_path = os.path.dirname(script_path) +pro2cmake = script_path + '/pro2cmake.py' + +if len(sys.argv) > 1: + base_path = os.path.abspath(sys.argv[1]) + +for filename in glob.iglob(base_path + '/**/*.pro', recursive=True): + print('Converting:', filename) + subprocess.run([pro2cmake, filename]) diff --git a/util/cmake/tests/__init__.py b/util/cmake/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/util/cmake/tests/data/complex_values.pro b/util/cmake/tests/data/complex_values.pro new file mode 100644 index 0000000000..4d747c1dd7 --- /dev/null +++ b/util/cmake/tests/data/complex_values.pro @@ -0,0 +1,22 @@ +linux:!static { + precompile_header { + # we'll get an error if we just use SOURCES += + no_pch_assembler.commands = $$QMAKE_CC -c $(CFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} + no_pch_assembler.dependency_type = TYPE_C + no_pch_assembler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} + no_pch_assembler.input = NO_PCH_ASM + no_pch_assembler.name = compiling[no_pch] ${QMAKE_FILE_IN} + silent: no_pch_assembler.commands = @echo compiling[no_pch] ${QMAKE_FILE_IN} && $$no_pch_assembler.commands + CMAKE_ANGLE_GLES2_IMPLIB_RELEASE = libGLESv2.$${QMAKE_EXTENSION_STATICLIB} + HOST_BINS = $$[QT_HOST_BINS] + CMAKE_HOST_DATA_DIR = $$[QT_HOST_DATA/src]/ + TR_EXCLUDE += ../3rdparty/* + + QMAKE_EXTRA_COMPILERS += no_pch_assembler + NO_PCH_ASM += global/minimum-linux.S + } else { + SOURCES += global/minimum-linux.S + } + HEADERS += global/minimum-linux_p.h +} + diff --git a/util/cmake/tests/data/definetest.pro b/util/cmake/tests/data/definetest.pro new file mode 100644 index 0000000000..76b63d239f --- /dev/null +++ b/util/cmake/tests/data/definetest.pro @@ -0,0 +1,6 @@ +defineTest(pathIsAbsolute) { + p = $$clean_path($$1) + !isEmpty(p):isEqual(p, $$absolute_path($$p)): return(true) + return(false) +} + diff --git a/util/cmake/tests/data/else.pro b/util/cmake/tests/data/else.pro new file mode 100644 index 0000000000..bbf9c5ac9f --- /dev/null +++ b/util/cmake/tests/data/else.pro @@ -0,0 +1,6 @@ + +linux { + SOURCES += a.cpp +} else { + SOURCES += b.cpp +} diff --git a/util/cmake/tests/data/else2.pro b/util/cmake/tests/data/else2.pro new file mode 100644 index 0000000000..f2ef36ec28 --- /dev/null +++ b/util/cmake/tests/data/else2.pro @@ -0,0 +1,4 @@ + +osx: A = 1 +else: win32: B = 2 +else: C = 3 diff --git a/util/cmake/tests/data/else3.pro b/util/cmake/tests/data/else3.pro new file mode 100644 index 0000000000..0de9c2c1d9 --- /dev/null +++ b/util/cmake/tests/data/else3.pro @@ -0,0 +1,7 @@ +qtConfig(timezone) { + A = 1 +} else:win32 { + B = 2 +} else { + C = 3 +} diff --git a/util/cmake/tests/data/else4.pro b/util/cmake/tests/data/else4.pro new file mode 100644 index 0000000000..9ed676ccfa --- /dev/null +++ b/util/cmake/tests/data/else4.pro @@ -0,0 +1,6 @@ +qtConfig(timezone) { + A = 1 +} else:win32: B = 2 +else { + C = 3 +} diff --git a/util/cmake/tests/data/else5.pro b/util/cmake/tests/data/else5.pro new file mode 100644 index 0000000000..3de76af50a --- /dev/null +++ b/util/cmake/tests/data/else5.pro @@ -0,0 +1,10 @@ +# comments +qtConfig(timezone) { # bar + A = 1 +} else:win32 { + B = 2 # foo +} else { C = 3 +# baz + # foobar +} +# endcomment diff --git a/util/cmake/tests/data/else6.pro b/util/cmake/tests/data/else6.pro new file mode 100644 index 0000000000..9eaa834a19 --- /dev/null +++ b/util/cmake/tests/data/else6.pro @@ -0,0 +1,11 @@ +qtConfig(timezone) \ +{ + A = \ +1 +} \ +else:win32: \ +B = 2 +else: \ + C \ += 3 + diff --git a/util/cmake/tests/data/else7.pro b/util/cmake/tests/data/else7.pro new file mode 100644 index 0000000000..e663b1c05e --- /dev/null +++ b/util/cmake/tests/data/else7.pro @@ -0,0 +1,2 @@ +msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x65000000 + diff --git a/util/cmake/tests/data/else8.pro b/util/cmake/tests/data/else8.pro new file mode 100644 index 0000000000..6d4d5f01ed --- /dev/null +++ b/util/cmake/tests/data/else8.pro @@ -0,0 +1,5 @@ +qtConfig(timezone) { A = 1 } else:win32: {\ +B = 2 \ +} else: \ + C \ += 3 \ diff --git a/util/cmake/tests/data/function_if.pro b/util/cmake/tests/data/function_if.pro new file mode 100644 index 0000000000..9af018f864 --- /dev/null +++ b/util/cmake/tests/data/function_if.pro @@ -0,0 +1,4 @@ +pathIsAbsolute($$CMAKE_HOST_DATA_DIR) { + CMAKE_HOST_DATA_DIR = $$[QT_HOST_DATA/src]/ +} + diff --git a/util/cmake/tests/data/include.pro b/util/cmake/tests/data/include.pro new file mode 100644 index 0000000000..22d8a40919 --- /dev/null +++ b/util/cmake/tests/data/include.pro @@ -0,0 +1,3 @@ +A = 42 +include(foo) # load foo +B=23 diff --git a/util/cmake/tests/data/load.pro b/util/cmake/tests/data/load.pro new file mode 100644 index 0000000000..c9717e9832 --- /dev/null +++ b/util/cmake/tests/data/load.pro @@ -0,0 +1,3 @@ +A = 42 +load(foo)# load foo +B=23 diff --git a/util/cmake/tests/data/quoted.pro b/util/cmake/tests/data/quoted.pro new file mode 100644 index 0000000000..61682aa0d0 --- /dev/null +++ b/util/cmake/tests/data/quoted.pro @@ -0,0 +1,5 @@ +if(linux*|hurd*):!cross_compile:!static:!*-armcc* { + prog=$$quote(if (/program interpreter: (.*)]/) { print $1; }) + DEFINES += ELF_INTERPRETER=\\\"$$system(LC_ALL=C readelf -l /bin/ls | perl -n -e \'$$prog\')\\\" +} + diff --git a/util/cmake/tests/data/unset.pro b/util/cmake/tests/data/unset.pro new file mode 100644 index 0000000000..7ffb0582f1 --- /dev/null +++ b/util/cmake/tests/data/unset.pro @@ -0,0 +1,2 @@ +unset(f16c_cxx) + diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py new file mode 100755 index 0000000000..78ac7fed26 --- /dev/null +++ b/util/cmake/tests/test_parsing.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import os +from pro2cmake import QmakeParser + + +_tests_path = os.path.dirname(os.path.abspath(__file__)) + + +def validate_op(key, op, value, to_validate): + assert key == to_validate['key'] + assert op == to_validate['operation'] + assert value == to_validate['value'] + + +def validate_single_op(key, op, value, to_validate): + assert len(to_validate) == 1 + validate_op(key, op, value, to_validate[0]) + + +def evaluate_condition(to_validate): + assert 'condition' in to_validate + assert 'statements' in to_validate + + return (to_validate['condition'], to_validate['statements'], to_validate.get('else_statements', {})) + + +def validate_default_else_test(file_name): + result = parse_file(file_name) + assert len(result) == 1 + + (cond, if_branch, else_branch) = evaluate_condition(result[0]) + assert cond == 'qtConfig(timezone)' + validate_single_op('A', '=', ['1'], if_branch) + + assert len(else_branch) == 1 + (cond2, if2_branch, else2_branch) = evaluate_condition(else_branch[0]) + assert cond2 == 'win32' + validate_single_op('B', '=', ['2'], if2_branch) + validate_single_op('C', '=', ['3'], else2_branch) + + +def parse_file(file): + p = QmakeParser(debug=True) + result = p.parseFile(file).asDict() + assert len(result) == 1 + + return result['statements'] + + +def test_else(): + result = parse_file(_tests_path + '/data/else.pro') + assert len(result) == 1 + + (cond, if_branch, else_branch) = evaluate_condition(result[0]) + + assert cond == 'linux' + validate_single_op('SOURCES', '+=', ['a.cpp'], if_branch) + validate_single_op('SOURCES', '+=', ['b.cpp'], else_branch) + + +def test_else2(): + result = parse_file(_tests_path + '/data/else2.pro') + assert len(result) == 1 + + (cond, if_branch, else_branch) = evaluate_condition(result[0]) + assert cond == 'osx' + validate_single_op('A', '=', ['1'], if_branch) + + assert len(else_branch) == 1 + (cond2, if2_branch, else2_branch) = evaluate_condition(else_branch[0]) + assert cond2 == 'win32' + validate_single_op('B', '=', ['2'], if2_branch) + + validate_single_op('C', '=', ['3'], else2_branch) + + +def test_else3(): + validate_default_else_test(_tests_path + '/data/else3.pro') + +def test_else4(): + validate_default_else_test(_tests_path + '/data/else4.pro') + +def test_else5(): + validate_default_else_test(_tests_path + '/data/else5.pro') + +def test_else6(): + validate_default_else_test(_tests_path + '/data/else6.pro') + +def test_else7(): + result = parse_file(_tests_path + '/data/else7.pro') + assert len(result) == 1 + +def test_else8(): + validate_default_else_test(_tests_path + '/data/else8.pro') + +def test_include(): + result = parse_file(_tests_path + '/data/include.pro') + assert len(result) == 3 + validate_op('A', '=', ['42'], result[0]) + include = result[1] + assert len(include) == 1 + assert include.get('included', '') == 'foo' + validate_op('B', '=', ['23'], result[2]) + +def test_load(): + result = parse_file(_tests_path + '/data/load.pro') + assert len(result) == 3 + validate_op('A', '=', ['42'], result[0]) + load = result[1] + assert len(load) == 1 + assert load.get('loaded', '') == 'foo' + validate_op('B', '=', ['23'], result[2]) + +def test_definetest(): + result = parse_file(_tests_path + '/data/definetest.pro') + assert len(result) == 1 + assert result[0] == [] + +def test_unset(): + result = parse_file(_tests_path + '/data/unset.pro') + assert len(result) == 1 + assert result[0] == [] + +def test_quoted(): + result = parse_file(_tests_path + '/data/quoted.pro') + assert len(result) == 1 + +def test_complex_values(): + result = parse_file(_tests_path + '/data/complex_values.pro') + assert len(result) == 1 + +def test_function_if(): + result = parse_file(_tests_path + '/data/function_if.pro') + assert len(result) == 1 -- cgit v1.2.3 From 6ae0821a090ddd68437e0ffe01aab97b86b1ae19 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 29 Oct 2018 17:03:48 +0100 Subject: configurejson2cmake: Sort library map Change-Id: Ifb8aa5f1583592a9eb71a93973f17d9714628953 Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 56 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 4623e8d874..513f7498dd 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -47,45 +47,45 @@ class LibraryMapping: def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: libmap = { - 'zlib': 'ZLIB', - 'gbm': 'gbm', - 'host_dbus': None, - 'libdl': None, # handled by CMAKE_DL_LIBS - 'libatomic': 'Atomic', 'double-conversion': 'WrapDoubleConversion', + 'drm': 'Libdrm', + 'egl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_EGL"), + 'fontconfig': LibraryMapping(package='Fontconfig', resultVariable="FONTCONFIG"), + 'freetype': 'Freetype', + 'gbm': 'gbm', + 'glib': 'GLib', 'gnu_iconv': None, - 'sun_iconv': None, - 'posix_iconv': None, + 'harfbuzz': 'harfbuzz', + 'host_dbus': None, 'icu': ['ICU', 'COMPONENTS', 'i18n', 'uc', 'data'], - 'pcre2': ['PCRE2', 'REQUIRED'], + 'journald': 'Libsystemd', + 'libatomic': 'Atomic', + 'libdl': None, # handled by CMAKE_DL_LIBS + 'libinput': 'Libinput', + 'libjpeg': 'JPEG', 'libpng': 'PNG', + 'libpng': 'PNG', + 'libproxy': 'libproxy', 'libudev': 'Libudev', - 'udev': 'Libudev', - 'journald': 'Libsystemd', - 'vulkan': 'Vulkan', - 'glib': 'GLib', - 'harfbuzz': 'harfbuzz', 'opengl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_OpenGL"), - 'egl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_EGL"), 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), - 'libpng': 'PNG', - 'libjpeg': 'JPEG', - 'freetype': 'Freetype', - 'fontconfig': LibraryMapping(package='Fontconfig', resultVariable="FONTCONFIG"), - 'libinput': 'Libinput', + 'pcre2': ['PCRE2', 'REQUIRED'], + 'posix_iconv': None, + 'sun_iconv': None, + 'udev': 'Libudev', + 'vulkan': 'Vulkan', + 'wayland_server': 'Wayland', + 'x11sm': LibraryMapping(package="X11", resultVariable="X11_SM"), + 'xcb_glx': LibraryMapping(package="XCB", resultVariable="XCB_GLX"), + 'xcb_render': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), 'xcb': ['XCB', '1.9'], - 'libproxy': 'libproxy', - 'drm': 'Libdrm', + 'xcb_xinput': LibraryMapping(package="XCB", resultVariable="XCB_XINPUT"), + 'xcb_xkb': LibraryMapping(package="XCB", resultVariable="XCB_XKB"), + 'xcb_xlib': 'X11_XCB', 'xkbcommon': ['XKB', '0.4.1'], 'xlib': 'X11', - 'xcb_xlib': 'X11_XCB', 'xrender': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), - 'xcb_render': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), - 'xcb_glx': LibraryMapping(package="XCB", resultVariable="XCB_GLX"), - 'xcb_xkb': LibraryMapping(package="XCB", resultVariable="XCB_XKB"), - 'xcb_xinput': LibraryMapping(package="XCB", resultVariable="XCB_XINPUT"), - 'x11sm': LibraryMapping(package="X11", resultVariable="X11_SM"), - 'wayland_server': 'Wayland', + 'zlib': 'ZLIB', } # type: Dict[str, Union[str, List[str], LibraryMapping]] if lib not in libmap: raise Exception(' XXXX Unknown library "{}".'.format(lib)) -- cgit v1.2.3 From b102b41be71a18dbc94e289051ddb3437824ae35 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 29 Oct 2018 17:09:14 +0100 Subject: Add ATSPI2 library support Change-Id: I304ba2b9df65008340b698eb3a74dc3b4abfb826 Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 513f7498dd..934c53f354 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -47,6 +47,7 @@ class LibraryMapping: def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: libmap = { + 'atspi': 'ATSPI2', 'double-conversion': 'WrapDoubleConversion', 'drm': 'Libdrm', 'egl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_EGL"), -- cgit v1.2.3 From f3cf1a6856c6f981d0fd8c229ae7f41ffd93f84b Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 1 Nov 2018 14:56:13 +0100 Subject: CMake: Improve handling of qtHaveModule in pro2cmake script Map "qtHaveModule(foo)" in qmake to "TARGET Qt::Foo" in cmake. Change-Id: I63c251f0f2dfd2e95adc996a83b528e9b4e9636e Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 37364d63ad..f79bf19ca4 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -37,7 +37,7 @@ import typing import pyparsing as pp -from helper import map_qt_library, featureName, substitute_platform, substitute_libs +from helper import map_qt_library, map_qt_base_library, featureName, substitute_platform, substitute_libs def _parse_commandline(): @@ -386,7 +386,10 @@ def map_condition(condition: str) -> str: # some features contain e.g. linux, that should not be turned upper case feature = re.match(r"(qtConfig|qtHaveModule)\(([a-zA-Z0-9_-]+)\)", part) if feature: - part = 'QT_FEATURE_' + featureName(feature.group(2)) + if (feature.group(1) == "qtHaveModule"): + part = 'TARGET {}'.format(map_qt_base_library(feature.group(2))) + else: + part = 'QT_FEATURE_' + featureName(feature.group(2)) else: part = substitute_platform(part) -- cgit v1.2.3 From 96ddd79b032b80410c98b8e71f2b153973226d0a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 1 Nov 2018 14:57:31 +0100 Subject: CMake: Handle '-' better in pro2cmake Do not fail on tests/benchmarks/benchmark.pro. Change-Id: I0ffdf9d38ea6fa73856f33d44c5a428c9cab9107 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f79bf19ca4..b39ca744e7 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -300,7 +300,7 @@ class QmakeParser: LC = pp.Suppress(pp.Literal('\\') + pp.LineEnd()) EOL = pp.Suppress(pp.Optional(pp.pythonStyleComment()) + pp.LineEnd()) - Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_./') + Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_-./') Substitution = pp.Combine(pp.Literal('$') + (((pp.Literal('$') + Identifier + pp.Optional(pp.nestedExpr())) | (pp.Literal('(') + Identifier + pp.Literal(')')) -- cgit v1.2.3 From 41eabf01c415af078b3853c801ac7ab491025367 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 1 Nov 2018 15:52:21 +0100 Subject: pro2cmake.py: Make handling of TEMPLATE more robust Change-Id: I43b6dac3984b66585408c9fb91ae28c5b134fe40 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b39ca744e7..25cdab3115 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -129,7 +129,10 @@ class Scope: other.set_basedir(self._basedir) for k in self._values.keys(): - self.append_value(k, other.get(k, [])) + if k == 'TEMPLATE': + assert other.get(k, []) == self.get(k, []) + else: + self.append_value(k, other.get(k, [])) for k in other._values.keys(): if k not in self._values: @@ -214,10 +217,9 @@ class Scope: return self._condition def _push_down_TEMPLATE(self, template: str) -> None: - if not self._rawTemplate(): - self.set_value('TEMPLATE', [template, ]) - for c in self._children: - c._push_down_TEMPLATE(template) + self.set_value('TEMPLATE', [template, ]) + for c in self._children: + c._push_down_TEMPLATE(template) def add_child(self, scope: 'Scope') -> None: scope._parent = self @@ -632,7 +634,7 @@ def write_plugin(cm_fh, scope, *, indent: int=0): def handle_app_or_lib(scope: Scope, cm_fh: IO[str], *, indent=0) -> None: - assert scope.getTemplate() in ('app', 'lib', None) + assert scope.getTemplate() in ('app', 'lib') is_lib = scope.getTemplate() == 'lib' is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED', [])) @@ -669,7 +671,7 @@ def cmakeify_scope(scope: Scope, cm_fh: IO[str], *, indent: int=0) -> None: handle_qt_for_config(scope, cm_fh) if template == 'subdirs': handle_subdir(scope, cm_fh, indent=indent) - elif template in ('app', 'lib', None): + elif template in ('app', 'lib'): handle_app_or_lib(scope, cm_fh, indent=indent) else: print(' XXXX: {}: Template type {} not yet supported.' @@ -694,6 +696,8 @@ def do_include(scope: Scope, *, debug: bool=False) -> None: include_result = parseProFile(include_file, debug=debug) include_scope = Scope.FromDict(include_file, include_result.asDict().get('statements'), '', dir) + if not include_scope._rawTemplate(): + include_scope._push_down_TEMPLATE(scope.getTemplate()) do_include(include_scope) -- cgit v1.2.3 From 187c466d9aeac94db39e1ad4899663c74e2f54f9 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 1 Nov 2018 15:55:19 +0100 Subject: pro2cmake.py: Improve debugging off included files Change-Id: I1007a5134a3d3e9ca7e5d3cd9b23ad64f76602be Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 25cdab3115..353d22762d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -710,8 +710,10 @@ def do_include(scope: Scope, *, debug: bool=False) -> None: def main() -> None: args = _parse_commandline() + debug_parsing = args.debug_parser or args.debug + for file in args.files: - parseresult = parseProFile(file, debug=args.debug_parser or args.debug) + parseresult = parseProFile(file, debug=debug_parsing) if args.debug_parse_result or args.debug: print('\n\n#### Parser result:') @@ -729,7 +731,7 @@ def main() -> None: print(file_scope.dump()) print('\n#### End of .pro/.pri file structure.\n') - do_include(file_scope) + do_include(file_scope, debug=debug_parsing) if args.debug_full_pro_structure or args.debug: print('\n\n#### Full .pro/.pri file structure:') -- cgit v1.2.3 From ead9c0b1b894505097f4de0039c9b55dd799cf3c Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 1 Nov 2018 16:12:21 +0100 Subject: pro2cmake: Handle Statements before Scopes This fixes among other things the unit tests. Change-Id: If9e23735d1d52cf49dc1f03749129fd726e8dea5 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 353d22762d..6471f3ff5f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -331,7 +331,7 @@ class QmakeParser: Statement = pp.Group(Load | Include | Option | DefineTest | FunctionCall | Operation) StatementLine = Statement + EOL - StatementGroup = pp.ZeroOrMore(Scope | EOL | StatementLine) + StatementGroup = pp.ZeroOrMore(StatementLine | Scope | EOL) Block = pp.Suppress('{') + pp.Optional(EOL) \ + pp.ZeroOrMore(EOL | Statement + EOL | Scope) \ -- cgit v1.2.3 From da7811dc758a99b4aee5737713d90a5c12959fc9 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 1 Nov 2018 16:13:11 +0100 Subject: pro2cmake: Explicitly handle for loops Handle for loops by ignoring them:-) Change-Id: If4716e0615f4a0fa982281b94dbd4f70ae868fd8 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6471f3ff5f..3053b36598 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -325,11 +325,13 @@ class QmakeParser: Option = pp.Keyword('option') + pp.Suppress('(') + Identifier('option') + pp.Suppress(')') DefineTest = pp.Suppress(pp.Keyword('defineTest') + pp.Suppress('(') + Identifier + pp.Suppress(')') + pp.nestedExpr(opener='{', closer='}') + pp.LineEnd()) # ignore the whole thing... + ForLoop = pp.Suppress(pp.Keyword('for') + pp.nestedExpr() + + pp.nestedExpr(opener='{', closer='}', ignoreExpr=None) + pp.LineEnd()) # ignore the whole thing... FunctionCall = pp.Suppress(Identifier + pp.nestedExpr()) Scope = pp.Forward() - Statement = pp.Group(Load | Include | Option | DefineTest | FunctionCall | Operation) + Statement = pp.Group(Load | Include | Option | DefineTest | ForLoop | FunctionCall | Operation) StatementLine = Statement + EOL StatementGroup = pp.ZeroOrMore(StatementLine | Scope | EOL) @@ -350,7 +352,7 @@ class QmakeParser: Scope <<= pp.Group(Condition('condition') + (SingleLineScope | MultiLineScope) + pp.Optional(Else)) if debug: - for ename in "EOL Identifier Substitution SubstitutionValue LiteralValuePart Value Values SingleLineScope MultiLineScope Scope SingleLineElse MultiLineElse Else Condition Block StatementGroup Statement Load Include Option DefineTest FunctionCall Operation".split(): + for ename in "EOL Identifier Substitution SubstitutionValue LiteralValuePart Value Values SingleLineScope MultiLineScope Scope SingleLineElse MultiLineElse Else Condition Block StatementGroup Statement Load Include Option DefineTest ForLoop FunctionCall Operation".split(): expr = locals()[ename] expr.setName(ename) expr.setDebug() -- cgit v1.2.3 From 4769830034ee2cb503173229c79bd69042e4fffb Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 2 Nov 2018 12:32:57 +0100 Subject: pro2cmake.py: Do not try to add libraries that qmake substracts Add a comment that a library was removed when running into "QT -= gui" and similar lines. Change-Id: I17b7922827f228c6b45e1e6867fdc5e316af3781 Reviewed-by: Frederik Gladhorn Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3053b36598..40043d15d5 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -522,7 +522,11 @@ def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0, d = '${FW%s}' % d if d.startswith('-l'): d = d[2:] - d = substitute_libs(d) + + if d.startswith('-'): + d = '# Remove: {}'.format(d[1:]) + else: + d = substitute_libs(d) cm_fh.write('{} {}\n'.format(ind, d)) is_framework = False -- cgit v1.2.3 From 89368f5b26b32501d70265259c67aa0f6794273d Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 6 Nov 2018 11:18:25 +0100 Subject: configurejson2cmake: Add generic feature mapping Add a way to map individual parts of a feature to new values and use that also to skip features. Change-Id: Ibddfcbbf9dfac29d460922e991934997b3e8387b Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 127 +++++++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 44 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 934c53f354..0cd0f24e4d 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -620,55 +620,94 @@ def parseTest(ctx, test, data, cm_fh): def parseFeature(ctx, feature, data, cm_fh): - skip_features = { - 'c++11', 'c++14', 'c++1y', 'c++1z', # C++ versions - 'c89', 'c99', 'c11', # C versions - 'stl', # Do we really need to test for this in 2018?! - 'rpath', 'rpath_dir', # rpath related - 'static', 'shared', # static/shared libs - 'debug', 'release', 'debug_and_release', 'build_all', 'optimize_debug', 'optimize_size', # build types - 'release_tools', 'gcov', 'silent', 'profile', - 'msvc_mp', 'static_runtime', 'incredibuild_xge', 'ccache', # compiler specific stuff - 'sanitize_address', 'sanitize_thread', 'sanitize_memory', # sanitizer - 'sanitize_undefined', 'sanitizer', - 'force_debug_info', 'separate_debug_info', 'warnings_are_errors', # FIXME: Do we need these? - 'strip', 'precompile_header', 'ltcg', 'enable_new_dtags', - 'enable_gdb_index', 'reduce_relocations', - 'stack-protector-strong', - 'host-dbus', # dbus related - 'cross_compile', 'gcc-sysroot', # cross compile related - 'gc_binaries', 'qmakeargs', 'use_gold_linker', 'pkg-config', 'verifyspec', # qmake stuff... - 'GNUmake', 'compiler-flags', - 'system-doubleconversion', 'system-pcre2', 'system-zlib', 'system-png', 'system-jpeg', 'system-freetype', 'system-xcb', 'xkbcommon-system', # system libraries - 'doubleconversion', - 'dlopen', # handled by CMAKE_DL_LIBS - 'alloc_stdlib_h', 'alloc_h', 'alloc_malloc_h', # handled by alloc target - 'posix_fallocate', # Only needed for sqlite, which we do not want to build - 'qpa_default_platform', # Not a bool! - 'sun-libiconv', # internal feature but not referenced in our system + # This is *before* the feature name gets normalized! So keep - and + chars, etc. + feature_mapping = { + 'alloc_h': None, # handled by alloc target + 'alloc_malloc_h': None, + 'alloc_stdlib_h': None, + 'build_all': None, + 'c++11': None, # C and C++ versions + 'c11': None, + 'c++14': None, + 'c++1y': None, + 'c++1z': None, + 'c89': None, + 'c99': None, + 'ccache': None, + 'compiler-flags': None, + 'cross_compile': None, + 'debug_and_release': None, + 'debug': None, + 'dlopen': None, # handled by CMAKE_DL_LIBS + 'doubleconversion': None, + 'enable_gdb_index': None, + 'enable_new_dtags': None, + 'force_debug_info': None, + 'gc_binaries': None, + 'gcc-sysroot': None, + 'gcov': None, + 'GNUmake': None, + 'host-dbus': None, + 'incredibuild_xge': None, + 'ltcg': None, + 'msvc_mp': None, + 'optimize_debug': None, + 'optimize_size': None, + 'pkg-config': None, + 'posix_fallocate': None, # Only needed for sqlite, which we do not want to build + '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, + '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': None, # internal feature but not referenced in our system + 'system-doubleconversion': None, # No system libraries anymore! + 'system-freetype': None, + 'system-jpeg': None, + 'system-pcre2': None, + 'system-png': None, + 'system-xcb': None, + 'system-zlib': None, + 'use_gold_linker': None, + 'verifyspec': None, # qmake specific... + 'warnings_are_errors': None, # FIXME: Do we need these? + 'xkbcommon-system': None, # another system library, just named a bit different from the rest } - if feature in skip_features: - print(' **** Skipping features {}: masked.'.format(feature)) - return - disabled_features = set() + mapping = feature_mapping.get(feature, {}) - override_condition = {} + if mapping is None: + print(' **** Skipping features {}: masked.'.format(feature)) + return handled = { 'autoDetect', 'comment', 'condition', 'description', 'disable', 'emitIf', 'enable', 'label', 'output', 'purpose', 'section' } - label = data.get('label', '') - purpose = data.get('purpose', data.get('description', label)) - autoDetect = map_condition(data.get('autoDetect', '')) - condition = override_condition.get(feature, map_condition(data.get('condition', ''))) - output = data.get('output', []) - comment = data.get('comment', '') - section = data.get('section', '') - enable = map_condition(data.get('enable', '')) - disable = map_condition(data.get('disable', '')) - emitIf = map_condition(data.get('emitIf', '')) - - if feature in disabled_features: - condition = "FALSE" + 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)) -- cgit v1.2.3 From df2975236dcf522cf9f70097ac6fbfa467439664 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 6 Nov 2018 11:23:45 +0100 Subject: CMake: Replace QT_FEATURE_shared with BUILD_SHARED_LIBS Change-Id: I7f3efde01a98cd6a25de661624afbf7eda2c57af Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 0cd0f24e4d..27a72202d2 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -643,6 +643,7 @@ def parseFeature(ctx, feature, data, cm_fh): '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, -- cgit v1.2.3 From bebbb410301f388af1a85de30e6a05588c3ab4d0 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 5 Nov 2018 14:18:57 +0100 Subject: CMake: Find librt Change-Id: Ibc39ba7a385146cd0428b78e12a793f0ddbfae91 Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 27a72202d2..03902f1648 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -67,6 +67,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'libpng': 'PNG', 'libpng': 'PNG', 'libproxy': 'libproxy', + 'librt': 'WrapRt', 'libudev': 'Libudev', 'opengl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_OpenGL"), 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), -- cgit v1.2.3 From e11522726bc7f05947e447ae96b85bc1e2d4685c Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 5 Nov 2018 14:49:53 +0100 Subject: CMake: Add FindPPS and use it in src/corelib/configure.cmake Find the PPS library and use the result PPS_FOUND in configure.cmake where needed. Change-Id: I08d3ace421278dc0ae5c3128d4234e6bca906c05 Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 03902f1648..e0335a0e52 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -73,6 +73,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), 'pcre2': ['PCRE2', 'REQUIRED'], 'posix_iconv': None, + 'pps': 'PPS', 'sun_iconv': None, 'udev': 'Libudev', 'vulkan': 'Vulkan', -- cgit v1.2.3 From 38f1a10b6f6deeee0871535fe7430ebd90f3a935 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 6 Nov 2018 14:20:19 +0100 Subject: CMake: Improve handling of the different kinds of iconv Improve conditions/enable/disable conditions for iconv related features. These are detected a bit different from what qmake does, so adapt to that. Change-Id: I7b3e4baf05dc324507f370d7f651a62f29e42a98 Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index e0335a0e52..5c137c394f 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -274,7 +274,6 @@ def map_condition(condition): mapped_features = { "dlopen": "UNIX", 'gbm': 'gbm_FOUND', - "sun-libiconv": "TEST_sun_iconv", "system-xcb": "ON", "system-freetype": "ON", } @@ -645,12 +644,22 @@ def parseFeature(ctx, feature, data, cm_fh): 'enable_gdb_index': None, 'enable_new_dtags': None, 'force_debug_info': None, - 'framework': { 'condition': 'APPLE AND BUILD_SHARED_LIBS', }, + 'framework': { + 'condition': 'APPLE AND BUILD_SHARED_LIBS', + }, 'gc_binaries': None, 'gcc-sysroot': None, 'gcov': None, + 'gnu-libiconv': { + 'condition': 'NOT WIN32 AND NOT QNX AND NOT ANDROID AND NOT APPLE AND TEST_posix_iconv AND NOT TEST_iconv_needlib', + 'enable': 'TEST_posix_iconv AND NOT TEST_iconv_needlib', + 'disable': 'NOT TEST_posix_iconv OR TEST_iconv_needlib', + }, 'GNUmake': None, 'host-dbus': None, + 'iconv': { + 'condition': 'NOT QT_FEATURE_icu AND QT_FEATURE_textcodec AND ( TEST_posix_iconv OR TEST_sun_iconv )' + }, 'incredibuild_xge': None, 'ltcg': None, 'msvc_mp': None, @@ -658,6 +667,11 @@ def parseFeature(ctx, feature, data, cm_fh): 'optimize_size': None, 'pkg-config': None, 'posix_fallocate': None, # Only needed for sqlite, which we do not want to build + 'posix_libiconv': { + 'condition': 'NOT WIN32 AND NOT QNX AND NOT ANDROID AND NOT APPLE AND TEST_posix_iconv AND TEST_iconv_needlib', + 'enable': 'TEST_posix_iconv AND TEST_iconv_needlib', + 'disable': 'NOT TEST_posix_iconv OR NOT TEST_iconv_needlib', + }, 'precompile_header': None, 'profile': None, 'qmakeargs': None, @@ -680,7 +694,11 @@ def parseFeature(ctx, feature, data, cm_fh): 'static_runtime': None, 'stl': None, # Do we really need to test for this in 2018?! 'strip': None, - 'sun-libiconv': None, # internal feature but not referenced in our system + 'sun-libiconv': { + 'condition': 'NOT WIN32 AND NOT QNX AND NOT ANDROID AND NOT APPLE AND TEST_sun_iconv', + 'enable': 'TEST_sun_iconv', + 'disable': 'NOT TEST_sun_iconv', + }, 'system-doubleconversion': None, # No system libraries anymore! 'system-freetype': None, 'system-jpeg': None, -- cgit v1.2.3 From eb2ece3c59773f8948819349d6f86fc453967588 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 6 Nov 2018 14:58:26 +0100 Subject: CMake: Add FindSlog2 Add FindSlog2 and use it in src/corelib/configure.cmake. Change-Id: I7e6a696a49df568a41ed1224228ab608db2dbb0e Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 5c137c394f..a54a692fac 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -74,6 +74,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'pcre2': ['PCRE2', 'REQUIRED'], 'posix_iconv': None, 'pps': 'PPS', + 'slog2': 'Slog2', 'sun_iconv': None, 'udev': 'Libudev', 'vulkan': 'Vulkan', -- cgit v1.2.3 From 3acebfe6eb9ff0c97ab0e79f9f0b35835a4df57a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 6 Nov 2018 15:06:25 +0100 Subject: CMake: Add support to find LTTng-ust CMake comes with a Find module for this, so use it in src/corelib/configure.cmake Change-Id: I8f4abcb32fb10513d67c3c959310eefaf7b56d3d Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index a54a692fac..ad80146b9f 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -69,6 +69,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'libproxy': 'libproxy', 'librt': 'WrapRt', 'libudev': 'Libudev', + 'lttng-ust': LibraryMapping(package='LTTngUST', resultVariable="LTTNGUST"), 'opengl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_OpenGL"), 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), 'pcre2': ['PCRE2', 'REQUIRED'], -- cgit v1.2.3 From f0aa8fa48e3fbd601d983d809a38945f2814665e Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 20 Dec 2018 10:41:56 +0100 Subject: pro2cmake: Handle VPATH Change-Id: Ia72f55489129c50ca730c42b75bbc2bda926b82f Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 40043d15d5..f740b4353f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -81,7 +81,7 @@ def map_to_file(f: str, top_dir: str, current_dir: str, return f -def map_source_to_cmake(source: str) -> typing.Optional[str]: +def map_source_to_cmake(source: str, base_dir: str, vpath: List[str]) -> typing.Optional[str]: if not source or source == '$$NO_PCH_SOURCES': return None if source.startswith('$$PWD/'): @@ -90,7 +90,19 @@ def map_source_to_cmake(source: str) -> typing.Optional[str]: return "${CMAKE_CURRENT_SOURCE_DIR}" if source.startswith('$$QT_SOURCE_TREE/'): return "${PROJECT_SOURCE_DIR}/" + source[17:] - return source + + if os.path.exists(os.path.join(base_dir, source)): + return source + + + for v in vpath: + fullpath = os.path.join(v, source) + if os.path.exists(fullpath): + relpath = os.path.relpath(fullpath, base_dir) + return relpath + + print(' XXXX: Source {}: Not found.'.format(source)) + return '{}-NOTFOUND'.format(source) def map_source_to_fs(base_dir: str, file: str, source: str) -> typing.Optional[str]: @@ -492,7 +504,9 @@ def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0, else: sources += resources - sources = [map_source_to_cmake(s) for s in sources] + vpath = scope.get('VPATH') + + sources = [map_source_to_cmake(s, scope.basedir(), vpath) for s in sources] if sources: cm_fh.write('{} SOURCES\n'.format(ind)) for l in sort_sources(sources): -- cgit v1.2.3 From 19874e2381df0b6831b365fc4932b3c19a5290a9 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 20 Dec 2018 16:15:10 +0100 Subject: pro2cmake: Be more faithful in the representation of operations Do a better approximation of =, +=, *= and -=. Change-Id: I94765532f278deaac330b27cd5a3f41f319c6477 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 238 +++++++++++++++++++++++++++++------------------- 1 file changed, 146 insertions(+), 92 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f740b4353f..402e763dc5 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -119,9 +119,74 @@ def map_source_to_fs(base_dir: str, file: str, source: str) -> typing.Optional[s return os.path.join(base_dir, source) +class Operation: + def __init__(self, value): + if isinstance(value, list): + self._value = value + else: + self._value = [str(value),] + + def process(self, input): + assert(False) + + def __str__(self): + assert(False) + + +class AddOperation(Operation): + def process(self, input): + return input + self._value + + def __str__(self): + return '+({})'.format(self._value) + + +class UniqueAddOperation(Operation): + def process(self, input): + result = input + for v in self._value: + if not v in result: + result += [v,] + return result + + def __str__(self): + return '*({})'.format(self._value) + + +class SetOperation(Operation): + def process(self, input): + return self._value + + def __str__(self): + return '=({})'.format(self._value) + + +class RemoveOperation(Operation): + def __init__(self, value): + super().__init__(value) + + def process(self, input): + input_set = set(input) + result = [] + for v in self._value: + if v in input_set: + continue + else: + result += ['-{}'.format(v),] + return result + + def __str__(self): + return '-({})'.format(self._value) + + class Scope: - def __init__(self, file: typing.Optional[str]=None, condition: str='', base_dir: str='') -> None: - self._parent = None # type: Scope + def __init__(self, parent_scope: typing.Optional['Scope'], + file: typing.Optional[str]=None, condition: str='', base_dir: str='') -> None: + if parent_scope: + parent_scope._add_child(self) + else: + self._parent = None + self._basedir = base_dir if file: self._currentdir = os.path.dirname(file) @@ -133,27 +198,14 @@ class Scope: self._file = file self._condition = map_condition(condition) self._children = [] # type: List[Scope] - self._values = {} # type: Dict[str, List[str]] + self._operations = {} # type: Dict[str] def merge(self, other: 'Scope') -> None: for c in other._children: - self.add_child(c) - other.set_basedir(self._basedir) + self._add_child(c) - for k in self._values.keys(): - if k == 'TEMPLATE': - assert other.get(k, []) == self.get(k, []) - else: - self.append_value(k, other.get(k, [])) - - for k in other._values.keys(): - if k not in self._values: - self.set_value(k, other.get(k)) - - def set_basedir(self, dir: str) -> None: - self._basedir = dir - for c in self._children: - c.set_basedir(dir) + for k in other._operations.keys(): + self._operations[key] = other._operations[k] def basedir(self) -> str: return self._basedir @@ -161,9 +213,27 @@ class Scope: def currentdir(self) -> str: return self._currentdir + def diff(self, key: str, default: typing.Optional[List[str]]=[]) -> List[str]: + mine = self.get(key, default) + + if self._parent: + parent = self._parent.get(key, default) + if (parent == mine): + return [] + + parent_set = set(parent) + mine_set = set(mine) + + added = [x for x in mine if not x in parent_set] + removed = [x for x in parent if not x in mine_set] + + return added + list('# {}'.format(x) for x in removed) + return mine + @staticmethod - def FromDict(file: str, statements, cond: str = '', base_dir: str = ''): - scope = Scope(file, cond, base_dir) + def FromDict(parent_scope: typing.Optional['Scope'], + file: str, statements, cond: str = '', base_dir: str = ''): + scope = Scope(parent_scope, file, cond, base_dir) for statement in statements: if isinstance(statement, list): # Handle skipped parts... assert not statement @@ -174,16 +244,19 @@ class Scope: key = statement.get('key', '') value = statement.get('value', []) assert key != '' + print('#### {}: {} = {}.'.format(operation, key, value)) if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH') or key.endswith('_HEADERS') or key.endswith('_SOURCES'): value = [map_to_file(v, scope.basedir(), scope.currentdir()) for v in value] if operation == '=': - scope.set_value(key, value) + scope._append_operation(key, SetOperation(value)) elif operation == '-=': - scope.substract_value(key, value) - elif operation == '+=' or operation == '*=': - scope.append_value(key, value) + scope._append_operation(key, RemoveOperation(value)) + elif operation == '+=': + scope._append_operation(key, AddOperation(value)) + elif operation == '*=': + scope._append_operation(key, UniqueAddOperation(value)) else: print('Unexpected operation "{}" in scope with condition {}.'.format(operation, cond)) assert(False) @@ -192,33 +265,36 @@ class Scope: condition = statement.get('condition', None) if condition: - child = Scope.FromDict(file, statement.get('statements'), condition, scope.basedir()) - scope.add_child(child) + child = Scope.FromDict(scope, file, statement.get('statements'), condition, scope.basedir()) else_statements = statement.get('else_statements') if else_statements: - child = Scope.FromDict(file, else_statements, 'NOT ' + condition, scope.basedir()) - scope.add_child(child) + child = Scope.FromDict(scope, file, else_statements, 'NOT ' + condition, scope.basedir()) continue - loaded = statement.get('loaded', None) + loaded = statement.get('loaded') if loaded: - scope.append_value('_LOADED', loaded) + scope._append_operation('_LOADED', UniqueAddOperation(loaded)) continue option = statement.get('option', None) if option: - scope.append_value('_OPTION', option) + scope._append_operation('_OPTION', UniqueAddOperation(option)) continue included = statement.get('included', None) if included: - scope.append_value('_INCLUDED', - map_to_file(included, scope.basedir(), scope.currentdir())) + scope.append_operation('_INCLUDED', UniqueAddOperation(map_to_file(included, scope.basedir(), scope.currentdir()))) continue 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,] + def file(self) -> str: return self._file or '' @@ -228,38 +304,10 @@ class Scope: def condition(self) -> str: return self._condition - def _push_down_TEMPLATE(self, template: str) -> None: - self.set_value('TEMPLATE', [template, ]) - for c in self._children: - c._push_down_TEMPLATE(template) - - def add_child(self, scope: 'Scope') -> None: + def _add_child(self, scope: 'Scope') -> None: scope._parent = self - if not scope._rawTemplate(): - scope._push_down_TEMPLATE(self.getTemplate()) self._children.append(scope) - def set_value(self, key: str, value: List[str]) -> None: - self._values[key] = value - - def append_value(self, key: str, value: Union[str, List[str]]) -> None: - array = self._values.get(key, []) - if isinstance(value, str): - array.append(value) - elif isinstance(value, list): - array += value - else: - assert False - self._values[key] = array - - def substract_value(self, key: str, value: Union[str, List[str]]) -> None: - if isinstance(value, str): - to_remove = [value, ] - if isinstance(value, list): - to_remove = value - - self.append_value(key, ['-{}'.format(v) for v in to_remove]) - def children(self) -> List['Scope']: return self._children @@ -270,28 +318,34 @@ class Scope: else: print('{}Scope {} in {} with condition: {}.'.format(ind, self._file, self._basedir, self._condition)) print('{}Keys:'.format(ind)) - for k in sorted(self._values.keys()): - print('{} {} = "{}"'.format(ind, k, self._values[k])) + for k in sorted(self._operations.keys()): + print('{} {} = "{}"'.format(ind, k, self._operations.get(k, []))) print('{}Children:'.format(ind)) for c in self._children: c.dump(indent=indent + 1) def get(self, key: str, default=None) -> List[str]: - default = default or [] - return self._values.get(key, default) + result = [] - def getString(self, key: str, default: str = '') -> str: - v = self.get(key) - if isinstance(v, list): - if len(v) == 0: - return default - assert len(v) == 1 - return v[0] - elif isinstance(v, str): - return v + if self._parent: + result = self._parent.get(key, default) else: - assert False - return default + if default: + if isinstance(default, list): + result = default + else: + result = [str(default),] + + for op in self._operations.get(key, []): + result = op.process(result) + return result + + def getString(self, key: str, default: str = '') -> str: + v = self.get(key, default) + if len(v) == 0: + return default + assert len(v) == 1 + return v[0] def getTemplate(self) -> str: return self.getString('TEMPLATE', 'app') @@ -425,7 +479,7 @@ def handle_subdir(scope: Scope, cm_fh: IO[str], *, indent: int = 0) -> None: cm_fh.write('{}add_subdirectory({})\n'.format(ind, sd)) elif os.path.isfile(full_sd): subdir_result = parseProFile(full_sd, debug=False) - subdir_scope = Scope.FromDict(full_sd, subdir_result.asDict().get('statements'), + subdir_scope = Scope.FromDict(scope, full_sd, subdir_result.asDict().get('statements'), '', scope.basedir()) cmakeify_scope(subdir_scope, cm_fh, indent=indent + 1) @@ -490,8 +544,8 @@ def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0, if plugin_type: cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0])) - sources = scope.get('SOURCES') + scope.get('HEADERS') + scope.get('OBJECTIVE_SOURCES') + scope.get('NO_PCH_SOURCES') + scope.get('FORMS') - resources = scope.get('RESOURCES') + sources = scope.diff('SOURCES') + scope.diff('HEADERS') + scope.diff('OBJECTIVE_SOURCES') + scope.diff('NO_PCH_SOURCES') + scope.diff('FORMS') + resources = scope.diff('RESOURCES') if resources: qrc_only = True for r in resources: @@ -512,19 +566,21 @@ def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0, for l in sort_sources(sources): cm_fh.write('{} {}\n'.format(ind, l)) - if scope.get('DEFINES'): + defines = scope.diff('DEFINES') + if defines: cm_fh.write('{} DEFINES\n'.format(ind)) - for d in scope.get('DEFINES'): + for d in defines: d = d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"') cm_fh.write('{} {}\n'.format(ind, d)) - if scope.get('INCLUDEPATH'): + includes = scope.diff('INCLUDEPATH') + if includes: cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind)) - for i in scope.get('INCLUDEPATH'): + for i in includes: cm_fh.write('{} {}\n'.format(ind, i)) - dependencies = [map_qt_library(q) for q in scope.get('QT') if map_qt_library(q) not in known_libraries] - dependencies += [map_qt_library(q) for q in scope.get('QT_FOR_PRIVATE') if map_qt_library(q) not in known_libraries] - dependencies += scope.get('QMAKE_USE_PRIVATE') + scope.get('LIBS_PRIVATE') + scope.get('LIBS') + dependencies = [map_qt_library(q) for q in scope.diff('QT') if map_qt_library(q) not in known_libraries] + dependencies += [map_qt_library(q) for q in scope.diff('QT_FOR_PRIVATE') if map_qt_library(q) not in known_libraries] + dependencies += scope.diff('QMAKE_USE_PRIVATE') + scope.diff('LIBS_PRIVATE') + scope.diff('LIBS') if dependencies: cm_fh.write('{} LIBRARIES\n'.format(ind)) is_framework = False @@ -714,10 +770,8 @@ def do_include(scope: Scope, *, debug: bool=False) -> None: continue include_result = parseProFile(include_file, debug=debug) - include_scope = Scope.FromDict(include_file, include_result.asDict().get('statements'), + include_scope = Scope.FromDict(scope, include_file, include_result.asDict().get('statements'), '', dir) - if not include_scope._rawTemplate(): - include_scope._push_down_TEMPLATE(scope.getTemplate()) do_include(include_scope) @@ -744,7 +798,7 @@ def main() -> None: print(parseresult.asDict()) print('\n#### End of parser result dictionary.\n') - file_scope = Scope.FromDict(file, parseresult.asDict().get('statements')) + file_scope = Scope.FromDict(None, file, parseresult.asDict().get('statements')) if args.debug_pro_structure or args.debug: print('\n\n#### .pro/.pri file structure:') -- cgit v1.2.3 From 54d372ea67584d57b8c819ff7e221a23a8b34cd2 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 21 Dec 2018 12:13:38 +0100 Subject: pro2cmake: Clean up linter and pypi issues Also remove one stray line of debug output Change-Id: If3d2e0b31e5e1b1c05c321d63e8762b23f55bb66 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 358 +++++++++++++++++++++++++++++++----------------- 1 file changed, 230 insertions(+), 128 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 402e763dc5..a1c73fcd5f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -30,32 +30,38 @@ from argparse import ArgumentParser import os.path import re -import sys import io -from typing import IO, List, Dict, Union import typing import pyparsing as pp -from helper import map_qt_library, map_qt_base_library, featureName, substitute_platform, substitute_libs +from helper import map_qt_library, map_qt_base_library, featureName, \ + substitute_platform, substitute_libs def _parse_commandline(): - parser = ArgumentParser(description='Generate CMakeLists.txt files from .pro files.') + parser = ArgumentParser(description='Generate CMakeLists.txt files from .' + 'pro files.') 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', + 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', + 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', + 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', + 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('files', metavar='<.pro/.pri file>', type=str, nargs='+', - help='The .pro/.pri file to process') + 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('files', metavar='<.pro/.pri file>', type=str, + nargs='+', help='The .pro/.pri file to process') return parser.parse_args() @@ -81,9 +87,10 @@ def map_to_file(f: str, top_dir: str, current_dir: str, return f -def map_source_to_cmake(source: str, base_dir: str, vpath: List[str]) -> typing.Optional[str]: +def map_source_to_cmake(source: str, base_dir: str, + vpath: typing.List[str]) -> str: if not source or source == '$$NO_PCH_SOURCES': - return None + return '' if source.startswith('$$PWD/'): return source[6:] if source == '.': @@ -94,7 +101,6 @@ def map_source_to_cmake(source: str, base_dir: str, vpath: List[str]) -> typing. if os.path.exists(os.path.join(base_dir, source)): return source - for v in vpath: fullpath = os.path.join(v, source) if os.path.exists(fullpath): @@ -105,9 +111,10 @@ def map_source_to_cmake(source: str, base_dir: str, vpath: List[str]) -> typing. return '{}-NOTFOUND'.format(source) -def map_source_to_fs(base_dir: str, file: str, source: str) -> typing.Optional[str]: +def map_source_to_fs(base_dir: str, file: str, + source: str) -> str: if source is None or source == '$$NO_PCH_SOURCES': - return None + return '' if source.startswith('$$PWD/'): return os.path.join(os.path.dirname(file), source[6:]) if source.startswith('$$QT_SOURCE_TREE/'): @@ -124,7 +131,7 @@ class Operation: if isinstance(value, list): self._value = value else: - self._value = [str(value),] + self._value = [str(value), ] def process(self, input): assert(False) @@ -145,8 +152,8 @@ class UniqueAddOperation(Operation): def process(self, input): result = input for v in self._value: - if not v in result: - result += [v,] + if v not in result: + result += [v, ] return result def __str__(self): @@ -172,7 +179,7 @@ class RemoveOperation(Operation): if v in input_set: continue else: - result += ['-{}'.format(v),] + result += ['-{}'.format(v), ] return result def __str__(self): @@ -181,11 +188,12 @@ class RemoveOperation(Operation): class Scope: def __init__(self, parent_scope: typing.Optional['Scope'], - file: typing.Optional[str]=None, condition: str='', base_dir: str='') -> None: + file: typing.Optional[str] = None, condition: str = '', + base_dir: str = '') -> None: if parent_scope: parent_scope._add_child(self) else: - self._parent = None + self._parent = None # type: typing.Optional[Scope] self._basedir = base_dir if file: @@ -197,15 +205,15 @@ class Scope: self._file = file self._condition = map_condition(condition) - self._children = [] # type: List[Scope] - self._operations = {} # type: Dict[str] + self._children = [] # type: typing.List[Scope] + self._operations = {} # type: typing.Dict[str, typing.List[Operation]] def merge(self, other: 'Scope') -> None: for c in other._children: self._add_child(c) - for k in other._operations.keys(): - self._operations[key] = other._operations[k] + for key in other._operations.keys(): + self._operations[key] = other._operations[key] def basedir(self) -> str: return self._basedir @@ -213,7 +221,9 @@ class Scope: def currentdir(self) -> str: return self._currentdir - def diff(self, key: str, default: typing.Optional[List[str]]=[]) -> List[str]: + def diff(self, key: str, + default: typing.Optional[typing.List[str]] = []) \ + -> typing.List[str]: mine = self.get(key, default) if self._parent: @@ -224,8 +234,8 @@ class Scope: parent_set = set(parent) mine_set = set(mine) - added = [x for x in mine if not x in parent_set] - removed = [x for x in parent if not x in mine_set] + added = [x for x in mine if x not in parent_set] + removed = [x for x in parent if x not in mine_set] return added + list('# {}'.format(x) for x in removed) return mine @@ -244,10 +254,12 @@ class Scope: key = statement.get('key', '') value = statement.get('value', []) assert key != '' - print('#### {}: {} = {}.'.format(operation, key, value)) - if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH') or key.endswith('_HEADERS') or key.endswith('_SOURCES'): - value = [map_to_file(v, scope.basedir(), scope.currentdir()) for v in value] + if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH') \ + or key.endswith('_HEADERS') \ + or key.endswith('_SOURCES'): + value = [map_to_file(v, scope.basedir(), + scope.currentdir()) for v in value] if operation == '=': scope._append_operation(key, SetOperation(value)) @@ -258,18 +270,22 @@ class Scope: elif operation == '*=': scope._append_operation(key, UniqueAddOperation(value)) else: - print('Unexpected operation "{}" in scope with condition {}.'.format(operation, cond)) + print('Unexpected operation "{}" in scope with ' + 'condition {}.'.format(operation, cond)) assert(False) continue condition = statement.get('condition', None) if condition: - child = 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') if else_statements: - child = Scope.FromDict(scope, file, else_statements, 'NOT ' + condition, scope.basedir()) + Scope.FromDict(scope, file, else_statements, + 'NOT ' + condition, scope.basedir()) continue loaded = statement.get('loaded') @@ -284,7 +300,11 @@ class Scope: included = statement.get('included', None) if included: - scope.append_operation('_INCLUDED', UniqueAddOperation(map_to_file(included, scope.basedir(), scope.currentdir()))) + scope._append_operation('_INCLUDED', + UniqueAddOperation( + map_to_file(included, + scope.basedir(), + scope.currentdir()))) continue return scope @@ -293,7 +313,7 @@ class Scope: if key in self._operations: self._operations[key].append(op) else: - self._operations[key] = [op,] + self._operations[key] = [op, ] def file(self) -> str: return self._file or '' @@ -308,7 +328,7 @@ class Scope: scope._parent = self self._children.append(scope) - def children(self) -> List['Scope']: + def children(self) -> typing.List['Scope']: return self._children def dump(self, *, indent: int = 0) -> None: @@ -316,7 +336,8 @@ class Scope: if self._condition == '': print('{}Scope {} in {}.'.format(ind, self._file, self._basedir)) else: - print('{}Scope {} in {} with condition: {}.'.format(ind, self._file, self._basedir, self._condition)) + print('{}Scope {} in {} with condition: {}.' + .format(ind, self._file, self._basedir, self._condition)) print('{}Keys:'.format(ind)) for k in sorted(self._operations.keys()): print('{} {} = "{}"'.format(ind, k, self._operations.get(k, []))) @@ -324,8 +345,8 @@ class Scope: for c in self._children: c.dump(indent=indent + 1) - def get(self, key: str, default=None) -> List[str]: - result = [] + def get(self, key: str, default=None) -> typing.List[str]: + result = [] # type: typing.List[str] if self._parent: result = self._parent.get(key, default) @@ -334,7 +355,7 @@ class Scope: if isinstance(default, list): result = default else: - result = [str(default),] + result = [str(default), ] for op in self._operations.get(key, []): result = op.process(result) @@ -354,7 +375,8 @@ class Scope: return self.getString('TEMPLATE') def getTarget(self) -> str: - return self.getString('TARGET') or os.path.splitext(os.path.basename(self.file()))[0] + return self.getString('TARGET') \ + or os.path.splitext(os.path.basename(self.file()))[0] class QmakeParser: @@ -369,35 +391,54 @@ class QmakeParser: EOL = pp.Suppress(pp.Optional(pp.pythonStyleComment()) + pp.LineEnd()) Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_-./') - 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(']')) - ))) - # Do not match word ending in '\' since that breaks line continuation:-/ + 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(']')) + ))) + # Do not match word ending in '\' since that breaks line + # continuation:-/ LiteralValuePart = pp.Word(pp.printables, excludeChars='$#{}()') - SubstitutionValue = pp.Combine(pp.OneOrMore(Substitution | LiteralValuePart | pp.Literal('$'))) - Value = (pp.QuotedString(quoteChar='"', escChar='\\') | SubstitutionValue) + SubstitutionValue \ + = pp.Combine(pp.OneOrMore(Substitution | LiteralValuePart + | pp.Literal('$'))) + Value = (pp.QuotedString(quoteChar='"', escChar='\\') + | SubstitutionValue) Values = pp.ZeroOrMore(Value)('value') - Op = pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') | pp.Literal('*=') - - Operation = Identifier('key') + Op('operation') + Values('value') - Load = pp.Keyword('load') + pp.Suppress('(') + Identifier('loaded') + pp.Suppress(')') - Include = pp.Keyword('include') + pp.Suppress('(') + pp.CharsNotIn(':{=}#)\n')('included') + pp.Suppress(')') - Option = pp.Keyword('option') + pp.Suppress('(') + Identifier('option') + pp.Suppress(')') - DefineTest = pp.Suppress(pp.Keyword('defineTest') + pp.Suppress('(') + Identifier + pp.Suppress(')') - + pp.nestedExpr(opener='{', closer='}') + pp.LineEnd()) # ignore the whole thing... + Op = pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') \ + | pp.Literal('*=') + + Operation = Identifier('key') + Op('operation') + Values('value') + Load = pp.Keyword('load') + pp.Suppress('(') \ + + Identifier('loaded') + pp.Suppress(')') + Include = pp.Keyword('include') + pp.Suppress('(') \ + + pp.CharsNotIn(':{=}#)\n')('included') + pp.Suppress(')') + Option = pp.Keyword('option') + pp.Suppress('(') \ + + Identifier('option') + pp.Suppress(')') + DefineTest = pp.Suppress(pp.Keyword('defineTest') + + pp.Suppress('(') + Identifier + + pp.Suppress(')') + + pp.nestedExpr(opener='{', closer='}') + + pp.LineEnd()) # ignore the whole thing... ForLoop = pp.Suppress(pp.Keyword('for') + pp.nestedExpr() - + pp.nestedExpr(opener='{', closer='}', ignoreExpr=None) + pp.LineEnd()) # ignore the whole thing... + + pp.nestedExpr(opener='{', closer='}', + ignoreExpr=None) + + pp.LineEnd()) # ignore the whole thing... FunctionCall = pp.Suppress(Identifier + pp.nestedExpr()) Scope = pp.Forward() - Statement = pp.Group(Load | Include | Option | DefineTest | ForLoop | FunctionCall | Operation) + Statement = pp.Group(Load | Include | Option | DefineTest + | ForLoop | FunctionCall | Operation) StatementLine = Statement + EOL StatementGroup = pp.ZeroOrMore(StatementLine | Scope | EOL) @@ -409,16 +450,26 @@ class QmakeParser: Condition = pp.Optional(pp.White()) + pp.CharsNotIn(':{=}#\\\n') Condition.setParseAction(lambda x: ' '.join(x).strip()) - SingleLineScope = pp.Suppress(pp.Literal(':')) + pp.Group(Scope | Block | StatementLine)('statements') + SingleLineScope = pp.Suppress(pp.Literal(':')) \ + + pp.Group(Scope | Block | StatementLine)('statements') MultiLineScope = Block('statements') - SingleLineElse = pp.Suppress(pp.Literal(':')) + pp.Group(Scope | StatementLine)('else_statements') - MultiLineElse = pp.Group(Block)('else_statements') - Else = pp.Suppress(pp.Keyword('else')) + (SingleLineElse | MultiLineElse) - Scope <<= pp.Group(Condition('condition') + (SingleLineScope | MultiLineScope) + pp.Optional(Else)) + SingleLineElse = pp.Suppress(pp.Literal(':')) \ + + pp.Group(Scope | StatementLine)('else_statements') + MultiLineElse = pp.Group(Block)('else_statements') + Else = pp.Suppress(pp.Keyword('else')) \ + + (SingleLineElse | MultiLineElse) + Scope <<= pp.Group(Condition('condition') + + (SingleLineScope | MultiLineScope) + + pp.Optional(Else)) if debug: - for ename in "EOL Identifier Substitution SubstitutionValue LiteralValuePart Value Values SingleLineScope MultiLineScope Scope SingleLineElse MultiLineElse Else Condition Block StatementGroup Statement Load Include Option DefineTest ForLoop FunctionCall Operation".split(): + for ename in 'EOL Identifier Substitution SubstitutionValue ' \ + 'LiteralValuePart Value Values SingleLineScope ' \ + 'MultiLineScope Scope SingleLineElse ' \ + 'MultiLineElse Else Condition Block ' \ + 'StatementGroup Statement Load Include Option ' \ + 'DefineTest ForLoop FunctionCall Operation'.split(): expr = locals()[ename] expr.setName(ename) expr.setDebug() @@ -453,11 +504,14 @@ def map_condition(condition: str) -> str: 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) + # some features contain e.g. linux, that should not be + # turned upper case + feature = re.match(r"(qtConfig|qtHaveModule)\(([a-zA-Z0-9_-]+)\)", + part) if feature: if (feature.group(1) == "qtHaveModule"): - part = 'TARGET {}'.format(map_qt_base_library(feature.group(2))) + part = 'TARGET {}'.format(map_qt_base_library( + feature.group(2))) else: part = 'QT_FEATURE_' + featureName(feature.group(2)) else: @@ -470,7 +524,8 @@ def map_condition(condition: str) -> str: return cmake_condition.strip() -def handle_subdir(scope: Scope, cm_fh: IO[str], *, indent: int = 0) -> None: +def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, + indent: int = 0) -> None: assert scope.getTemplate() == 'subdirs' ind = ' ' * indent for sd in scope.get('SUBDIRS', []): @@ -479,14 +534,18 @@ def handle_subdir(scope: Scope, cm_fh: IO[str], *, indent: int = 0) -> None: cm_fh.write('{}add_subdirectory({})\n'.format(ind, sd)) elif os.path.isfile(full_sd): subdir_result = parseProFile(full_sd, debug=False) - subdir_scope = Scope.FromDict(scope, full_sd, subdir_result.asDict().get('statements'), - '', scope.basedir()) + subdir_scope \ + = Scope.FromDict(scope, full_sd, + subdir_result.asDict().get('statements'), + '', scope.basedir()) cmakeify_scope(subdir_scope, cm_fh, indent=indent + 1) elif sd.startswith('-'): - cm_fh.write('{}### remove_subdirectory("{}")\n'.format(ind, sd[1:])) + cm_fh.write('{}### remove_subdirectory' + '("{}")\n'.format(ind, sd[1:])) else: - print(' XXXX: SUBDIR {} in {}: Not found.'.format(sd, scope.file())) + print(' XXXX: SUBDIR {} in {}: ' + 'Not found.'.format(sd, scope.file())) for c in scope.children(): cond = c.condition() @@ -501,8 +560,8 @@ def handle_subdir(scope: Scope, cm_fh: IO[str], *, indent: int = 0) -> None: cm_fh.write('{}endif()\n'.format(ind)) -def sort_sources(sources) -> List[str]: - to_sort = {} # type: Dict[str, List[str]] +def sort_sources(sources) -> typing.List[str]: + to_sort = {} # type: typing.Dict[str, typing.List[str]] for s in sources: if s is None: continue @@ -525,26 +584,32 @@ def sort_sources(sources) -> List[str]: return lines -def write_header(cm_fh: IO[str], name: str, typename: str, *, indent: int=0): - cm_fh.write('{}#####################################################################\n'.format(spaces(indent))) +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))) + cm_fh.write('{}###########################################' + '##########################\n\n'.format(spaces(indent))) -def write_scope_header(cm_fh: IO[str], *, indent: int=0): +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))) + cm_fh.write('{}###########################################' + '##########################\n'.format(spaces(indent))) -def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0, - known_libraries=set()) -> None: +def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, + indent: int = 0, known_libraries=set()) -> None: ind = spaces(indent) plugin_type = scope.get('PLUGIN_TYPE') if plugin_type: cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0])) - sources = scope.diff('SOURCES') + scope.diff('HEADERS') + scope.diff('OBJECTIVE_SOURCES') + scope.diff('NO_PCH_SOURCES') + scope.diff('FORMS') + sources = scope.diff('SOURCES') + scope.diff('HEADERS') \ + + scope.diff('OBJECTIVE_SOURCES') + scope.diff('NO_PCH_SOURCES') \ + + scope.diff('FORMS') resources = scope.diff('RESOURCES') if resources: qrc_only = True @@ -570,7 +635,8 @@ def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0, if defines: cm_fh.write('{} DEFINES\n'.format(ind)) for d in defines: - d = d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"') + d = d.replace('=\\\\\\"$$PWD/\\\\\\"', + '="${CMAKE_CURRENT_SOURCE_DIR}/"') cm_fh.write('{} {}\n'.format(ind, d)) includes = scope.diff('INCLUDEPATH') if includes: @@ -578,9 +644,12 @@ def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0, for i in includes: cm_fh.write('{} {}\n'.format(ind, i)) - dependencies = [map_qt_library(q) for q in scope.diff('QT') if map_qt_library(q) not in known_libraries] - dependencies += [map_qt_library(q) for q in scope.diff('QT_FOR_PRIVATE') if map_qt_library(q) not in known_libraries] - dependencies += scope.diff('QMAKE_USE_PRIVATE') + scope.diff('LIBS_PRIVATE') + scope.diff('LIBS') + dependencies = [map_qt_library(q) for q in scope.diff('QT') + if map_qt_library(q) not in known_libraries] + dependencies += [map_qt_library(q) for q in scope.diff('QT_FOR_PRIVATE') + if map_qt_library(q) not in known_libraries] + dependencies += scope.diff('QMAKE_USE_PRIVATE') \ + + scope.diff('LIBS_PRIVATE') + scope.diff('LIBS') if dependencies: cm_fh.write('{} LIBRARIES\n'.format(ind)) is_framework = False @@ -601,40 +670,50 @@ def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0, is_framework = False -def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, parent_condition: str='', - previous_conditon: str='', *, indent: int=0) -> str: +def write_extend_target(cm_fh: typing.IO[str], target: str, + scope: Scope, parent_condition: str = '', + previous_conditon: str = '', *, + indent: int = 0) -> str: total_condition = scope.condition() if total_condition == 'else': - assert previous_conditon, "Else branch without previous condition in: %s" % scope.file() + assert previous_conditon, \ + "Else branch without previous condition in: %s" % scope.file() total_condition = 'NOT ({})'.format(previous_conditon) if parent_condition: - total_condition = '({}) AND ({})'.format(parent_condition, total_condition) + total_condition = '({}) AND ({})'.format(parent_condition, + total_condition) 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(spaces(indent), target, total_condition, extend_qt_string) + extend_scope = '\n{}extend_target({} CONDITION {}\n' \ + '{})\n'.format(spaces(indent), target, total_condition, + extend_qt_string) if not extend_qt_string: - # Comment out the generated extend_target call because there no sources were found, but keep it commented - # for informational purposes. - extend_scope = ''.join(['#' + line for line in extend_scope.splitlines(keepends=True)]) + # Comment out the generated extend_target call because there + # no sources were found, but keep it commented for + # informational purposes. + extend_scope = ''.join(['#' + line for line in + extend_scope.splitlines(keepends=True)]) cm_fh.write(extend_scope) children = scope.children() if children: prev_condition = '' for c in children: - prev_condition = write_extend_target(cm_fh, target, c, total_condition, prev_condition) + prev_condition = write_extend_target(cm_fh, target, c, + total_condition, + prev_condition) return total_condition -def write_main_part(cm_fh: IO[str], name: str, typename: str, +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, + indent: int = 0, **kwargs: typing.Any): write_header(cm_fh, name, typename, indent=indent) @@ -657,8 +736,8 @@ def write_main_part(cm_fh: IO[str], name: str, typename: str, write_extend_target(cm_fh, name, c, '', indent=indent) - -def write_module(cm_fh: IO[str], scope: Scope, *, indent: int=0) -> None: +def write_module(cm_fh: typing.IO[str], scope: Scope, *, + indent: int = 0) -> None: module_name = scope.getTarget() assert module_name.startswith('Qt') @@ -669,22 +748,26 @@ def write_module(cm_fh: IO[str], scope: Scope, *, indent: int=0) -> None: extra.append('NO_MODULE_HEADERS') write_main_part(cm_fh, module_name[2:], 'Module', 'add_qt_module', scope, - extra_lines=extra, indent=indent, known_libraries={'Qt::Core', }) - + extra_lines=extra, indent=indent, + known_libraries={'Qt::Core', }) if 'qt_tracepoints' in scope.get('CONFIG'): - tracepoints = map_to_file(scope.getString('TRACEPOINT_PROVIDER'), scope.basedir(), scope.currentdir()) - cm_fh.write('\n\n{}qt_create_tracepoints({} {})\n'.format(spaces(indent), module_name[2:], tracepoints)) + tracepoints = map_to_file(scope.getString('TRACEPOINT_PROVIDER'), + scope.basedir(), scope.currentdir()) + cm_fh.write('\n\n{}qt_create_tracepoints({} {})\n' + .format(spaces(indent), module_name[2:], tracepoints)) -def write_tool(cm_fh: IO[str], scope: Scope, *, indent: int=0) -> None: +def write_tool(cm_fh: typing.IO[str], scope: Scope, *, + indent: int = 0) -> None: tool_name = scope.getTarget() write_main_part(cm_fh, tool_name, 'Tool', 'add_qt_tool', scope, indent=indent, known_libraries={'Qt::Core', }) -def write_test(cm_fh: IO[str], scope: Scope, *, indent: int=0) -> None: +def write_test(cm_fh: typing.IO[str], scope: Scope, *, + indent: int = 0) -> None: test_name = scope.getTarget() assert test_name @@ -692,16 +775,18 @@ def write_test(cm_fh: IO[str], scope: Scope, *, indent: int=0) -> None: indent=indent, known_libraries={'Qt::Core', 'Qt::Test', }) -def write_binary(cm_fh: IO[str], scope: Scope, gui: bool=False, *, indent: int=0) -> None: +def write_binary(cm_fh: typing.IO[str], scope: Scope, + gui: bool = False, *, indent: int = 0) -> None: binary_name = scope.getTarget() assert binary_name - extra = ['GUI',] if gui else [] + extra = ['GUI', ] if gui else [] write_main_part(cm_fh, binary_name, 'Binary', 'add_qt_executable', scope, - extra_lines=extra, indent=indent, known_libraries={'Qt::Core', }) + extra_lines=extra, indent=indent, + known_libraries={'Qt::Core', }) -def write_plugin(cm_fh, scope, *, indent: int=0): +def write_plugin(cm_fh, scope, *, indent: int = 0): plugin_name = scope.getTarget() assert plugin_name @@ -709,7 +794,8 @@ def write_plugin(cm_fh, scope, *, indent: int=0): indent=indent, known_libraries={'QtCore', }) -def handle_app_or_lib(scope: Scope, cm_fh: IO[str], *, indent=0) -> None: +def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, + indent: int = 0) -> None: assert scope.getTemplate() in ('app', 'lib') is_lib = scope.getTemplate() == 'lib' @@ -722,7 +808,8 @@ def handle_app_or_lib(scope: Scope, cm_fh: IO[str], *, indent=0) -> None: elif 'qt_tool' in scope.get('_LOADED', []): write_tool(cm_fh, scope, indent=indent) else: - if 'testcase' in scope.get('CONFIG') or 'testlib' in scope.get('CONFIG'): + if 'testcase' in scope.get('CONFIG') \ + or 'testlib' in scope.get('CONFIG'): write_test(cm_fh, scope, indent=indent) else: gui = 'console' not in scope.get('CONFIG') @@ -730,19 +817,27 @@ def handle_app_or_lib(scope: Scope, cm_fh: IO[str], *, indent=0) -> None: docs = scope.getString("QMAKE_DOCS") if docs: - cm_fh.write("\n{}add_qt_docs({})\n".format(spaces(indent), map_to_file(docs, scope.basedir(), scope.currentdir()))) + cm_fh.write("\n{}add_qt_docs({})\n" + .format(spaces(indent), + map_to_file(docs, scope.basedir(), + scope.currentdir()))) -def handle_qt_for_config(scope: Scope, cm_fh: IO[str], *, indent: int=0) -> None: +def handle_qt_for_config(scope: Scope, cm_fh: typing.IO[str], *, + indent: int = 0) -> None: for config in scope.get("QT_FOR_CONFIG") or []: lib = map_qt_library(config) if lib.endswith("Private"): - cm_fh.write('{}qt_pull_features_into_current_scope(PRIVATE_FEATURES {})\n'.format(spaces(indent), lib[:-len("Private")])) + cm_fh.write('{}qt_pull_features_into_current_scope' + '(PRIVATE_FEATURES {})\n' + .format(spaces(indent), lib[:-len("Private")])) else: - cm_fh.write('{}qt_pull_features_into_current_scope(PUBLIC_FEATURES {})\n'.format(spaces(indent), lib)) + cm_fh.write('{}qt_pull_features_into_current_scope' + '(PUBLIC_FEATURES {})\n'.format(spaces(indent), lib)) -def cmakeify_scope(scope: Scope, cm_fh: IO[str], *, indent: int=0) -> None: +def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, + indent: int = 0) -> None: template = scope.getTemplate() handle_qt_for_config(scope, cm_fh) if template == 'subdirs': @@ -757,21 +852,27 @@ def cmakeify_scope(scope: Scope, cm_fh: IO[str], *, indent: int=0) -> None: def generate_cmakelists(scope: Scope) -> None: with open(scope.cMakeListsFile(), 'w') as cm_fh: assert scope.file() - cm_fh.write('# Generated from {}.\n\n'.format(os.path.basename(scope.file()))) + cm_fh.write('# Generated from {}.\n\n' + .format(os.path.basename(scope.file()))) cmakeify_scope(scope, cm_fh) -def do_include(scope: Scope, *, debug: bool=False) -> None: +def do_include(scope: Scope, *, debug: bool = False) -> None: for i in scope.get('_INCLUDED', []): dir = scope.basedir() - include_file = map_to_file(i, dir, scope.currentdir(), want_absolute_path=True) + include_file = map_to_file(i, dir, scope.currentdir(), + want_absolute_path=True) + if not include_file: + continue if not os.path.isfile(include_file): print(' XXXX: Failed to include {}.'.format(include_file)) continue include_result = parseProFile(include_file, debug=debug) - include_scope = Scope.FromDict(scope, include_file, include_result.asDict().get('statements'), - '', dir) + include_scope \ + = Scope.FromDict(scope, include_file, + include_result.asDict().get('statements'), + '', dir) do_include(include_scope) @@ -798,7 +899,8 @@ def main() -> None: print(parseresult.asDict()) print('\n#### End of parser result dictionary.\n') - file_scope = Scope.FromDict(None, file, parseresult.asDict().get('statements')) + file_scope = Scope.FromDict(None, file, + parseresult.asDict().get('statements')) if args.debug_pro_structure or args.debug: print('\n\n#### .pro/.pri file structure:') -- cgit v1.2.3 From 6a1ee4de07cbaaeb0583b191fee6258e0a4003e4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 14 Jan 2019 11:35:53 +0100 Subject: CMake: Store Qt features in CMake Cache This is less self-contained than what we have, but significantly speeds up cmake configure/generate runs. This patch also warns when a feature is already defined. Change-Id: I8cab63e208ba98756b47d362a39b462f5ec55e20 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index a1c73fcd5f..80a10bd944 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -823,23 +823,9 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, scope.currentdir()))) -def handle_qt_for_config(scope: Scope, cm_fh: typing.IO[str], *, - indent: int = 0) -> None: - for config in scope.get("QT_FOR_CONFIG") or []: - lib = map_qt_library(config) - if lib.endswith("Private"): - cm_fh.write('{}qt_pull_features_into_current_scope' - '(PRIVATE_FEATURES {})\n' - .format(spaces(indent), lib[:-len("Private")])) - else: - cm_fh.write('{}qt_pull_features_into_current_scope' - '(PUBLIC_FEATURES {})\n'.format(spaces(indent), lib)) - - def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, indent: int = 0) -> None: template = scope.getTemplate() - handle_qt_for_config(scope, cm_fh) if template == 'subdirs': handle_subdir(scope, cm_fh, indent=indent) elif template in ('app', 'lib'): -- cgit v1.2.3 From 9162aa5da9cd29d1231bf268dcee349b6bc25dd3 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 17 Jan 2019 13:41:17 +0100 Subject: CMake: pro2cmake: fix include file handling Do not include the same file over and over again... Change-Id: Ia0748b9ebe58388549ba23ec7e24ce3d9b738987 Reviewed-by: Frederik Gladhorn Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 80a10bd944..33b8ffb6c0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -346,6 +346,7 @@ class Scope: c.dump(indent=indent + 1) def get(self, key: str, default=None) -> typing.List[str]: + assert key != '_INCLUDED' # Special case things that may not recurse! result = [] # type: typing.List[str] if self._parent: @@ -378,6 +379,12 @@ class Scope: return self.getString('TARGET') \ or os.path.splitext(os.path.basename(self.file()))[0] + def getIncludes(self) -> typing.List[str]: + result = [] + for op in self._operations.get('_INCLUDED', []): + result = op.process(result) + return result + class QmakeParser: def __init__(self, *, debug: bool = False) -> None: @@ -844,7 +851,7 @@ def generate_cmakelists(scope: Scope) -> None: def do_include(scope: Scope, *, debug: bool = False) -> None: - for i in scope.get('_INCLUDED', []): + for i in scope.getIncludes(): dir = scope.basedir() include_file = map_to_file(i, dir, scope.currentdir(), want_absolute_path=True) -- cgit v1.2.3 From db3cc891852823ae022f7c16587187df8573f98f Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 17 Jan 2019 15:23:30 +0100 Subject: CMake: Fix include path handling in pro2cmake.py Change-Id: Ice9db97db5bf1fb347bff07bcaf91daac87fa983 Reviewed-by: Frederik Gladhorn Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 33b8ffb6c0..71664869a4 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -853,8 +853,7 @@ def generate_cmakelists(scope: Scope) -> None: def do_include(scope: Scope, *, debug: bool = False) -> None: for i in scope.getIncludes(): dir = scope.basedir() - include_file = map_to_file(i, dir, scope.currentdir(), - want_absolute_path=True) + include_file = i if not include_file: continue if not os.path.isfile(include_file): -- cgit v1.2.3 From d73b06fc3526498a94b46ec6102888556137d08a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 17 Jan 2019 15:30:56 +0100 Subject: CMake: pro2cmake.py: Fix merging of scopes Set new keys in the parent scope instead of just appending to existing scopes. Change-Id: I901c3f418429d8aed56acb39b1dbe566c5468920 Reviewed-by: Frederik Gladhorn Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 71664869a4..6401e52051 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -213,7 +213,10 @@ class Scope: self._add_child(c) for key in other._operations.keys(): - self._operations[key] = other._operations[key] + if key in self._operations: + self._operations[key] += other._operations[key] + else: + self._operations[key] = other._operations[key] def basedir(self) -> str: return self._basedir -- cgit v1.2.3 From 1d7e724e395bf6bf7f6213edb3794d4c8b34ce80 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 17 Jan 2019 17:10:17 +0100 Subject: CMake: pro2cmake: Fix printing of the different operations Change-Id: Ieb33141601c29ec2f3c30c6725179f1fb234e53a Reviewed-by: Frederik Gladhorn Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6401e52051..69c6f1fb81 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -136,7 +136,7 @@ class Operation: def process(self, input): assert(False) - def __str__(self): + def __repr__(self): assert(False) @@ -144,8 +144,8 @@ class AddOperation(Operation): def process(self, input): return input + self._value - def __str__(self): - return '+({})'.format(self._value) + def __repr__(self): + return '+({})'.format(','.join(self._value)) class UniqueAddOperation(Operation): @@ -156,7 +156,7 @@ class UniqueAddOperation(Operation): result += [v, ] return result - def __str__(self): + def __repr__(self): return '*({})'.format(self._value) @@ -164,7 +164,7 @@ class SetOperation(Operation): def process(self, input): return self._value - def __str__(self): + def __repr__(self): return '=({})'.format(self._value) @@ -182,7 +182,7 @@ class RemoveOperation(Operation): result += ['-{}'.format(v), ] return result - def __str__(self): + def __repr__(self): return '-({})'.format(self._value) -- cgit v1.2.3 From e5bee377620de0ca0d80acdd396bbd5804bc2e61 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 17 Jan 2019 17:10:57 +0100 Subject: CMake: pro2cmake.py: Improve debugging dump Make the output of the project structure debug dump more readable. Change-Id: Ib80b41f7fdb8e14ff878284c46f3760d84f4f004 Reviewed-by: Frederik Gladhorn Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 69c6f1fb81..f95ac43ea8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -337,16 +337,23 @@ class Scope: def dump(self, *, indent: int = 0) -> None: ind = ' ' * indent if self._condition == '': - print('{}Scope {} in {}.'.format(ind, self._file, self._basedir)) + print('{}Scope {} in "{}".'.format(ind, self._file, self._basedir)) else: - print('{}Scope {} in {} with condition: {}.' + print('{}Scope {} in "{}" with condition: "{}".' .format(ind, self._file, self._basedir, self._condition)) print('{}Keys:'.format(ind)) - for k in sorted(self._operations.keys()): - print('{} {} = "{}"'.format(ind, k, self._operations.get(k, []))) + keys = self._operations.keys() + if not keys: + print('{} -- NONE --'.format(ind)) + else: + for k in sorted(keys): + print('{} {} = "{}"'.format(ind, k, self._operations.get(k, []))) print('{}Children:'.format(ind)) - for c in self._children: - c.dump(indent=indent + 1) + if not self._children: + print('{} -- NONE --'.format(ind)) + else: + for c in self._children: + c.dump(indent=indent + 1) def get(self, key: str, default=None) -> typing.List[str]: assert key != '_INCLUDED' # Special case things that may not recurse! -- cgit v1.2.3 From 23e3aaf2cdcae292034b57e437d3a9744a3c9468 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 17 Jan 2019 17:11:52 +0100 Subject: CMake: pro2cmake.py: Nicer printing of conditions Do not needlessly add () and NOT all over the conditions. Change-Id: If907357306eb3f8ab5b2878bcad5902171a90476 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f95ac43ea8..8c24ef43f6 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -519,6 +519,8 @@ def map_condition(condition: str) -> str: condition = condition.replace('|', ' OR ') condition = condition.replace('==', ' STREQUAL ') + condition = condition.replace('NOT NOT', '') # remove double negation + cmake_condition = '' for part in condition.split(): # some features contain e.g. linux, that should not be @@ -686,19 +688,40 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, cm_fh.write('{} {}\n'.format(ind, d)) is_framework = False + return set(scope.keys()) - scope.visited_keys() + + +def is_simple_condition(condition: str) -> bool: + return ' ' not in condition or (condition.startswith('NOT ') and ' ' not in condition[4:]) + def write_extend_target(cm_fh: typing.IO[str], target: str, scope: Scope, parent_condition: str = '', - previous_conditon: str = '', *, + previous_condition: str = '', *, indent: int = 0) -> str: total_condition = scope.condition() if total_condition == 'else': - assert previous_conditon, \ + assert previous_condition, \ "Else branch without previous condition in: %s" % scope.file() - total_condition = 'NOT ({})'.format(previous_conditon) + if previous_condition.startswith('NOT '): + total_condition = previous_condition[4:] + elif is_simple_condition(previous_condition): + total_condition = 'NOT {}'.format(previous_condition) + else: + total_condition = 'NOT ({})'.format(previous_condition) if parent_condition: - total_condition = '({}) AND ({})'.format(parent_condition, - total_condition) + if not total_condition: + total_condition = parent_condition + else: + if is_simple_condition(parent_condition) and is_simple_condition(total_condition): + total_condition = '{} AND {}'.format(parent_condition, + total_condition) + elif is_simple_condition(total_condition): + total_condition = '({}) AND {}'.format(parent_condition, total_condition) + elif is_simple_condition(parent_condition): + total_condition = '{} AND ({})'.format(parent_condition, total_condition) + else: + total_condition = '({}) AND ({})'.format(parent_condition, total_condition) extend_qt_io_string = io.StringIO() write_sources_section(extend_qt_io_string, scope) -- cgit v1.2.3 From 3f3517923e979a06cb01e999a7be12d32a14e2b2 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 17 Jan 2019 17:12:58 +0100 Subject: CMake: pro2cmake.py: Ignore 3rdparty code that gets included Ignore include files that go into 3rdparty code. Qt 6 should work with external libraries, so there is no need to drag in 3rdparty include directories, etc. Change-Id: I990f3a8a4983e1458843b4122420b9daec763ccb Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8c24ef43f6..d99863cbcc 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -889,6 +889,9 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: include_file = i if not include_file: continue + if '/3rdparty/' in include_file: + print(' ****: Ignoring include file in 3rdparty: {}.'.format(include_file)) + continue if not os.path.isfile(include_file): print(' XXXX: Failed to include {}.'.format(include_file)) continue -- cgit v1.2.3 From 6e9a2dd3c6994d83f1a24dadba0ecc1350e441dd Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 17 Jan 2019 17:14:19 +0100 Subject: CMake: pro2cmake.py: Visit scopes depth-first for include resolution This avoids some includes being done several times: We used to generate scopes for the include files, add them to our current scope and then traverse the children of the current scope. Switch the order to avoid traversing the newly added scopes twice. Change-Id: Icb9c53c9f54772d3305e0fb121824c23d5469e09 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index d99863cbcc..e37c765f7a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -884,6 +884,9 @@ def generate_cmakelists(scope: Scope) -> None: def do_include(scope: Scope, *, debug: bool = False) -> None: + for c in scope.children(): + do_include(c) + for i in scope.getIncludes(): dir = scope.basedir() include_file = i @@ -906,9 +909,6 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: scope.merge(include_scope) - for c in scope.children(): - do_include(c) - def main() -> None: args = _parse_commandline() -- cgit v1.2.3 From cc593426eb26b441285b75708cd3374075a31270 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 18 Jan 2019 12:40:29 +0100 Subject: CMake: pro2cmake.py: Better indentation in debug dump Indent the individual parts of a scope relative to the scope itself. This makes things a bit more readable. Change-Id: Iffbffd69bc960118f9657a1b7fb488da3c7b48dd Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e37c765f7a..78c75683a2 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -341,14 +341,14 @@ class Scope: else: print('{}Scope {} in "{}" with condition: "{}".' .format(ind, self._file, self._basedir, self._condition)) - print('{}Keys:'.format(ind)) + print('{} Keys:'.format(ind)) keys = self._operations.keys() if not keys: print('{} -- NONE --'.format(ind)) else: for k in sorted(keys): print('{} {} = "{}"'.format(ind, k, self._operations.get(k, []))) - print('{}Children:'.format(ind)) + print('{} Children:'.format(ind)) if not self._children: print('{} -- NONE --'.format(ind)) else: -- cgit v1.2.3 From 29ff5f77ee9dbdd5a4aac292fee2599e2ea9cfc1 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 18 Jan 2019 12:44:15 +0100 Subject: CMake: pro2cmake.py: Do not double-report include files Do not add scopes that are going to get merged into their parent scope as a child of the parent scope. This leads to the information of the child scope being duplicated. Change-Id: If4d6a83b9c9eac477959e7774e9cf65fd4df98e6 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 78c75683a2..9b819e58ec 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -901,9 +901,9 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: include_result = parseProFile(include_file, debug=debug) include_scope \ - = Scope.FromDict(scope, include_file, + = Scope.FromDict(None, include_file, include_result.asDict().get('statements'), - '', dir) + '', dir) # This scope will be merged into scope, so no parent_scope! do_include(include_scope) -- cgit v1.2.3 From 6ad93525445e258b76cc0376387a0f93dcb9b76f Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 18 Jan 2019 12:02:04 +0100 Subject: CMake: pro2cmake.py: Do not print trailing / in directory names Change-Id: I3da83b8908791e033cf33221631d2fe988f83957 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9b819e58ec..ede2a325b9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -661,6 +661,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, if includes: cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind)) for i in includes: + i = i.rstrip('/') or ('/') cm_fh.write('{} {}\n'.format(ind, i)) dependencies = [map_qt_library(q) for q in scope.diff('QT') -- cgit v1.2.3 From 024edf4bfdd8652f0a87a90044127dc582239590 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 18 Jan 2019 12:43:11 +0100 Subject: CMake: pro2cmake.py: Report keys that were not converted in each scope This makes it easier to see what is still missing. Change-Id: I186abd609f20be680ac0943ac89068f764142a79 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 56 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 10 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ede2a325b9..f4d929ab0c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -207,6 +207,10 @@ class Scope: self._condition = map_condition(condition) self._children = [] # type: typing.List[Scope] self._operations = {} # type: typing.Dict[str, typing.List[Operation]] + self._visited_keys = set() # type: typing.Set[str] + + def reset_visited_keys(self): + self._visited_keys = set() def merge(self, other: 'Scope') -> None: for c in other._children: @@ -355,8 +359,15 @@ class Scope: for c in self._children: c.dump(indent=indent + 1) + def keys(self): + return self._operations.keys() + + def visited_keys(self): + return self._visited_keys; + def get(self, key: str, default=None) -> typing.List[str]: assert key != '_INCLUDED' # Special case things that may not recurse! + self._visited_keys.add(key) result = [] # type: typing.List[str] if self._parent: @@ -619,10 +630,12 @@ def write_scope_header(cm_fh: typing.IO[str], *, indent: int = 0): def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0, known_libraries=set()) -> None: + indent: int = 0, known_libraries=set()) -> typing.Set[str]: ind = spaces(indent) + scope.reset_visited_keys(); plugin_type = scope.get('PLUGIN_TYPE') + if plugin_type: cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0])) @@ -696,6 +709,18 @@ def is_simple_condition(condition: str) -> bool: return ' ' not in condition or (condition.startswith('NOT ') and ' ' not in condition[4:]) +def write_ignored_keys(scope: Scope, ignored_keys, indent) -> str: + result = '' + for k in sorted(ignored_keys): + if k == '_INCLUDED' or k == 'TARGET' or k == 'QMAKE_DOCS': + # All these keys are actually reported using "non-standard" means:-) + continue + values = scope.get(k) + value_string = '' if not values else '"' + '" "'.join(scope.get(k)) + '"' + result += '{}# {} = {}\n'.format(indent, k, value_string) + return result + + def write_extend_target(cm_fh: typing.IO[str], target: str, scope: Scope, parent_condition: str = '', previous_condition: str = '', *, @@ -725,19 +750,27 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, total_condition = '({}) AND ({})'.format(parent_condition, total_condition) extend_qt_io_string = io.StringIO() - write_sources_section(extend_qt_io_string, scope) + ignored_keys = write_sources_section(extend_qt_io_string, scope) extend_qt_string = extend_qt_io_string.getvalue() + ignored_keys_report = write_ignored_keys(scope, ignored_keys, spaces(indent + 1)) + if extend_qt_string and ignored_keys_report: + ignored_keys_report = '\n' + ignored_keys_report + extend_scope = '\n{}extend_target({} CONDITION {}\n' \ - '{})\n'.format(spaces(indent), target, total_condition, - extend_qt_string) + '{}{})\n'.format(spaces(indent), target, total_condition, + extend_qt_string, ignored_keys_report) if not extend_qt_string: - # Comment out the generated extend_target call because there - # no sources were found, but keep it commented for - # informational purposes. - extend_scope = ''.join(['#' + line for line in - extend_scope.splitlines(keepends=True)]) + if ignored_keys_report: + # Comment out the generated extend_target call because there + # no sources were found, but keep it commented for + # informational purposes. + extend_scope = ''.join(['#' + line for line in + extend_scope.splitlines(keepends=True)]) + else: + extend_scope = '' # Nothing to report, so don't! + cm_fh.write(extend_scope) children = scope.children() @@ -762,7 +795,10 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, for extra_line in extra_lines: cm_fh.write('{} {}\n'.format(spaces(indent), extra_line)) - write_sources_section(cm_fh, scope, indent=indent, **kwargs) + ignored_keys = write_sources_section(cm_fh, scope, indent=indent, **kwargs) + ignored_keys_report = write_ignored_keys(scope, ignored_keys, spaces(indent + 1)) + if ignored_keys_report: + cm_fh.write(ignored_keys_report) # Footer: cm_fh.write('{})\n'.format(spaces(indent))) -- cgit v1.2.3 From 2f504669951e3d5538b55ce4dbad2e05f466ff19 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 18 Jan 2019 12:46:08 +0100 Subject: CMake: pro2cmake.py: Give up on faithful representation of -=, +=, *= and = Having a good representation is nice, but unfortunately very hard: pro2cmake.py sees just a small part of the entire qmake build system at a time, so the information it has is incomplete to start with. It is not worthwhile to pass the supposedly full information around -- which will then need to get de-duplicated again -- considering that the information is not complete in the first place. So go back to the simplistic approach we used earsier. That is good enough and does not suffer from the problem of having multi-part arguments getting messed up when the scopes get de-duplicated. Change-Id: I553329650d6b8046f7ad8b1282e3d0c961293a28 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 54 ++++++++++--------------------------------------- 1 file changed, 11 insertions(+), 43 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f4d929ab0c..ca53234f06 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -228,25 +228,6 @@ class Scope: def currentdir(self) -> str: return self._currentdir - def diff(self, key: str, - default: typing.Optional[typing.List[str]] = []) \ - -> typing.List[str]: - mine = self.get(key, default) - - if self._parent: - parent = self._parent.get(key, default) - if (parent == mine): - return [] - - parent_set = set(parent) - mine_set = set(mine) - - added = [x for x in mine if x not in parent_set] - removed = [x for x in parent if x not in mine_set] - - return added + list('# {}'.format(x) for x in removed) - return mine - @staticmethod def FromDict(parent_scope: typing.Optional['Scope'], file: str, statements, cond: str = '', base_dir: str = ''): @@ -366,19 +347,9 @@ class Scope: return self._visited_keys; def get(self, key: str, default=None) -> typing.List[str]: - assert key != '_INCLUDED' # Special case things that may not recurse! self._visited_keys.add(key) result = [] # type: typing.List[str] - if self._parent: - result = self._parent.get(key, default) - else: - if default: - if isinstance(default, list): - result = default - else: - result = [str(default), ] - for op in self._operations.get(key, []): result = op.process(result) return result @@ -401,10 +372,7 @@ class Scope: or os.path.splitext(os.path.basename(self.file()))[0] def getIncludes(self) -> typing.List[str]: - result = [] - for op in self._operations.get('_INCLUDED', []): - result = op.process(result) - return result + return self.get('_INCLUDED', []) class QmakeParser: @@ -639,10 +607,10 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, if plugin_type: cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0])) - sources = scope.diff('SOURCES') + scope.diff('HEADERS') \ - + scope.diff('OBJECTIVE_SOURCES') + scope.diff('NO_PCH_SOURCES') \ - + scope.diff('FORMS') - resources = scope.diff('RESOURCES') + sources = scope.get('SOURCES') + scope.get('HEADERS') \ + + scope.get('OBJECTIVE_SOURCES') + scope.get('NO_PCH_SOURCES') \ + + scope.get('FORMS') + resources = scope.get('RESOURCES') if resources: qrc_only = True for r in resources: @@ -663,26 +631,26 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, for l in sort_sources(sources): cm_fh.write('{} {}\n'.format(ind, l)) - defines = scope.diff('DEFINES') + defines = scope.get('DEFINES') if defines: cm_fh.write('{} DEFINES\n'.format(ind)) for d in defines: d = d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"') cm_fh.write('{} {}\n'.format(ind, d)) - includes = scope.diff('INCLUDEPATH') + includes = scope.get('INCLUDEPATH') if includes: cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind)) for i in includes: i = i.rstrip('/') or ('/') cm_fh.write('{} {}\n'.format(ind, i)) - dependencies = [map_qt_library(q) for q in scope.diff('QT') + dependencies = [map_qt_library(q) for q in scope.get('QT') if map_qt_library(q) not in known_libraries] - dependencies += [map_qt_library(q) for q in scope.diff('QT_FOR_PRIVATE') + dependencies += [map_qt_library(q) for q in scope.get('QT_FOR_PRIVATE') if map_qt_library(q) not in known_libraries] - dependencies += scope.diff('QMAKE_USE_PRIVATE') \ - + scope.diff('LIBS_PRIVATE') + scope.diff('LIBS') + dependencies += scope.get('QMAKE_USE_PRIVATE') \ + + scope.get('LIBS_PRIVATE') + scope.get('LIBS') if dependencies: cm_fh.write('{} LIBRARIES\n'.format(ind)) is_framework = False -- cgit v1.2.3 From 0690c145db99da66dd3e2dc936848862661a161b Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 22 Jan 2019 14:16:41 +0100 Subject: CMake: pro2cmake.py: Better output in debug dumps Report more error conditions in debug dump and make the output easier to read in the non-error case by wrapping all strings in "". Change-Id: I3c99deda3dfa27dcc0c9ce2800bfb891747e6934 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ca53234f06..ff814efcf2 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -139,13 +139,27 @@ class Operation: def __repr__(self): assert(False) + def _dump(self): + if not self._value: + return '' + + if not isinstance(self._value, list): + return '' + + result = [] + for i in self._value: + if not i: + result.append('') + else: + result.append(str(i)) + return '"' + '", "'.join(result) + '"' class AddOperation(Operation): def process(self, input): return input + self._value def __repr__(self): - return '+({})'.format(','.join(self._value)) + return '+({})'.format(self._dump()) class UniqueAddOperation(Operation): @@ -157,7 +171,7 @@ class UniqueAddOperation(Operation): return result def __repr__(self): - return '*({})'.format(self._value) + return '*({})'.format(self._dump()) class SetOperation(Operation): @@ -165,7 +179,7 @@ class SetOperation(Operation): return self._value def __repr__(self): - return '=({})'.format(self._value) + return '=({})'.format(self._dump()) class RemoveOperation(Operation): @@ -183,7 +197,7 @@ class RemoveOperation(Operation): return result def __repr__(self): - return '-({})'.format(self._value) + return '-({})'.format(self._dump()) class Scope: -- cgit v1.2.3 From 1c1bb53c5508c3015d7d24262af53647cca62622 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 25 Jan 2019 15:41:02 +0100 Subject: CMake: pro2cmake.py: Fix pyls warnings Fix pyls warnings in pro2cmake.py as well as its tests. Change-Id: Ib8ee1daa9b97735d13c0fde43616daa46de9e171 Reviewed-by: Frederik Gladhorn Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 54 ++++++++++++++++++++++++---------------- util/cmake/tests/test_parsing.py | 16 +++++++++++- 2 files changed, 48 insertions(+), 22 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ff814efcf2..b87b20a8c2 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -154,6 +154,7 @@ class Operation: result.append(str(i)) return '"' + '", "'.join(result) + '"' + class AddOperation(Operation): def process(self, input): return input + self._value @@ -346,7 +347,8 @@ class Scope: print('{} -- NONE --'.format(ind)) else: for k in sorted(keys): - print('{} {} = "{}"'.format(ind, k, self._operations.get(k, []))) + print('{} {} = "{}"' + .format(ind, k, self._operations.get(k, []))) print('{} Children:'.format(ind)) if not self._children: print('{} -- NONE --'.format(ind)) @@ -358,7 +360,7 @@ class Scope: return self._operations.keys() def visited_keys(self): - return self._visited_keys; + return self._visited_keys def get(self, key: str, default=None) -> typing.List[str]: self._visited_keys.add(key) @@ -404,13 +406,13 @@ class QmakeParser: 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('{') + + 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('$') + pp.Literal('[') + Identifier + pp.Literal(']')) ))) # Do not match word ending in '\' since that breaks line @@ -612,9 +614,10 @@ def write_scope_header(cm_fh: typing.IO[str], *, indent: int = 0): def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0, known_libraries=set()) -> typing.Set[str]: + indent: int = 0, known_libraries=set()) \ + -> typing.Set[str]: ind = spaces(indent) - scope.reset_visited_keys(); + scope.reset_visited_keys() plugin_type = scope.get('PLUGIN_TYPE') @@ -688,17 +691,19 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, 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, ignored_keys, indent) -> str: result = '' for k in sorted(ignored_keys): if k == '_INCLUDED' or k == 'TARGET' or k == 'QMAKE_DOCS': - # All these keys are actually reported using "non-standard" means:-) + # All these keys are actually reported already continue values = scope.get(k) - value_string = '' if not values else '"' + '" "'.join(scope.get(k)) + '"' + value_string = '' if not values \ + else '"' + '" "'.join(scope.get(k)) + '"' result += '{}# {} = {}\n'.format(indent, k, value_string) return result @@ -721,27 +726,32 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, if not total_condition: total_condition = parent_condition else: - if is_simple_condition(parent_condition) and is_simple_condition(total_condition): + if is_simple_condition(parent_condition) \ + and is_simple_condition(total_condition): total_condition = '{} AND {}'.format(parent_condition, total_condition) elif is_simple_condition(total_condition): - total_condition = '({}) AND {}'.format(parent_condition, total_condition) + total_condition = '({}) AND {}'.format(parent_condition, + total_condition) elif is_simple_condition(parent_condition): - total_condition = '{} AND ({})'.format(parent_condition, total_condition) + total_condition = '{} AND ({})'.format(parent_condition, + total_condition) else: - total_condition = '({}) AND ({})'.format(parent_condition, total_condition) + total_condition = '({}) AND ({})'.format(parent_condition, + total_condition) extend_qt_io_string = io.StringIO() ignored_keys = write_sources_section(extend_qt_io_string, scope) extend_qt_string = extend_qt_io_string.getvalue() - ignored_keys_report = write_ignored_keys(scope, ignored_keys, spaces(indent + 1)) + ignored_keys_report = write_ignored_keys(scope, ignored_keys, + spaces(indent + 1)) if extend_qt_string and ignored_keys_report: ignored_keys_report = '\n' + ignored_keys_report extend_scope = '\n{}extend_target({} CONDITION {}\n' \ '{}{})\n'.format(spaces(indent), target, total_condition, - extend_qt_string, ignored_keys_report) + extend_qt_string, ignored_keys_report) if not extend_qt_string: if ignored_keys_report: @@ -778,7 +788,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, cm_fh.write('{} {}\n'.format(spaces(indent), extra_line)) ignored_keys = write_sources_section(cm_fh, scope, indent=indent, **kwargs) - ignored_keys_report = write_ignored_keys(scope, ignored_keys, spaces(indent + 1)) + ignored_keys_report = write_ignored_keys(scope, ignored_keys, + spaces(indent + 1)) if ignored_keys_report: cm_fh.write(ignored_keys_report) @@ -912,7 +923,8 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: if not include_file: continue if '/3rdparty/' in include_file: - print(' ****: Ignoring include file in 3rdparty: {}.'.format(include_file)) + print(' ****: Ignoring include file in 3rdparty: {}.' + .format(include_file)) continue if not os.path.isfile(include_file): print(' XXXX: Failed to include {}.'.format(include_file)) @@ -922,7 +934,7 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: include_scope \ = Scope.FromDict(None, include_file, include_result.asDict().get('statements'), - '', dir) # This scope will be merged into scope, so no parent_scope! + '', dir) # This scope will be merged into scope! do_include(include_scope) diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index 78ac7fed26..0802fe4742 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -49,7 +49,9 @@ def evaluate_condition(to_validate): assert 'condition' in to_validate assert 'statements' in to_validate - return (to_validate['condition'], to_validate['statements'], to_validate.get('else_statements', {})) + return (to_validate['condition'], + to_validate['statements'], + to_validate.get('else_statements', {})) def validate_default_else_test(file_name): @@ -105,22 +107,28 @@ def test_else2(): def test_else3(): validate_default_else_test(_tests_path + '/data/else3.pro') + def test_else4(): validate_default_else_test(_tests_path + '/data/else4.pro') + def test_else5(): validate_default_else_test(_tests_path + '/data/else5.pro') + def test_else6(): validate_default_else_test(_tests_path + '/data/else6.pro') + def test_else7(): result = parse_file(_tests_path + '/data/else7.pro') assert len(result) == 1 + def test_else8(): validate_default_else_test(_tests_path + '/data/else8.pro') + def test_include(): result = parse_file(_tests_path + '/data/include.pro') assert len(result) == 3 @@ -130,6 +138,7 @@ def test_include(): assert include.get('included', '') == 'foo' validate_op('B', '=', ['23'], result[2]) + def test_load(): result = parse_file(_tests_path + '/data/load.pro') assert len(result) == 3 @@ -139,24 +148,29 @@ def test_load(): assert load.get('loaded', '') == 'foo' validate_op('B', '=', ['23'], result[2]) + def test_definetest(): result = parse_file(_tests_path + '/data/definetest.pro') assert len(result) == 1 assert result[0] == [] + def test_unset(): result = parse_file(_tests_path + '/data/unset.pro') assert len(result) == 1 assert result[0] == [] + def test_quoted(): result = parse_file(_tests_path + '/data/quoted.pro') assert len(result) == 1 + def test_complex_values(): result = parse_file(_tests_path + '/data/complex_values.pro') assert len(result) == 1 + def test_function_if(): result = parse_file(_tests_path + '/data/function_if.pro') assert len(result) == 1 -- cgit v1.2.3 From 0e2762651ccbec541f59c7af5078fa69bd23d130 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 22 Jan 2019 14:20:47 +0100 Subject: CMake: pro2cmake.py: merge extend_targets with identical conditions This should somewhat simplify the generated CMakeLists.txt files, especially in complex cases like corelib or network. Change-Id: I208c8f99a64cbb6c97d51dd3ae428bb234982202 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 76 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 14 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b87b20a8c2..3130aeea02 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -223,6 +223,7 @@ class Scope: self._children = [] # type: typing.List[Scope] self._operations = {} # type: typing.Dict[str, typing.List[Operation]] self._visited_keys = set() # type: typing.Set[str] + self._total_condition = None # type: typing.Optional[str] def reset_visited_keys(self): self._visited_keys = set() @@ -322,11 +323,18 @@ class Scope: return self._file or '' def cMakeListsFile(self) -> str: + assert self.basedir() return os.path.join(self.basedir(), 'CMakeLists.txt') def condition(self) -> str: return self._condition + def set_total_condition(self, condition: str) -> None: + self._total_condition = condition + + def total_condition(self) -> typing.Optional[str]: + return self._total_condition + def _add_child(self, scope: 'Scope') -> None: scope._parent = self self._children.append(scope) @@ -708,10 +716,8 @@ def write_ignored_keys(scope: Scope, ignored_keys, indent) -> str: return result -def write_extend_target(cm_fh: typing.IO[str], target: str, - scope: Scope, parent_condition: str = '', - previous_condition: str = '', *, - indent: int = 0) -> str: +def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', + previous_condition: str = '') -> str: total_condition = scope.condition() if total_condition == 'else': assert previous_condition, \ @@ -740,6 +746,18 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, total_condition = '({}) AND ({})'.format(parent_condition, total_condition) + scope.set_total_condition(total_condition) + + prev_condition = '' + for c in scope.children(): + prev_condition = recursive_evaluate_scope(c, total_condition, + prev_condition) + + return total_condition + + +def write_extend_target(cm_fh: typing.IO[str], target: str, + scope: Scope, indent: int = 0): extend_qt_io_string = io.StringIO() ignored_keys = write_sources_section(extend_qt_io_string, scope) extend_qt_string = extend_qt_io_string.getvalue() @@ -750,7 +768,8 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, ignored_keys_report = '\n' + ignored_keys_report extend_scope = '\n{}extend_target({} CONDITION {}\n' \ - '{}{})\n'.format(spaces(indent), target, total_condition, + '{}{})\n'.format(spaces(indent), target, + scope.total_condition(), extend_qt_string, ignored_keys_report) if not extend_qt_string: @@ -765,15 +784,30 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, cm_fh.write(extend_scope) - children = scope.children() - if children: - prev_condition = '' - for c in children: - prev_condition = write_extend_target(cm_fh, target, c, - total_condition, - prev_condition) - return total_condition +def flatten_scopes(scope: Scope) -> typing.List[Scope]: + result = [] # type: typing.List[Scope] + for c in scope.children(): + result.append(c) + result += flatten_scopes(c) + return result + + +def merge_scopes(scopes: typing.List[Scope]) -> typing.List[Scope]: + result = [] # type: typing.List[Scope] + + current_scope = None + for scope in scopes: + if not current_scope \ + or scope.total_condition() != current_scope.total_condition(): + if current_scope: + result.append(current_scope) + current_scope = scope + continue + + current_scope.merge(scope) + + return result def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, @@ -802,8 +836,22 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, write_scope_header(cm_fh, indent=indent) + # Evaluate total condition of all scopes: for c in scope.children(): - write_extend_target(cm_fh, name, c, '', indent=indent) + recursive_evaluate_scope(c) + + # Get a flat list of all scopes but the main one: + scopes = flatten_scopes(scope) + + scopes = sorted(scopes, key=lambda x: x.total_condition()) + print("xxxxxx Sorted to {} scopes!".format(len(scopes))) + + # Merge scopes with identical conditions: + scopes = merge_scopes(scopes) + print("xxxxxx Merged to {} scopes!".format(len(scopes))) + + for c in scopes: + write_extend_target(cm_fh, name, c, indent=indent) def write_module(cm_fh: typing.IO[str], scope: Scope, *, -- cgit v1.2.3 From 0a9e5e139a2c23553f4046897c3be4b1df3dfdfa Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 22 Jan 2019 14:23:59 +0100 Subject: CMake: pro2cmake.py: Fix handling of complex else branches Return the current scope's condition, not the total condition from recursive_evaluate_scope. That is the part the "else" referrs to. The parent_condition stays identical for all branches, so that should not get negated. Change-Id: I0da91483d4d748d01a29ac16890d709d8d659843 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3130aeea02..114677e86e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -718,7 +718,8 @@ def write_ignored_keys(scope: Scope, ignored_keys, indent) -> str: def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', previous_condition: str = '') -> str: - total_condition = scope.condition() + current_condition = scope.condition() + total_condition = current_condition if total_condition == 'else': assert previous_condition, \ "Else branch without previous condition in: %s" % scope.file() @@ -753,7 +754,7 @@ def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', prev_condition = recursive_evaluate_scope(c, total_condition, prev_condition) - return total_condition + return current_condition def write_extend_target(cm_fh: typing.IO[str], target: str, -- cgit v1.2.3 From 404d0b59754a4568264c285e7bf517d1c01d8228 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 23 Jan 2019 12:56:11 +0100 Subject: CMake: pro2cmake.py: Resolve file names for RESOURCES Change-Id: I821cdc88c7f45260997e118ab2abfd7ba6526b75 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 114677e86e..0e4ed74362 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -259,7 +259,7 @@ class Scope: value = statement.get('value', []) assert key != '' - if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH') \ + if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH', 'RESOURCES',) \ or key.endswith('_HEADERS') \ or key.endswith('_SOURCES'): value = [map_to_file(v, scope.basedir(), -- cgit v1.2.3 From aee1d1e96b100c503344d86779ece72325ac17e9 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 23 Jan 2019 12:57:06 +0100 Subject: CMake: pro2cmake.py: Work on mapping of conditions to cmake Make sure complex things in qmake (e.g. function calls, etc.) are mapped better to cmake. When there is no way to properly map the qmake construct, then make sure to map everything into one identifier. This is to keep the structure of the condition simple. Change-Id: I6d88e0cb85fce1041bbfdc96604dab7bd4e65856 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0e4ed74362..52e67e482d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -517,6 +517,14 @@ def parseProFile(file: str, *, debug=False): def map_condition(condition: str) -> str: + re.sub(r"\bif\s*\((.*?)\)", r"\1", condition) + re.sub(r"\bisEmpty\s*\((.*?)\)", r"\1 STREQUAL \"\"", condition) + re.sub(r"\bcontains\s*\((.*?), (.*)?\)", r"\1___contains___\2", condition) + + condition = condition.replace('*', '_x_') + condition = condition.replace('.$$', '__ss_') + condition = condition.replace('$$', '_ss_') + condition = condition.replace('!', 'NOT ') condition = condition.replace('&&', ' AND ') condition = condition.replace('|', ' OR ') -- cgit v1.2.3 From 2cdef0f5278d49fd868ed358c240c47fe43bcbe2 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 23 Jan 2019 16:38:07 +0100 Subject: CMake: pro2cmake.py: More mappings! Add some more mappings from qmake to CMake equivalents conditions. Change-Id: I57b7bea9d6628e1c2b8d6ae88f799219942b571e Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 2ddeee98c1..1998defeff 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -183,11 +183,18 @@ platform_mapping = { 'msvc': 'MSVC', 'clang': 'CLANG', 'gcc': 'GCC', + 'icc': 'ICC', 'osx': 'APPLE_OSX', + 'ios': 'APPLE_IOS', 'freebsd': 'FREEBSD', + 'openbsd': 'OPENBSD', + 'netbsd': 'NETBSD', 'haiku': 'HAIKU', 'netbsd': 'NETBSD', 'mac': 'APPLE_OSX', + 'macx': 'APPLE_OSX', + 'macos': 'APPLE_OSX', + 'macx-icc': '(APPLE_OSX AND ICC)', } -- cgit v1.2.3 From 4a2562e5db1605ba2d99445e6ea2bda69867a4d7 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 23 Jan 2019 16:40:23 +0100 Subject: CMake: pro2cmake.py: Simplify conditions Use pysym to simplify conditions in extend_target. Do some manual changes to the condition based on domain knowledge. Change-Id: I7fbb9ebc93b620a483c6a3a796d84c9bc0e36ef7 Reviewed-by: Simon Hausmann --- util/cmake/Pipfile | 1 + util/cmake/pro2cmake.py | 156 +++++++++++++++++++++++++++++-- util/cmake/tests/test_logic_mapping.py | 165 +++++++++++++++++++++++++++++++++ 3 files changed, 314 insertions(+), 8 deletions(-) create mode 100755 util/cmake/tests/test_logic_mapping.py (limited to 'util/cmake') diff --git a/util/cmake/Pipfile b/util/cmake/Pipfile index d7e1905378..7fbf716eb8 100644 --- a/util/cmake/Pipfile +++ b/util/cmake/Pipfile @@ -7,6 +7,7 @@ name = "pypi" pytest = "*" mypy = "*" pyparsing = "*" +sympy = "*" [dev-packages] diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 52e67e482d..fb55988600 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -33,6 +33,8 @@ import re import io import typing +from sympy.logic import (simplify_logic, And, Or, Not,) +from sympy.core import SympifyError import pyparsing as pp from helper import map_qt_library, map_qt_base_library, featureName, \ @@ -517,9 +519,13 @@ def parseProFile(file: str, *, debug=False): def map_condition(condition: str) -> str: - re.sub(r"\bif\s*\((.*?)\)", r"\1", condition) - re.sub(r"\bisEmpty\s*\((.*?)\)", r"\1 STREQUAL \"\"", condition) - re.sub(r"\bcontains\s*\((.*?), (.*)?\)", r"\1___contains___\2", condition) + print('##### Mapping condition: {}.'.format(condition)) + re.sub(r'if\s*\((.*?)\)', r'\1', condition) + re.sub(r'(^|[^a-zA-Z0-9_])isEmpty\s*\((.*?)\)', r'\2_ISEMPTY', condition) + re.sub(r'(^|[^a-zA-Z0-9_])contains\s*\((.*?), (.*)?\)', + r'\2___contains___\3', condition) + re.sub(r'\s*==\s*', '___STREQUAL___', condition) + print(' # after regexp: {}.'.format(condition)) condition = condition.replace('*', '_x_') condition = condition.replace('.$$', '__ss_') @@ -528,9 +534,6 @@ def map_condition(condition: str) -> str: condition = condition.replace('!', 'NOT ') condition = condition.replace('&&', ' AND ') condition = condition.replace('|', ' OR ') - condition = condition.replace('==', ' STREQUAL ') - - condition = condition.replace('NOT NOT', '') # remove double negation cmake_condition = '' for part in condition.split(): @@ -550,7 +553,6 @@ def map_condition(condition: str) -> str: part = part.replace('true', 'ON') part = part.replace('false', 'OFF') cmake_condition += ' ' + part - return cmake_condition.strip() @@ -724,6 +726,144 @@ def write_ignored_keys(scope: Scope, ignored_keys, indent) -> str: return result +def _iterate_expr_tree(expr, op, matches): + assert expr.func == op + keepers = () + for arg in expr.args: + if arg in matches: + matches = tuple(x for x in matches if x != arg) + elif arg == op: + (matches, extra_keepers) = _iterate_expr_tree(arg, op, matches) + keepers = (*keepers, *extra_keepers) + else: + keepers = (*keepers, arg) + return (matches, keepers) + + +def _simplify_expressions(expr, op, matches, replacement): + args = expr.args + for arg in args: + expr = expr.subs(arg, _simplify_expressions(arg, op, matches, + replacement)) + + if expr.func == op: + (to_match, keepers) = tuple(_iterate_expr_tree(expr, op, matches)) + if len(to_match) == 0: + # build expression with keepers and replacement: + if keepers: + start = replacement + current_expr = None + last_expr = keepers[-1] + for repl_arg in keepers[:-1]: + current_expr = op(start, repl_arg) + start = current_expr + top_expr = op(start, last_expr) + else: + top_expr = replacement + + expr = expr.subs(expr, top_expr) + + return expr + + +def _simplify_flavors_in_condition(base: str, flavors, expr): + ''' Simplify conditions based on the knownledge of which flavors + belong to which OS. ''' + base_expr = simplify_logic(base) + 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) + return expr + + +def _recursive_simplify(expr): + ''' Simplify the expression as much as possible based on + domain knowledge. ''' + input_expr = expr + + # Simplify even further, based on domain knowledge: + apples = ('APPLE_OSX', 'APPLE_UIKIT', 'APPLE_IOS', + 'APPLE_TVOS', 'APPLE_WATCHOS',) + bsds = ('APPLE', 'FREEBSD', 'OPENBSD', 'NETBSD',) + unixes = ('APPLE', *apples, 'BSD', *bsds, 'LINUX', + 'ANDROID', 'ANDROID_EMBEDDED', + 'INTEGRITY', 'VXWORKS', 'QNX', 'WASM') + + 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) + # UNIX [AND foo ]AND WIN32 -> OFF [AND foo] + expr = _simplify_expressions(expr, And, (unix_expr, win_expr,), false_expr) + for unix_flavor in unixes: + # unix_flavor [AND foo ] AND WIN32 -> FALSE [AND foo] + flavor_expr = simplify_logic(unix_flavor) + expr = _simplify_expressions(expr, And, (win_expr, flavor_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) + + # Now simplify further: + expr = simplify_logic(expr) + + while expr != input_expr: + input_expr = expr + expr = _recursive_simplify(expr) + + return expr + + +def simplify_condition(condition: str) -> str: + input_condition = condition.strip() + + # Map to sympy syntax: + condition = ' ' + input_condition + ' ' + condition = condition.replace('(', ' ( ') + condition = condition.replace(')', ' ) ') + + 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') + + try: + # Generate and simplify condition using sympy: + condition_expr = simplify_logic(condition) + condition = str(_recursive_simplify(condition_expr)) + + # 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') + except: + # sympy did not like our input, so leave this condition alone: + condition = input_condition + + return condition + + def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', previous_condition: str = '') -> str: current_condition = scope.condition() @@ -755,7 +895,7 @@ def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', total_condition = '({}) AND ({})'.format(parent_condition, total_condition) - scope.set_total_condition(total_condition) + scope.set_total_condition(simplify_condition(total_condition)) prev_condition = '' for c in scope.children(): diff --git a/util/cmake/tests/test_logic_mapping.py b/util/cmake/tests/test_logic_mapping.py new file mode 100755 index 0000000000..cf7913a6e4 --- /dev/null +++ b/util/cmake/tests/test_logic_mapping.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from pro2cmake import simplify_condition + + +def validate_simplify(input: str, expected: str) -> None: + output = simplify_condition(input) + assert output == expected + + +def validate_simplify_unchanged(input: str) -> None: + validate_simplify(input, input) + + +def test_simplify_on(): + validate_simplify_unchanged('ON') + + +def test_simplify_off(): + validate_simplify_unchanged('OFF') + + +def test_simplify_isEmpty(): + validate_simplify_unchanged('isEmpty(foo)') + + +def test_simplify_not_isEmpty(): + validate_simplify_unchanged('NOT isEmpty(foo)') + + +def test_simplify_simple_and(): + validate_simplify_unchanged('QT_FEATURE_bar AND QT_FEATURE_foo') + + +def test_simplify_simple_or(): + validate_simplify_unchanged('QT_FEATURE_bar OR QT_FEATURE_foo') + + +def test_simplify_simple_not(): + validate_simplify_unchanged('NOT QT_FEATURE_foo') + + +def test_simplify_simple_and_reorder(): + validate_simplify('QT_FEATURE_foo AND QT_FEATURE_bar', 'QT_FEATURE_bar AND QT_FEATURE_foo') + + +def test_simplify_simple_or_reorder(): + validate_simplify('QT_FEATURE_foo OR QT_FEATURE_bar', 'QT_FEATURE_bar OR QT_FEATURE_foo') + + +def test_simplify_unix_or_win32(): + validate_simplify('WIN32 OR UNIX', 'ON') + + +def test_simplify_unix_or_win32_or_foobar_or_barfoo(): + validate_simplify('WIN32 OR UNIX OR foobar OR barfoo', 'ON') + + +def test_simplify_not_not_bar(): + validate_simplify(' NOT NOT bar ', 'bar') + + +def test_simplify_not_unix(): + validate_simplify('NOT UNIX', 'WIN32') + + +def test_simplify_not_win32(): + validate_simplify('NOT WIN32', 'UNIX') + + +def test_simplify_unix_and_win32(): + validate_simplify('WIN32 AND UNIX', 'OFF') + + +def test_simplify_unix_and_win32_or_foobar_or_barfoo(): + validate_simplify('WIN32 AND foobar AND UNIX AND barfoo', 'OFF') + + +def test_simplify_watchos_and_win32(): + validate_simplify('WIN32 AND APPLE_WATCHOS', 'OFF') + + +def test_simplify_apple_and_appleosx(): + validate_simplify('APPLE AND APPLE_OSX', 'APPLE_OSX') + + +def test_simplify_apple_or_appleosx(): + validate_simplify('APPLE OR APPLE_OSX', 'APPLE') + + +def test_simplify_apple_or_appleosx_level1(): + validate_simplify('foobar AND (APPLE OR APPLE_OSX )', 'APPLE AND foobar') + + +def test_simplify_apple_or_appleosx_level1_double(): + validate_simplify('foobar AND (APPLE OR APPLE_OSX )', 'APPLE AND foobar') + + +def test_simplify_apple_or_appleosx_level1_double_with_extra_spaces(): + validate_simplify('foobar AND (APPLE OR APPLE_OSX ) ' + 'AND ( APPLE_OSX OR APPLE )', 'APPLE AND foobar') + + +def test_simplify_apple_or_appleosx_level2(): + validate_simplify('foobar AND ( ( APPLE OR APPLE_WATCHOS ) ' + 'OR APPLE_OSX ) AND ( APPLE_OSX OR APPLE ) ' + 'AND ( (WIN32 OR WINRT) OR UNIX) ', 'APPLE AND foobar') + + +def test_simplify_not_apple_and_appleosx(): + validate_simplify('NOT APPLE AND APPLE_OSX', 'OFF') + + +def test_simplify_unix_and_bar_or_win32(): + validate_simplify('WIN32 AND bar AND UNIX', 'OFF') + + +def test_simplify_unix_or_bar_or_win32(): + validate_simplify('WIN32 OR bar OR UNIX', 'ON') + + +def test_simplify_complex_true(): + validate_simplify('WIN32 OR ( APPLE OR UNIX)', 'ON') + + +def test_simplify_apple_unix_freebsd(): + validate_simplify('( APPLE OR ( UNIX OR FREEBSD ))', 'UNIX') + + +def test_simplify_apple_unix_freebsd_foobar(): + validate_simplify('( APPLE OR ( UNIX OR FREEBSD ) OR foobar)', + 'UNIX OR foobar') + + +def test_simplify_complex_false(): + validate_simplify('WIN32 AND foobar AND ( ' + 'APPLE OR ( UNIX OR FREEBSD ))', + 'OFF') -- cgit v1.2.3 From 0c43850687fa3b56f40b03a1bef8a54cda38ac14 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 24 Jan 2019 15:43:13 +0100 Subject: CMake: pro2cmake.py: Better printing of scopes Change-Id: Ibeb77a23c90d3a47c78d17c4683e4af3c64449fc Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index fb55988600..6883a117e0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -227,6 +227,10 @@ class Scope: self._visited_keys = set() # type: typing.Set[str] self._total_condition = None # type: typing.Optional[str] + def __repr__(self): + return '{}:{}:{}'.format(self._basedir, self._file, + self._condition or '') + def reset_visited_keys(self): self._visited_keys = set() @@ -276,8 +280,8 @@ class Scope: elif operation == '*=': scope._append_operation(key, UniqueAddOperation(value)) else: - print('Unexpected operation "{}" in scope with ' - 'condition {}.'.format(operation, cond)) + print('Unexpected operation "{}" in scope "{}".' + .format(operation, scope)) assert(False) continue @@ -346,11 +350,7 @@ class Scope: def dump(self, *, indent: int = 0) -> None: ind = ' ' * indent - if self._condition == '': - print('{}Scope {} in "{}".'.format(ind, self._file, self._basedir)) - else: - print('{}Scope {} in "{}" with condition: "{}".' - .format(ind, self._file, self._basedir, self._condition)) + print('{}Scope "{}":'.format(ind, self)) print('{} Keys:'.format(ind)) keys = self._operations.keys() if not keys: @@ -576,8 +576,7 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, cm_fh.write('{}### remove_subdirectory' '("{}")\n'.format(ind, sd[1:])) else: - print(' XXXX: SUBDIR {} in {}: ' - 'Not found.'.format(sd, scope.file())) + print(' XXXX: SUBDIR {} in {}: Not found.'.format(sd, scope)) for c in scope.children(): cond = c.condition() -- cgit v1.2.3 From b6408318de6d82261cdc9ad18caee46a0b79fc15 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 30 Jan 2019 16:39:09 +0100 Subject: CMake: configure2cmake.py: Handle Libproxy and corewlan Update to cover more libraries Change-Id: I32a26b0fa0a79531fcb55e7a4646a2e63fec1d5b Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index ad80146b9f..0a30e74d6f 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -48,6 +48,7 @@ class LibraryMapping: def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: libmap = { 'atspi': 'ATSPI2', + 'corewlan': None, # Framework 'double-conversion': 'WrapDoubleConversion', 'drm': 'Libdrm', 'egl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_EGL"), @@ -66,7 +67,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'libjpeg': 'JPEG', 'libpng': 'PNG', 'libpng': 'PNG', - 'libproxy': 'libproxy', + 'libproxy': 'Libproxy', 'librt': 'WrapRt', 'libudev': 'Libudev', 'lttng-ust': LibraryMapping(package='LTTngUST', resultVariable="LTTNGUST"), -- cgit v1.2.3 From 9cee04ac9464af82da9f7b54844509b7bb8a62e8 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 24 Jan 2019 16:01:17 +0100 Subject: CMake: pro2cmake.py: Merge more scopes * Remove scopes with condition 'OFF' * Merge scopes with identical conditions This e.g. merges children with a condition that simplifies to 'ON' with their parent scope. Change-Id: Ieb3d60e1234f189ac45869853555ca8c0cfb5c76 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 86 +++++----- util/cmake/tests/test_scope_handling.py | 282 ++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+), 40 deletions(-) create mode 100755 util/cmake/tests/test_scope_handling.py (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6883a117e0..d4d352d4f6 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -27,14 +27,17 @@ ## ############################################################################# + +from __future__ import annotations + from argparse import ArgumentParser +import copy import os.path import re import io import typing from sympy.logic import (simplify_logic, And, Or, Not,) -from sympy.core import SympifyError import pyparsing as pp from helper import map_qt_library, map_qt_base_library, featureName, \ @@ -204,9 +207,11 @@ class RemoveOperation(Operation): class Scope: - def __init__(self, parent_scope: typing.Optional['Scope'], + def __init__(self, *, + parent_scope: typing.Optional[Scope], file: typing.Optional[str] = None, condition: str = '', - base_dir: str = '') -> None: + base_dir: str = '', + operations: typing.Mapping[str, typing.List[Operation]] = {}) -> None: if parent_scope: parent_scope._add_child(self) else: @@ -223,7 +228,7 @@ class Scope: self._file = file self._condition = map_condition(condition) self._children = [] # type: typing.List[Scope] - self._operations = {} # type: typing.Dict[str, typing.List[Operation]] + self._operations = copy.deepcopy(operations) self._visited_keys = set() # type: typing.Set[str] self._total_condition = None # type: typing.Optional[str] @@ -244,6 +249,9 @@ class Scope: else: self._operations[key] = other._operations[key] + def parent(self) -> typing.Optional[Scope]: + return self._parent + def basedir(self) -> str: return self._basedir @@ -253,7 +261,7 @@ class Scope: @staticmethod def FromDict(parent_scope: typing.Optional['Scope'], file: str, statements, cond: str = '', base_dir: str = ''): - scope = Scope(parent_scope, file, cond, base_dir) + 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 @@ -519,13 +527,11 @@ def parseProFile(file: str, *, debug=False): def map_condition(condition: str) -> str: - print('##### Mapping condition: {}.'.format(condition)) re.sub(r'if\s*\((.*?)\)', r'\1', condition) re.sub(r'(^|[^a-zA-Z0-9_])isEmpty\s*\((.*?)\)', r'\2_ISEMPTY', condition) re.sub(r'(^|[^a-zA-Z0-9_])contains\s*\((.*?), (.*)?\)', r'\2___contains___\3', condition) re.sub(r'\s*==\s*', '___STREQUAL___', condition) - print(' # after regexp: {}.'.format(condition)) condition = condition.replace('*', '_x_') condition = condition.replace('.$$', '__ss_') @@ -860,6 +866,8 @@ def simplify_condition(condition: str) -> str: # sympy did not like our input, so leave this condition alone: condition = input_condition + if condition == '': + condition = 'ON' return condition @@ -934,9 +942,8 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, def flatten_scopes(scope: Scope) -> typing.List[Scope]: - result = [] # type: typing.List[Scope] + result = [scope] # type: typing.List[Scope] for c in scope.children(): - result.append(c) result += flatten_scopes(c) return result @@ -944,16 +951,19 @@ def flatten_scopes(scope: Scope) -> typing.List[Scope]: def merge_scopes(scopes: typing.List[Scope]) -> typing.List[Scope]: result = [] # type: typing.List[Scope] - current_scope = None + # Merge scopes with their parents: + known_scopes = {} # type: typing.Mapping[str, Scope] for scope in scopes: - if not current_scope \ - or scope.total_condition() != current_scope.total_condition(): - if current_scope: - result.append(current_scope) - current_scope = scope - continue - - current_scope.merge(scope) + total_condition = scope.total_condition() + if total_condition == 'OFF': + # ignore this scope entirely! + pass + elif total_condition in known_scopes: + known_scopes[total_condition].merge(scope) + else: + # Keep everything else: + result.append(scope) + known_scopes[total_condition] = scope return result @@ -963,14 +973,28 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, extra_lines: typing.List[str] = [], indent: int = 0, **kwargs: typing.Any): + # Evaluate total condition of all scopes: + recursive_evaluate_scope(scope) + + # Get a flat list of all scopes but the main one: + scopes = flatten_scopes(scope) + total_scopes = len(scopes) + # Merge scopes based on their conditions: + scopes = merge_scopes(scopes) + print("xxxxxx {} scopes, {} after merging!".format(total_scopes, len(scopes))) + + assert len(scopes) + assert scopes[0].total_condition() == 'ON' + + # Now write out the scopes: write_header(cm_fh, name, typename, indent=indent) cm_fh.write('{}{}({}\n'.format(spaces(indent), cmake_function, name)) for extra_line in extra_lines: cm_fh.write('{} {}\n'.format(spaces(indent), extra_line)) - ignored_keys = write_sources_section(cm_fh, scope, indent=indent, **kwargs) - ignored_keys_report = write_ignored_keys(scope, ignored_keys, + ignored_keys = write_sources_section(cm_fh, scopes[0], indent=indent, **kwargs) + ignored_keys_report = write_ignored_keys(scopes[0], ignored_keys, spaces(indent + 1)) if ignored_keys_report: cm_fh.write(ignored_keys_report) @@ -979,26 +1003,12 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, cm_fh.write('{})\n'.format(spaces(indent))) # Scopes: - if not scope.children(): + if len(scopes) == 1: return write_scope_header(cm_fh, indent=indent) - # Evaluate total condition of all scopes: - for c in scope.children(): - recursive_evaluate_scope(c) - - # Get a flat list of all scopes but the main one: - scopes = flatten_scopes(scope) - - scopes = sorted(scopes, key=lambda x: x.total_condition()) - print("xxxxxx Sorted to {} scopes!".format(len(scopes))) - - # Merge scopes with identical conditions: - scopes = merge_scopes(scopes) - print("xxxxxx Merged to {} scopes!".format(len(scopes))) - - for c in scopes: + for c in scopes[1:]: write_extend_target(cm_fh, name, c, indent=indent) @@ -1118,10 +1128,6 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: include_file = i if not include_file: continue - if '/3rdparty/' in include_file: - print(' ****: Ignoring include file in 3rdparty: {}.' - .format(include_file)) - continue if not os.path.isfile(include_file): print(' XXXX: Failed to include {}.'.format(include_file)) continue diff --git a/util/cmake/tests/test_scope_handling.py b/util/cmake/tests/test_scope_handling.py new file mode 100755 index 0000000000..3977f2291f --- /dev/null +++ b/util/cmake/tests/test_scope_handling.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from pro2cmake import Scope, SetOperation, merge_scopes, recursive_evaluate_scope + +import pytest +import typing + +ScopeList = typing.List[Scope] + +def _map_to_operation(**kwargs): + result = {} # type: typing.Mapping[str, typing.List[SetOperation]] + for (key, value) in kwargs.items(): + result[key] = [SetOperation(value)] + return result + + +def _new_scope(*, parent_scope=None, condition='', **kwargs) -> Scope: + return Scope(parent_scope=parent_scope, + file='file1', condition=condition, operations=_map_to_operation(**kwargs)) + + +def _evaluate_scopes(scopes: ScopeList) -> ScopeList: + for s in scopes: + if not s.parent(): + recursive_evaluate_scope(s) + return scopes + + +def _validate(input_scopes: ScopeList, output_scopes: ScopeList): + merged_scopes = merge_scopes(input_scopes) + assert merged_scopes == output_scopes + + +def test_evaluate_one_scope(): + scope = _new_scope(condition='QT_FEATURE_foo', test1='bar') + + input_scope = scope + recursive_evaluate_scope(scope) + assert scope == input_scope + + +def test_evaluate_child_scope(): + scope = _new_scope(condition='QT_FEATURE_foo', test1='bar') + _new_scope(parent_scope=scope, condition='QT_FEATURE_bar', test2='bar') + + input_scope = scope + recursive_evaluate_scope(scope) + + assert scope.total_condition() == 'QT_FEATURE_foo' + assert len(scope.children()) == 1 + assert scope.getString('test1') == 'bar' + assert scope.getString('test2', 'not found') == 'not found' + + child = scope.children()[0] + assert child.total_condition() == 'QT_FEATURE_bar AND QT_FEATURE_foo' + assert child.getString('test1', 'not found') == 'not found' + assert child.getString('test2') == 'bar' + + +def test_evaluate_two_child_scopes(): + scope = _new_scope(condition='QT_FEATURE_foo', test1='bar') + _new_scope(parent_scope=scope, condition='QT_FEATURE_bar', test2='bar') + _new_scope(parent_scope=scope, condition='QT_FEATURE_buz', test3='buz') + + input_scope = scope + recursive_evaluate_scope(scope) + + assert scope.total_condition() == 'QT_FEATURE_foo' + assert len(scope.children()) == 2 + assert scope.getString('test1') == 'bar' + assert scope.getString('test2', 'not found') == 'not found' + assert scope.getString('test3', 'not found') == 'not found' + + child1 = scope.children()[0] + assert child1.total_condition() == 'QT_FEATURE_bar AND QT_FEATURE_foo' + assert child1.getString('test1', 'not found') == 'not found' + assert child1.getString('test2') == 'bar' + assert child1.getString('test3', 'not found') == 'not found' + + child2 = scope.children()[1] + assert child2.total_condition() == 'QT_FEATURE_buz AND QT_FEATURE_foo' + assert child2.getString('test1', 'not found') == 'not found' + assert child2.getString('test2') == '' + assert child2.getString('test3', 'not found') == 'buz' + + +def test_evaluate_else_child_scopes(): + scope = _new_scope(condition='QT_FEATURE_foo', test1='bar') + _new_scope(parent_scope=scope, condition='QT_FEATURE_bar', test2='bar') + _new_scope(parent_scope=scope, condition='else', test3='buz') + + input_scope = scope + recursive_evaluate_scope(scope) + + assert scope.total_condition() == 'QT_FEATURE_foo' + assert len(scope.children()) == 2 + assert scope.getString('test1') == 'bar' + assert scope.getString('test2', 'not found') == 'not found' + assert scope.getString('test3', 'not found') == 'not found' + + child1 = scope.children()[0] + assert child1.total_condition() == 'QT_FEATURE_bar AND QT_FEATURE_foo' + assert child1.getString('test1', 'not found') == 'not found' + assert child1.getString('test2') == 'bar' + assert child1.getString('test3', 'not found') == 'not found' + + child2 = scope.children()[1] + assert child2.total_condition() == 'QT_FEATURE_foo AND NOT QT_FEATURE_bar' + assert child2.getString('test1', 'not found') == 'not found' + assert child2.getString('test2') == '' + assert child2.getString('test3', 'not found') == 'buz' + + +def test_evaluate_invalid_else_child_scopes(): + scope = _new_scope(condition='QT_FEATURE_foo', test1='bar') + _new_scope(parent_scope=scope, condition='else', test3='buz') + _new_scope(parent_scope=scope, condition='QT_FEATURE_bar', test2='bar') + + input_scope = scope + with pytest.raises(AssertionError): + recursive_evaluate_scope(scope) + + +def test_merge_empty_scope_list(): + _validate([], []) + + +def test_merge_one_scope(): + scopes = [_new_scope(test='foo')] + + recursive_evaluate_scope(scopes[0]) + + _validate(scopes, scopes) + + +def test_merge_one_on_scope(): + scopes = [_new_scope(condition='ON', test='foo')] + + recursive_evaluate_scope(scopes[0]) + + _validate(scopes, scopes) + + +def test_merge_one_off_scope(): + scopes = [_new_scope(condition='OFF', test='foo')] + + recursive_evaluate_scope(scopes[0]) + + _validate(scopes, []) + + +def test_merge_one_conditioned_scope(): + scopes = [_new_scope(condition='QT_FEATURE_foo', test='foo')] + + recursive_evaluate_scope(scopes[0]) + + _validate(scopes, scopes) + + +def test_merge_two_scopes_with_same_condition(): + scopes = [_new_scope(condition='QT_FEATURE_bar', test='foo'), + _new_scope(condition='QT_FEATURE_bar', test2='bar')] + + recursive_evaluate_scope(scopes[0]) + recursive_evaluate_scope(scopes[1]) + + result = merge_scopes(scopes) + + assert len(result) == 1 + r0 = result[0] + assert r0.total_condition() == 'QT_FEATURE_bar' + assert r0.getString('test') == 'foo' + assert r0.getString('test2') == 'bar' + + +def test_merge_three_scopes_two_with_same_condition(): + scopes = [_new_scope(condition='QT_FEATURE_bar', test='foo'), + _new_scope(condition='QT_FEATURE_baz', test1='buz'), + _new_scope(condition='QT_FEATURE_bar', test2='bar')] + + recursive_evaluate_scope(scopes[0]) + recursive_evaluate_scope(scopes[1]) + recursive_evaluate_scope(scopes[2]) + + result = merge_scopes(scopes) + + assert len(result) == 2 + r0 = result[0] + assert r0.total_condition() == 'QT_FEATURE_bar' + assert r0.getString('test') == 'foo' + assert r0.getString('test2') == 'bar' + + assert result[1] == scopes[1] + + +def test_merge_two_unrelated_on_off_scopes(): + scopes = [_new_scope(condition='ON', test='foo'), + _new_scope(condition='OFF', test2='bar')] + + recursive_evaluate_scope(scopes[0]) + recursive_evaluate_scope(scopes[1]) + + _validate(scopes, [scopes[0]]) + + +def test_merge_two_unrelated_on_off_scopes(): + scopes = [_new_scope(condition='OFF', test='foo'), + _new_scope(condition='ON', test2='bar')] + + recursive_evaluate_scope(scopes[0]) + recursive_evaluate_scope(scopes[1]) + + _validate(scopes, [scopes[1]]) + + +def test_merge_parent_child_scopes_with_different_conditions(): + scope = _new_scope(condition='FOO', test1='parent') + scopes = [scope, _new_scope(parent_scope=scope, condition='bar', test2='child')] + + recursive_evaluate_scope(scope) + + _validate(scopes, scopes) + + +def test_merge_parent_child_scopes_with_same_conditions(): + scope = _new_scope(condition='FOO AND bar', test1='parent') + scopes = [scope, _new_scope(parent_scope=scope, condition='FOO AND bar', test2='child')] + + recursive_evaluate_scope(scope) + + result = merge_scopes(scopes) + + assert len(result) == 1 + r0 = result[0] + assert r0.parent() == None + assert r0.total_condition() == 'FOO AND bar' + assert r0.getString('test1') == 'parent' + assert r0.getString('test2') == 'child' + + +def test_merge_parent_child_scopes_with_on_child_condition(): + scope = _new_scope(condition='FOO AND bar', test1='parent') + scopes = [scope, _new_scope(parent_scope=scope, condition='ON', test2='child')] + + recursive_evaluate_scope(scope) + + result = merge_scopes(scopes) + + assert len(result) == 1 + r0 = result[0] + assert r0.parent() == None + assert r0.total_condition() == 'FOO AND bar' + assert r0.getString('test1') == 'parent' + assert r0.getString('test2') == 'child' + -- cgit v1.2.3 From 328de7aab92abbaa6e25b8ad3d55f6841504bfee Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 28 Jan 2019 15:06:44 +0100 Subject: CMake: pro2cmake: default QT_FEATURE_system_* to "ON" Qt 6 will use external libraries for everything, so default all QT_FEATURE_system_foo to "ON". Change-Id: I884a4293c64487271be08de5fab82e6858d0a2ed Reviewed-by: Albert Astals Cid Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index d4d352d4f6..3e2e5e13b0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -552,7 +552,12 @@ def map_condition(condition: str) -> str: part = 'TARGET {}'.format(map_qt_base_library( feature.group(2))) else: - part = 'QT_FEATURE_' + featureName(feature.group(2)) + feature = featureName(feature.group(2)) + if feature.startswith('system_') and substitute_libs(feature[7:]) != feature[7:]: + # Qt6 always uses system libraries! + part = 'ON' + else: + part = 'QT_FEATURE_' + feature else: part = substitute_platform(part) -- cgit v1.2.3 From 194022d3a60598aa44119c0c92cc796b5746c32a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 29 Jan 2019 10:18:21 +0100 Subject: CMake: pro2cmake.py: Use properties Make use of @property to make code a bit nicer. Change-Id: Iff0bfed57874cf13b7e5f85acde2660a397933d7 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 110 ++++++++++++++++++-------------- util/cmake/tests/test_scope_handling.py | 46 ++++++------- 2 files changed, 85 insertions(+), 71 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3e2e5e13b0..6aab8ddd70 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -206,7 +206,7 @@ class RemoveOperation(Operation): return '-({})'.format(self._dump()) -class Scope: +class Scope(object): def __init__(self, *, parent_scope: typing.Optional[Scope], file: typing.Optional[str] = None, condition: str = '', @@ -249,12 +249,15 @@ class Scope: else: self._operations[key] = other._operations[key] + @property def parent(self) -> typing.Optional[Scope]: return self._parent + @property def basedir(self) -> str: return self._basedir + @property def currentdir(self) -> str: return self._currentdir @@ -276,8 +279,8 @@ class Scope: if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH', 'RESOURCES',) \ or key.endswith('_HEADERS') \ or key.endswith('_SOURCES'): - value = [map_to_file(v, scope.basedir(), - scope.currentdir()) for v in value] + value = [map_to_file(v, scope.basedir, + scope.currentdir) for v in value] if operation == '=': scope._append_operation(key, SetOperation(value)) @@ -298,12 +301,12 @@ class Scope: if condition: Scope.FromDict(scope, file, statement.get('statements'), condition, - scope.basedir()) + scope.basedir) else_statements = statement.get('else_statements') if else_statements: Scope.FromDict(scope, file, else_statements, - 'NOT ' + condition, scope.basedir()) + 'NOT ' + condition, scope.basedir) continue loaded = statement.get('loaded') @@ -321,8 +324,8 @@ class Scope: scope._append_operation('_INCLUDED', UniqueAddOperation( map_to_file(included, - scope.basedir(), - scope.currentdir()))) + scope.basedir, + scope.currentdir))) continue return scope @@ -333,26 +336,32 @@ class Scope: else: self._operations[key] = [op, ] + @property def file(self) -> str: return self._file or '' + @property def cMakeListsFile(self) -> str: - assert self.basedir() - return os.path.join(self.basedir(), 'CMakeLists.txt') + assert self.basedir + return os.path.join(self.basedir, 'CMakeLists.txt') + @property def condition(self) -> str: return self._condition - def set_total_condition(self, condition: str) -> None: - self._total_condition = condition - + @property def total_condition(self) -> typing.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: scope._parent = self self._children.append(scope) + @property def children(self) -> typing.List['Scope']: return self._children @@ -374,9 +383,11 @@ class Scope: for c in self._children: c.dump(indent=indent + 1) + @property def keys(self): return self._operations.keys() + @property def visited_keys(self): return self._visited_keys @@ -395,17 +406,20 @@ class Scope: assert len(v) == 1 return v[0] - def getTemplate(self) -> str: + @property + def TEMPLATE(self) -> str: return self.getString('TEMPLATE', 'app') def _rawTemplate(self) -> str: return self.getString('TEMPLATE') - def getTarget(self) -> str: + @property + def TARGET(self) -> str: return self.getString('TARGET') \ - or os.path.splitext(os.path.basename(self.file()))[0] + or os.path.splitext(os.path.basename(self.file))[0] - def getIncludes(self) -> typing.List[str]: + @property + def _INCLUDED(self) -> typing.List[str]: return self.get('_INCLUDED', []) @@ -569,10 +583,10 @@ def map_condition(condition: str) -> str: def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, indent: int = 0) -> None: - assert scope.getTemplate() == 'subdirs' + assert scope.TEMPLATE == 'subdirs' ind = ' ' * indent for sd in scope.get('SUBDIRS', []): - full_sd = os.path.join(scope.basedir(), sd) + full_sd = os.path.join(scope.basedir, sd) if os.path.isdir(full_sd): cm_fh.write('{}add_subdirectory({})\n'.format(ind, sd)) elif os.path.isfile(full_sd): @@ -580,7 +594,7 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, subdir_scope \ = Scope.FromDict(scope, full_sd, subdir_result.asDict().get('statements'), - '', scope.basedir()) + '', scope.basedir) cmakeify_scope(subdir_scope, cm_fh, indent=indent + 1) elif sd.startswith('-'): @@ -590,7 +604,7 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, print(' XXXX: SUBDIR {} in {}: Not found.'.format(sd, scope)) for c in scope.children(): - cond = c.condition() + cond = c.condition if cond == 'else': cm_fh.write('\n{}else()\n'.format(ind)) elif cond: @@ -670,7 +684,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, vpath = scope.get('VPATH') - sources = [map_source_to_cmake(s, scope.basedir(), vpath) for s in sources] + sources = [map_source_to_cmake(s, scope.basedir, vpath) for s in sources] if sources: cm_fh.write('{} SOURCES\n'.format(ind)) for l in sort_sources(sources): @@ -715,7 +729,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, cm_fh.write('{} {}\n'.format(ind, d)) is_framework = False - return set(scope.keys()) - scope.visited_keys() + return set(scope.keys) - scope.visited_keys def is_simple_condition(condition: str) -> bool: @@ -878,11 +892,11 @@ def simplify_condition(condition: str) -> str: def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', previous_condition: str = '') -> str: - current_condition = scope.condition() + current_condition = scope.condition total_condition = current_condition if total_condition == 'else': assert previous_condition, \ - "Else branch without previous condition in: %s" % scope.file() + "Else branch without previous condition in: %s" % scope.file if previous_condition.startswith('NOT '): total_condition = previous_condition[4:] elif is_simple_condition(previous_condition): @@ -907,10 +921,10 @@ def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', total_condition = '({}) AND ({})'.format(parent_condition, total_condition) - scope.set_total_condition(simplify_condition(total_condition)) + scope.total_condition = simplify_condition(total_condition) prev_condition = '' - for c in scope.children(): + for c in scope.children: prev_condition = recursive_evaluate_scope(c, total_condition, prev_condition) @@ -930,7 +944,7 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, extend_scope = '\n{}extend_target({} CONDITION {}\n' \ '{}{})\n'.format(spaces(indent), target, - scope.total_condition(), + scope.total_condition, extend_qt_string, ignored_keys_report) if not extend_qt_string: @@ -948,7 +962,7 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, def flatten_scopes(scope: Scope) -> typing.List[Scope]: result = [scope] # type: typing.List[Scope] - for c in scope.children(): + for c in scope.children: result += flatten_scopes(c) return result @@ -959,7 +973,7 @@ def merge_scopes(scopes: typing.List[Scope]) -> typing.List[Scope]: # Merge scopes with their parents: known_scopes = {} # type: typing.Mapping[str, Scope] for scope in scopes: - total_condition = scope.total_condition() + total_condition = scope.total_condition if total_condition == 'OFF': # ignore this scope entirely! pass @@ -989,7 +1003,7 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, print("xxxxxx {} scopes, {} after merging!".format(total_scopes, len(scopes))) assert len(scopes) - assert scopes[0].total_condition() == 'ON' + assert scopes[0].total_condition == 'ON' # Now write out the scopes: write_header(cm_fh, name, typename, indent=indent) @@ -1019,7 +1033,7 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, def write_module(cm_fh: typing.IO[str], scope: Scope, *, indent: int = 0) -> None: - module_name = scope.getTarget() + module_name = scope.TARGET assert module_name.startswith('Qt') extra = [] @@ -1034,14 +1048,14 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, if 'qt_tracepoints' in scope.get('CONFIG'): tracepoints = map_to_file(scope.getString('TRACEPOINT_PROVIDER'), - scope.basedir(), scope.currentdir()) + scope.basedir, scope.currentdir) cm_fh.write('\n\n{}qt_create_tracepoints({} {})\n' .format(spaces(indent), module_name[2:], tracepoints)) def write_tool(cm_fh: typing.IO[str], scope: Scope, *, indent: int = 0) -> None: - tool_name = scope.getTarget() + tool_name = scope.TARGET write_main_part(cm_fh, tool_name, 'Tool', 'add_qt_tool', scope, indent=indent, known_libraries={'Qt::Core', }) @@ -1049,7 +1063,7 @@ def write_tool(cm_fh: typing.IO[str], scope: Scope, *, def write_test(cm_fh: typing.IO[str], scope: Scope, *, indent: int = 0) -> None: - test_name = scope.getTarget() + test_name = scope.TARGET assert test_name write_main_part(cm_fh, test_name, 'Test', 'add_qt_test', scope, @@ -1058,7 +1072,7 @@ def write_test(cm_fh: typing.IO[str], scope: Scope, *, def write_binary(cm_fh: typing.IO[str], scope: Scope, gui: bool = False, *, indent: int = 0) -> None: - binary_name = scope.getTarget() + binary_name = scope.TARGET assert binary_name extra = ['GUI', ] if gui else [] @@ -1068,7 +1082,7 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope, def write_plugin(cm_fh, scope, *, indent: int = 0): - plugin_name = scope.getTarget() + plugin_name = scope.TARGET assert plugin_name write_main_part(cm_fh, plugin_name, 'Plugin', 'add_qt_plugin', scope, @@ -1077,9 +1091,9 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, indent: int = 0) -> None: - assert scope.getTemplate() in ('app', 'lib') + assert scope.TEMPLATE in ('app', 'lib') - is_lib = scope.getTemplate() == 'lib' + is_lib = scope.TEMPLATE == 'lib' is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED', [])) if is_lib or 'qt_module' in scope.get('_LOADED', []): @@ -1100,36 +1114,36 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, if docs: cm_fh.write("\n{}add_qt_docs({})\n" .format(spaces(indent), - map_to_file(docs, scope.basedir(), - scope.currentdir()))) + map_to_file(docs, scope.basedir, + scope.currentdir))) def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, indent: int = 0) -> None: - template = scope.getTemplate() + template = scope.TEMPLATE if template == 'subdirs': handle_subdir(scope, cm_fh, indent=indent) elif template in ('app', 'lib'): handle_app_or_lib(scope, cm_fh, indent=indent) else: print(' XXXX: {}: Template type {} not yet supported.' - .format(scope.file(), template)) + .format(scope.file, template)) def generate_cmakelists(scope: Scope) -> None: - with open(scope.cMakeListsFile(), 'w') as cm_fh: - assert scope.file() + with open(scope.cMakeListsFile, 'w') as cm_fh: + assert scope.file cm_fh.write('# Generated from {}.\n\n' - .format(os.path.basename(scope.file()))) + .format(os.path.basename(scope.file))) cmakeify_scope(scope, cm_fh) def do_include(scope: Scope, *, debug: bool = False) -> None: - for c in scope.children(): + for c in scope.children: do_include(c) - for i in scope.getIncludes(): - dir = scope.basedir() + for i in scope._INCLUDED: + dir = scope.basedir include_file = i if not include_file: continue diff --git a/util/cmake/tests/test_scope_handling.py b/util/cmake/tests/test_scope_handling.py index 3977f2291f..2d4bc183d7 100755 --- a/util/cmake/tests/test_scope_handling.py +++ b/util/cmake/tests/test_scope_handling.py @@ -48,7 +48,7 @@ def _new_scope(*, parent_scope=None, condition='', **kwargs) -> Scope: def _evaluate_scopes(scopes: ScopeList) -> ScopeList: for s in scopes: - if not s.parent(): + if not s.parent: recursive_evaluate_scope(s) return scopes @@ -73,13 +73,13 @@ def test_evaluate_child_scope(): input_scope = scope recursive_evaluate_scope(scope) - assert scope.total_condition() == 'QT_FEATURE_foo' - assert len(scope.children()) == 1 + assert scope.total_condition == 'QT_FEATURE_foo' + assert len(scope.children) == 1 assert scope.getString('test1') == 'bar' assert scope.getString('test2', 'not found') == 'not found' - child = scope.children()[0] - assert child.total_condition() == 'QT_FEATURE_bar AND QT_FEATURE_foo' + child = scope.children[0] + assert child.total_condition == 'QT_FEATURE_bar AND QT_FEATURE_foo' assert child.getString('test1', 'not found') == 'not found' assert child.getString('test2') == 'bar' @@ -92,20 +92,20 @@ def test_evaluate_two_child_scopes(): input_scope = scope recursive_evaluate_scope(scope) - assert scope.total_condition() == 'QT_FEATURE_foo' - assert len(scope.children()) == 2 + assert scope.total_condition == 'QT_FEATURE_foo' + assert len(scope.children) == 2 assert scope.getString('test1') == 'bar' assert scope.getString('test2', 'not found') == 'not found' assert scope.getString('test3', 'not found') == 'not found' - child1 = scope.children()[0] - assert child1.total_condition() == 'QT_FEATURE_bar AND QT_FEATURE_foo' + child1 = scope.children[0] + assert child1.total_condition == 'QT_FEATURE_bar AND QT_FEATURE_foo' assert child1.getString('test1', 'not found') == 'not found' assert child1.getString('test2') == 'bar' assert child1.getString('test3', 'not found') == 'not found' - child2 = scope.children()[1] - assert child2.total_condition() == 'QT_FEATURE_buz AND QT_FEATURE_foo' + child2 = scope.children[1] + assert child2.total_condition == 'QT_FEATURE_buz AND QT_FEATURE_foo' assert child2.getString('test1', 'not found') == 'not found' assert child2.getString('test2') == '' assert child2.getString('test3', 'not found') == 'buz' @@ -119,20 +119,20 @@ def test_evaluate_else_child_scopes(): input_scope = scope recursive_evaluate_scope(scope) - assert scope.total_condition() == 'QT_FEATURE_foo' - assert len(scope.children()) == 2 + assert scope.total_condition == 'QT_FEATURE_foo' + assert len(scope.children) == 2 assert scope.getString('test1') == 'bar' assert scope.getString('test2', 'not found') == 'not found' assert scope.getString('test3', 'not found') == 'not found' - child1 = scope.children()[0] - assert child1.total_condition() == 'QT_FEATURE_bar AND QT_FEATURE_foo' + child1 = scope.children[0] + assert child1.total_condition == 'QT_FEATURE_bar AND QT_FEATURE_foo' assert child1.getString('test1', 'not found') == 'not found' assert child1.getString('test2') == 'bar' assert child1.getString('test3', 'not found') == 'not found' - child2 = scope.children()[1] - assert child2.total_condition() == 'QT_FEATURE_foo AND NOT QT_FEATURE_bar' + child2 = scope.children[1] + assert child2.total_condition == 'QT_FEATURE_foo AND NOT QT_FEATURE_bar' assert child2.getString('test1', 'not found') == 'not found' assert child2.getString('test2') == '' assert child2.getString('test3', 'not found') == 'buz' @@ -195,7 +195,7 @@ def test_merge_two_scopes_with_same_condition(): assert len(result) == 1 r0 = result[0] - assert r0.total_condition() == 'QT_FEATURE_bar' + assert r0.total_condition == 'QT_FEATURE_bar' assert r0.getString('test') == 'foo' assert r0.getString('test2') == 'bar' @@ -213,7 +213,7 @@ def test_merge_three_scopes_two_with_same_condition(): assert len(result) == 2 r0 = result[0] - assert r0.total_condition() == 'QT_FEATURE_bar' + assert r0.total_condition == 'QT_FEATURE_bar' assert r0.getString('test') == 'foo' assert r0.getString('test2') == 'bar' @@ -259,8 +259,8 @@ def test_merge_parent_child_scopes_with_same_conditions(): assert len(result) == 1 r0 = result[0] - assert r0.parent() == None - assert r0.total_condition() == 'FOO AND bar' + assert r0.parent == None + assert r0.total_condition == 'FOO AND bar' assert r0.getString('test1') == 'parent' assert r0.getString('test2') == 'child' @@ -275,8 +275,8 @@ def test_merge_parent_child_scopes_with_on_child_condition(): assert len(result) == 1 r0 = result[0] - assert r0.parent() == None - assert r0.total_condition() == 'FOO AND bar' + assert r0.parent == None + assert r0.total_condition == 'FOO AND bar' assert r0.getString('test1') == 'parent' assert r0.getString('test2') == 'child' -- cgit v1.2.3 From 4d4bf61f9fbd10a48f43a99d43a667b0ac3323f0 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 29 Jan 2019 12:07:24 +0100 Subject: CMake: pro2cmake.py: Convert more settings from .pro-file to CMake Convert QMAKE_USE, QMAKE_CXX_FLAGS and QMAKE_LFLAGS into CMake. Change-Id: I53a5b91664b6ab71892d4381c00f8d744d7d7abd Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6aab8ddd70..fc5bda6854 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -708,7 +708,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, if map_qt_library(q) not in known_libraries] dependencies += [map_qt_library(q) for q in scope.get('QT_FOR_PRIVATE') if map_qt_library(q) not in known_libraries] - dependencies += scope.get('QMAKE_USE_PRIVATE') \ + dependencies += scope.get('QMAKE_USE_PRIVATE') + scope.get('QMAKE_USE') \ + scope.get('LIBS_PRIVATE') + scope.get('LIBS') if dependencies: cm_fh.write('{} LIBRARIES\n'.format(ind)) @@ -729,6 +729,18 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, cm_fh.write('{} {}\n'.format(ind, d)) is_framework = False + compile_options = scope.get('QMAKE_CXXFLAGS') + if compile_options: + cm_fh.write('{} COMPILE_OPTIONS\n'.format(ind)) + for co in compile_options: + cm_fh.write('{} "{}"\n'.format(ind, co)) + + link_options = scope.get('QMAKE_LFLAGS') + if link_options: + cm_fh.write('{} LINK_OPTIONS\n'.format(ind)) + for lo in link_options: + cm_fh.write('{} "{}"\n'.format(ind, lo)) + return set(scope.keys) - scope.visited_keys -- cgit v1.2.3 From 210762ae461c972c2fdfbe73f20e016ea20452e5 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 30 Jan 2019 16:43:11 +0100 Subject: CMake: pro2cmake.py: Pass QMAKE_MOC_OPTIONS on to CMake Change-Id: I39c1b0aedbffaa29a34253e0c1c4bb4a4dddbe98 Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index fc5bda6854..aa993f5a62 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -741,6 +741,12 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, for lo in link_options: cm_fh.write('{} "{}"\n'.format(ind, lo)) + moc_options = scope.get('QMAKE_MOC_OPTIONS') + if moc_options: + cm_fh.write('{} MOC_OPTIONS\n'.format(ind)) + for mo in moc_options: + cm_fh.write('{} "{}"\n'.format(ind, mo)) + return set(scope.keys) - scope.visited_keys -- cgit v1.2.3 From 1218d8a9f0bcacf4dee3795c3172c9ce05b8baa8 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 29 Jan 2019 12:53:24 +0100 Subject: CMake: Use FindGLIB2 from cmake-extra-modules Use FindGLIB2 from cmake-extra-modules over hand-rolled own version. Change-Id: I1f8e055bc12dd728c033fd88480690643d90159a Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 2 +- util/cmake/helper.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 0a30e74d6f..312f0cc54d 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -55,7 +55,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'fontconfig': LibraryMapping(package='Fontconfig', resultVariable="FONTCONFIG"), 'freetype': 'Freetype', 'gbm': 'gbm', - 'glib': 'GLib', + 'glib': 'GLIB2', 'gnu_iconv': None, 'harfbuzz': 'harfbuzz', 'host_dbus': None, diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 1998defeff..759f61a383 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -184,6 +184,7 @@ platform_mapping = { 'clang': 'CLANG', 'gcc': 'GCC', 'icc': 'ICC', + 'intel_icc': 'ICC', 'osx': 'APPLE_OSX', 'ios': 'APPLE_IOS', 'freebsd': 'FREEBSD', @@ -205,7 +206,7 @@ def substitute_platform(platform: str) -> str: libray_mapping = { 'zlib': 'ZLIB::ZLIB', - 'glib': 'PkgConfig::GLib', + 'glib': 'GLIB2::GLIB2', } -- cgit v1.2.3 From e7e793555ff4ee6dca0d9da581fa019bf9f93a24 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 30 Jan 2019 16:53:40 +0100 Subject: CMake: Add FindZSTD.cmake and wire it up in configurejson2cmake.py Zstd is used in the dev branch, so prepare for it. Change-Id: I130d98e3888a1eb4c7444728fc5088c5dae9d911 Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 312f0cc54d..03625da1a5 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -92,6 +92,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'xlib': 'X11', 'xrender': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), 'zlib': 'ZLIB', + 'zstd': 'ZSTD', } # type: Dict[str, Union[str, List[str], LibraryMapping]] if lib not in libmap: raise Exception(' XXXX Unknown library "{}".'.format(lib)) -- cgit v1.2.3 From 645002cd43eb24051504d7dc60ea3ac961d16121 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 30 Jan 2019 16:41:45 +0100 Subject: CMake: pro2cmake.py: Handle more libraries Change-Id: I561b619c4dbc56a1f6ad383a68ce4b82858a031c Reviewed-by: Tobias Hunger --- util/cmake/helper.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 759f61a383..872f968ba1 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -205,8 +205,16 @@ def substitute_platform(platform: str) -> str: libray_mapping = { - 'zlib': 'ZLIB::ZLIB', + 'libdl': '${CMAKE_DL_LIBS}', + 'doubleconversion': 'double-conversion', 'glib': 'GLIB2::GLIB2', + 'icu': 'ICU::i18n ICU::uc ICU::data', + 'libatomic': 'Atomic', + 'libproxy': 'LibProxy::LibProxy', + 'pcre2': 'PCRE2', + 'librt': 'WrapRt', + 'zlib': 'ZLIB::ZLIB', + 'zstd': 'ZSTD::ZSTD', } -- cgit v1.2.3 From ca4edbec6616b30256dcbc81a2d5d8e2e82cc202 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 31 Jan 2019 10:18:14 +0100 Subject: CMake: pro2cmake.py: Add a way to debug merges of scopes Generate debug output whenever a qmake scope with a variable 'PRO2CMAKE_MERGE_DEBUG' is involved in a scope merge. Change-Id: I0ad94b881db9930de689c199adbac084efe6c03b Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index aa993f5a62..88869a8346 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -233,13 +233,22 @@ class Scope(object): self._total_condition = None # type: typing.Optional[str] def __repr__(self): - return '{}:{}:{}'.format(self._basedir, self._file, - self._condition or '') + debug_mark = ' [MERGE_DEBUG]' if self.merge_debug else '' + return '{}:{}:{}{}'.format(self._basedir, self._file, + self._condition or '', debug_mark) def reset_visited_keys(self): self._visited_keys = set() def merge(self, other: 'Scope') -> None: + assert self != other + merge_debug = self.merge_debug or other.merge_debug + if merge_debug: + print('..... [MERGE_DEBUG]: Merging scope {}:'.format(other)) + other.dump(indent=1) + print('..... [MERGE_DEBUG]: ... into scope {}:'.format(self)) + self.dump(indent=1) + for c in other._children: self._add_child(c) @@ -249,6 +258,15 @@ class Scope(object): else: self._operations[key] = other._operations[key] + if merge_debug: + print('..... [MERGE_DEBUG]: Result scope {}:'.format(self)) + self.dump(indent=1) + print('..... [MERGE_DEBUG]: <>') + + @property + def merge_debug(self) -> bool: + return self.getString('PRO2CMAKE_MERGE_DEBUG', None) != None + @property def parent(self) -> typing.Optional[Scope]: return self._parent -- cgit v1.2.3 From 0efd241d200e4080d80bc8336514a656583e2d18 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 31 Jan 2019 11:54:55 +0100 Subject: CMake: pro2cmake.py: report total condition when dumping a scope Show the total_condition (if set) when dumping a scope. Change-Id: I9dfe98c2251f1d28881771042f17d723cedc8907 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 88869a8346..15c35ac9ae 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -386,6 +386,8 @@ class Scope(object): def dump(self, *, indent: int = 0) -> None: ind = ' ' * indent print('{}Scope "{}":'.format(ind, self)) + if self.total_condition: + print('{} Total condition = {}'.format(ind, self.total_condition)) print('{} Keys:'.format(ind)) keys = self._operations.keys() if not keys: -- cgit v1.2.3 From cfbb110abe3f3ac9c85c9e2cf0bbe587f1082af5 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 31 Jan 2019 12:17:27 +0100 Subject: CMake: pro2cmake.py: Assign a unique id to each scope This makes scopes much simpler to destinguish from each other. Change-Id: I1af42f181b5899aba749bcf9267a345385149f90 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 15c35ac9ae..dd1ed59255 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -207,6 +207,9 @@ class RemoveOperation(Operation): class Scope(object): + + SCOPE_ID: int = 1 + def __init__(self, *, parent_scope: typing.Optional[Scope], file: typing.Optional[str] = None, condition: str = '', @@ -225,6 +228,8 @@ class Scope(object): if not self._basedir: self._basedir = self._currentdir + self._scope_id = Scope.SCOPE_ID + Scope.SCOPE_ID += 1 self._file = file self._condition = map_condition(condition) self._children = [] # type: typing.List[Scope] @@ -234,8 +239,8 @@ class Scope(object): def __repr__(self): debug_mark = ' [MERGE_DEBUG]' if self.merge_debug else '' - return '{}:{}:{}{}'.format(self._basedir, self._file, - self._condition or '', debug_mark) + return '{}:{}:{}:{}{}'.format(self._scope_id, self._basedir, self._file, + self._condition or '', debug_mark) def reset_visited_keys(self): self._visited_keys = set() -- cgit v1.2.3 From 6dd989fe6163ce24324a35816803fb36730ac42c Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 31 Jan 2019 14:23:57 +0100 Subject: CMake: pro2cmake.py: Add scope debugging support Dump scope trees for all scopes that contain a variable 'PRO2CMAKE_SCOPE_DEBUG' (set to any value). Change-Id: If17bb1697a32ccaa427a858c2330ab2b019d0fa8 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index dd1ed59255..215ee21b10 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -272,6 +272,10 @@ class Scope(object): def merge_debug(self) -> bool: return self.getString('PRO2CMAKE_MERGE_DEBUG', None) != None + @property + def scope_debug(self) -> bool: + return self.getString('PRO2CMAKE_SCOPE_DEBUG', None) != None + @property def parent(self) -> typing.Optional[Scope]: return self._parent @@ -351,6 +355,10 @@ class Scope(object): scope.currentdir))) continue + if scope.scope_debug: + print('..... [SCOPE_DEBUG]: Created scope {}:'.format(scope)) + scope.dump(indent=1) + print('..... [SCOPE_DEBUG]: <>') return scope def _append_operation(self, key: str, op: Operation) -> None: -- cgit v1.2.3 From e616976fdf5d3912bbcbced0394330805fec462a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 31 Jan 2019 14:25:32 +0100 Subject: CMake: pro2cmake.py: Small fix to type markup Change-Id: I2f7dc94f379d51c3816649849aa7365b17334ab5 Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 215ee21b10..6d63461e0d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -290,7 +290,7 @@ class Scope(object): @staticmethod def FromDict(parent_scope: typing.Optional['Scope'], - file: str, statements, cond: str = '', base_dir: str = ''): + 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... -- cgit v1.2.3 From 25457f29db8b0321202afba25c3168c5ac9f9bbb Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 31 Jan 2019 16:18:06 +0100 Subject: CMake: pro2cmake.py: Add XCB and X11 libraries Add all the XCB and X11 libraries needed for the XCB plugin. Change-Id: I772b99c68521cd46cbba736912c8d8594d9d2ad8 Reviewed-by: Albert Astals Cid Reviewed-by: Frederik Gladhorn --- util/cmake/helper.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 872f968ba1..948392d925 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -205,14 +205,34 @@ def substitute_platform(platform: str) -> str: libray_mapping = { - 'libdl': '${CMAKE_DL_LIBS}', 'doubleconversion': 'double-conversion', + 'freetype': 'Freetype::Freetype', 'glib': 'GLIB2::GLIB2', 'icu': 'ICU::i18n ICU::uc ICU::data', 'libatomic': 'Atomic', + 'libdl': '${CMAKE_DL_LIBS}', 'libproxy': 'LibProxy::LibProxy', - 'pcre2': 'PCRE2', 'librt': 'WrapRt', + 'pcre2': 'PCRE2', + 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', + 'xcb_icccm': 'XCB::ICCCM', + 'xcb_image': 'XCB::IMAGE', + 'xcb_keysyms': 'XCB::KEYSYMS', + 'xcb_randr': 'XCB::RANDR', + 'xcb_renderutil': 'XCB::RENDERUTIL', + 'xcb_render': 'XCB::RENDER', + 'xcb_shape': 'XCB::SHAPE', + 'xcb_shm': 'XCB::SHM', + 'xcb_sync': 'XCB::SYNC', + 'xcb': 'XCB::XCB', + 'xcb_xfixes': 'XCB::XFIXES', + 'xcb_xinerama': 'XCB::XINERAMA', + 'xcb_xinput': 'XCB::XINPUT', + 'xcb_xkb': 'XCB::XKB', + 'xcb_xlib': 'X11::XCB', + 'xkbcommon_x11': 'XKB::XKB', + 'xkbcommon': 'XKB::XKB', + 'xrender': 'XCB::RENDER', 'zlib': 'ZLIB::ZLIB', 'zstd': 'ZSTD::ZSTD', } -- cgit v1.2.3 From 73f5036be58c52ab0c8a101e5e34ea6de12b9a29 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 31 Jan 2019 16:20:32 +0100 Subject: CMake: Map dlopen feature to 'ON' Map the dlopen feature to 'ON'. The effect is that cmake will figure out whether or not linking to 'dl' is necessary or not. The user-visible feature is 'library' anyway: That enables dynamic library loading -- and will link in 'dl' as needed. Change-Id: I0d68275a7234efba7f926150f120bb37b4a1163f Reviewed-by: Albert Astals Cid Reviewed-by: Frederik Gladhorn --- util/cmake/configurejson2cmake.py | 2 +- util/cmake/pro2cmake.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 03625da1a5..770f7588a3 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -276,7 +276,7 @@ def map_condition(condition): assert isinstance(condition, str) mapped_features = { - "dlopen": "UNIX", + "dlopen": "ON", 'gbm': 'gbm_FOUND', "system-xcb": "ON", "system-freetype": "ON", diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6d63461e0d..4f4303e3d1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -603,6 +603,8 @@ def map_condition(condition: str) -> str: if feature.startswith('system_') and substitute_libs(feature[7:]) != feature[7:]: # Qt6 always uses system libraries! part = 'ON' + elif feature == 'dlopen': + part = 'ON' else: part = 'QT_FEATURE_' + feature else: -- cgit v1.2.3 From 64e3c8bb190b95ab4da581dc5667864d8f4a176c Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 31 Jan 2019 14:26:06 +0100 Subject: CMake: pro2cmake: Fix handling of chained scopes with else branches Fix handling of things like: foo:bar:buz: { do something } else: wat { do something else } The else relates to foo AND bar AND buz, not just to buz in this case. Change-Id: I40d1fa295b4d6dd95ae5e1ce2d58372edc807b86 Reviewed-by: Albert Astals Cid Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4f4303e3d1..891e42003a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -288,6 +288,31 @@ class Scope(object): def currentdir(self) -> str: return self._currentdir + def can_merge_condition(self): + if self._condition == 'else': + return False + if self._operations: + return False + + 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' + + def settle_condition(self): + new_children: typing.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) + 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: @@ -333,7 +358,7 @@ class Scope(object): else_statements = statement.get('else_statements') if else_statements: Scope.FromDict(scope, file, else_statements, - 'NOT ' + condition, scope.basedir) + 'else', scope.basedir) continue loaded = statement.get('loaded') @@ -355,6 +380,8 @@ class Scope(object): scope.currentdir))) continue + scope.settle_condition() + if scope.scope_debug: print('..... [SCOPE_DEBUG]: Created scope {}:'.format(scope)) scope.dump(indent=1) -- cgit v1.2.3 From 6104643db05607898f660bcbf84cebad440deef5 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 7 Feb 2019 15:26:31 +0100 Subject: CMake: pro2cmake.py: Remove leading ./ in paths Remove leading './' from paths before writing them into CMakeLists.txt. Change-Id: I5680a3470cf491a8805b559197f94f8e6a6ce9b7 Reviewed-by: Albert Astals Cid Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 891e42003a..5e93a21a02 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -86,7 +86,7 @@ def map_to_file(f: str, top_dir: str, current_dir: str, if f.startswith('$$QT_SOURCE_TREE'): return "${PROJECT_SOURCE_DIR}/" + f[17:] if f.startswith("./"): - return os.path.join(current_dir, f) + return os.path.join(current_dir, f) if current_dir != '.' else f[2:] if want_absolute_path and not os.path.isabs(f): return os.path.join(current_dir, f) return f @@ -98,6 +98,8 @@ def map_source_to_cmake(source: str, base_dir: str, return '' if source.startswith('$$PWD/'): return source[6:] + if source.startswith('./'): + return source[2:] if source == '.': return "${CMAKE_CURRENT_SOURCE_DIR}" if source.startswith('$$QT_SOURCE_TREE/'): -- cgit v1.2.3 From d796410045404436eb35cc14da6c36179d978cb5 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 7 Feb 2019 15:28:10 +0100 Subject: CMake: pro2cmake.py: Remove some dead code Change-Id: I8e3e7e4c4ef9747e0749734bdf875baccc4646bd Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5e93a21a02..da81294e3a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -118,21 +118,6 @@ def map_source_to_cmake(source: str, base_dir: str, return '{}-NOTFOUND'.format(source) -def map_source_to_fs(base_dir: str, file: str, - source: str) -> str: - if source is None or source == '$$NO_PCH_SOURCES': - return '' - if source.startswith('$$PWD/'): - return os.path.join(os.path.dirname(file), source[6:]) - if source.startswith('$$QT_SOURCE_TREE/'): - return os.path.join('.', source[17:]) - if source.startswith('${PROJECT_SOURCE_DIR}/'): - return os.path.join('.', source[22:]) - if source.startswith('${CMAKE_CURRENT_SOURCE_DIR}/'): - return os.path.join(base_dir, source[28:]) - return os.path.join(base_dir, source) - - class Operation: def __init__(self, value): if isinstance(value, list): -- cgit v1.2.3 From 199cce0c20108e657d3b3555cdeacb76412fa28f Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 7 Feb 2019 15:28:49 +0100 Subject: CMake: pro2cmake.py: Better mapping of complex conditions .pro-files support things like contains(Foo, bar), etc. Map that in such a way that only one identifier will be visible to CMake to unconfuse the logic handling on that side. These conditions will need manual fixup later! Change-Id: Id4946e694a9adccf9f54bcce26a6c227cd921007 Reviewed-by: Albert Astals Cid Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index da81294e3a..5c804826c4 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -588,11 +588,11 @@ def parseProFile(file: str, *, debug=False): def map_condition(condition: str) -> str: - re.sub(r'if\s*\((.*?)\)', r'\1', condition) - re.sub(r'(^|[^a-zA-Z0-9_])isEmpty\s*\((.*?)\)', r'\2_ISEMPTY', condition) - re.sub(r'(^|[^a-zA-Z0-9_])contains\s*\((.*?), (.*)?\)', - r'\2___contains___\3', condition) - re.sub(r'\s*==\s*', '___STREQUAL___', 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*\((.*?), (.*)?\)', + r'\1___contains___\2', condition) + condition = re.sub(r'\s*==\s*', '___STREQUAL___', condition) condition = condition.replace('*', '_x_') condition = condition.replace('.$$', '__ss_') -- cgit v1.2.3 From 35a30c7ebbd50e8b5b1fad7c00bd6c36b0dca8b9 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 7 Feb 2019 15:30:44 +0100 Subject: CMake: pro2cmake.py: Expand qmake values in file names, etc. Expand qmake values when dealing with source file names, include directories and more. This handles cases where variables are used to refer to sources (e.g. $$VERSIONTAGGING_SOURCES in corelib) as well as things like $$QT_SOURCE_TREE and friends. Note that $$PWD and $$OUT_PWD are still need hand-holding since they refer to the scopes directory relative to the top level directory -- which pro2cmake.py does not know. Change-Id: I011ef55416ff820053d5f844b3008836849f5075 Reviewed-by: Albert Astals Cid Reviewed-by: Frederik Gladhorn --- util/cmake/pro2cmake.py | 64 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 19 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5c804826c4..9cd1b7a542 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -77,14 +77,10 @@ def spaces(indent: int) -> str: def map_to_file(f: str, top_dir: str, current_dir: str, want_absolute_path: bool = False) -> typing.Optional[str]: - if f == '$$NO_PCH_SOURCES': - return None if f.startswith('$$PWD/') or f == '$$PWD': # INCLUDEPATH += $$PWD return os.path.join(os.path.relpath(current_dir, top_dir), f[6:]) if f.startswith('$$OUT_PWD/'): return "${CMAKE_CURRENT_BUILD_DIR}/" + f[10:] - if f.startswith('$$QT_SOURCE_TREE'): - return "${PROJECT_SOURCE_DIR}/" + f[17:] if f.startswith("./"): return os.path.join(current_dir, f) if current_dir != '.' else f[2:] if want_absolute_path and not os.path.isabs(f): @@ -102,8 +98,6 @@ def map_source_to_cmake(source: str, base_dir: str, return source[2:] if source == '.': return "${CMAKE_CURRENT_SOURCE_DIR}" - if source.startswith('$$QT_SOURCE_TREE/'): - return "${PROJECT_SOURCE_DIR}/" + source[17:] if os.path.exists(os.path.join(base_dir, source)): return source @@ -201,7 +195,10 @@ class Scope(object): parent_scope: typing.Optional[Scope], file: typing.Optional[str] = None, condition: str = '', base_dir: str = '', - operations: typing.Mapping[str, typing.List[Operation]] = {}) -> None: + operations: typing.Mapping[str, typing.List[Operation]] = { + 'QT_SOURCE_TREE': [SetOperation('${PROJECT_SOURCE_DIR}')], + 'QT_BUILD_TREE': [SetOperation('${PROJECT_BUILD_DIR}')], + }) -> None: if parent_scope: parent_scope._add_child(self) else: @@ -453,6 +450,33 @@ class Scope(object): assert len(v) == 1 return v[0] + def _expand_value(self, value: str) -> typing.List[str]: + result = value + pattern = re.compile(r'\$\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?') + match = re.search(pattern, result) + while match: + if match.group(0) == value: + return self.expand(match.group(1), '') + else: + result = result[:match.start()] \ + + self.expandString(match.group(1)) \ + + result[match.end():] + match = re.search(pattern, result) + return [result] + + def expand(self, key: str, default=None) -> typing.List[str]: + value = self.get(key, default) + result: typing.List[str] = [] + assert isinstance(value, list) + for v in value: + result += self._expand_value(v) + return result + + def expandString(self, key: str) -> str: + result = self._expand_value(self.getString(key)) + assert len(result) == 1 + return result[0] + @property def TEMPLATE(self) -> str: return self.getString('TEMPLATE', 'app') @@ -715,10 +739,10 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, if plugin_type: cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0])) - sources = scope.get('SOURCES') + scope.get('HEADERS') \ - + scope.get('OBJECTIVE_SOURCES') + scope.get('NO_PCH_SOURCES') \ - + scope.get('FORMS') - resources = scope.get('RESOURCES') + sources = scope.expand('SOURCES') + scope.expand('HEADERS') \ + + scope.expand('OBJECTIVE_SOURCES') + scope.expand('NO_PCH_SOURCES') \ + + scope.expand('FORMS') + resources = scope.expand('RESOURCES') if resources: qrc_only = True for r in resources: @@ -731,7 +755,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, else: sources += resources - vpath = scope.get('VPATH') + vpath = scope.expand('VPATH') sources = [map_source_to_cmake(s, scope.basedir, vpath) for s in sources] if sources: @@ -739,26 +763,27 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, for l in sort_sources(sources): cm_fh.write('{} {}\n'.format(ind, l)) - defines = scope.get('DEFINES') + defines = scope.expand('DEFINES') if defines: cm_fh.write('{} DEFINES\n'.format(ind)) for d in defines: d = d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"') cm_fh.write('{} {}\n'.format(ind, d)) - includes = scope.get('INCLUDEPATH') + includes = scope.expand('INCLUDEPATH') if includes: cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind)) for i in includes: i = i.rstrip('/') or ('/') + i = map_source_to_cmake(i, scope.basedir, vpath) cm_fh.write('{} {}\n'.format(ind, i)) - dependencies = [map_qt_library(q) for q in scope.get('QT') + dependencies = [map_qt_library(q) for q in scope.expand('QT') if map_qt_library(q) not in known_libraries] - dependencies += [map_qt_library(q) for q in scope.get('QT_FOR_PRIVATE') + dependencies += [map_qt_library(q) for q in scope.expand('QT_FOR_PRIVATE') if map_qt_library(q) not in known_libraries] - dependencies += scope.get('QMAKE_USE_PRIVATE') + scope.get('QMAKE_USE') \ - + scope.get('LIBS_PRIVATE') + scope.get('LIBS') + dependencies += scope.expand('QMAKE_USE_PRIVATE') + scope.expand('QMAKE_USE') \ + + scope.expand('LIBS_PRIVATE') + scope.expand('LIBS') if dependencies: cm_fh.write('{} LIBRARIES\n'.format(ind)) is_framework = False @@ -807,7 +832,8 @@ def is_simple_condition(condition: str) -> bool: def write_ignored_keys(scope: Scope, ignored_keys, indent) -> str: result = '' for k in sorted(ignored_keys): - if k == '_INCLUDED' or k == 'TARGET' or k == 'QMAKE_DOCS': + if k == '_INCLUDED' or k == 'TARGET' or k == 'QMAKE_DOCS' or k == 'QT_SOURCE_TREE' \ + or k == 'QT_BUILD_TREE': # All these keys are actually reported already continue values = scope.get(k) -- cgit v1.2.3 From b388bc7e770439d3842034010f41ff2f20326d40 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 11 Feb 2019 11:36:00 +0100 Subject: CMake: pro2cmake.py: Handle equals(foo, bar) in conditions This adds an overly simplistic mapping -- just like it does for the rest of the qmake test functions. Change-Id: I0c9e3b70c1c3d0c68a245706a141aa7b7cb4d8bf Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9cd1b7a542..04e76635c0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -616,6 +616,8 @@ def map_condition(condition: str) -> str: condition = re.sub(r'\bisEmpty\s*\((.*?)\)', r'\1_ISEMPTY', condition) condition = re.sub(r'\bcontains\s*\((.*?), (.*)?\)', r'\1___contains___\2', condition) + condition = re.sub(r'\bequals\s*\((.*?), (.*)?\)', + r'\1___equals___\2', condition) condition = re.sub(r'\s*==\s*', '___STREQUAL___', condition) condition = condition.replace('*', '_x_') -- cgit v1.2.3 From fffc12cc34812ceb8104e78534bb0be338727765 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 11 Feb 2019 17:45:35 +0100 Subject: CMake: helper.py: Map harfbuzz library Change-Id: I71daa9acf391c70c6cba99609c1f67ca5eeaa220 Reviewed-by: Kevin Funk --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 948392d925..16502fd274 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -208,6 +208,7 @@ libray_mapping = { 'doubleconversion': 'double-conversion', 'freetype': 'Freetype::Freetype', 'glib': 'GLIB2::GLIB2', + 'harfbuzz': 'harfbuzz::harfbuzz', 'icu': 'ICU::i18n ICU::uc ICU::data', 'libatomic': 'Atomic', 'libdl': '${CMAKE_DL_LIBS}', -- cgit v1.2.3 From fb33efd2743933211455048aa111e4fc12e1e6a7 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 11 Feb 2019 17:46:31 +0100 Subject: CMake: pro2cmake.py: Do not go into infinite loop Do not go into an infinite loop when replacing a variable with itself. Change-Id: I2765b1c0c567753f26ca75e0533c1d193362b456 Reviewed-by: Kevin Funk --- util/cmake/pro2cmake.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 04e76635c0..13ee97cf00 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -455,17 +455,24 @@ class Scope(object): 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: - return self.expand(match.group(1), '') - else: - result = result[:match.start()] \ - + self.expandString(match.group(1)) \ - + result[match.end():] + return self.get(match.group(1), []) + + replacement = self.expand(match.group(1)) + 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 + match = re.search(pattern, result) return [result] - def expand(self, key: str, default=None) -> typing.List[str]: - value = self.get(key, default) + def expand(self, key: str) -> typing.List[str]: + value = self.get(key, []) result: typing.List[str] = [] assert isinstance(value, list) for v in value: -- cgit v1.2.3 From 93564b3f8be7506dbd9941ebc4fbdbf575f4beb4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 11 Feb 2019 17:52:39 +0100 Subject: CMake: pro2cmake.py: Treat ANDROID_EMBEDDED as ANDROID Treat ANDROID_EMBEDDED as ANDROID when simplifying conditions. Change-Id: I2cf0ea1e1a3e882e3a7b7276867dcee452866ade Reviewed-by: Kevin Funk --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 13ee97cf00..f18abcc81f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -943,6 +943,7 @@ def _recursive_simplify(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) # Now simplify further: expr = simplify_logic(expr) -- cgit v1.2.3 From f18db41f8586e60460649b4032cc8b6c01700441 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Mon, 11 Feb 2019 15:47:04 +0100 Subject: cmake: Find tslib and build the tslib plugin if found Change-Id: I7119ddf473a3ede29bbfef23cffb08a4fcbd5681 Reviewed-by: Tobias Hunger --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 770f7588a3..c26a90d4f5 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -78,6 +78,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'pps': 'PPS', 'slog2': 'Slog2', 'sun_iconv': None, + 'tslib': 'Tslib', 'udev': 'Libudev', 'vulkan': 'Vulkan', 'wayland_server': 'Wayland', -- cgit v1.2.3 From 3726b58d3b7c595291c8a3f22ee518dffc382950 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 11 Feb 2019 17:53:54 +0100 Subject: CMake: pro2cmake.py: Simplify condition generation There is no need to try and avoid extra () and NOTs: Those will be removed by sympy later anyway. Change-Id: I39d3e4d1d829579e532bfbbf6c69e0f1e06e9a22 Reviewed-by: Kevin Funk --- util/cmake/pro2cmake.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f18abcc81f..90c36fc57c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1000,29 +1000,13 @@ def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', if total_condition == 'else': assert previous_condition, \ "Else branch without previous condition in: %s" % scope.file - if previous_condition.startswith('NOT '): - total_condition = previous_condition[4:] - elif is_simple_condition(previous_condition): - total_condition = 'NOT {}'.format(previous_condition) - else: - total_condition = 'NOT ({})'.format(previous_condition) + total_condition = 'NOT ({})'.format(previous_condition) if parent_condition: if not total_condition: total_condition = parent_condition else: - if is_simple_condition(parent_condition) \ - and is_simple_condition(total_condition): - total_condition = '{} AND {}'.format(parent_condition, + total_condition = '({}) AND ({})'.format(parent_condition, total_condition) - elif is_simple_condition(total_condition): - total_condition = '({}) AND {}'.format(parent_condition, - total_condition) - elif is_simple_condition(parent_condition): - total_condition = '{} AND ({})'.format(parent_condition, - total_condition) - else: - total_condition = '({}) AND ({})'.format(parent_condition, - total_condition) scope.total_condition = simplify_condition(total_condition) -- cgit v1.2.3 From ac96d08b9d0900a8e368c2e72c86190335480911 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Mon, 11 Feb 2019 16:27:26 +0100 Subject: cmake: find gtk3 and build the gtk3 platformtheme Change-Id: I7db7321a2fd5ea0eda1924f3dece3b1c86d87d10 Reviewed-by: Tobias Hunger --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index c26a90d4f5..2d635e0c8a 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -57,6 +57,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'gbm': 'gbm', 'glib': 'GLIB2', 'gnu_iconv': None, + 'gtk3': 'GTK3', 'harfbuzz': 'harfbuzz', 'host_dbus': None, 'icu': ['ICU', 'COMPONENTS', 'i18n', 'uc', 'data'], -- cgit v1.2.3 From 71c9db7da804cc169c7c42c763555d85657a9669 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Mon, 11 Feb 2019 23:01:27 +0100 Subject: cmake: Blacklist the libinput_axis_api test We already have a cmake find module for libinput so we know it is there Change-Id: I153544c5c13cb57b1ce258243ede17f4be9507fd Reviewed-by: Liang Qi --- util/cmake/configurejson2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 2d635e0c8a..5a2b95d699 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -184,6 +184,8 @@ def map_tests(test: str) -> str: 'openssl11': '(OPENSSL_VERSION VERSION_GREATER_EQUAL "1.1.0")', 'reduce_exports': 'CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY', + + 'libinput_axis_api': 'ON', } if test in testmap: return testmap.get(test, None) @@ -546,6 +548,7 @@ def parseTest(ctx, test, data, cm_fh): 'posix-iconv', "sun-iconv", 'separate_debug_info', # FIXME: see if cmake can do this 'gc_binaries', + 'libinput_axis_api', } if test in skip_tests: -- cgit v1.2.3 From 3882e9ea09ec9062fd3c711ae6caadb69b67c752 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Mon, 11 Feb 2019 18:27:52 +0100 Subject: cmake: Find Cups and enable its plugin Change-Id: I44bf176587331d86eb147d106f28deb0a6618ab4 Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 5a2b95d699..d24a3d4b5e 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -49,6 +49,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: libmap = { 'atspi': 'ATSPI2', 'corewlan': None, # Framework + 'cups': 'Cups', 'double-conversion': 'WrapDoubleConversion', 'drm': 'Libdrm', 'egl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_EGL"), -- cgit v1.2.3 From f6a830095fbb607307541305a03da663bfb82cf3 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 09:57:53 +0100 Subject: cmake: blacklist xlib test, we can just use X11_FOUND Change-Id: Ic733c42e11b00fa8bf107f460a0596aeb10ac37b Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index d24a3d4b5e..6e6c402b07 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -187,6 +187,7 @@ def map_tests(test: str) -> str: 'reduce_exports': 'CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY', 'libinput_axis_api': 'ON', + "xlib": "X11_FOUND", } if test in testmap: return testmap.get(test, None) @@ -550,6 +551,7 @@ def parseTest(ctx, test, data, cm_fh): 'separate_debug_info', # FIXME: see if cmake can do this 'gc_binaries', 'libinput_axis_api', + 'xlib', } if test in skip_tests: -- cgit v1.2.3 From 2e64ac90d8b71bf1afe4ae4af87096a3888ff3be Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 10:01:07 +0100 Subject: cmake: also write FIXME on the configure.cmake for 'use' Since most of the times it means we need to link with some other stuff Change-Id: I06262d4403225bca7a5e68d47145fefcf6702e5a Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 6e6c402b07..7efeb88522 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -610,6 +610,8 @@ def parseTest(ctx, test, data, cm_fh): cm_fh.write('"' + sourceCode + '"') if "qmake" in details: cm_fh.write("# FIXME: qmake: {}\n".format(details["qmake"])) + if "use" in data: + cm_fh.write("# FIXME: use: {}\n".format(data["use"])) cm_fh.write(")\n\n") elif data["type"] == "x86Simd": -- cgit v1.2.3 From cfba14e1e9198f3b902e267e44b9f57d40920e1d Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 10:07:57 +0100 Subject: configurejson2cmake.py: Support for the two cases that have LIBS += Since there's only two i hardcoded it for the moment instead of trying to parse the line Change-Id: I0da578af64ef9621cbbc78bf6ce15bf8a3f63f1c Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 7efeb88522..5357ccc44e 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -604,12 +604,32 @@ def parseTest(ctx, test, data, cm_fh): sourceCode = sourceCode.replace('"', '\\"') + librariesCmakeName = "" + qmakeFixme = "" + cm_fh.write("# {}\n".format(test)) + if "qmake" in details: # We don't really have many so we can just enumerate them all + if details["qmake"] == "unix:LIBS += -lpthread": + librariesCmakeName = format(test) + "_TEST_LIBRARIES" + cm_fh.write("if (UNIX)\n") + cm_fh.write(" set(" + librariesCmakeName + " pthread)\n") + cm_fh.write("endif()\n") + elif details["qmake"] == "linux: LIBS += -lpthread -lrt": + librariesCmakeName = format(test) + "_TEST_LIBRARIES" + cm_fh.write("if (LINUX)\n") + cm_fh.write(" set(" + librariesCmakeName + " pthread rt)\n") + cm_fh.write("endif()\n") + else: + qmakeFixme = "# FIXME: qmake: {}\n".format(details["qmake"]) + cm_fh.write("qt_config_compile_test({}\n".format(featureName(test))) cm_fh.write(lineify("LABEL", data.get("label", ""))) + if librariesCmakeName != "": + cm_fh.write(lineify("LIBRARIES", "${"+librariesCmakeName+"}")) + cm_fh.write(" CODE\n") cm_fh.write('"' + sourceCode + '"') - if "qmake" in details: - cm_fh.write("# FIXME: qmake: {}\n".format(details["qmake"])) + if qmakeFixme != "": + cm_fh.write(qmakeFixme) if "use" in data: cm_fh.write("# FIXME: use: {}\n".format(data["use"])) cm_fh.write(")\n\n") -- cgit v1.2.3 From 443ee65d3b73c901c62e642130cbe3063c405467 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 10:11:24 +0100 Subject: configurejson2cmake.py: Don't emit a fixme for C++11 config Change-Id: I8408f6126115a0f76b0fed2cc42b54e5c148821d Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 5357ccc44e..8bf9b77459 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -619,6 +619,9 @@ def parseTest(ctx, test, data, cm_fh): cm_fh.write("if (LINUX)\n") cm_fh.write(" set(" + librariesCmakeName + " pthread rt)\n") cm_fh.write("endif()\n") + elif details["qmake"] == "CONFIG += c++11": + # do nothing we're always in c++11 mode + pass else: qmakeFixme = "# FIXME: qmake: {}\n".format(details["qmake"]) -- cgit v1.2.3 From 7b434c0287a67eb58413b7f5781d849d2080786f Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Mon, 11 Feb 2019 15:45:33 +0100 Subject: CMake: Fix build of system jpeg is not available Change-Id: Ie7462db556d0615e74755fa4fc3b51f625aade2f Reviewed-by: Mikhail Svetkin Reviewed-by: Liang Qi --- util/cmake/configurejson2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 8bf9b77459..23bd81cb5e 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -698,6 +698,9 @@ def parseFeature(ctx, feature, data, cm_fh): '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, -- cgit v1.2.3 From 92f81ebc21ef4b3ea60a04dcae1d20387082a7e4 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 10:34:19 +0100 Subject: configurejson2cmake.py: openssl -> OpenSSL mapping Change-Id: I29480dfbd4f144e3d5620b43419ec46fb866cf4b Reviewed-by: Liang Qi --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 23bd81cb5e..4bfe023c2a 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -74,6 +74,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'libudev': 'Libudev', 'lttng-ust': LibraryMapping(package='LTTngUST', resultVariable="LTTNGUST"), 'opengl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_OpenGL"), + 'openssl': 'OpenSSL', 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), 'pcre2': ['PCRE2', 'REQUIRED'], 'posix_iconv': None, -- cgit v1.2.3 From 05aeaed906cbec3654178d1438ae2ad361a35ddd Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 10:47:12 +0100 Subject: Find mtdev and use it Change-Id: I90db48efaa6a23add770fcf69b46c4f4c84866c1 Reviewed-by: Liang Qi --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 4bfe023c2a..870c56d4d6 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -73,6 +73,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'librt': 'WrapRt', 'libudev': 'Libudev', 'lttng-ust': LibraryMapping(package='LTTngUST', resultVariable="LTTNGUST"), + 'mtdev': 'Mtdev', 'opengl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_OpenGL"), 'openssl': 'OpenSSL', 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), -- cgit v1.2.3 From 9bef044a0bec636ff08f7dcd6e16f6fadd612532 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 12:00:49 +0100 Subject: cmake: Search and enable the sqlite[3] plugin Added to QtFeature.cmake a way to be able to run feature_module begin and end without having an actual module by passing NO_MODULE Change-Id: Ib708bd3878e2591da193d18563c8932cc4b75e7f Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 5 +++++ util/cmake/helper.py | 1 + 2 files changed, 6 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 870c56d4d6..e99bb27bc2 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -81,6 +81,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'posix_iconv': None, 'pps': 'PPS', 'slog2': 'Slog2', + 'sqlite3': 'SQLite3', 'sun_iconv': None, 'tslib': 'Tslib', 'udev': 'Libudev', @@ -731,6 +732,9 @@ def parseFeature(ctx, feature, data, cm_fh): '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, @@ -746,6 +750,7 @@ def parseFeature(ctx, feature, data, cm_fh): 'system-jpeg': None, 'system-pcre2': None, 'system-png': None, + 'system-sqlite': None, 'system-xcb': None, 'system-zlib': None, 'use_gold_linker': None, diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 16502fd274..6234d26d09 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -215,6 +215,7 @@ libray_mapping = { 'libproxy': 'LibProxy::LibProxy', 'librt': 'WrapRt', 'pcre2': 'PCRE2', + 'sqlite': 'SQLite3', 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', 'xcb_icccm': 'XCB::ICCCM', 'xcb_image': 'XCB::IMAGE', -- cgit v1.2.3 From 11acba6504560dde54e94989b84df94157f9cc00 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 14:34:40 +0100 Subject: cmake: Find and build psql plugin Change-Id: I8cbc8ab0061f67824d78198cbb926f0625fc7e41 Reviewed-by: Liang Qi --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index e99bb27bc2..c46d3167f8 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -80,6 +80,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'pcre2': ['PCRE2', 'REQUIRED'], 'posix_iconv': None, 'pps': 'PPS', + 'psql': 'PostgreSQL', 'slog2': 'Slog2', 'sqlite3': 'SQLite3', 'sun_iconv': None, -- cgit v1.2.3 From 0a14d13b3082bb245ddbb839e15025b9709f4eff Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 14:55:20 +0100 Subject: cmake: Find and build odbc plugin Change-Id: I479d2b1cc897f601ef68b10272c9396e52228201 Reviewed-by: Liang Qi --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index c46d3167f8..df77ea0704 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -74,6 +74,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'libudev': 'Libudev', 'lttng-ust': LibraryMapping(package='LTTngUST', resultVariable="LTTNGUST"), 'mtdev': 'Mtdev', + 'odbc': 'ODBC', 'opengl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_OpenGL"), 'openssl': 'OpenSSL', 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), -- cgit v1.2.3 From 519ef817ffb4dbadb00ac958e7402cbfeaf135af Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Feb 2019 16:58:26 +0100 Subject: Fix Desktop GL/EGL/GLESv2 linkage This change fixes a few things in one go: * cmake's FindOpenGL cannot be used reliably to detect EGL. So use a custom module for that. * Added a custom module for GLESv2 detection, as cmake's FindOpenGL does not support that. * Map CONFIG += opengl to a WrapOpenGL target, which links against either GLESv2 or libGL - just like mkspecs/features/*/opengl.prf * cmake's FindOpenGL remains in use solely to detect the availability of desktop gl. Change-Id: I9315e5ad1fd88e1b7dc7e920053e98fb51fea7fc Reviewed-by: Volker Krause --- util/cmake/configurejson2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index df77ea0704..840ffefd05 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -52,7 +52,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'cups': 'Cups', 'double-conversion': 'WrapDoubleConversion', 'drm': 'Libdrm', - 'egl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_EGL"), + 'egl': 'EGL', 'fontconfig': LibraryMapping(package='Fontconfig', resultVariable="FONTCONFIG"), 'freetype': 'Freetype', 'gbm': 'gbm', @@ -101,6 +101,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'xrender': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), 'zlib': 'ZLIB', 'zstd': 'ZSTD', + 'opengl_es2': 'GLESv2', } # type: Dict[str, Union[str, List[str], LibraryMapping]] if lib not in libmap: raise Exception(' XXXX Unknown library "{}".'.format(lib)) -- cgit v1.2.3 From d523a642dc7f8b75e7a3a945149cce2a5c4451d3 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 12 Feb 2019 17:27:16 +0100 Subject: cmake: enable vnc platform plugin Change-Id: I5015681aff3c9ceb5c0b72571bc8756f3ada104c Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 6234d26d09..c11632cd06 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -63,6 +63,7 @@ def map_qt_base_library(lib: str) -> str: 'enginio_client': 'Enginio', 'eventdispatchers': 'Qt::EventDispatcherSupport', 'extras': 'Qt::3DExtras', + 'fb_support': 'Qt::FbSupport', 'fbconvenience': 'Qt::FbSupport', 'fontdatabase_support': 'Qt::FontDatabaseSupport', 'gamepad': 'Qt::Gamepad', @@ -73,6 +74,7 @@ def map_qt_base_library(lib: str) -> str: 'help': 'Qt::Help', 'hunspellinputmethod': 'Qt::HunspellInputMethod', 'input': 'Qt::InputSupport', + 'input_support': 'Qt::InputSupport', 'installer-lib': 'Qt::AppManInstaller', 'kmsconvenience': 'Qt::KmsSupport', 'launcher-lib': 'Qt::AppManLauncher', -- cgit v1.2.3 From db9cb61993aa8780281a308542c47fd775aa01b4 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Fri, 15 Feb 2019 15:48:58 +0100 Subject: cmake: helper.py more mappings Change-Id: I69a1715bbda8f5ddfb367e7c7c693e4ec412f5c3 Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index c11632cd06..e2dfbf8a7c 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -56,10 +56,12 @@ def map_qt_base_library(lib: str) -> str: 'crypto-lib': 'Qt::AppManCrypto', 'dbus': 'Qt::DBus', 'devicediscovery': 'Qt::DeviceDiscoverySupport', + 'devicediscovery_support': 'Qt::DeviceDiscoverySupport', 'edid': 'Qt::EdidSupport', 'eglconvenience': 'Qt::EglSupport', 'eglfsdeviceintegration': 'Qt::EglFSDeviceIntegration', 'eglfs_kms_support': 'Qt::EglFsKmsSupport', + 'egl_support': 'Qt::EglSupport', 'enginio_client': 'Enginio', 'eventdispatchers': 'Qt::EventDispatcherSupport', 'extras': 'Qt::3DExtras', @@ -97,6 +99,7 @@ def map_qt_base_library(lib: str) -> str: 'packetprotocol': 'Qt::PacketProtocol', 'particles': 'Qt::QuickParticles', 'platformcompositor': 'Qt::PlatformCompositorSupport', + 'platformcompositor_support': 'Qt::PlatformCompositorSupport', 'plugin-interfaces': 'Qt::AppManPluginInterfaces', 'positioning': 'Qt::Positioning', 'positioningquick': 'Qt::PositioningQuick', -- cgit v1.2.3 From dc0870db74c214b6727f53467c005c98fb8cef5d Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Fri, 15 Feb 2019 15:51:30 +0100 Subject: pro2cmake: Target dependencies can be on -private libs so use map_qt_library instead of map_qt_base_library Change-Id: I4dd0097fff3ffd9ec4aad36d11d79ea23a08cb90 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 90c36fc57c..149da8e76d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -643,7 +643,7 @@ def map_condition(condition: str) -> str: part) if feature: if (feature.group(1) == "qtHaveModule"): - part = 'TARGET {}'.format(map_qt_base_library( + part = 'TARGET {}'.format(map_qt_library( feature.group(2))) else: feature = featureName(feature.group(2)) -- cgit v1.2.3 From 5f8e4b7cf05d8acccdf237fcd4c1a59fc29c2aa4 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Fri, 15 Feb 2019 17:30:04 +0100 Subject: cmake: add more mappings to helper.py Change-Id: Ie5096c1fe74105db84ccd60655a20d389a887b14 Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index e2dfbf8a7c..c7749e50a6 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -79,6 +79,7 @@ def map_qt_base_library(lib: str) -> str: 'input_support': 'Qt::InputSupport', 'installer-lib': 'Qt::AppManInstaller', 'kmsconvenience': 'Qt::KmsSupport', + 'kms_support': 'Qt::KmsSupport', 'launcher-lib': 'Qt::AppManLauncher', 'lib': 'Qt::Designer', 'linuxaccessibility': 'Qt::LinuxAccessibilitySupport', @@ -210,8 +211,10 @@ def substitute_platform(platform: str) -> str: libray_mapping = { + 'drm': 'Libdrm::Libdrm', 'doubleconversion': 'double-conversion', 'freetype': 'Freetype::Freetype', + 'gbm': 'gbm::gbm', 'glib': 'GLIB2::GLIB2', 'harfbuzz': 'harfbuzz::harfbuzz', 'icu': 'ICU::i18n ICU::uc ICU::data', -- cgit v1.2.3 From e03596c2a7d7a3d4b62c90170175fc61957776a9 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Fri, 15 Feb 2019 15:50:24 +0100 Subject: pro2cmake: account for child .pro files having includes Change-Id: Ic2213578c9bd27787ae9788acbe4455252a1158c Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 149da8e76d..5537ba38dc 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -678,7 +678,8 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, subdir_result.asDict().get('statements'), '', scope.basedir) - cmakeify_scope(subdir_scope, cm_fh, indent=indent + 1) + do_include(subdir_scope) + cmakeify_scope(subdir_scope, cm_fh, indent=indent) elif sd.startswith('-'): cm_fh.write('{}### remove_subdirectory' '("{}")\n'.format(ind, sd[1:])) -- cgit v1.2.3 From 31e1dda94b66581580dd92a3664d5f5773b81065 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Thu, 21 Feb 2019 14:46:22 +0100 Subject: cmake: Give the egl_x11 test the necessary libs to compile And add the eglfs_x11 plugin We need to actually try to compile the test as the comment it it says that having x11 and egl is not enough since sometimes they are actually incompatible Change-Id: If6bdc08c21b91fa9c41663f2fa653fd59e5ddd2e Reviewed-by: Tobias Hunger --- util/cmake/configurejson2cmake.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 840ffefd05..0997608ed9 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -616,12 +616,12 @@ def parseTest(ctx, test, data, cm_fh): cm_fh.write("# {}\n".format(test)) if "qmake" in details: # We don't really have many so we can just enumerate them all if details["qmake"] == "unix:LIBS += -lpthread": - librariesCmakeName = format(test) + "_TEST_LIBRARIES" + librariesCmakeName = format(featureName(test)) + "_TEST_LIBRARIES" cm_fh.write("if (UNIX)\n") cm_fh.write(" set(" + librariesCmakeName + " pthread)\n") cm_fh.write("endif()\n") elif details["qmake"] == "linux: LIBS += -lpthread -lrt": - librariesCmakeName = format(test) + "_TEST_LIBRARIES" + librariesCmakeName = format(featureName(test)) + "_TEST_LIBRARIES" cm_fh.write("if (LINUX)\n") cm_fh.write(" set(" + librariesCmakeName + " pthread rt)\n") cm_fh.write("endif()\n") @@ -631,6 +631,15 @@ def parseTest(ctx, test, data, cm_fh): else: qmakeFixme = "# FIXME: qmake: {}\n".format(details["qmake"]) + if "use" in data: + if data["use"] == "egl xcb_xlib": + librariesCmakeName = format(featureName(test)) + "_TEST_LIBRARIES" + cm_fh.write("if (HAVE_EGL AND X11_XCB_FOUND AND X11_FOUND)\n") + 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"]) + cm_fh.write("qt_config_compile_test({}\n".format(featureName(test))) cm_fh.write(lineify("LABEL", data.get("label", ""))) if librariesCmakeName != "": @@ -639,8 +648,6 @@ def parseTest(ctx, test, data, cm_fh): cm_fh.write('"' + sourceCode + '"') if qmakeFixme != "": cm_fh.write(qmakeFixme) - if "use" in data: - cm_fh.write("# FIXME: use: {}\n".format(data["use"])) cm_fh.write(")\n\n") elif data["type"] == "x86Simd": -- cgit v1.2.3 From 885494c50f75b79a33a9275221ae9e0efb91349c Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Mon, 7 Jan 2019 15:22:24 +0100 Subject: CMake: Fix build without double-conversion on macOS Change-Id: I5102e93141eec95044df44884dcf6ecd1b9e8dd0 Reviewed-by: Mikhail Svetkin Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 0997608ed9..5d189558bd 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -50,7 +50,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'atspi': 'ATSPI2', 'corewlan': None, # Framework 'cups': 'Cups', - 'double-conversion': 'WrapDoubleConversion', + 'double-conversion': None, 'drm': 'Libdrm', 'egl': 'EGL', 'fontconfig': LibraryMapping(package='Fontconfig', resultVariable="FONTCONFIG"), -- cgit v1.2.3 From b1fa25e7b8974fd9ce4b6d1283b9a43da532992e Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 30 Jan 2019 16:54:42 +0100 Subject: CMake: Small unsorted fixes in preparation of upgrading to dev branch Change-Id: Id4d03558e956c6994dc6a8b701030ba4edf86adf Reviewed-by: Alexandru Croitor Reviewed-by: Albert Astals Cid --- util/cmake/configurejson2cmake.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 5d189558bd..a684130c68 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -550,10 +550,12 @@ def parseInput(ctx, input, data, cm_fh): # }, def parseTest(ctx, test, data, cm_fh): skip_tests = { - 'c11', 'c99', 'c++11', 'c++14', 'c++1y', 'c++1z', - 'reduce_exports', + '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', @@ -720,7 +722,7 @@ def parseFeature(ctx, feature, data, cm_fh): 'optimize_size': None, 'pkg-config': None, 'posix_fallocate': None, # Only needed for sqlite, which we do not want to build - 'posix_libiconv': { + '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', -- cgit v1.2.3 From 35f23a3dad20e09bada4e8fcdcc0de16b8a6af2f Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 11 Feb 2019 18:02:22 +0100 Subject: CMake: pro2cmake.py: Better parsing of scopes with else Parse conditions more exactly as before, enabling proper handling of else scopes. Change-Id: Icb5dcc73010be4833b2d1cbc1396191992df1ee4 Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 123 ++++++++++++++++------------- util/cmake/tests/data/comment_scope.pro | 6 ++ util/cmake/tests/data/contains_scope.pro | 4 + util/cmake/tests/data/multiline_assign.pro | 4 + util/cmake/tests/data/standardpaths.pro | 17 ++++ util/cmake/tests/test_parsing.py | 88 ++++++++++++++++++++- util/cmake/tests/test_scope_handling.py | 56 +++++++++++++ 7 files changed, 237 insertions(+), 61 deletions(-) create mode 100644 util/cmake/tests/data/comment_scope.pro create mode 100644 util/cmake/tests/data/contains_scope.pro create mode 100644 util/cmake/tests/data/multiline_assign.pro create mode 100644 util/cmake/tests/data/standardpaths.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5537ba38dc..63938d75c8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -32,6 +32,7 @@ from __future__ import annotations from argparse import ArgumentParser import copy +from itertools import chain import os.path import re import io @@ -509,10 +510,12 @@ class QmakeParser: # Define grammar: pp.ParserElement.setDefaultWhitespaceChars(' \t') - LC = pp.Suppress(pp.Literal('\\') + pp.LineEnd()) - EOL = pp.Suppress(pp.Optional(pp.pythonStyleComment()) + pp.LineEnd()) - + LC = pp.Suppress(pp.Literal('\\\n')) + EOL = pp.Suppress(pp.Literal('\n')) + Else = pp.Keyword('else') + DefineTest = pp.Keyword('defineTest') Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_-./') + Substitution \ = pp.Combine(pp.Literal('$') + (((pp.Literal('$') + Identifier @@ -525,32 +528,31 @@ class QmakeParser: | (pp.Literal('$') + pp.Literal('[') + Identifier + pp.Literal(']')) ))) - # Do not match word ending in '\' since that breaks line - # continuation:-/ LiteralValuePart = pp.Word(pp.printables, excludeChars='$#{}()') SubstitutionValue \ = pp.Combine(pp.OneOrMore(Substitution | LiteralValuePart | pp.Literal('$'))) - Value = (pp.QuotedString(quoteChar='"', escChar='\\') - | SubstitutionValue) + Value = pp.NotAny(Else | pp.Literal('}') | EOL | pp.Literal('\\')) \ + + (pp.QuotedString(quoteChar='"', escChar='\\') + | SubstitutionValue) - Values = pp.ZeroOrMore(Value)('value') + Values = pp.ZeroOrMore(Value + pp.Optional(LC))('value') Op = pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') \ | pp.Literal('*=') - Operation = Identifier('key') + Op('operation') + Values('value') - Load = pp.Keyword('load') + pp.Suppress('(') \ - + Identifier('loaded') + pp.Suppress(')') - Include = pp.Keyword('include') + pp.Suppress('(') \ - + pp.CharsNotIn(':{=}#)\n')('included') + pp.Suppress(')') - Option = pp.Keyword('option') + pp.Suppress('(') \ - + Identifier('option') + pp.Suppress(')') - DefineTest = pp.Suppress(pp.Keyword('defineTest') - + pp.Suppress('(') + Identifier - + pp.Suppress(')') - + pp.nestedExpr(opener='{', closer='}') - + pp.LineEnd()) # ignore the whole thing... + Key = Identifier + + Operation = Key('key') + pp.Optional(LC) \ + + Op('operation') + pp.Optional(LC) \ + + Values('value') + CallArgs = pp.nestedExpr() + CallArgs.setParseAction(lambda x: ' '.join(chain(*x))) + Load = pp.Keyword('load') + CallArgs('loaded') + Include = pp.Keyword('include') + CallArgs('included') + Option = pp.Keyword('option') + CallArgs('option') + DefineTestDefinition = pp.Suppress(DefineTest + CallArgs \ + + pp.nestedExpr(opener='{', closer='}')) # ignore the whole thing... ForLoop = pp.Suppress(pp.Keyword('for') + pp.nestedExpr() + pp.nestedExpr(opener='{', closer='}', ignoreExpr=None) @@ -559,45 +561,54 @@ class QmakeParser: Scope = pp.Forward() - Statement = pp.Group(Load | Include | Option | DefineTest - | ForLoop | FunctionCall | Operation) - StatementLine = Statement + EOL - StatementGroup = pp.ZeroOrMore(StatementLine | Scope | EOL) - - Block = pp.Suppress('{') + pp.Optional(EOL) \ - + pp.ZeroOrMore(EOL | Statement + EOL | Scope) \ - + pp.Optional(Statement) + pp.Optional(EOL) \ - + pp.Suppress('}') + pp.Optional(EOL) - - Condition = pp.Optional(pp.White()) + pp.CharsNotIn(':{=}#\\\n') - Condition.setParseAction(lambda x: ' '.join(x).strip()) - - SingleLineScope = pp.Suppress(pp.Literal(':')) \ - + pp.Group(Scope | Block | StatementLine)('statements') - MultiLineScope = Block('statements') - - SingleLineElse = pp.Suppress(pp.Literal(':')) \ - + pp.Group(Scope | StatementLine)('else_statements') - MultiLineElse = pp.Group(Block)('else_statements') - Else = pp.Suppress(pp.Keyword('else')) \ - + (SingleLineElse | MultiLineElse) - Scope <<= pp.Group(Condition('condition') - + (SingleLineScope | MultiLineScope) - + pp.Optional(Else)) + Statement = pp.Group(Load | Include | Option | ForLoop \ + | DefineTestDefinition | FunctionCall | Operation) + StatementLine = Statement + (EOL | pp.FollowedBy('}')) + StatementGroup = pp.ZeroOrMore(StatementLine | Scope | pp.Suppress(EOL)) + + Block = pp.Suppress('{') + pp.Optional(LC | EOL) \ + + StatementGroup + pp.Optional(LC | EOL) \ + + pp.Suppress('}') + pp.Optional(LC | EOL) + + ConditionEnd = pp.FollowedBy((pp.Optional(LC) + (pp.Literal(':') \ + | pp.Literal('{') \ + | pp.Literal('|')))) + ConditionPart = pp.CharsNotIn('#{}|:=\\\n') + pp.Optional(LC) + ConditionEnd + Condition = pp.Combine(ConditionPart \ + + pp.ZeroOrMore((pp.Literal('|') ^ pp.Literal(':')) \ + + ConditionPart)) + Condition.setParseAction(lambda x: ' '.join(x).strip().replace(':', ' && ').strip(' && ')) + + SingleLineScope = pp.Suppress(pp.Literal(':')) + pp.Optional(LC) \ + + pp.Group(Block | (Statement + EOL))('statements') + MultiLineScope = pp.Optional(LC) + Block('statements') + + SingleLineElse = pp.Suppress(pp.Literal(':')) + pp.Optional(LC) \ + + (Scope | Block | (Statement + pp.Optional(EOL))) + MultiLineElse = Block + ElseBranch = pp.Suppress(Else) + (SingleLineElse | MultiLineElse) + Scope <<= pp.Optional(LC) \ + + pp.Group(Condition('condition') \ + + (SingleLineScope | MultiLineScope) \ + + pp.Optional(ElseBranch)('else_statements')) if debug: - for ename in 'EOL Identifier Substitution SubstitutionValue ' \ - 'LiteralValuePart Value Values SingleLineScope ' \ - 'MultiLineScope Scope SingleLineElse ' \ - 'MultiLineElse Else Condition Block ' \ - 'StatementGroup Statement Load Include Option ' \ - 'DefineTest ForLoop FunctionCall Operation'.split(): + for ename in 'LC EOL ' \ + 'Condition ConditionPart ConditionEnd ' \ + 'Else ElseBranch SingleLineElse MultiLineElse ' \ + 'SingleLineScope MultiLineScope ' \ + 'Identifier ' \ + 'Key Op Values Value ' \ + 'Scope Block ' \ + 'StatementGroup StatementLine Statement '\ + 'Load Include Option DefineTest ForLoop ' \ + 'FunctionCall CallArgs Operation'.split(): expr = locals()[ename] expr.setName(ename) expr.setDebug() Grammar = StatementGroup('statements') - Grammar.ignore(LC) + Grammar.ignore(pp.pythonStyleComment()) return Grammar @@ -971,8 +982,8 @@ def simplify_condition(condition: str) -> str: 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(' ON ', ' true ') + condition = condition.replace(' OFF ', ' false ') try: # Generate and simplify condition using sympy: @@ -989,9 +1000,7 @@ def simplify_condition(condition: str) -> str: # sympy did not like our input, so leave this condition alone: condition = input_condition - if condition == '': - condition = 'ON' - return condition + return condition or 'ON' def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', diff --git a/util/cmake/tests/data/comment_scope.pro b/util/cmake/tests/data/comment_scope.pro new file mode 100644 index 0000000000..be43cad37d --- /dev/null +++ b/util/cmake/tests/data/comment_scope.pro @@ -0,0 +1,6 @@ +# QtCore can't be compiled with -Wl,-no-undefined because it uses the "environ" +# variable and on FreeBSD and OpenBSD, this variable is in the final executable itself. +# OpenBSD 6.0 will include environ in libc. +freebsd|openbsd: QMAKE_LFLAGS_NOUNDEF = + +include(animation/animation.pri) diff --git a/util/cmake/tests/data/contains_scope.pro b/util/cmake/tests/data/contains_scope.pro new file mode 100644 index 0000000000..0f51350a45 --- /dev/null +++ b/util/cmake/tests/data/contains_scope.pro @@ -0,0 +1,4 @@ +contains(DEFINES,QT_EVAL):include(eval.pri) + +HOST_BINS = $$[QT_HOST_BINS] + diff --git a/util/cmake/tests/data/multiline_assign.pro b/util/cmake/tests/data/multiline_assign.pro new file mode 100644 index 0000000000..42a3d0a674 --- /dev/null +++ b/util/cmake/tests/data/multiline_assign.pro @@ -0,0 +1,4 @@ +A = 42 \ + 43 \ + 44 +B=23 diff --git a/util/cmake/tests/data/standardpaths.pro b/util/cmake/tests/data/standardpaths.pro new file mode 100644 index 0000000000..4b45788e4f --- /dev/null +++ b/util/cmake/tests/data/standardpaths.pro @@ -0,0 +1,17 @@ +win32 { + !winrt { + SOURCES +=io/qstandardpaths_win.cpp + } else { + SOURCES +=io/qstandardpaths_winrt.cpp + } +} else:unix { + mac { + OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm + } else:android:!android-embedded { + SOURCES += io/qstandardpaths_android.cpp + } else:haiku { + SOURCES += io/qstandardpaths_haiku.cpp + } else { + SOURCES += io/qstandardpaths_unix.cpp + } +} diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index 0802fe4742..e4f9680f60 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -37,7 +37,7 @@ _tests_path = os.path.dirname(os.path.abspath(__file__)) def validate_op(key, op, value, to_validate): assert key == to_validate['key'] assert op == to_validate['operation'] - assert value == to_validate['value'] + assert value == to_validate.get('value', None) def validate_single_op(key, op, value, to_validate): @@ -71,10 +71,21 @@ def validate_default_else_test(file_name): def parse_file(file): p = QmakeParser(debug=True) - result = p.parseFile(file).asDict() - assert len(result) == 1 + result = p.parseFile(file) + + print('\n\n#### Parser result:') + print(result) + print('\n#### End of parser result.\n') + + print('\n\n####Parser result dictionary:') + print(result.asDict()) + print('\n#### End of parser result dictionary.\n') + + result_dictionary = result.asDict() + + assert len(result_dictionary) == 1 - return result['statements'] + return result_dictionary['statements'] def test_else(): @@ -129,6 +140,13 @@ def test_else8(): validate_default_else_test(_tests_path + '/data/else8.pro') +def test_multiline_assign(): + result = parse_file(_tests_path + '/data/multiline_assign.pro') + assert len(result) == 2 + validate_op('A', '=', ['42', '43', '44'], result[0]) + validate_op('B', '=', ['23'], result[1]) + + def test_include(): result = parse_file(_tests_path + '/data/include.pro') assert len(result) == 3 @@ -174,3 +192,65 @@ def test_complex_values(): def test_function_if(): result = parse_file(_tests_path + '/data/function_if.pro') assert len(result) == 1 + + +def test_realworld_standardpaths(): + result = parse_file(_tests_path + '/data/standardpaths.pro') + + (cond, if_branch, else_branch) = evaluate_condition(result[0]) + assert cond == 'win32' + assert len(if_branch) == 1 + assert len(else_branch) == 1 + + # win32: + (cond1, if_branch1, else_branch1) = evaluate_condition(if_branch[0]) + assert cond1 == '!winrt' + assert len(if_branch1) == 1 + validate_op('SOURCES', '+=', ['io/qstandardpaths_win.cpp'], if_branch1[0]) + assert len(else_branch1) == 1 + validate_op('SOURCES', '+=', ['io/qstandardpaths_winrt.cpp'], else_branch1[0]) + + # unix: + (cond2, if_branch2, else_branch2) = evaluate_condition(else_branch[0]) + assert cond2 == 'unix' + assert len(if_branch2) == 1 + assert len(else_branch2) == 0 + + # mac / else: + (cond3, if_branch3, else_branch3) = evaluate_condition(if_branch2[0]) + assert cond3 == 'mac' + assert len(if_branch3) == 1 + validate_op('OBJECTIVE_SOURCES', '+=', ['io/qstandardpaths_mac.mm'], if_branch3[0]) + assert len(else_branch3) == 1 + + # android / else: + (cond4, if_branch4, else_branch4) = evaluate_condition(else_branch3[0]) + assert cond4 == 'android && !android-embedded' + assert len(if_branch4) == 1 + validate_op('SOURCES', '+=', ['io/qstandardpaths_android.cpp'], if_branch4[0]) + assert len(else_branch4) == 1 + + # haiku / else: + (cond5, if_branch5, else_branch5) = evaluate_condition(else_branch4[0]) + assert cond5 == 'haiku' + assert len(if_branch5) == 1 + validate_op('SOURCES', '+=', ['io/qstandardpaths_haiku.cpp'], if_branch5[0]) + assert len(else_branch5) == 1 + validate_op('SOURCES', '+=', ['io/qstandardpaths_unix.cpp'], else_branch5[0]) + + +def test_realworld_comment_scope(): + result = parse_file(_tests_path + '/data/comment_scope.pro') + assert len(result) == 2 + (cond, if_branch, else_branch) = evaluate_condition(result[0]) + assert cond == 'freebsd|openbsd' + assert len(if_branch) == 1 + validate_op('QMAKE_LFLAGS_NOUNDEF', '=', None, if_branch[0]) + + assert result[1].get('included', '') == 'animation/animation.pri' + + +def test_realworld_contains_scope(): + result = parse_file(_tests_path + '/data/contains_scope.pro') + assert len(result) == 2 + diff --git a/util/cmake/tests/test_scope_handling.py b/util/cmake/tests/test_scope_handling.py index 2d4bc183d7..1c3406bac8 100755 --- a/util/cmake/tests/test_scope_handling.py +++ b/util/cmake/tests/test_scope_handling.py @@ -280,3 +280,59 @@ def test_merge_parent_child_scopes_with_on_child_condition(): assert r0.getString('test1') == 'parent' assert r0.getString('test2') == 'child' + +# Real world examples: + +# qstandardpaths selection: + +def test_qstandardpaths_scopes(): + # top level: + scope1 = _new_scope(condition='ON', scope_id=1) + + # win32 { + scope2 = _new_scope(parent_scope=scope1, condition='WIN32') + # !winrt { + # SOURCES += io/qstandardpaths_win.cpp + scope3 = _new_scope(parent_scope=scope2, condition='NOT WINRT', + SOURCES='qsp_win.cpp') + # } else { + # SOURCES += io/qstandardpaths_winrt.cpp + scope4 = _new_scope(parent_scope=scope2, condition='else', + SOURCES='qsp_winrt.cpp') + # } + # else: unix { + scope5 = _new_scope(parent_scope=scope1, condition='else') + scope6 = _new_scope(parent_scope=scope5, condition='UNIX') + # mac { + # OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm + scope7 = _new_scope(parent_scope=scope6, condition='APPLE_OSX', SOURCES='qsp_mac.mm') + # } else:android:!android-embedded { + # SOURCES += io/qstandardpaths_android.cpp + scope8 = _new_scope(parent_scope=scope6, condition='else') + scope9 = _new_scope(parent_scope=scope8, + condition='ANDROID AND NOT ANDROID_EMBEDDED', + SOURCES='qsp_android.cpp') + # } else:haiku { + # SOURCES += io/qstandardpaths_haiku.cpp + scope10 = _new_scope(parent_scope=scope8, condition='else') + scope11 = _new_scope(parent_scope=scope10, condition='HAIKU', SOURCES='qsp_haiku.cpp') + # } else { + # SOURCES +=io/qstandardpaths_unix.cpp + scope12 = _new_scope(parent_scope=scope10, condition='else', SOURCES='qsp_unix.cpp') + # } + # } + + recursive_evaluate_scope(scope1) + + assert scope1.total_condition == 'ON' + assert scope2.total_condition == 'WIN32' + assert scope3.total_condition == 'WIN32 AND NOT WINRT' + assert scope4.total_condition == 'WINRT' + assert scope5.total_condition == 'UNIX' + assert scope6.total_condition == 'UNIX' + assert scope7.total_condition == 'APPLE_OSX' + assert scope8.total_condition == 'UNIX AND NOT APPLE_OSX' + assert scope9.total_condition == 'ANDROID AND NOT ANDROID_EMBEDDED AND NOT APPLE_OSX' + assert scope10.total_condition == 'UNIX AND NOT APPLE_OSX AND (ANDROID_EMBEDDED OR NOT ANDROID)' + assert scope11.total_condition == 'HAIKU AND UNIX AND NOT APPLE_OSX AND (ANDROID_EMBEDDED OR NOT ANDROID)' + assert scope12.total_condition == 'UNIX AND NOT APPLE_OSX AND NOT HAIKU AND (ANDROID_EMBEDDED OR NOT ANDROID)' -- cgit v1.2.3 From 8c04d6c967b33a311fe17e69e9c5e2289a2fcd14 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 13 Feb 2019 00:15:15 +0100 Subject: CMake: pro2cmake.py: Improve condition simplification code Improve the code that simplifies conditions to take "OS families" into account. E.g. if a system must be ANDROID, then it is redundant to express that it is NOT APPLE_OSX. Change-Id: Ib7e62726c309bf84b9e5e0d6a6e3465511db0ead Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 37 +++++++++++++++++++++++++-------- util/cmake/tests/test_logic_mapping.py | 21 +++++++++++++++++++ util/cmake/tests/test_scope_handling.py | 4 ++-- 3 files changed, 51 insertions(+), 11 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 63938d75c8..60e7f433bf 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -875,12 +875,11 @@ def _iterate_expr_tree(expr, op, matches): keepers = (*keepers, *extra_keepers) else: keepers = (*keepers, arg) - return (matches, keepers) + return matches, keepers def _simplify_expressions(expr, op, matches, replacement): - args = expr.args - for arg in args: + for arg in expr.args: expr = expr.subs(arg, _simplify_expressions(arg, op, matches, replacement)) @@ -920,17 +919,34 @@ def _simplify_flavors_in_condition(base: str, flavors, expr): return expr +def _simplify_os_families(expr, family_members, other_family_members): + for family in family_members: + for other in other_family_members: + if other in family_members: + continue # skip those in the sub-family + + f_expr = simplify_logic(family) + o_expr = simplify_logic(other) + + 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')) + return expr + + def _recursive_simplify(expr): ''' 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 = ('APPLE', 'FREEBSD', 'OPENBSD', 'NETBSD',) + androids = ('ANDROID', 'ANDROID_EMBEDDED') unixes = ('APPLE', *apples, 'BSD', *bsds, 'LINUX', - 'ANDROID', 'ANDROID_EMBEDDED', + *androids, 'HAIKU', 'INTEGRITY', 'VXWORKS', 'QNX', 'WASM') unix_expr = simplify_logic('UNIX') @@ -945,11 +961,6 @@ def _recursive_simplify(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) - for unix_flavor in unixes: - # unix_flavor [AND foo ] AND WIN32 -> FALSE [AND foo] - flavor_expr = simplify_logic(unix_flavor) - expr = _simplify_expressions(expr, And, (win_expr, flavor_expr,), - false_expr) expr = _simplify_flavors_in_condition('WIN32', ('WINRT',), expr) expr = _simplify_flavors_in_condition('APPLE', apples, expr) @@ -957,6 +968,14 @@ def _recursive_simplify(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, androids, unixes) + expr = _simplify_os_families(expr, ('BSD', *bsds), unixes) + + for family in ('HAIKU', 'QNX', 'INTEGRITY', 'LINUX', 'VXWORKS'): + expr = _simplify_os_families(expr, (family,), unixes) + # Now simplify further: expr = simplify_logic(expr) diff --git a/util/cmake/tests/test_logic_mapping.py b/util/cmake/tests/test_logic_mapping.py index cf7913a6e4..c477aa8351 100755 --- a/util/cmake/tests/test_logic_mapping.py +++ b/util/cmake/tests/test_logic_mapping.py @@ -47,6 +47,14 @@ def test_simplify_off(): validate_simplify_unchanged('OFF') +def test_simplify_not_on(): + validate_simplify('NOT ON', 'OFF') + + +def test_simplify_not_off(): + validate_simplify('NOT OFF', 'ON') + + def test_simplify_isEmpty(): validate_simplify_unchanged('isEmpty(foo)') @@ -99,11 +107,19 @@ def test_simplify_unix_and_win32(): validate_simplify('WIN32 AND UNIX', 'OFF') +def test_simplify_unix_or_win32(): + validate_simplify('WIN32 OR UNIX', 'ON') + + def test_simplify_unix_and_win32_or_foobar_or_barfoo(): validate_simplify('WIN32 AND foobar AND UNIX AND barfoo', 'OFF') def test_simplify_watchos_and_win32(): + validate_simplify('APPLE_WATCHOS AND WIN32', 'OFF') + + +def test_simplify_win32_and_watchos(): validate_simplify('WIN32 AND APPLE_WATCHOS', 'OFF') @@ -163,3 +179,8 @@ def test_simplify_complex_false(): validate_simplify('WIN32 AND foobar AND ( ' 'APPLE OR ( UNIX OR FREEBSD ))', 'OFF') + + +def test_simplify_android_not_apple(): + validate_simplify('ANDROID AND NOT ANDROID_EMBEDDED AND NOT APPLE_OSX', + 'ANDROID AND NOT ANDROID_EMBEDDED') diff --git a/util/cmake/tests/test_scope_handling.py b/util/cmake/tests/test_scope_handling.py index 1c3406bac8..8bca8c8ec5 100755 --- a/util/cmake/tests/test_scope_handling.py +++ b/util/cmake/tests/test_scope_handling.py @@ -332,7 +332,7 @@ def test_qstandardpaths_scopes(): assert scope6.total_condition == 'UNIX' assert scope7.total_condition == 'APPLE_OSX' assert scope8.total_condition == 'UNIX AND NOT APPLE_OSX' - assert scope9.total_condition == 'ANDROID AND NOT ANDROID_EMBEDDED AND NOT APPLE_OSX' + assert scope9.total_condition == 'ANDROID AND NOT ANDROID_EMBEDDED' assert scope10.total_condition == 'UNIX AND NOT APPLE_OSX AND (ANDROID_EMBEDDED OR NOT ANDROID)' - assert scope11.total_condition == 'HAIKU AND UNIX AND NOT APPLE_OSX AND (ANDROID_EMBEDDED OR NOT ANDROID)' + assert scope11.total_condition == 'HAIKU AND (ANDROID_EMBEDDED OR NOT ANDROID)' assert scope12.total_condition == 'UNIX AND NOT APPLE_OSX AND NOT HAIKU AND (ANDROID_EMBEDDED OR NOT ANDROID)' -- cgit v1.2.3 From 22dc78f41749d76df38ef1e17d6dc8c272d7cab0 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 13 Feb 2019 09:13:37 +0100 Subject: CMake: pro2cmake.py: Fix handling of TEMPLATE=subdir .pro-files Change-Id: I52f575df199c4d9b38123ab5d838f2e85344835d Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 60e7f433bf..1d48d2ba58 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -676,7 +676,6 @@ def map_condition(condition: str) -> str: def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, indent: int = 0) -> None: - assert scope.TEMPLATE == 'subdirs' ind = ' ' * indent for sd in scope.get('SUBDIRS', []): full_sd = os.path.join(scope.basedir, sd) @@ -697,7 +696,7 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, else: print(' XXXX: SUBDIR {} in {}: Not found.'.format(sd, scope)) - for c in scope.children(): + for c in scope.children: cond = c.condition if cond == 'else': cm_fh.write('\n{}else()\n'.format(ind)) -- cgit v1.2.3 From c971d2d3593def66c5627f47c575c2ed7ce04fec Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 13 Feb 2019 10:42:56 +0100 Subject: CMake: pro2cmake.py: Handle values with () in assignments Change-Id: I0f59c7fa57cd6c64b151f439d4eea4ae56dca288 Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 11 +++++++++-- util/cmake/tests/data/complex_assign.pro | 2 ++ util/cmake/tests/test_parsing.py | 6 ++++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 util/cmake/tests/data/complex_assign.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1d48d2ba58..1f85e977aa 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -515,6 +515,12 @@ class QmakeParser: Else = pp.Keyword('else') DefineTest = pp.Keyword('defineTest') Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_-./') + BracedValue = pp.nestedExpr(ignoreExpr=pp.quotedString \ + | pp.QuotedString(quoteChar='$(', + endQuoteChar=')', + escQuote='\\', + unquoteResults=False) + ).setParseAction(lambda s, l, t: ['(', *t[0], ')']) Substitution \ = pp.Combine(pp.Literal('$') @@ -534,7 +540,8 @@ class QmakeParser: | pp.Literal('$'))) Value = pp.NotAny(Else | pp.Literal('}') | EOL | pp.Literal('\\')) \ + (pp.QuotedString(quoteChar='"', escChar='\\') - | SubstitutionValue) + | SubstitutionValue + | BracedValue) Values = pp.ZeroOrMore(Value + pp.Optional(LC))('value') @@ -598,7 +605,7 @@ class QmakeParser: 'Else ElseBranch SingleLineElse MultiLineElse ' \ 'SingleLineScope MultiLineScope ' \ 'Identifier ' \ - 'Key Op Values Value ' \ + 'Key Op Values Value BracedValue ' \ 'Scope Block ' \ 'StatementGroup StatementLine Statement '\ 'Load Include Option DefineTest ForLoop ' \ diff --git a/util/cmake/tests/data/complex_assign.pro b/util/cmake/tests/data/complex_assign.pro new file mode 100644 index 0000000000..d251afcdd5 --- /dev/null +++ b/util/cmake/tests/data/complex_assign.pro @@ -0,0 +1,2 @@ +qmake-clean.commands += (cd qmake && $(MAKE) clean ":-(==)-:" '(Foo)' ) + diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index e4f9680f60..c238c80c08 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -254,3 +254,9 @@ def test_realworld_contains_scope(): result = parse_file(_tests_path + '/data/contains_scope.pro') assert len(result) == 2 + +def test_realworld_complex_assign(): + result = parse_file(_tests_path + '/data/complex_assign.pro') + assert len(result) == 1 + validate_op('qmake-clean.commands', '+=', '( cd qmake && $(MAKE) clean ":-(==)-:" \'(Foo)\' )'.split(), + result[0]) -- cgit v1.2.3 From 951e297362917675f605154ff6da89fb1acb3d40 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 13 Feb 2019 12:24:14 +0100 Subject: CMake: pro2cmake.py: Warn and fix broken line continuation Warn on broken line continuation in .pro-files, but fix up the issue and proceed. Change-Id: Ibe68011b312bcea25620ce790a0b44b2983fbd88 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 18 ++++++++++++++- util/cmake/tests/test_lc_fixup.py | 46 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100755 util/cmake/tests/test_lc_fixup.py (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1f85e977aa..eccf045559 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -72,6 +72,13 @@ def _parse_commandline(): return parser.parse_args() +def fixup_linecontinuation(contents: str) -> str: + contents = re.sub(r'([^\t ])\\[ \t]*\n', '\\1 \\\n', contents) + contents = re.sub(r'\\[ \t]*\n', '\\\n', contents) + + return contents + + def spaces(indent: int) -> str: return ' ' * indent @@ -622,7 +629,16 @@ class QmakeParser: def parseFile(self, file: str): print('Parsing \"{}\"...'.format(file)) try: - result = self._Grammar.parseFile(file, parseAll=True) + with open(file, 'r') as file_fd: + contents = file_fd.read() + + old_contents = contents + contents = fixup_linecontinuation(contents) + + if old_contents != contents: + print('Warning: Fixed line continuation in .pro-file!\n' + ' Position information in Parsing output might be wrong!') + result = self._Grammar.parseString(contents, parseAll=True) except pp.ParseException as pe: print(pe.line) print(' '*(pe.col-1) + '^') diff --git a/util/cmake/tests/test_lc_fixup.py b/util/cmake/tests/test_lc_fixup.py new file mode 100755 index 0000000000..d3680e895d --- /dev/null +++ b/util/cmake/tests/test_lc_fixup.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from pro2cmake import fixup_linecontinuation + +from textwrap import dedent + + +def test_no_change(): + input = "test \\\nline2\n line3" + result = fixup_linecontinuation(input) + assert input == result + + +def test_fix(): + input = "test \\\t\nline2\\\n line3\\ \nline4 \\ \t\nline5\\\n\n\n" + result = fixup_linecontinuation(input) + assert 'test \\\nline2 \\\n line3 \\\nline4 \\\nline5 \\\n\n\n' == result + + -- cgit v1.2.3 From eb832cb00ae231d31052e885d4482e34aeb2640d Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 13 Feb 2019 13:04:45 +0100 Subject: CMake: pro2cmake.py: Handle complex conditions Change-Id: Ifb047e5736f1831ddbd65b210e760c2729378334 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 19 +++++++++++-------- util/cmake/tests/data/complex_condition.pro | 4 ++++ util/cmake/tests/test_parsing.py | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 util/cmake/tests/data/complex_condition.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index eccf045559..9dbcac9fc4 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -546,9 +546,9 @@ class QmakeParser: = pp.Combine(pp.OneOrMore(Substitution | LiteralValuePart | pp.Literal('$'))) Value = pp.NotAny(Else | pp.Literal('}') | EOL | pp.Literal('\\')) \ - + (pp.QuotedString(quoteChar='"', escChar='\\') - | SubstitutionValue - | BracedValue) + + (pp.QuotedString(quoteChar='"', escChar='\\') + | SubstitutionValue + | BracedValue) Values = pp.ZeroOrMore(Value + pp.Optional(LC))('value') @@ -584,10 +584,12 @@ class QmakeParser: + StatementGroup + pp.Optional(LC | EOL) \ + pp.Suppress('}') + pp.Optional(LC | EOL) - ConditionEnd = pp.FollowedBy((pp.Optional(LC) + (pp.Literal(':') \ - | pp.Literal('{') \ - | pp.Literal('|')))) - ConditionPart = pp.CharsNotIn('#{}|:=\\\n') + pp.Optional(LC) + ConditionEnd + ConditionEnd = pp.FollowedBy((pp.Optional(pp.White()) + + pp.Optional(LC) + (pp.Literal(':') \ + | pp.Literal('{') \ + | pp.Literal('|')))) + ConditionPart = ((pp.Optional('!') + Identifier + pp.Optional(BracedValue)) \ + ^ pp.CharsNotIn('#{}|:=\\\n')) + pp.Optional(LC) + ConditionEnd Condition = pp.Combine(ConditionPart \ + pp.ZeroOrMore((pp.Literal('|') ^ pp.Literal(':')) \ + ConditionPart)) @@ -1172,7 +1174,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, def write_module(cm_fh: typing.IO[str], scope: Scope, *, indent: int = 0) -> None: module_name = scope.TARGET - assert module_name.startswith('Qt') + if not module_name.startswith('Qt'): + print('XXXXXX Module name {} does not start with Qt!'.format(module_name)) extra = [] if 'static' in scope.get('CONFIG'): diff --git a/util/cmake/tests/data/complex_condition.pro b/util/cmake/tests/data/complex_condition.pro new file mode 100644 index 0000000000..bc3369bd63 --- /dev/null +++ b/util/cmake/tests/data/complex_condition.pro @@ -0,0 +1,4 @@ +!system("dbus-send --session --type=signal / local.AutotestCheck.Hello >$$QMAKE_SYSTEM_NULL_DEVICE 2>&1") { + SOURCES = dbus.cpp +} + diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index c238c80c08..f0c80f560e 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -260,3 +260,17 @@ def test_realworld_complex_assign(): assert len(result) == 1 validate_op('qmake-clean.commands', '+=', '( cd qmake && $(MAKE) clean ":-(==)-:" \'(Foo)\' )'.split(), result[0]) + + +def test_realworld_complex_condition(): + result = parse_file(_tests_path + '/data/complex_condition.pro') + assert len(result) == 1 + (cond, if_branch, else_branch) = evaluate_condition(result[0]) + assert cond == '!system("dbus-send --session --type=signal / ' \ + 'local.AutotestCheck.Hello >$$QMAKE_SYSTEM_NULL_DEVICE ' \ + '2>&1")' + assert len(if_branch) == 1 + validate_op('SOURCES', '=', ['dbus.cpp'], if_branch[0]) + + assert len(else_branch) == 0 + -- cgit v1.2.3 From 04d69817027c37440a85b0f0ec8b9b0e8c037b56 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 26 Feb 2019 13:53:04 +0100 Subject: CMake: run_pro2cmake.py: Add statistics Change-Id: I78e65970694d0c37c4b3c3ba6e6a83155579ea0f Reviewed-by: Alexandru Croitor --- util/cmake/run_pro2cmake.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index 4340eab094..1a3c1581e3 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -34,11 +34,27 @@ import sys script_path = os.path.dirname(os.path.abspath(__file__)) base_path = os.path.dirname(script_path) -pro2cmake = script_path + '/pro2cmake.py' +pro2cmake = os.path.join(script_path, 'pro2cmake.py') if len(sys.argv) > 1: base_path = os.path.abspath(sys.argv[1]) -for filename in glob.iglob(base_path + '/**/*.pro', recursive=True): - print('Converting:', filename) - subprocess.run([pro2cmake, filename]) +failed_files = [] + +pro_file_count = 0 +for filename in glob.iglob(os.path.join(base_path, '**/*.pro'), + recursive=True): + pro_file_count += 1 + print('{} ({}): Converting: {} ...' + .format(pro_file_count, len(failed_files), filename)) + result = subprocess.run([pro2cmake, os.path.basename(filename)], + cwd=os.path.dirname(filename)) + if result.returncode != 0: + failed_files.append(filename) + +if failed_files: + print('The following files were not successfully ' + 'converted ({} of {}):'.format(len(failed_files), pro_file_count)) + for f in failed_files: + print(' "{}"'.format(f)) + -- cgit v1.2.3 From 8512f5179d2674dd9c0b89eeebbf2c6d32e3e4b4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 27 Feb 2019 13:37:01 +0100 Subject: CMake: pro2cmake.py: Fix parsing of for loops Ignore for loops in the pro2cmake.py parser and add a unit test for that. Change-Id: I2a0c075c45cf56f4f24ada2d53e8e8e94ce19f26 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 15 ++++++--------- util/cmake/tests/data/for.pro | 11 +++++++++++ util/cmake/tests/test_parsing.py | 7 +++++++ 3 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 util/cmake/tests/data/for.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9dbcac9fc4..6002f78037 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -520,7 +520,6 @@ class QmakeParser: LC = pp.Suppress(pp.Literal('\\\n')) EOL = pp.Suppress(pp.Literal('\n')) Else = pp.Keyword('else') - DefineTest = pp.Keyword('defineTest') Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_-./') BracedValue = pp.nestedExpr(ignoreExpr=pp.quotedString \ | pp.QuotedString(quoteChar='$(', @@ -560,17 +559,15 @@ class QmakeParser: Operation = Key('key') + pp.Optional(LC) \ + Op('operation') + pp.Optional(LC) \ + Values('value') - CallArgs = pp.nestedExpr() + CallArgs = pp.Optional(LC) + pp.nestedExpr() CallArgs.setParseAction(lambda x: ' '.join(chain(*x))) Load = pp.Keyword('load') + CallArgs('loaded') Include = pp.Keyword('include') + CallArgs('included') Option = pp.Keyword('option') + CallArgs('option') - DefineTestDefinition = pp.Suppress(DefineTest + CallArgs \ - + pp.nestedExpr(opener='{', closer='}')) # ignore the whole thing... - ForLoop = pp.Suppress(pp.Keyword('for') + pp.nestedExpr() - + pp.nestedExpr(opener='{', closer='}', - ignoreExpr=None) - + pp.LineEnd()) # ignore the whole thing... + DefineTestDefinition = pp.Suppress(pp.Keyword('defineTest') + CallArgs + + pp.nestedExpr(opener='{', closer='}', ignoreExpr=pp.LineEnd())) # ignore the whole thing... + ForLoop = pp.Suppress(pp.Keyword('for') + CallArgs + + pp.nestedExpr(opener='{', closer='}', ignoreExpr=pp.LineEnd())) # ignore the whole thing... FunctionCall = pp.Suppress(Identifier + pp.nestedExpr()) Scope = pp.Forward() @@ -617,7 +614,7 @@ class QmakeParser: 'Key Op Values Value BracedValue ' \ 'Scope Block ' \ 'StatementGroup StatementLine Statement '\ - 'Load Include Option DefineTest ForLoop ' \ + 'Load Include Option DefineTestDefinition ForLoop ' \ 'FunctionCall CallArgs Operation'.split(): expr = locals()[ename] expr.setName(ename) diff --git a/util/cmake/tests/data/for.pro b/util/cmake/tests/data/for.pro new file mode 100644 index 0000000000..5751432980 --- /dev/null +++ b/util/cmake/tests/data/for.pro @@ -0,0 +1,11 @@ +SOURCES = main.cpp +for (config, SIMD) { + uc = $$upper($$config) + DEFINES += QT_COMPILER_SUPPORTS_$${uc} + + add_cflags { + cflags = QMAKE_CFLAGS_$${uc} + !defined($$cflags, var): error("This compiler does not support $${uc}") + QMAKE_CXXFLAGS += $$eval($$cflags) + } +} diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index f0c80f560e..d0a9960dc7 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -173,6 +173,13 @@ def test_definetest(): assert result[0] == [] +def test_for(): + result = parse_file(_tests_path + '/data/for.pro') + assert len(result) == 2 + validate_op('SOURCES', '=', ['main.cpp'], result[0]) + assert result[1] == [] + + def test_unset(): result = parse_file(_tests_path + '/data/unset.pro') assert len(result) == 1 -- cgit v1.2.3 From 754ba287999e0d1681f77d12f6d7c3ae0362745a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 27 Feb 2019 13:58:00 +0100 Subject: CMake: pro2cmake.py: Fix parsing of Line continuation before end of file ... and add a test case for this. Change-Id: If20d737b54ecb3f9e128e59070b238c840acad6c Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- util/cmake/tests/data/sql.pro | 3 +++ util/cmake/tests/test_parsing.py | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 util/cmake/tests/data/sql.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6002f78037..5d161a139a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -518,7 +518,7 @@ class QmakeParser: pp.ParserElement.setDefaultWhitespaceChars(' \t') LC = pp.Suppress(pp.Literal('\\\n')) - EOL = pp.Suppress(pp.Literal('\n')) + EOL = pp.Suppress(pp.Literal('\n') ^ pp.LineEnd()) Else = pp.Keyword('else') Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_-./') BracedValue = pp.nestedExpr(ignoreExpr=pp.quotedString \ diff --git a/util/cmake/tests/data/sql.pro b/util/cmake/tests/data/sql.pro new file mode 100644 index 0000000000..a9d7fc7c5a --- /dev/null +++ b/util/cmake/tests/data/sql.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + kernel \ diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index d0a9960dc7..1f50fc87ab 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -281,3 +281,9 @@ def test_realworld_complex_condition(): assert len(else_branch) == 0 + +def test_realworld_sql(): + result = parse_file(_tests_path + '/data/sql.pro') + assert len(result) == 2 + validate_op('TEMPLATE', '=', ['subdirs'], result[0]) + validate_op('SUBDIRS', '=', ['kernel'], result[1]) -- cgit v1.2.3 From 33fe56c630d9e59b2a33e28db5e062323d577d34 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 27 Feb 2019 15:17:36 +0100 Subject: CMake: pro2cmake.py: Make \$\$QT_FOO work in assignments This broke somewhere along the way. Add a test for this. Change-Id: I106ddff6eb86a51ef132285d1bc623f3b5cf71fb Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- util/cmake/tests/data/escaped_value.pro | 2 ++ util/cmake/tests/test_parsing.py | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 util/cmake/tests/data/escaped_value.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5d161a139a..1f76d9dedd 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -544,7 +544,7 @@ class QmakeParser: SubstitutionValue \ = pp.Combine(pp.OneOrMore(Substitution | LiteralValuePart | pp.Literal('$'))) - Value = pp.NotAny(Else | pp.Literal('}') | EOL | pp.Literal('\\')) \ + Value = pp.NotAny(Else | pp.Literal('}') | EOL) \ + (pp.QuotedString(quoteChar='"', escChar='\\') | SubstitutionValue | BracedValue) diff --git a/util/cmake/tests/data/escaped_value.pro b/util/cmake/tests/data/escaped_value.pro new file mode 100644 index 0000000000..7c95b1fc30 --- /dev/null +++ b/util/cmake/tests/data/escaped_value.pro @@ -0,0 +1,2 @@ +MODULE_AUX_INCLUDES = \ + \$\$QT_MODULE_INCLUDE_BASE/QtANGLE diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index 1f50fc87ab..dd4e5508f6 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -287,3 +287,10 @@ def test_realworld_sql(): assert len(result) == 2 validate_op('TEMPLATE', '=', ['subdirs'], result[0]) validate_op('SUBDIRS', '=', ['kernel'], result[1]) + + +def test_realworld_qtconfig(): + result = parse_file(_tests_path + '/data/escaped_value.pro') + assert len(result) == 1 + validate_op('MODULE_AUX_INCLUDES', '=', ['\\$\\$QT_MODULE_INCLUDE_BASE/QtANGLE'], result[0]) + -- cgit v1.2.3 From f2e968b245e0428b75eecb0bb7244a0391e3b355 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 27 Feb 2019 15:31:29 +0100 Subject: CMake: pro2cmake.py: Handle for loops without block Handle for loops with a single line of instructions and add a test for that. Change-Id: I041ae30f64abcbd3db7df29933647f047b92ede3 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 4 +++- util/cmake/tests/data/single_line_for.pro | 4 ++++ util/cmake/tests/test_parsing.py | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 util/cmake/tests/data/single_line_for.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1f76d9dedd..06d0c925d9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -568,11 +568,13 @@ class QmakeParser: + pp.nestedExpr(opener='{', closer='}', ignoreExpr=pp.LineEnd())) # ignore the whole thing... ForLoop = pp.Suppress(pp.Keyword('for') + CallArgs + pp.nestedExpr(opener='{', closer='}', ignoreExpr=pp.LineEnd())) # ignore the whole thing... + ForLoopSingleLine = pp.Suppress(pp.Keyword('for') + CallArgs + + pp.Literal(':') + pp.SkipTo(EOL, ignore=LC)) # ignore the whole thing... FunctionCall = pp.Suppress(Identifier + pp.nestedExpr()) Scope = pp.Forward() - Statement = pp.Group(Load | Include | Option | ForLoop \ + Statement = pp.Group(Load | Include | Option | ForLoop | ForLoopSingleLine \ | DefineTestDefinition | FunctionCall | Operation) StatementLine = Statement + (EOL | pp.FollowedBy('}')) StatementGroup = pp.ZeroOrMore(StatementLine | Scope | pp.Suppress(EOL)) diff --git a/util/cmake/tests/data/single_line_for.pro b/util/cmake/tests/data/single_line_for.pro new file mode 100644 index 0000000000..806d08a49c --- /dev/null +++ b/util/cmake/tests/data/single_line_for.pro @@ -0,0 +1,4 @@ +for(d, sd): \ + exists($$d/$${d}.pro): \ + SUBDIRS += $$d + diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index dd4e5508f6..2f227e0ba2 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -180,6 +180,12 @@ def test_for(): assert result[1] == [] +def test_single_line_for(): + result = parse_file(_tests_path + '/data/single_line_for.pro') + assert len(result) == 1 + assert result[0] == [] + + def test_unset(): result = parse_file(_tests_path + '/data/unset.pro') assert len(result) == 1 -- cgit v1.2.3 From 83354655b2f96f0fc5fa8f39d10b58c36a9e8bf5 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 27 Feb 2019 16:12:13 +0100 Subject: CMake: pro2cmake.py: Simplify code and add test for line continuation Simplify code a bit and add a test for line continuation fixup. Change-Id: If865bc94d7d419c65d3280b5f9613ebc0d3db74a Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- util/cmake/tests/data/lc.pro | 10 ++++++++++ util/cmake/tests/test_parsing.py | 5 +++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 util/cmake/tests/data/lc.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 06d0c925d9..ef81d4cfef 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -518,7 +518,7 @@ class QmakeParser: pp.ParserElement.setDefaultWhitespaceChars(' \t') LC = pp.Suppress(pp.Literal('\\\n')) - EOL = pp.Suppress(pp.Literal('\n') ^ pp.LineEnd()) + EOL = pp.Suppress(pp.LineEnd()) Else = pp.Keyword('else') Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_-./') BracedValue = pp.nestedExpr(ignoreExpr=pp.quotedString \ diff --git a/util/cmake/tests/data/lc.pro b/util/cmake/tests/data/lc.pro new file mode 100644 index 0000000000..def80e7c95 --- /dev/null +++ b/util/cmake/tests/data/lc.pro @@ -0,0 +1,10 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qmacstyle \ + qstyle \ + qstyleoption \ + qstylesheetstyle \ + +!qtConfig(private_tests): SUBDIRS -= \ + qstylesheetstyle \ + diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index 2f227e0ba2..79ad0a4945 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -300,3 +300,8 @@ def test_realworld_qtconfig(): assert len(result) == 1 validate_op('MODULE_AUX_INCLUDES', '=', ['\\$\\$QT_MODULE_INCLUDE_BASE/QtANGLE'], result[0]) + +def test_realworld_lc(): + result = parse_file(_tests_path + '/data/lc.pro') + assert len(result) == 3 + -- cgit v1.2.3 From 3c9d7dfb304dcad8ceea29ac729e76d8eca913f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=A1=D0=B2=D0=B5?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=D0=BD?= Date: Mon, 11 Feb 2019 13:27:00 +0100 Subject: cmake: Cleanup harfbuzz features Remove system-harfbuzz feature and use harfbuzz feature as system Change-Id: I441345a667450f1c2d19380b0709911011c7ceb7 Reviewed-by: Tobias Hunger --- util/cmake/configurejson2cmake.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index a684130c68..efce7fee4a 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -432,6 +432,8 @@ def parseInput(ctx, input, data, cm_fh): "gui", + "harfbuzz", + "headersclean", "incredibuild-xge", @@ -708,6 +710,9 @@ def parseFeature(ctx, feature, data, cm_fh): '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 )' @@ -759,6 +764,7 @@ def parseFeature(ctx, feature, data, cm_fh): }, 'system-doubleconversion': None, # No system libraries anymore! 'system-freetype': None, + 'system-harfbuzz': None, 'system-jpeg': None, 'system-pcre2': None, 'system-png': None, -- cgit v1.2.3 From 363eccf0108baccbabab9747a2eb9d7aa0373045 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 1 Mar 2019 13:32:44 +0100 Subject: CMake: pro2cmake.py: Handle BOOTSTRAP for add_qt_tool Add BOOTSTRAP for tools that need it automatically. Change-Id: I33b2ec16dfcb09709f844ed232ce9974a9d7c7ed Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ef81d4cfef..d21ae36a82 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1197,8 +1197,11 @@ def write_tool(cm_fh: typing.IO[str], scope: Scope, *, indent: int = 0) -> None: tool_name = scope.TARGET + 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', }) + indent=indent, known_libraries={'Qt::Core', }, + extra_lines=extra) def write_test(cm_fh: typing.IO[str], scope: Scope, *, -- cgit v1.2.3 From a5f51e489885babf48cbc3fd224e1492a02bd60f Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 1 Mar 2019 15:00:19 +0100 Subject: CMake: pro2cmake.py: Automatically map equals(QT_ARCH, "foo") in conditions Change-Id: I10d8001ba8330deaa622ef8c499b2b6fe438e28a Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index d21ae36a82..7c00afad1c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -656,9 +656,9 @@ def parseProFile(file: str, *, debug=False): def map_condition(condition: str) -> str: 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*\((.*?), (.*)?\)', + condition = re.sub(r'\bcontains\s*\((.*?),\s*"?(.*?)"?\)', r'\1___contains___\2', condition) - condition = re.sub(r'\bequals\s*\((.*?), (.*)?\)', + condition = re.sub(r'\bequals\s*\((.*?),\s*"?(.*?)"?\)', r'\1___equals___\2', condition) condition = re.sub(r'\s*==\s*', '___STREQUAL___', condition) @@ -1070,6 +1070,14 @@ def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', return current_condition +def map_to_cmake_condition(condition: str) -> str: + condition = re.sub(r'\bQT_ARCH___equals___([a-zA-Z_0-9]*)', + r'(TEST_architecture STREQUAL "\1")', condition) + condition = re.sub(r'\bQT_ARCH___contains___([a-zA-Z_0-9]*)', + r'(TEST_architecture STREQUAL "\1")', condition) + return condition + + def write_extend_target(cm_fh: typing.IO[str], target: str, scope: Scope, indent: int = 0): extend_qt_io_string = io.StringIO() @@ -1083,7 +1091,7 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, extend_scope = '\n{}extend_target({} CONDITION {}\n' \ '{}{})\n'.format(spaces(indent), target, - scope.total_condition, + map_to_cmake_condition(scope.total_condition), extend_qt_string, ignored_keys_report) if not extend_qt_string: -- cgit v1.2.3 From 66a4267f93b24b5cc6aac9aea1133f79675c1756 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 1 Mar 2019 15:54:40 +0100 Subject: CMake: pro2cmake.py: Map libpng to PNG::PNG Change-Id: Ia8dd20b777e24e2783bedb0f063db2189e0b7ed3 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index c7749e50a6..b50289e97d 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -220,6 +220,7 @@ libray_mapping = { 'icu': 'ICU::i18n ICU::uc ICU::data', 'libatomic': 'Atomic', 'libdl': '${CMAKE_DL_LIBS}', + 'libpng' : 'PNG::PNG', 'libproxy': 'LibProxy::LibProxy', 'librt': 'WrapRt', 'pcre2': 'PCRE2', -- cgit v1.2.3 From 4b105d77c24fb09fc65f1b1e622a47c69dabccaf Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 7 Mar 2019 11:06:23 +0100 Subject: CMake: pro2cmake.py: Remove .qrc files! Remove qrc files from CMake. Use add_qt_resource function instead. Change-Id: I64cdbd9498f97d23cd8e03f34ab5ae4a52dba5af Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 107 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 16 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7c00afad1c..7abb8f5a1c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -32,6 +32,7 @@ from __future__ import annotations from argparse import ArgumentParser import copy +import xml.etree.ElementTree as ET from itertools import chain import os.path import re @@ -72,6 +73,61 @@ def _parse_commandline(): return parser.parse_args() +def process_qrc_file(target: str, filepath: str, base_dir: str = '') -> str: + assert(target) + resource_name = os.path.splitext(os.path.basename(filepath))[0] + base_dir = os.path.join('' if base_dir == '.' else base_dir, os.path.dirname(filepath)) + + tree = ET.parse(filepath) + root = tree.getroot() + assert(root.tag == 'RCC') + + output = '' + + resource_count = 0 + for resource in root: + assert(resource.tag == 'qresource') + lang = resource.get('lang', '') + prefix = resource.get('prefix', '') + + full_resource_name = resource_name + (str(resource_count) if resource_count > 0 else '') + + files: Dict[str, str] = {} + for file in resource: + path = file.text + assert path + + # Get alias: + alias = file.get('alias', '') + files[path] = alias + + sorted_files = sorted(files.keys()) + + assert(sorted_files) + + for source in sorted_files: + alias = files[source] + if alias: + full_source = os.path.join(base_dir, source) + output += 'set_source_files_properties("{}"\n' \ + ' PROPERTIES alias "{}")\n'.format(full_source, alias) + + params = '' + if lang: + params += ' LANG "{}"'.format(lang) + if prefix: + params += ' PREFIX "{}"'.format(prefix) + if base_dir: + params += ' BASE "{}"'.format(base_dir) + output += 'add_qt_resource({} "{}"{} FILES\n {})\n'.format(target, full_resource_name, + params, + '\n '.join(sorted_files)) + + resource_count += 1 + + return output + + def fixup_linecontinuation(contents: str) -> str: contents = re.sub(r'([^\t ])\\[ \t]*\n', '\\1 \\\n', contents) contents = re.sub(r'\\[ \t]*\n', '\\\n', contents) @@ -778,28 +834,19 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, ind = spaces(indent) scope.reset_visited_keys() + # mark RESOURCES as visited: + scope.get('RESOURCES', '') + plugin_type = scope.get('PLUGIN_TYPE') if plugin_type: cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0])) + vpath = scope.expand('VPATH') + sources = scope.expand('SOURCES') + scope.expand('HEADERS') \ + scope.expand('OBJECTIVE_SOURCES') + scope.expand('NO_PCH_SOURCES') \ + scope.expand('FORMS') - resources = scope.expand('RESOURCES') - if resources: - qrc_only = True - for r in resources: - if not r.endswith('.qrc'): - qrc_only = False - break - - if not qrc_only: - print(' XXXX Ignoring non-QRC file resources.') - else: - sources += resources - - vpath = scope.expand('VPATH') sources = [map_source_to_cmake(s, scope.basedir, vpath) for s in sources] if sources: @@ -1078,6 +1125,31 @@ def map_to_cmake_condition(condition: str) -> str: return condition +def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: int = 0): + vpath = scope.expand('VPATH') + + # Handle QRC files by turning them into add_qt_resource: + resources = scope.expand('RESOURCES') + qrc_output = '' + if resources: + qrc_only = True + for r in resources: + if r.endswith('.qrc'): + qrc_output += process_qrc_file(target, + map_source_to_cmake(r, scope.basedir, vpath), + scope.basedir) + else: + qrc_only = False + + if not qrc_only: + print(' XXXX Ignoring non-QRC file resources.') + + if qrc_output: + cm_fh.write('\n# Resources:\n') + for line in qrc_output.split('\n'): + cm_fh.write(' ' * indent + line + '\n') + + def write_extend_target(cm_fh: typing.IO[str], target: str, scope: Scope, indent: int = 0): extend_qt_io_string = io.StringIO() @@ -1106,6 +1178,8 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, 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] @@ -1137,8 +1211,7 @@ def merge_scopes(scopes: typing.List[Scope]) -> typing.List[Scope]: 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, - **kwargs: typing.Any): + indent: int = 0, **kwargs: typing.Any): # Evaluate total condition of all scopes: recursive_evaluate_scope(scope) @@ -1168,6 +1241,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, # Footer: cm_fh.write('{})\n'.format(spaces(indent))) + write_resources(cm_fh, name, scope, indent) + # Scopes: if len(scopes) == 1: return -- cgit v1.2.3 From 42ae8218b1e18b002df5a26a2f4e961af61aaa5d Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 11 Mar 2019 15:10:22 +0100 Subject: CMake: configurejson2cmake.py: Fix typo Change-Id: Ib142032577f1d84bfb2402103271db04a4bb25a1 Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index efce7fee4a..244bc4ae4c 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -138,7 +138,7 @@ def map_tests(test: str) -> str: 'bmi': 'TEST_subarch_bmi', 'bmi2': 'TEST_subarch_bmi2', 'cx16': 'TEST_subarch_cx16', - 'f16c': 'TEST_subarch_c16c', + 'f16c': 'TEST_subarch_f16c', 'fma': 'TEST_subarch_fma', 'fma4': 'TEST_subarch_fma4', 'fsgsbase': 'TEST_subarch_fsgsbase', -- cgit v1.2.3 From 6e16f127ad4f1bf8f8191b72be12aa20785d816c Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 11 Mar 2019 15:09:33 +0100 Subject: CMake: pro2cmake.py: Handle SIMD sources Change-Id: Ib445888e769432e8c247ae2d2fb5d8af2d5cd275 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7abb8f5a1c..37ead10513 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1208,6 +1208,30 @@ 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']; + ind = spaces(indent) + + for simd in simd_options: + SIMD = simd.upper(); + sources = scope.get('{}_HEADERS'.format(SIMD), []) \ + + scope.get('{}_SOURCES'.format(SIMD), []) \ + + scope.get('{}_C_SOURCES'.format(SIMD), []) \ + + scope.get('{}_ASM'.format(SIMD), []) + + if not sources: + continue + + cm_fh.write('{}add_qt_simd_part({} SIMD {}\n'.format(ind, target, simd)) + cm_fh.write('{} SOURCES\n'.format(ind)) + cm_fh.write('{} {}\n'.format(ind, '\n{} '.format(ind).join(sources))) + cm_fh.write('{})\n\n'.format(ind)) + + def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, cmake_function: str, scope: Scope, *, extra_lines: typing.List[str] = [], @@ -1243,6 +1267,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, write_resources(cm_fh, name, scope, indent) + write_simd_part(cm_fh, name, scope, indent) + # Scopes: if len(scopes) == 1: return -- cgit v1.2.3 From fbf98bf255e2fe134c3274e314b945097e2905e4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 12 Mar 2019 19:55:51 +0100 Subject: CMake: pro2cmake.py: Better reporting of unused keys from qmake Change-Id: Ie1b1f446c314bb5248cc13efeae1f279b0182e04 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 53 +++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 37ead10513..adcbaa9868 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -829,10 +829,8 @@ def write_scope_header(cm_fh: typing.IO[str], *, indent: int = 0): def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0, known_libraries=set()) \ - -> typing.Set[str]: + indent: int = 0, known_libraries=set()): ind = spaces(indent) - scope.reset_visited_keys() # mark RESOURCES as visited: scope.get('RESOURCES', '') @@ -912,25 +910,28 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, for mo in moc_options: cm_fh.write('{} "{}"\n'.format(ind, mo)) - return set(scope.keys) - scope.visited_keys - def is_simple_condition(condition: str) -> bool: return ' ' not in condition \ or (condition.startswith('NOT ') and ' ' not in condition[4:]) -def write_ignored_keys(scope: Scope, ignored_keys, indent) -> str: +def write_ignored_keys(scope: Scope, indent: str) -> str: 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 == 'QT_BUILD_TREE' or k == '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) + + if result: + result = '\n#### Keys ignored in scope {}:\n{}'.format(scope, result) + return result @@ -1152,29 +1153,18 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in def write_extend_target(cm_fh: typing.IO[str], target: str, scope: Scope, indent: int = 0): + ind = spaces(indent) extend_qt_io_string = io.StringIO() - ignored_keys = write_sources_section(extend_qt_io_string, scope) + write_sources_section(extend_qt_io_string, scope) extend_qt_string = extend_qt_io_string.getvalue() - ignored_keys_report = write_ignored_keys(scope, ignored_keys, - spaces(indent + 1)) - if extend_qt_string and ignored_keys_report: - ignored_keys_report = '\n' + ignored_keys_report - extend_scope = '\n{}extend_target({} CONDITION {}\n' \ - '{}{})\n'.format(spaces(indent), target, + '{}{})\n'.format(ind, target, map_to_cmake_condition(scope.total_condition), - extend_qt_string, ignored_keys_report) + extend_qt_string, ind) if not extend_qt_string: - if ignored_keys_report: - # Comment out the generated extend_target call because there - # no sources were found, but keep it commented for - # informational purposes. - extend_scope = ''.join(['#' + line for line in - extend_scope.splitlines(keepends=True)]) - else: - extend_scope = '' # Nothing to report, so don't! + extend_scope = '' # Nothing to report, so don't! cm_fh.write(extend_scope) @@ -1249,6 +1239,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, assert len(scopes) assert scopes[0].total_condition == 'ON' + scopes[0].reset_visited_keys() + # Now write out the scopes: write_header(cm_fh, name, typename, indent=indent) @@ -1256,11 +1248,7 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, for extra_line in extra_lines: cm_fh.write('{} {}\n'.format(spaces(indent), extra_line)) - ignored_keys = write_sources_section(cm_fh, scopes[0], indent=indent, **kwargs) - ignored_keys_report = write_ignored_keys(scopes[0], ignored_keys, - spaces(indent + 1)) - if ignored_keys_report: - cm_fh.write(ignored_keys_report) + write_sources_section(cm_fh, scopes[0], indent=indent, **kwargs) # Footer: cm_fh.write('{})\n'.format(spaces(indent))) @@ -1269,6 +1257,11 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, write_simd_part(cm_fh, name, scope, indent) + ignored_keys_report = write_ignored_keys(scopes[0], spaces(indent)) + if ignored_keys_report: + cm_fh.write(ignored_keys_report) + + # Scopes: if len(scopes) == 1: return @@ -1276,7 +1269,11 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, write_scope_header(cm_fh, indent=indent) for c in scopes[1:]: + c.reset_visited_keys() write_extend_target(cm_fh, name, c, indent=indent) + ignored_keys_report = write_ignored_keys(c, spaces(indent)) + if ignored_keys_report: + cm_fh.write(ignored_keys_report) def write_module(cm_fh: typing.IO[str], scope: Scope, *, -- cgit v1.2.3 From 37b154858f41c0aa32ad124878fae96c39563d39 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 14 Mar 2019 13:16:13 +0100 Subject: Improve configurejson2cmake feature output generation There were a few cases of feature outputs that were handled incorrectly before this change, specifically: - publicFeatures and privateFeatures with custom names - privateFeatures that also ended up writing defines into public headers - publicFeatures that ended up in private headers - internal features (that should have no QT_FEATURE_foo defines) that were still written to either public or private headers The change takes care of all those cases by keeping a map of which features need to be written along with any visibility specifications, as well as custom name changes. Change-Id: I37baeaeacdfe4935128a392c72ca71b5c3ca1c8d Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 102 +++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 30 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 244bc4ae4c..fe2bf50eaa 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -802,9 +802,11 @@ def parseFeature(ctx, feature, data, cm_fh): # feature that is only used in the conditions of other features output = ["internalFeature"] - publicInfo = False - privateInfo = False - internalFeature = False + 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 @@ -815,44 +817,84 @@ def parseFeature(ctx, feature, data, cm_fh): if outputType in ['varAssign', 'varAppend', 'varRemove', 'publicQtConfig', 'privateConfig', 'publicConfig']: continue - - elif outputType in ['feature', 'publicFeature', 'define']: - publicInfo = True + elif outputType == 'define': + publicDefine = True + elif outputType == 'feature': + negativeFeature = True + elif outputType == 'publicFeature': + publicFeature = True elif outputType == 'privateFeature': - privateInfo = True + privateFeature = True elif outputType == 'internalFeature': internalFeature = True else: print(' XXXX UNHANDLED OUTPUT TYPE {} in feature {}.'.format(outputType, feature)) continue - if not publicInfo and not privateInfo and not internalFeature: + if not any([publicFeature, privateFeature, internalFeature, publicDefine, negativeFeature]): print(' **** Skipping feature {}: Not relevant for C++.'.format(feature)) return - # write feature: cxxFeature = featureName(feature) - if comment: - cm_fh.write('# {}\n'.format(comment)) - - cm_fh.write('qt_feature("{}"'.format(cxxFeature)) - if publicInfo: - cm_fh.write(' PUBLIC') - if privateInfo: - cm_fh.write(' PRIVATE') - cm_fh.write('\n') - - cm_fh.write(lineify('SECTION', section)) - cm_fh.write(lineify('LABEL', label)) - if purpose != label: - cm_fh.write(lineify('PURPOSE', purpose)) - cm_fh.write(lineify('AUTODETECT', autoDetect, quote=False)) - cm_fh.write(lineify('CONDITION', condition, quote=False)) - cm_fh.write(lineify('ENABLE', enable, quote=False)) - cm_fh.write(lineify('DISABLE', disable, quote=False)) - cm_fh.write(lineify('EMIT_IF', emitIf, quote=False)) - cm_fh.write(')\n') + def writeFeature(name, publicFeature=False, privateFeature=False, labelAppend=''): + if comment: + cm_fh.write('# {}\n'.format(comment)) + + cm_fh.write('qt_feature("{}"'.format(name)) + if publicFeature: + cm_fh.write(' PUBLIC') + if privateFeature: + cm_fh.write(' PRIVATE') + cm_fh.write('\n') + + cm_fh.write(lineify('SECTION', section)) + cm_fh.write(lineify('LABEL', label + labelAppend)) + if purpose != label: + cm_fh.write(lineify('PURPOSE', purpose)) + cm_fh.write(lineify('AUTODETECT', autoDetect, quote=False)) + cm_fh.write(lineify('CONDITION', condition, quote=False)) + cm_fh.write(lineify('ENABLE', enable, quote=False)) + cm_fh.write(lineify('DISABLE', disable, quote=False)) + cm_fh.write(lineify('EMIT_IF', emitIf, quote=False)) + cm_fh.write(')\n') + + # Write qt_feature() calls before any qt_feature_definition() calls + + # Default internal feature case. + featureCalls = {} + featureCalls[cxxFeature] = {'name': cxxFeature, 'labelAppend': ''} + + # Go over all outputs to compute the number of features that have to be declared + for o in output: + outputType = o + name = cxxFeature + + # The label append is to provide a unique label for features that have more than one output + # with different names. + labelAppend = '' + + if isinstance(o, dict): + outputType = o['type'] + if 'name' in o: + name = o['name'] + labelAppend = ': {}'.format(o['name']) + + if outputType not in ['feature', 'publicFeature', 'privateFeature']: + continue + if name not in featureCalls: + featureCalls[name] = {'name': name, 'labelAppend': labelAppend} + + if outputType in ['feature', 'publicFeature']: + featureCalls[name]['publicFeature'] = True + elif outputType == 'privateFeature': + featureCalls[name]['privateFeature'] = True + + # Write the qt_feature() calls from the computed feature map + for _, args in featureCalls.items(): + writeFeature(**args) + + # Write qt_feature_definition() calls for o in output: outputType = o outputArgs = {} @@ -860,7 +902,7 @@ def parseFeature(ctx, feature, data, cm_fh): outputType = o['type'] outputArgs = o - # Map feature to define: + # Map negative feature to define: if outputType == 'feature': outputType = 'define' outputArgs = {'name': 'QT_NO_{}'.format(cxxFeature.upper()), -- cgit v1.2.3 From d84a2b82747d83b8b52e106ee516cba7b62386a8 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 18 Mar 2019 19:12:52 +0100 Subject: CMake: pro2cmake.py: Fix typo Change-Id: I5eebe64f825ab28a67093b1a2cef417ed0908ec0 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index adcbaa9868..56d5f24b67 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -349,7 +349,7 @@ class Scope(object): return child_count == 1 or self._children[1]._condition == 'else' def settle_condition(self): - new_children: typing.List[scope] = [] + new_children: typing.List[Scope] = [] for c in self._children: c.settle_condition() -- cgit v1.2.3 From 347261aaf64ce30f5dc45f2b16c7bc7a693cdea3 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 18 Mar 2019 19:13:41 +0100 Subject: CMake: pro2cmake.py: Handle DBUS_*S Handle DBUS_ADAPTORS and DBUS_INTERFACES and turn them into equivalent CMake statements. Change-Id: Ia8a69e7ab97d9f18df93097a6e2c7c1686cb16a3 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 56d5f24b67..c5bd96b90b 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -852,6 +852,18 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, for l in sort_sources(sources): cm_fh.write('{} {}\n'.format(ind, l)) + dbus_adaptors = scope.expand('DBUS_ADAPTORS') + if dbus_adaptors: + cm_fh.write('{} DBUS_ADAPTOR_SOURCES\n'.format(ind)) + for d in sort_sources(dbus_adaptors): + cm_fh.write('{} {}\n'.format(ind, d)) + + dbus_interfaces = scope.expand('DBUS_INTERFACES') + if dbus_interfaces: + cm_fh.write('{} DBUS_INTERFACE_SOURCES\n'.format(ind)) + for d in sort_sources(dbus_interfaces): + cm_fh.write('{} {}\n'.format(ind, d)) + defines = scope.expand('DEFINES') if defines: cm_fh.write('{} DEFINES\n'.format(ind)) -- cgit v1.2.3 From 88549c28551538277a944d9ba690b19533a14e36 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 18 Mar 2019 19:15:22 +0100 Subject: CMake: pro2cmake.py: Handle "target.path" and "INSTALLS = target" Add installation location information into CMakeLists.txt files if available in CMake. Change-Id: I498deac71b1cc33c7d30790e32da236afdcb23af Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index c5bd96b90b..a9d70ce84c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1336,7 +1336,15 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope, binary_name = scope.TARGET assert binary_name - extra = ['GUI', ] if gui else [] + extra = ['GUI',] if gui else[] + + target_path = scope.getString('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)) + write_main_part(cm_fh, binary_name, 'Binary', 'add_qt_executable', scope, extra_lines=extra, indent=indent, known_libraries={'Qt::Core', }) -- cgit v1.2.3 From cb7a3aaa4cceff2b4502ce3d5c568e0b58f8e5fe Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 18 Mar 2019 19:16:40 +0100 Subject: CMake: pro2cmake.py: Report more qmake variables as used Report some more qmake variables as used when they are used to decide which kind of target to write. Change-Id: Id2602bb8cc07130456c04c53148acb73f21b0f21 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index a9d70ce84c..4c8d281549 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1237,7 +1237,8 @@ def write_simd_part(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in 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, **kwargs: typing.Any): + indent: int = 0, extra_keys: typing.List[str], + **kwargs: typing.Any): # Evaluate total condition of all scopes: recursive_evaluate_scope(scope) @@ -1252,6 +1253,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, assert scopes[0].total_condition == 'ON' scopes[0].reset_visited_keys() + for k in extra_keys: + scopes[0].get(k) # Now write out the scopes: write_header(cm_fh, name, typename, indent=indent) @@ -1302,7 +1305,7 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, write_main_part(cm_fh, module_name[2:], 'Module', 'add_qt_module', scope, extra_lines=extra, indent=indent, - known_libraries={'Qt::Core', }) + known_libraries={'Qt::Core', }, extra_keys=[]) if 'qt_tracepoints' in scope.get('CONFIG'): tracepoints = map_to_file(scope.getString('TRACEPOINT_PROVIDER'), @@ -1319,7 +1322,7 @@ def write_tool(cm_fh: typing.IO[str], scope: Scope, *, write_main_part(cm_fh, tool_name, 'Tool', 'add_qt_tool', scope, indent=indent, known_libraries={'Qt::Core', }, - extra_lines=extra) + extra_lines=extra, extra_keys=['CONFIG']) def write_test(cm_fh: typing.IO[str], scope: Scope, *, @@ -1328,7 +1331,8 @@ def write_test(cm_fh: typing.IO[str], scope: Scope, *, assert test_name write_main_part(cm_fh, test_name, 'Test', 'add_qt_test', scope, - indent=indent, known_libraries={'Qt::Core', 'Qt::Test', }) + indent=indent, known_libraries={'Qt::Core', 'Qt::Test',}, + extra_keys=[]) def write_binary(cm_fh: typing.IO[str], scope: Scope, @@ -1347,7 +1351,7 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope, write_main_part(cm_fh, binary_name, 'Binary', 'add_qt_executable', scope, extra_lines=extra, indent=indent, - known_libraries={'Qt::Core', }) + known_libraries={'Qt::Core', }, extra_keys=['target.path', 'INSTALLS']) def write_plugin(cm_fh, scope, *, indent: int = 0): @@ -1355,7 +1359,7 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): assert plugin_name write_main_part(cm_fh, plugin_name, 'Plugin', 'add_qt_plugin', scope, - indent=indent, known_libraries={'QtCore', }) + indent=indent, known_libraries={'QtCore', }, extra_keys=[]) def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, -- cgit v1.2.3 From ba7c62eed53b2304c54643cbddc29db887fcd869 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 21 Mar 2019 13:14:09 +0100 Subject: Fix sub-architecture (instruction sets / SIMD) handling In qmake there are at least 2 things to know regarding sub-architectures and instruction sets. Which instruction sets does the compiler know to compile for, represented by the various config.tests and features in qtbase/configure.json. And which instructions sets are enabled by the compiler by default, represented by the configure.json "architecture" test and accessed via QT_CPU_FEATURES.$$arch qmake argument. Before this patch there was some mishandling of the above concepts in CMake code. The former can now be checked in CMake with via TEST_subarch_foo and QT_FEATURE_foo (where foo is sse2, etc). The latter can now be checked by TEST_arch_${TEST_architecture_arch}_subarch_foo (where foo is sse2, etc and the main arch is dynamyicall evaluated). The configurejson2cmake script was adjusted to take care of the above changes, and the cmake files were regenerated as well. Change-Id: Ifbf558242e320cafae50da388eee56fa5de2a50c Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index fe2bf50eaa..eb3fd606fe 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -117,7 +117,7 @@ def map_tests(test: str) -> str: 'c99': '$', 'c11': '$', - 'x86SimdAlways': 'ON', # FIXME: Is this the right thing? + 'x86SimdAlways': 'ON', # FIXME: Make this actually do a compile test. 'aesni': 'TEST_subarch_aes', 'avx': 'TEST_subarch_avx', @@ -333,7 +333,8 @@ def map_condition(condition): substitution = 'QT_FEATURE_{}'.format(featureName(match.group(2))) elif match.group(1) == 'subarch': - substitution = 'TEST_subarch_{}'.format(match.group(2)) + substitution = 'TEST_arch_{}_subarch_{}'.format("${TEST_architecture_arch}", + match.group(2)) elif match.group(1) == 'call': if match.group(2) == 'crossCompile': -- cgit v1.2.3 From 8158a8767c4c0ece37c55c2cc6777267221693ee Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 21 Mar 2019 19:17:41 +0100 Subject: Temporarily disable the opengles2 feature on WIN32 The feature used to be implicitly enabled because qt source ships with ANGLE sources, and thus ANGLE could always be built. Yet because the CMake port of ANGLE is not done yet, and because the feature is implicitly enabled, the build failed when trying to find GLES headers. To provide a nicer out-of-the-box configuring of the Windows build, disable the opengles2 feature on Windows, to default to a desktop GL build. It can be re-enabled once (if) ANGLE porting is done. After this change, you shouldn't need to pass any additional custom FEATURE_foo options to cmake to build qtbase on Windows. Change-Id: I94c96d8ef70cf671d2ce0198311f70b55fa642b1 Reviewed-by: Liang Qi Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index eb3fd606fe..4cbcc981ce 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -726,6 +726,9 @@ def parseFeature(ctx, feature, data, cm_fh): 'msvc_mp': None, 'optimize_debug': None, 'optimize_size': None, + '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': { -- cgit v1.2.3 From eb3d73ffb778e191aa5705fd1867c62809eedf9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Mon, 25 Mar 2019 15:16:32 +0100 Subject: Speedup run_pro2cmake We can use all cores. Sadly it doesn't balance cores well as corelib.pro takes most of the time anyway, but the speedup is visible from ~15 to 5 min. Change-Id: Id8209c58491b38d19c6e9f1163d366c3e33a182c Reviewed-by: Simon Hausmann Reviewed-by: Tobias Hunger --- util/cmake/run_pro2cmake.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index 1a3c1581e3..b3a07c7522 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -30,7 +30,9 @@ import glob import os import subprocess +import concurrent.futures import sys +import typing script_path = os.path.dirname(os.path.abspath(__file__)) base_path = os.path.dirname(script_path) @@ -41,20 +43,30 @@ if len(sys.argv) > 1: failed_files = [] -pro_file_count = 0 -for filename in glob.iglob(os.path.join(base_path, '**/*.pro'), - recursive=True): - pro_file_count += 1 - print('{} ({}): Converting: {} ...' - .format(pro_file_count, len(failed_files), filename)) - result = subprocess.run([pro2cmake, os.path.basename(filename)], - cwd=os.path.dirname(filename)) - if result.returncode != 0: - failed_files.append(filename) +all_files = glob.glob(os.path.join(base_path, '**/*.pro'), recursive=True) +files_count = len(all_files) + +with concurrent.futures.ThreadPoolExecutor(initializer=os.nice, initargs=(10,)) as pool: + + def _process_a_file(data: typing.Tuple[str, int, int]) -> typing.Tuple[int, str, str]: + filename, index, total = data + result = subprocess.run((pro2cmake, os.path.basename(filename)), + cwd=os.path.dirname(filename), + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + stdout = 'Converted[{}/{}]: {}\n'.format(index, total, filename) + 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))): + if return_code: + failed_files.append(filename) + print(stdout) if failed_files: print('The following files were not successfully ' - 'converted ({} of {}):'.format(len(failed_files), pro_file_count)) + 'converted ({} of {}):'.format(len(failed_files), files_count)) for f in failed_files: print(' "{}"'.format(f)) -- cgit v1.2.3 From a0a94576fae26bcbbf3823a6ee4b554886e84925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Tue, 26 Mar 2019 10:17:58 +0100 Subject: Fix RemoveOperation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The operation was using an empty set as a base, so it was not really functional. Change-Id: I98fd80c1ede31994857aa1f0c8947ca7b9f76649 Reviewed-by: Tobias Hunger Reviewed-by: Jędrzej Nowacki --- util/cmake/pro2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4c8d281549..e77d6fdcaf 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -239,10 +239,13 @@ class RemoveOperation(Operation): def process(self, input): input_set = set(input) + result_set = set(self._value) result = [] for v in self._value: if v in input_set: continue + if v in result_set: + result += [v,] else: result += ['-{}'.format(v), ] return result -- cgit v1.2.3 From 89f34ee42a6f54e528533e673faa7e5fd371af4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Tue, 26 Mar 2019 14:12:09 +0100 Subject: Fix turned logic in RemoveOperation It is regression caused by a0a94576fae26bcbbf3823a6ee4b554886e84925 ("Fix RemoveOperation"). Add unit test for all operation types to make sure this code actually works:-) Change-Id: I97c94cb3411f05de89422e3fa2222f2217a09e49 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 16 +++++++---- util/cmake/tests/test_operations.py | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 6 deletions(-) create mode 100755 util/cmake/tests/test_operations.py (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e77d6fdcaf..676ed4b406 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -239,15 +239,19 @@ class RemoveOperation(Operation): def process(self, input): input_set = set(input) - result_set = set(self._value) + value_set = set(self._value) result = [] - for v in self._value: - if v in input_set: - continue - if v in result_set: + + # Add everything that is not going to get removed: + for v in input: + if v not in value_set: result += [v,] - else: + + # Add everything else with removal marker: + for v in self._value: + if v not in input_set: result += ['-{}'.format(v), ] + return result def __repr__(self): diff --git a/util/cmake/tests/test_operations.py b/util/cmake/tests/test_operations.py new file mode 100755 index 0000000000..3ea2f76a43 --- /dev/null +++ b/util/cmake/tests/test_operations.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from pro2cmake import AddOperation, SetOperation, UniqueAddOperation, RemoveOperation + +def test_add_operation(): + op = AddOperation(['bar', 'buz']) + + result = op.process(['foo', 'bar']) + assert ['foo', 'bar', 'bar', 'buz'] == result + + +def test_uniqueadd_operation(): + op = UniqueAddOperation(['bar', 'buz']) + + result = op.process(['foo', 'bar']) + assert ['foo', 'bar', 'buz'] == result + + +def test_set_operation(): + op = SetOperation(['bar', 'buz']) + + result = op.process(['foo', 'bar']) + assert ['bar', 'buz'] == result + + +def test_remove_operation(): + op = RemoveOperation(['bar', 'buz']) + + result = op.process(['foo', 'bar']) + assert ['foo', '-buz'] == result -- cgit v1.2.3 From 601c840973e91987c492f4b312215ff17a95e837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 28 Mar 2019 08:29:53 +0100 Subject: Do not validate file existence if path contains variables We do not need to validate everything, while converting project files. Some checks can be left to building step. It fixes some false positive NOTFOUND errors. Change-Id: I81ff2421fdea13add0bfc03086152a47bce39908 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 676ed4b406..60fd6297fb 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -166,6 +166,13 @@ def map_source_to_cmake(source: str, base_dir: str, if os.path.exists(os.path.join(base_dir, source)): return source + variable_pattern = re.compile(r'\$\{[A-Za-z0-9_]+\}') + match = re.match(variable_pattern, source) + if match: + # a complex, variable based path, skipping validation + # or resolving + return source + for v in vpath: fullpath = os.path.join(v, source) if os.path.exists(fullpath): -- cgit v1.2.3 From ce9a1434670196c151c34ce5e0f7ed0718f4215f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Mon, 25 Mar 2019 17:30:16 +0100 Subject: Do not overwrite CMakeLists.txt when running run_pro2cmake One directory may contain many pro files. The generator was happily generating the same CMakeLists.txt for all of them (overwriting). This patch implements a different logic. It tries to find the main pro file and skips others assuming that somehow implicitly they will be incorporated (for example through SUBDIRS). Change-Id: Ie07d75e900a96dd48bf981a896c9dfb920f39a23 Reviewed-by: Tobias Hunger --- util/cmake/run_pro2cmake.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index b3a07c7522..47bc6b661f 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -41,9 +41,33 @@ pro2cmake = os.path.join(script_path, 'pro2cmake.py') if len(sys.argv) > 1: base_path = os.path.abspath(sys.argv[1]) -failed_files = [] -all_files = glob.glob(os.path.join(base_path, '**/*.pro'), recursive=True) +def find_all_pro_files(): + + 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] + dir_name = os.path.dirname(pro_file) + if dir_name.endswith('/' + pro_file_without_suffix): + return dir_name + return dir_name + "/__" + pro_file + + all_files = [] + previous_dir_name: str = None + for pro_file in sorted(glob.glob(os.path.join(base_path, '**/*.pro'), + recursive=True), + key=sorter): + dir_name = os.path.dirname(pro_file) + if dir_name == previous_dir_name: + print("Skipping:", pro_file) + else: + all_files.append(pro_file) + previous_dir_name = dir_name + return all_files + + +failed_files = [] +all_files = find_all_pro_files() files_count = len(all_files) with concurrent.futures.ThreadPoolExecutor(initializer=os.nice, initargs=(10,)) as pool: -- cgit v1.2.3 From a697df786d44b5a9b6fa1de74c807fd8142c4791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 28 Mar 2019 13:25:04 +0100 Subject: Fix exception when parsing tests.pro The ParseResults may be a nested list of list. Now the code doesn't raise exceptions, but it fails in do_include as includes that doesn't provide resolved path will fail. Anyway step in the right direction. Change-Id: Ice44e4c10d221293cc6c1facca30abd5495791be Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 60fd6297fb..bdc277fa0a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -629,8 +629,18 @@ class QmakeParser: Operation = Key('key') + pp.Optional(LC) \ + Op('operation') + pp.Optional(LC) \ + Values('value') - CallArgs = pp.Optional(LC) + pp.nestedExpr() - CallArgs.setParseAction(lambda x: ' '.join(chain(*x))) + CallArgs = pp.Optional(LC) + pp.nestedExpr()\ + + def parse_call_args(results): + out = '' + for item in chain(*results): + if isinstance(item, str): + out += item + else: + out += "(" + parse_call_args(item) + ")" + return out + + CallArgs.setParseAction(parse_call_args) Load = pp.Keyword('load') + CallArgs('loaded') Include = pp.Keyword('include') + CallArgs('included') Option = pp.Keyword('option') + CallArgs('option') -- cgit v1.2.3 From f375876d1ad63b22c2dc8839e42baf7cc4a7d442 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 14:47:53 +0100 Subject: CMake: Map libudev to PkgConfig::Libudev Change-Id: Iac5d0fbf336f0c3905a3dca20524f90432227cf4 Reviewed-by: Albert Astals Cid --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index b50289e97d..6933c6aedd 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -223,6 +223,7 @@ libray_mapping = { 'libpng' : 'PNG::PNG', 'libproxy': 'LibProxy::LibProxy', 'librt': 'WrapRt', + 'libudev': 'PkgConfig::Libudev', 'pcre2': 'PCRE2', 'sqlite': 'SQLite3', 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', -- cgit v1.2.3 From 2b5e3590ca1b251f31587c9e837278d50b9fd85f Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 14:56:33 +0100 Subject: CMake: Map fontconfig to Fontconfig::Fontconfig Change-Id: I2761ef23cffefbee57da6bb1a07d06b2232d1c57 Reviewed-by: Albert Astals Cid --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 6933c6aedd..7c6d2dc802 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -213,6 +213,7 @@ def substitute_platform(platform: str) -> str: libray_mapping = { 'drm': 'Libdrm::Libdrm', 'doubleconversion': 'double-conversion', + 'fontconfig': 'Fontconfig::Fontconfig', 'freetype': 'Freetype::Freetype', 'gbm': 'gbm::gbm', 'glib': 'GLIB2::GLIB2', -- cgit v1.2.3 From 2268d171cdd7135af7b8a64e47e4e3b152ac15ce Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 15:02:01 +0100 Subject: CMake: Map tslib, mtdev, libinput and xkbcommen_evdev Change-Id: If144a8969904b63a3de3884370baaeca1cb4242a Reviewed-by: Albert Astals Cid --- util/cmake/helper.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 7c6d2dc802..8264ba2065 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -221,12 +221,15 @@ libray_mapping = { 'icu': 'ICU::i18n ICU::uc ICU::data', 'libatomic': 'Atomic', 'libdl': '${CMAKE_DL_LIBS}', + 'libinput': 'Libinput::Libinput', 'libpng' : 'PNG::PNG', 'libproxy': 'LibProxy::LibProxy', 'librt': 'WrapRt', 'libudev': 'PkgConfig::Libudev', + 'mtdev': 'PkgConfig::Mtdev', 'pcre2': 'PCRE2', 'sqlite': 'SQLite3', + 'tslib': 'PkgConfig::Tslib', 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', 'xcb_icccm': 'XCB::ICCCM', 'xcb_image': 'XCB::IMAGE', @@ -243,6 +246,7 @@ libray_mapping = { 'xcb_xinput': 'XCB::XINPUT', 'xcb_xkb': 'XCB::XKB', 'xcb_xlib': 'X11::XCB', + 'xkbcommon_evdev': 'XKB::XKB', 'xkbcommon_x11': 'XKB::XKB', 'xkbcommon': 'XKB::XKB', 'xrender': 'XCB::RENDER', -- cgit v1.2.3 From 0c5b63207be3881a71c7aefc049155879dfb7ec0 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 15:24:44 +0100 Subject: CMake: Map atspi2 to PkgConfig::ATSPI2 Change-Id: Ifd21eb278e06a6166ab87106c442d8ec94d92dc5 Reviewed-by: Albert Astals Cid --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 8264ba2065..9f53503608 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -211,6 +211,7 @@ def substitute_platform(platform: str) -> str: libray_mapping = { + 'atspi': 'PkgConfig::ATSPI2', 'drm': 'Libdrm::Libdrm', 'doubleconversion': 'double-conversion', 'fontconfig': 'Fontconfig::Fontconfig', -- cgit v1.2.3 From 828f2965e78a2178266ec3b54f922ccea324693d Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 15:08:00 +0100 Subject: CMake: pro2cmake.py: deduplicate and sort libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I924cfac365a0b4ba18c2579820bc37729f1ea8d9 Reviewed-by: Jędrzej Nowacki --- util/cmake/pro2cmake.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index bdc277fa0a..66edf5bd19 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -910,8 +910,9 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, dependencies += scope.expand('QMAKE_USE_PRIVATE') + scope.expand('QMAKE_USE') \ + scope.expand('LIBS_PRIVATE') + scope.expand('LIBS') if dependencies: - cm_fh.write('{} LIBRARIES\n'.format(ind)) + dependencies_to_print = [] is_framework = False + for d in dependencies: if d == '-framework': is_framework = True @@ -925,9 +926,14 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, d = '# Remove: {}'.format(d[1:]) else: d = substitute_libs(d) - cm_fh.write('{} {}\n'.format(ind, d)) + dependencies_to_print.append(d) is_framework = False + if dependencies_to_print: + cm_fh.write('{} LIBRARIES\n'.format(ind)) + for d in sorted(list(set(dependencies_to_print))): + cm_fh.write('{} {}\n'.format(ind, d)) + compile_options = scope.get('QMAKE_CXXFLAGS') if compile_options: cm_fh.write('{} COMPILE_OPTIONS\n'.format(ind)) -- cgit v1.2.3 From 61ec3d0b6e7c77c91449cc52a218fdab6a490896 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 29 Mar 2019 12:16:05 +0100 Subject: CMake: Map psql to PostgreSQL::PostgreSQL Change-Id: I8332d2120e8b629c8722b5c9a95b47950b327d4c Reviewed-by: Albert Astals Cid --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 9f53503608..24c2f143ab 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -229,6 +229,7 @@ libray_mapping = { 'libudev': 'PkgConfig::Libudev', 'mtdev': 'PkgConfig::Mtdev', 'pcre2': 'PCRE2', + 'psql': 'PostgreSQL::PostgreSQL', 'sqlite': 'SQLite3', 'tslib': 'PkgConfig::Tslib', 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', -- cgit v1.2.3 From 1ac36d4bc22da0cd7defad392c7619cd194da2d8 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 29 Mar 2019 12:34:22 +0100 Subject: CMake: Map glx_support and glx_supportPrivate Change-Id: I777ea84a080a6856961d644a0290e4d73e07c518 Reviewed-by: Albert Astals Cid --- util/cmake/helper.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 24c2f143ab..42e978bb71 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -218,6 +218,8 @@ libray_mapping = { 'freetype': 'Freetype::Freetype', 'gbm': 'gbm::gbm', 'glib': 'GLIB2::GLIB2', + 'glx_support': 'Qt::GlxSupport', + 'glx_supportPrivate': 'Qt::GlxSupportPrivate', 'harfbuzz': 'harfbuzz::harfbuzz', 'icu': 'ICU::i18n ICU::uc ICU::data', 'libatomic': 'Atomic', -- cgit v1.2.3 From b0d4ac8498b18e9ba89aaa51b8ec143dd0e8ae36 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 29 Mar 2019 12:21:23 +0100 Subject: CMake: Map SQLite3 to SQLite::SQLite3 Fix sqlite maping while at it. Change-Id: I712ca562fa362a7f5857047346e8b3083f901bfb Reviewed-by: Albert Astals Cid --- util/cmake/helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 42e978bb71..d5e0dd006e 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -232,7 +232,8 @@ libray_mapping = { 'mtdev': 'PkgConfig::Mtdev', 'pcre2': 'PCRE2', 'psql': 'PostgreSQL::PostgreSQL', - 'sqlite': 'SQLite3', + 'sqlite': 'SQLite::SQLite3', + 'SQLite3': 'SQLite::SQLite3', 'tslib': 'PkgConfig::Tslib', 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', 'xcb_icccm': 'XCB::ICCCM', -- cgit v1.2.3 From 6a834a0c595b70d888724610e6d4ad05826e7117 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 29 Mar 2019 12:18:19 +0100 Subject: CMake: Map odbc to ODBC::ODBC Change-Id: I55cf506cca87ac3e3fe9e0e1803f5118b46bf818 Reviewed-by: Albert Astals Cid --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index d5e0dd006e..56bd1419c8 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -230,6 +230,7 @@ libray_mapping = { 'librt': 'WrapRt', 'libudev': 'PkgConfig::Libudev', 'mtdev': 'PkgConfig::Mtdev', + 'odbc': 'ODBC::ODBC', 'pcre2': 'PCRE2', 'psql': 'PostgreSQL::PostgreSQL', 'sqlite': 'SQLite::SQLite3', -- cgit v1.2.3 From b9a3217c41560ee4a296797a394620761ca3a471 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 15:23:34 +0100 Subject: CMake: pro2cmake.py: Handle $$PWD better Change-Id: I2e28b652c60d3490138ae0548b32d010faccc5a4 Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 66edf5bd19..900914eab3 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -550,6 +550,9 @@ class Scope(object): return [result] def expand(self, key: str) -> typing.List[str]: + if key == 'PWD': + return os.path.relpath(self.currentdir, self.basedir) + value = self.get(key, []) result: typing.List[str] = [] assert isinstance(value, list) -- cgit v1.2.3 From 75658794e4a016090abd664b36acb5e4fdbbc802 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 15:24:05 +0100 Subject: CMake: pro2cmake.py: Improve handling of dbus interfaces/adaptor sources Change-Id: I3b642e1ae31996a81618e312183f4cc168bbbe1b Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 900914eab3..043b9568e8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -881,12 +881,14 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, dbus_adaptors = scope.expand('DBUS_ADAPTORS') if dbus_adaptors: + dbus_adaptors = [map_source_to_cmake(s, scope.basedir, vpath) for s in dbus_adaptors] cm_fh.write('{} DBUS_ADAPTOR_SOURCES\n'.format(ind)) for d in sort_sources(dbus_adaptors): cm_fh.write('{} {}\n'.format(ind, d)) dbus_interfaces = scope.expand('DBUS_INTERFACES') if dbus_interfaces: + dbus_interfaces = [map_source_to_cmake(s, scope.basedir, vpath) for s in dbus_interfaces] cm_fh.write('{} DBUS_INTERFACE_SOURCES\n'.format(ind)) for d in sort_sources(dbus_interfaces): cm_fh.write('{} {}\n'.format(ind, d)) -- cgit v1.2.3 From 03a64ab80dd31b0c0963801a3bf929093ddb8c05 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 15:31:07 +0100 Subject: CMake: pro2cmake.py: Handle QDBUSXML2CPP_*_HEADER_FLAGS Change-Id: Ib5d34a6bf550a11154109064e4e718d0c79c722b Reviewed-by: Albert Astals Cid --- util/cmake/pro2cmake.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 043b9568e8..7a6e6abbae 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -885,6 +885,10 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, cm_fh.write('{} DBUS_ADAPTOR_SOURCES\n'.format(ind)) for d in sort_sources(dbus_adaptors): cm_fh.write('{} {}\n'.format(ind, d)) + 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_interfaces = scope.expand('DBUS_INTERFACES') if dbus_interfaces: @@ -892,6 +896,10 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, cm_fh.write('{} DBUS_INTERFACE_SOURCES\n'.format(ind)) for d in sort_sources(dbus_interfaces): cm_fh.write('{} {}\n'.format(ind, d)) + 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))) defines = scope.expand('DEFINES') if defines: -- cgit v1.2.3 From 5c05c46e929e367ee024ce115d1f68945c8b344a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 26 Mar 2019 14:01:53 +0100 Subject: CMake: Set define for default QPA platform This is used to set the default QPA platform and without it all Gui applications trigger an assert in QString:-/ This is way simpler than going through configure.json. Change-Id: I2c053e95c0f7e99e97a0b2918d8e4ac13d3494fd Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 4cbcc981ce..1edb4b44a1 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -1008,9 +1008,6 @@ def processJson(dir, ctx, data): cm_fh.write('qt_extra_definition("QT_VERSION_MINOR" ${PROJECT_VERSION_MINOR} PUBLIC)\n') cm_fh.write('qt_extra_definition("QT_VERSION_PATCH" ${PROJECT_VERSION_PATCH} PUBLIC)\n') - if ctx.get('module') == 'gui': - cm_fh.write('\nqt_extra_definition("QT_QPA_DEFAULT_PLATFORM" "${QT_QPA_DEFAULT_PLATFORM}" PUBLIC)\n') - # do this late: processSubconfigs(dir, ctx, data) -- cgit v1.2.3 From e2e4525f7841d95c531218297ef0d57b6280aa3d Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 2 Apr 2019 14:13:27 +0200 Subject: Fix simplification of conditions involving apple Previously a condition like APPLE AND (NOT APPLE_OSX) got simplified to APPLE, which is wrong. This happened by accident due to some sub-family simplifications involving BSD, which APPLE was part of. Technically APPLE is BSD derived, but for the purposes of the conversion script consider APPLE not to be a BSD (hopefully there should be no cases of using the bsd scope for apple machines in qmake files. Also regenerate the fontdatabase project, where the issue was found. Change-Id: I18dcf4f29ffbff48c183ba95ca2a2e5cd5325d86 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7a6e6abbae..9173f84901 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1069,7 +1069,7 @@ def _recursive_simplify(expr): windowses = ('WIN32', 'WINRT') apples = ('APPLE_OSX', 'APPLE_UIKIT', 'APPLE_IOS', 'APPLE_TVOS', 'APPLE_WATCHOS',) - bsds = ('APPLE', 'FREEBSD', 'OPENBSD', 'NETBSD',) + bsds = ('FREEBSD', 'OPENBSD', 'NETBSD',) androids = ('ANDROID', 'ANDROID_EMBEDDED') unixes = ('APPLE', *apples, 'BSD', *bsds, 'LINUX', *androids, 'HAIKU', -- cgit v1.2.3 From 29f5c0ee702e291bb6e158a2d971aac7b5485059 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 2 Apr 2019 11:27:36 +0200 Subject: CMake: Map cups to Cups::Cups, vulkan_support_private to Qt::VulkanSupportPrivate Change-Id: Id03942becb08ca3ece589479ffe515f68034ddd4 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 56bd1419c8..80ef80ccb6 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -141,6 +141,7 @@ def map_qt_base_library(lib: str) -> str: 'uitools': 'Qt::UiTools', 'virtualkeyboard': 'Qt::VirtualKeyboard', 'vkconvenience': 'Qt::VulkanSupport', + 'vulkan_supportPrivate': 'Qt::VulkanSupportPrivate', 'webchannel': 'Qt::WebChannel', 'webengine': 'Qt::WebEngine', 'webenginewidgets': 'Qt::WebEngineWidgets', @@ -212,6 +213,7 @@ def substitute_platform(platform: str) -> str: libray_mapping = { 'atspi': 'PkgConfig::ATSPI2', + 'cups': 'Cups::Cups', 'drm': 'Libdrm::Libdrm', 'doubleconversion': 'double-conversion', 'fontconfig': 'Fontconfig::Fontconfig', @@ -236,6 +238,7 @@ libray_mapping = { 'sqlite': 'SQLite::SQLite3', 'SQLite3': 'SQLite::SQLite3', 'tslib': 'PkgConfig::Tslib', + 'vulkan_supportPrivate': 'Qt::VulkanSupportPrivate', 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', 'xcb_icccm': 'XCB::ICCCM', 'xcb_image': 'XCB::IMAGE', -- cgit v1.2.3 From 547c7f8ca5c13564460c4c47611c9681ad8b022b Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 2 Apr 2019 16:03:18 +0200 Subject: Fix architecture conditions The actual variable that contains the architecture is TEST_architecture_arch. TEST_architecture only contains the value if the test was performed or not. Fix the conversion script and all the generated files. Change-Id: Icb3480832cab894948f4fef03b8bc8187cab6152 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9173f84901..6e1f609514 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1175,9 +1175,9 @@ def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', def map_to_cmake_condition(condition: str) -> str: condition = re.sub(r'\bQT_ARCH___equals___([a-zA-Z_0-9]*)', - r'(TEST_architecture STREQUAL "\1")', condition) + r'(TEST_architecture_arch STREQUAL "\1")', condition) condition = re.sub(r'\bQT_ARCH___contains___([a-zA-Z_0-9]*)', - r'(TEST_architecture STREQUAL "\1")', condition) + r'(TEST_architecture_arch STREQUAL "\1")', condition) return condition -- cgit v1.2.3 From 4f07e711c9dbe80165ee62a6fdaa97b124f37155 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 5 Apr 2019 14:40:43 +0200 Subject: Add script to print out list of unconverted .pro files Also prints some statistics. Change-Id: Ieb55618c0d39604ca76d7a44390e61e02824a01f Reviewed-by: Tobias Hunger --- util/cmake/pro_conversion_rate.py | 218 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100755 util/cmake/pro_conversion_rate.py (limited to 'util/cmake') diff --git a/util/cmake/pro_conversion_rate.py b/util/cmake/pro_conversion_rate.py new file mode 100755 index 0000000000..740e834ca5 --- /dev/null +++ b/util/cmake/pro_conversion_rate.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import annotations + +""" +This utility script shows statistics about +converted .pro -> CMakeLists.txt files. + +To execute: python3 pro_conversion_rate.py +where can be any qt source directory. For better statistics, +specify a module root source dir (like ./qtbase or ./qtsvg). + +""" + +from argparse import ArgumentParser + +import os +import typing +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') + + return parser.parse_args() + + +class Blacklist: + """ Class to check if a certain dir_name / dir_path is blacklisted """ + + def __init__(self, names: typing.List[str], path_parts: typing.List[str]): + self.names = names + self.path_parts = path_parts + + # The lookup algorithm + self.lookup = self.is_blacklisted_part + self.tree = None + + 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: + self.tree.add(p) + self.tree.finalize() + + self.lookup = self.is_blacklisted_part_aho + except ImportError: + pass + + def is_blacklisted(self, dir_name: str, dir_path: str) -> bool: + # First check if exact dir name is blacklisted. + if dir_name in self.names: + return True + + # Check if a path part is blacklisted (e.g. util/cmake) + return self.lookup(dir_path) + + def is_blacklisted_part(self, dir_path: str) -> bool: + if any(part in dir_path for part in self.path_parts): + return True + return False + + def is_blacklisted_part_aho(self, dir_path: str) -> bool: + return self.tree.search(dir_path) is not None + + +def recursive_scan(path: str, extension: str, result_paths: typing.List[str], blacklist: Blacklist): + """ Find files ending with a certain extension, filtering out blacklisted entries """ + try: + for entry in os.scandir(path): + entry: os.DirEntry = entry + + if entry.is_file() and entry.path.endswith(extension): + result_paths.append(entry.path) + elif entry.is_dir(): + if blacklist.is_blacklisted(entry.name, entry.path): + continue + recursive_scan(entry.path, extension, result_paths, blacklist) + except Exception as e: + print(e) + + +def check_for_cmake_project(pro_path: str) -> bool: + pro_dir_name = os.path.dirname(pro_path) + cmake_project_path = os.path.join(pro_dir_name, "CMakeLists.txt") + 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: + 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} + + 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 + elif rel_path.startswith("tests"): + stats['missing tests']['value'] += 1 + elif rel_path.startswith(os.path.join("src", "plugins")): + stats['missing plugins']['value'] += 1 + elif rel_path.startswith("src"): + 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) + return stats + + +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: + print("No .pro files found. Did you specify a correct source path?") + return + + 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") + + for p in pros_with_missing_project: + rel_path = os.path.relpath(p, src_path) + print(rel_path) + + print("\nStatistics: \n") + + for stat in stats: + 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)) + + +def main(): + args = _parse_commandline() + src_path = os.path.abspath(args.source_directory) + pro_paths = [] + + extension = ".pro" + + blacklist_names = ["config.tests", "doc", "3rdparty", "angle"] + blacklist_path_parts = [ + os.path.join("util", "cmake") + ] + + script_start_time = default_timer() + blacklist = Blacklist(blacklist_names, blacklist_path_parts) + + scan_time_start = default_timer() + recursive_scan(src_path, extension, pro_paths, blacklist) + scan_time_end = default_timer() + scan_time = scan_time_end - scan_time_start + + total_pros = len(pro_paths) + + pros_with_missing_project = [] + for pro_path in pro_paths: + if not check_for_cmake_project(pro_path): + pros_with_missing_project.append(pro_path) + + 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) + 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__': + main() -- cgit v1.2.3 From 81542ffb91c9139f67386bc08aac6cf75218e0b4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 8 Apr 2019 11:59:34 +0200 Subject: CMake: Map linuxaccessibility_supportPrivate to Qt::LASP Change-Id: I6066f69c74c39fb97a286eb92748b5c4172b1efd Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 80ef80ccb6..b2f06d1a34 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -231,6 +231,7 @@ libray_mapping = { 'libproxy': 'LibProxy::LibProxy', 'librt': 'WrapRt', 'libudev': 'PkgConfig::Libudev', + 'linuxaccessibility_supportPrivate': 'Qt::LinuxAccessibilitySupportPrivate', 'mtdev': 'PkgConfig::Mtdev', 'odbc': 'ODBC::ODBC', 'pcre2': 'PCRE2', -- cgit v1.2.3 From 9365a015316d3eb663c36c25b2c391b38050b77f Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 4 Apr 2019 10:38:13 +0200 Subject: CMake: pro2cmake.py: Fix typing information Change-Id: Iaa6ea69c3c72eb1b626a976fcdb16243f15c609e Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6e1f609514..e3afce9c1b 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -92,7 +92,7 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '') -> str: full_resource_name = resource_name + (str(resource_count) if resource_count > 0 else '') - files: Dict[str, str] = {} + files: typing.Dict[str, str] = {} for file in resource: path = file.text assert path -- cgit v1.2.3 From 77a465ea2d9d13c742ca35bd155732fcd8af0972 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 4 Apr 2019 10:38:46 +0200 Subject: CMake: pro2cmake.py: Better handling of debug features Improve way that debug features are detected. Change-Id: Ic0f0e74885e6d1e7f236c9efe4c77482ac212dbc Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e3afce9c1b..0403961cbd 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -332,11 +332,13 @@ class Scope(object): @property def merge_debug(self) -> bool: - return self.getString('PRO2CMAKE_MERGE_DEBUG', None) != None + merge = self.getString('PRO2CMAKE_MERGE_DEBUG').lower() + return merge and (merge == '1' or merge == 'on' or merge == 'yes' or merge == 'true') @property def scope_debug(self) -> bool: - return self.getString('PRO2CMAKE_SCOPE_DEBUG', None) != None + merge = self.getString('PRO2CMAKE_SCOPE_DEBUG').lower() + return merge and (merge == '1' or merge == 'on' or merge == 'yes' or merge == 'true') @property def parent(self) -> typing.Optional[Scope]: -- cgit v1.2.3 From 52f676bf6602053336dff9bf9b00b40dad14ae4f Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 8 Apr 2019 11:28:49 +0200 Subject: CMake: pro2cmake.py: Better representation of scopes as str Include the current directory in the scope __repr__ output to make it easier to understand where things wrt. include or file names go wrong. Change-Id: I09a6c17c6d8d547f1f64801bcde3c2e10c925ee1 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0403961cbd..de715bbfef 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -301,8 +301,10 @@ class Scope(object): def __repr__(self): debug_mark = ' [MERGE_DEBUG]' if self.merge_debug else '' - return '{}:{}:{}:{}{}'.format(self._scope_id, self._basedir, self._file, - self._condition or '', debug_mark) + return '{}:{}:{}:{}:{}'.format(self._scope_id, + self._basedir, self._currentdir, + self._file, self._condition or '', + debug_mark) def reset_visited_keys(self): self._visited_keys = set() -- cgit v1.2.3 From 9d83b54015cd089fe8b59c660ca8dfa9e5ab84fb Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 13:54:56 +0100 Subject: CMake: pro2cmake.py: Fix handling of file paths This patch makes sure to store unchanged filenames as taken from qmake into the scopes it creates. The scopes are then kept longer: Merging/including scopes is handled by adding the scope to a private _included_children member of the parent scope. The methods to access data from scopes are then changed to take the _included_children into account and a new "get_files" method is added, that does all the necessary mapping to handle scope-dependent things like $$PWD, etc. This makes sure src/network is converted correctly incl. all the .pri-files it includes as well as src/platformsupport/themes.pro. Both have been troublesome before. Change-Id: I28e92b7bcee1b91b248c17201c2729a54a3ce4a1 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 329 +++++++++++++++++++++++++----------------------- 1 file changed, 173 insertions(+), 156 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index de715bbfef..9f40194d0a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -139,29 +139,28 @@ def spaces(indent: int) -> str: return ' ' * indent -def map_to_file(f: str, top_dir: str, current_dir: str, - want_absolute_path: bool = False) -> typing.Optional[str]: - if f.startswith('$$PWD/') or f == '$$PWD': # INCLUDEPATH += $$PWD - return os.path.join(os.path.relpath(current_dir, top_dir), f[6:]) - if f.startswith('$$OUT_PWD/'): - return "${CMAKE_CURRENT_BUILD_DIR}/" + f[10:] - if f.startswith("./"): - return os.path.join(current_dir, f) if current_dir != '.' else f[2:] - if want_absolute_path and not os.path.isabs(f): - return os.path.join(current_dir, f) +def map_to_file(f: str, scope: Scope, *, is_include: bool = False) -> str: + assert('$$' not in f) + + if f.startswith('${'): # Some cmake variable is prepended + return f + + base_dir = scope.currentdir if is_include else scope.basedir + f = os.path.join(base_dir, f) + + while f.startswith('./'): + f = f[2:] return f -def map_source_to_cmake(source: str, base_dir: str, - vpath: typing.List[str]) -> str: - if not source or source == '$$NO_PCH_SOURCES': +def handle_vpath(source: str, base_dir: str, vpath: typing.List[str]) -> str: + assert('$$' not in source) + + if not source: return '' - if source.startswith('$$PWD/'): - return source[6:] - if source.startswith('./'): - return source[2:] - if source == '.': - return "${CMAKE_CURRENT_SOURCE_DIR}" + + if not vpath: + return source if os.path.exists(os.path.join(base_dir, source)): return source @@ -190,7 +189,7 @@ class Operation: else: self._value = [str(value), ] - def process(self, input): + def process(self, input, transformer): assert(False) def __repr__(self): @@ -213,19 +212,19 @@ class Operation: class AddOperation(Operation): - def process(self, input): - return input + self._value + def process(self, input, transformer): + return input + transformer(self._value) def __repr__(self): return '+({})'.format(self._dump()) class UniqueAddOperation(Operation): - def process(self, input): + def process(self, input, transformer): result = input - for v in self._value: + for v in transformer(self._value): if v not in result: - result += [v, ] + result.append(v) return result def __repr__(self): @@ -233,8 +232,11 @@ class UniqueAddOperation(Operation): class SetOperation(Operation): - def process(self, input): - return self._value + def process(self, input, transformer): + if transformer: + return list(transformer(self._value)) + else: + return self._value def __repr__(self): return '=({})'.format(self._dump()) @@ -244,7 +246,7 @@ class RemoveOperation(Operation): def __init__(self, value): super().__init__(value) - def process(self, input): + def process(self, input, transformer): input_set = set(input) value_set = set(self._value) result = [] @@ -255,7 +257,7 @@ class RemoveOperation(Operation): result += [v,] # Add everything else with removal marker: - for v in self._value: + for v in transformer(self._value): if v not in input_set: result += ['-{}'.format(v), ] @@ -295,51 +297,26 @@ class Scope(object): self._file = file self._condition = map_condition(condition) self._children = [] # type: typing.List[Scope] + self._included_children = [] # type: typing.List[Scope] self._operations = copy.deepcopy(operations) self._visited_keys = set() # type: typing.Set[str] self._total_condition = None # type: typing.Optional[str] def __repr__(self): - debug_mark = ' [MERGE_DEBUG]' if self.merge_debug else '' return '{}:{}:{}:{}:{}'.format(self._scope_id, self._basedir, self._currentdir, - self._file, self._condition or '', - debug_mark) + self._file, self._condition or '') def reset_visited_keys(self): self._visited_keys = set() def merge(self, other: 'Scope') -> None: assert self != other - merge_debug = self.merge_debug or other.merge_debug - if merge_debug: - print('..... [MERGE_DEBUG]: Merging scope {}:'.format(other)) - other.dump(indent=1) - print('..... [MERGE_DEBUG]: ... into scope {}:'.format(self)) - self.dump(indent=1) - - for c in other._children: - self._add_child(c) - - for key in other._operations.keys(): - if key in self._operations: - self._operations[key] += other._operations[key] - else: - self._operations[key] = other._operations[key] - - if merge_debug: - print('..... [MERGE_DEBUG]: Result scope {}:'.format(self)) - self.dump(indent=1) - print('..... [MERGE_DEBUG]: <>') - - @property - def merge_debug(self) -> bool: - merge = self.getString('PRO2CMAKE_MERGE_DEBUG').lower() - return merge and (merge == '1' or merge == 'on' or merge == 'yes' or merge == 'true') + self._included_children.append(other) @property def scope_debug(self) -> bool: - merge = self.getString('PRO2CMAKE_SCOPE_DEBUG').lower() + merge = self.get_string('PRO2CMAKE_SCOPE_DEBUG').lower() return merge and (merge == '1' or merge == 'on' or merge == 'yes' or merge == 'true') @property @@ -394,12 +371,6 @@ class Scope(object): value = statement.get('value', []) assert key != '' - if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH', 'RESOURCES',) \ - or key.endswith('_HEADERS') \ - or key.endswith('_SOURCES'): - value = [map_to_file(v, scope.basedir, - scope.currentdir) for v in value] - if operation == '=': scope._append_operation(key, SetOperation(value)) elif operation == '-=': @@ -440,10 +411,7 @@ class Scope(object): included = statement.get('included', None) if included: scope._append_operation('_INCLUDED', - UniqueAddOperation( - map_to_file(included, - scope.basedir, - scope.currentdir))) + UniqueAddOperation(included)) continue scope.settle_condition() @@ -487,7 +455,10 @@ class Scope(object): @property def children(self) -> typing.List['Scope']: - return self._children + 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 @@ -508,6 +479,19 @@ class Scope(object): else: for c in self._children: c.dump(indent=indent + 1) + print('{} Includes:'.format(ind)) + if not self._included_children: + print('{} -- NONE --'.format(ind)) + 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)) + for i in self._included_children: + i.dump_structure(type='INCL', indent=indent + 1) + for i in self._children: + i.dump_structure(type='CHLD', indent=indent + 1) @property def keys(self): @@ -517,21 +501,64 @@ class Scope(object): def visited_keys(self): return self._visited_keys - def get(self, key: str, default=None) -> typing.List[str]: + def _evalOps(self, key: str, + transformer: typing.Optional[typing.Callable[[Scope, typing.List[str]], typing.List[str]]], + result: typing.List[str]) \ + -> typing.List[str]: self._visited_keys.add(key) - result = [] # type: typing.List[str] + + if transformer: + op_transformer = lambda files: transformer(self, files) + else: + op_transformer = lambda files: files for op in self._operations.get(key, []): - result = op.process(result) + result = op.process(result, op_transformer) + + for ic in self._included_children: + result = list(ic._evalOps(key, transformer, result)) + return result - def getString(self, key: str, default: str = '') -> str: - v = self.get(key, default) + def get(self, key: str, *, ignore_includes: bool = False) -> typing.List[str]: + if key == 'PWD': + return ['${CMAKE_CURRENT_SOURCE_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] + if key == 'OUT_PWD': + return ['${CMAKE_CURRENT_BUILD_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] + + return self._evalOps(key, None, []) + + def get_string(self, key: str, default: str = '') -> str: + v = self.get(key) 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]: + + expanded_files = [] # typing.List[str] + for f in files: + expanded_files += self._expand_value(f) + + 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')), 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)) + + 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) + return list(self._evalOps(key, transformer, [])) + def _expand_value(self, value: str) -> typing.List[str]: result = value pattern = re.compile(r'\$\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?') @@ -539,7 +566,7 @@ class Scope(object): while match: old_result = result if match.group(0) == value: - return self.get(match.group(1), []) + return self.get(match.group(1)) replacement = self.expand(match.group(1)) replacement_str = replacement[0] if replacement else '' @@ -548,16 +575,13 @@ class Scope(object): + 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]: - if key == 'PWD': - return os.path.relpath(self.currentdir, self.basedir) - - value = self.get(key, []) + value = self.get(key) result: typing.List[str] = [] assert isinstance(value, list) for v in value: @@ -565,25 +589,25 @@ class Scope(object): return result def expandString(self, key: str) -> str: - result = self._expand_value(self.getString(key)) + result = self._expand_value(self.get_string(key)) assert len(result) == 1 return result[0] @property def TEMPLATE(self) -> str: - return self.getString('TEMPLATE', 'app') + return self.get_string('TEMPLATE', 'app') def _rawTemplate(self) -> str: - return self.getString('TEMPLATE') + return self.get_string('TEMPLATE') @property def TARGET(self) -> str: - return self.getString('TARGET') \ + return self.get_string('TARGET') \ or os.path.splitext(os.path.basename(self.file))[0] @property def _INCLUDED(self) -> typing.List[str]: - return self.get('_INCLUDED', []) + return self.get('_INCLUDED') class QmakeParser: @@ -765,8 +789,7 @@ def map_condition(condition: str) -> str: part) if feature: if (feature.group(1) == "qtHaveModule"): - part = 'TARGET {}'.format(map_qt_library( - feature.group(2))) + part = 'TARGET {}'.format(map_qt_library(feature.group(2))) else: feature = featureName(feature.group(2)) if feature.startswith('system_') and substitute_libs(feature[7:]) != feature[7:]: @@ -788,14 +811,13 @@ def map_condition(condition: str) -> str: def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, indent: int = 0) -> None: ind = ' ' * indent - for sd in scope.get('SUBDIRS', []): - full_sd = os.path.join(scope.basedir, sd) - if os.path.isdir(full_sd): + for sd in scope.get_files('SUBDIRS'): + if os.path.isdir(sd): cm_fh.write('{}add_subdirectory({})\n'.format(ind, sd)) - elif os.path.isfile(full_sd): - subdir_result = parseProFile(full_sd, debug=False) + elif os.path.isfile(sd): + subdir_result = parseProFile(sd, debug=False) subdir_scope \ - = Scope.FromDict(scope, full_sd, + = Scope.FromDict(scope, sd, subdir_result.asDict().get('statements'), '', scope.basedir) @@ -820,7 +842,7 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, cm_fh.write('{}endif()\n'.format(ind)) -def sort_sources(sources) -> typing.List[str]: +def sort_sources(sources: typing.List[str]) -> typing.List[str]: to_sort = {} # type: typing.Dict[str, typing.List[str]] for s in sources: if s is None: @@ -859,47 +881,53 @@ def write_scope_header(cm_fh: typing.IO[str], *, indent: int = 0): '##########################\n'.format(spaces(indent))) +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 = ''): + ind = spaces(indent) + + # collect sources + sources: typing.List[str] = [] + for key in keys: + sources += scope.get_files(key, use_vpath=True) + + if not sources: + return + + cm_fh.write(header) + extra_indent = '' + if cmake_parameter: + cm_fh.write('{} {}\n'.format(ind, cmake_parameter)) + extra_indent = ' ' + for s in sort_sources(sources): + cm_fh.write('{} {}{}\n'.format(ind, extra_indent, s)) + cm_fh.write(footer) + + def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, indent: int = 0, known_libraries=set()): ind = spaces(indent) # mark RESOURCES as visited: - scope.get('RESOURCES', '') + scope.get('RESOURCES') - plugin_type = scope.get('PLUGIN_TYPE') + plugin_type = scope.get_string('PLUGIN_TYPE') if plugin_type: cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0])) - vpath = scope.expand('VPATH') + source_keys: typing.List[str] = [] + write_source_file_list(cm_fh, scope, 'SOURCES', + ['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'], + indent) - sources = scope.expand('SOURCES') + scope.expand('HEADERS') \ - + scope.expand('OBJECTIVE_SOURCES') + scope.expand('NO_PCH_SOURCES') \ - + scope.expand('FORMS') - - sources = [map_source_to_cmake(s, scope.basedir, vpath) for s in sources] - if sources: - cm_fh.write('{} SOURCES\n'.format(ind)) - for l in sort_sources(sources): - cm_fh.write('{} {}\n'.format(ind, l)) - - dbus_adaptors = scope.expand('DBUS_ADAPTORS') - if dbus_adaptors: - dbus_adaptors = [map_source_to_cmake(s, scope.basedir, vpath) for s in dbus_adaptors] - cm_fh.write('{} DBUS_ADAPTOR_SOURCES\n'.format(ind)) - for d in sort_sources(dbus_adaptors): - cm_fh.write('{} {}\n'.format(ind, d)) + write_source_file_list(cm_fh, scope, 'DBUS_ADAPTOR_SOURCES', ['DBUS_ADAPTORS',], indent) 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_interfaces = scope.expand('DBUS_INTERFACES') - if dbus_interfaces: - dbus_interfaces = [map_source_to_cmake(s, scope.basedir, vpath) for s in dbus_interfaces] - cm_fh.write('{} DBUS_INTERFACE_SOURCES\n'.format(ind)) - for d in sort_sources(dbus_interfaces): - cm_fh.write('{} {}\n'.format(ind, d)) + write_source_file_list(cm_fh, scope, 'DBUS_INTERFACE_SOURCES', ['DBUS_INTERFACES',], indent) dbus_interface_flags = scope.expand('QDBUSXML2CPP_INTERFACE_HEADER_FLAGS') if dbus_interface_flags: cm_fh.write('{} DBUS_INTERFACE_FLAGS\n'.format(ind)) @@ -912,12 +940,11 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, d = d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"') cm_fh.write('{} {}\n'.format(ind, d)) - includes = scope.expand('INCLUDEPATH') + includes = scope.get_files('INCLUDEPATH') if includes: cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind)) for i in includes: i = i.rstrip('/') or ('/') - i = map_source_to_cmake(i, scope.basedir, vpath) cm_fh.write('{} {}\n'.format(ind, i)) dependencies = [map_qt_library(q) for q in scope.expand('QT') @@ -1189,15 +1216,13 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in vpath = scope.expand('VPATH') # Handle QRC files by turning them into add_qt_resource: - resources = scope.expand('RESOURCES') + resources = scope.get_files('RESOURCES') qrc_output = '' if resources: qrc_only = True for r in resources: if r.endswith('.qrc'): - qrc_output += process_qrc_file(target, - map_source_to_cmake(r, scope.basedir, vpath), - scope.basedir) + qrc_output += process_qrc_file(target, r, scope.basedir) else: qrc_only = False @@ -1267,18 +1292,14 @@ def write_simd_part(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in for simd in simd_options: SIMD = simd.upper(); - sources = scope.get('{}_HEADERS'.format(SIMD), []) \ - + scope.get('{}_SOURCES'.format(SIMD), []) \ - + scope.get('{}_C_SOURCES'.format(SIMD), []) \ - + scope.get('{}_ASM'.format(SIMD), []) - - if not sources: - continue - - cm_fh.write('{}add_qt_simd_part({} SIMD {}\n'.format(ind, target, simd)) - cm_fh.write('{} SOURCES\n'.format(ind)) - cm_fh.write('{} {}\n'.format(ind, '\n{} '.format(ind).join(sources))) - cm_fh.write('{})\n\n'.format(ind)) + 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(ind, target, simd), + footer = '{})\n\n'.format(ind)) def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, @@ -1294,7 +1315,6 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, total_scopes = len(scopes) # Merge scopes based on their conditions: scopes = merge_scopes(scopes) - print("xxxxxx {} scopes, {} after merging!".format(total_scopes, len(scopes))) assert len(scopes) assert scopes[0].total_condition == 'ON' @@ -1355,10 +1375,9 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, known_libraries={'Qt::Core', }, extra_keys=[]) if 'qt_tracepoints' in scope.get('CONFIG'): - tracepoints = map_to_file(scope.getString('TRACEPOINT_PROVIDER'), - scope.basedir, scope.currentdir) + tracepoints = scope.get_files('TRACEPOINT_PROVIDER') cm_fh.write('\n\n{}qt_create_tracepoints({} {})\n' - .format(spaces(indent), module_name[2:], tracepoints)) + .format(spaces(indent), module_name[2:], ' '.join(tracepoints))) def write_tool(cm_fh: typing.IO[str], scope: Scope, *, @@ -1389,7 +1408,7 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope, extra = ['GUI',] if gui else[] - target_path = scope.getString('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)) @@ -1414,13 +1433,13 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, assert scope.TEMPLATE in ('app', 'lib') is_lib = scope.TEMPLATE == 'lib' - is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED', [])) + is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED')) - if is_lib or 'qt_module' in scope.get('_LOADED', []): + if is_lib or 'qt_module' in scope.get('_LOADED'): write_module(cm_fh, scope, indent=indent) elif is_plugin: write_plugin(cm_fh, scope, indent=indent) - elif 'qt_tool' in scope.get('_LOADED', []): + elif 'qt_tool' in scope.get('_LOADED'): write_tool(cm_fh, scope, indent=indent) else: if 'testcase' in scope.get('CONFIG') \ @@ -1430,12 +1449,12 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, gui = 'console' not in scope.get('CONFIG') write_binary(cm_fh, scope, gui, indent=indent) - docs = scope.getString("QMAKE_DOCS") - if docs: - cm_fh.write("\n{}add_qt_docs({})\n" - .format(spaces(indent), - map_to_file(docs, scope.basedir, - scope.currentdir))) + ind = spaces(indent) + write_source_file_list(cm_fh, scope, '', + ['QMAKE_DOCS',], + indent, + header = '{}add_qt_docs(\n'.format(ind), + footer = '{})\n'.format(ind)) def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, @@ -1462,9 +1481,7 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: for c in scope.children: do_include(c) - for i in scope._INCLUDED: - dir = scope.basedir - include_file = i + for include_file in scope.get_files('_INCLUDED', is_include=True): if not include_file: continue if not os.path.isfile(include_file): @@ -1475,7 +1492,7 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: include_scope \ = Scope.FromDict(None, include_file, include_result.asDict().get('statements'), - '', dir) # This scope will be merged into scope! + '', scope.basedir) # This scope will be merged into scope! do_include(include_scope) -- cgit v1.2.3 From 8c28c8546555c135b642f2a845c26815b085a427 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 8 Apr 2019 14:00:59 +0200 Subject: CMake: pro2cmake.py: Strip '-D' from compile options Change-Id: I8239d36dc6af1b5eeded7f293dd6c151c10e289f Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9f40194d0a..1cb3691f14 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -982,6 +982,8 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, if compile_options: cm_fh.write('{} COMPILE_OPTIONS\n'.format(ind)) for co in compile_options: + if co.startswith('-D'): + co = co[2:] cm_fh.write('{} "{}"\n'.format(ind, co)) link_options = scope.get('QMAKE_LFLAGS') -- cgit v1.2.3 From 3cf92ab183eec9caadcba4ababcf7ea644f711dd Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 8 Apr 2019 14:44:34 +0200 Subject: CMake: pro2cmake.py: Better separation between public and private libraries Change-Id: I95a941ae92a77e049437d3cdd7d06eece11588f4 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 97 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 30 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1cb3691f14..402589451e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -904,6 +904,68 @@ def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str, cm_fh.write(footer) +def write_library_list(cm_fh: typing.IO[str], cmake_keyword: str, + dependencies: typing.List[str], *, indent: int = 0): + dependencies_to_print = [] + is_framework = False + + for d in dependencies: + if d == '-framework': + is_framework = True + continue + if is_framework: + d = '${FW%s}' % d + if d.startswith('-l'): + d = d[2:] + + if d.startswith('-'): + d = '# Remove: {}'.format(d[1:]) + else: + d = substitute_libs(d) + dependencies_to_print.append(d) + is_framework = False + + if dependencies_to_print: + ind = spaces(indent) + cm_fh.write('{} {}\n'.format(ind, cmake_keyword)) + for d in sorted(list(set(dependencies_to_print))): + cm_fh.write('{} {}\n'.format(ind, d)) + + + +def write_library_section(cm_fh: typing.IO[str], scope: Scope, + public: typing.List[str], + private: typing.List[str], + mixed: typing.List[str], *, + indent: int = 0, known_libraries=set()): + public_dependencies = [] # typing.List[str] + private_dependencies = [] # typing.List[str] + + for key in public: + public_dependencies += [map_qt_library(q) for q in scope.expand(key) + if map_qt_library(q) not in known_libraries] + for key in private: + private_dependencies += [map_qt_library(q) for q in scope.expand(key) + if map_qt_library(q) not in known_libraries] + for key in mixed: + for lib in scope.expand(key): + if map_qt_library(lib) in known_libraries: + continue + + if lib.endswith('-private'): + mapped_lib_name = map_qt_base_library(lib[0:-8]) + if mapped_lib_name: + private_dependencies.append(mapped_lib_name + 'Private') + public_dependencies.append(mapped_lib_name) + continue + + public_dependencies.append(lib) + + write_library_list(cm_fh, 'LIBRARIES', private_dependencies, indent=indent) + write_library_list(cm_fh, 'PUBLIC_LIBRARIES', public_dependencies, indent=indent) + + + def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, indent: int = 0, known_libraries=set()): ind = spaces(indent) @@ -947,36 +1009,11 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, i = i.rstrip('/') or ('/') cm_fh.write('{} {}\n'.format(ind, i)) - dependencies = [map_qt_library(q) for q in scope.expand('QT') - if map_qt_library(q) not in known_libraries] - dependencies += [map_qt_library(q) for q in scope.expand('QT_FOR_PRIVATE') - if map_qt_library(q) not in known_libraries] - dependencies += scope.expand('QMAKE_USE_PRIVATE') + scope.expand('QMAKE_USE') \ - + scope.expand('LIBS_PRIVATE') + scope.expand('LIBS') - if dependencies: - dependencies_to_print = [] - is_framework = False - - for d in dependencies: - if d == '-framework': - is_framework = True - continue - if is_framework: - d = '${FW%s}' % d - if d.startswith('-l'): - d = d[2:] - - if d.startswith('-'): - d = '# Remove: {}'.format(d[1:]) - else: - d = substitute_libs(d) - dependencies_to_print.append(d) - is_framework = False - - if dependencies_to_print: - cm_fh.write('{} LIBRARIES\n'.format(ind)) - for d in sorted(list(set(dependencies_to_print))): - cm_fh.write('{} {}\n'.format(ind, d)) + write_library_section(cm_fh, scope, + ['QMAKE_USE', 'LIBS'], + ['QT_FOR_PRIVATE', 'QMAKE_USE_PRIVATE', 'LIBS_PRIVATE'], + ['QT',], + indent=indent, known_libraries=known_libraries) compile_options = scope.get('QMAKE_CXXFLAGS') if compile_options: -- cgit v1.2.3 From 3bc9586e617447be4453a7e882e7898ffdd7d8b6 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 8 Apr 2019 14:45:01 +0200 Subject: CMake: pro2cmake.py: Move defines from QMAKE_CXX_FLAGS into DEFINES Change-Id: If3bfe715032b21dc046e63a79b0318a161d7a371 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 402589451e..34921b4e37 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -996,6 +996,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, cm_fh.write('{} "{}"\n'.format(ind, '" "'.join(dbus_interface_flags))) defines = scope.expand('DEFINES') + defines += [d[2:] for d in scope.expand('QMAKE_CXXFLAGS') if d.startswith('-D')] if defines: cm_fh.write('{} DEFINES\n'.format(ind)) for d in defines: @@ -1015,12 +1016,10 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, ['QT',], indent=indent, known_libraries=known_libraries) - compile_options = scope.get('QMAKE_CXXFLAGS') + compile_options = [d for d in scope.expand('QMAKE_CXXFLAGS') if not d.startswith('-D')] if compile_options: cm_fh.write('{} COMPILE_OPTIONS\n'.format(ind)) for co in compile_options: - if co.startswith('-D'): - co = co[2:] cm_fh.write('{} "{}"\n'.format(ind, co)) link_options = scope.get('QMAKE_LFLAGS') -- cgit v1.2.3 From 9afc605eb5dea636d3805b0a4e14294030595da3 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 8 Apr 2019 16:07:23 +0200 Subject: CMake: Fix mappings in helper.py Some of the Qt names were wrong. Fix them and remove the work-arounds in the library mappings. Change-Id: I9b9afa3fb35c578e5c8d9cdef77224eb072ec8da Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index b2f06d1a34..5a22ff523d 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -69,7 +69,7 @@ def map_qt_base_library(lib: str) -> str: 'fbconvenience': 'Qt::FbSupport', 'fontdatabase_support': 'Qt::FontDatabaseSupport', 'gamepad': 'Qt::Gamepad', - 'glxconvenience': 'Qt::GlxSupport', + 'glx_support': 'Qt::GlxSupport', 'graphics_support': 'Qt::GraphicsSupport', 'gsttools': 'Qt::MultimediaGstTools', 'gui': 'Qt::Gui', @@ -82,7 +82,7 @@ def map_qt_base_library(lib: str) -> str: 'kms_support': 'Qt::KmsSupport', 'launcher-lib': 'Qt::AppManLauncher', 'lib': 'Qt::Designer', - 'linuxaccessibility': 'Qt::LinuxAccessibilitySupport', + 'linuxaccessibility_support': 'Qt::LinuxAccessibilitySupport', 'location': 'Qt::Location', 'logic': 'Qt::3DLogic', 'macextras': 'Qt::MacExtras', @@ -140,8 +140,7 @@ def map_qt_base_library(lib: str) -> str: 'uiplugin': 'Qt::UiPlugin', 'uitools': 'Qt::UiTools', 'virtualkeyboard': 'Qt::VirtualKeyboard', - 'vkconvenience': 'Qt::VulkanSupport', - 'vulkan_supportPrivate': 'Qt::VulkanSupportPrivate', + 'vulkan_support': 'Qt::VulkanSupport', 'webchannel': 'Qt::WebChannel', 'webengine': 'Qt::WebEngine', 'webenginewidgets': 'Qt::WebEngineWidgets', @@ -231,7 +230,6 @@ libray_mapping = { 'libproxy': 'LibProxy::LibProxy', 'librt': 'WrapRt', 'libudev': 'PkgConfig::Libudev', - 'linuxaccessibility_supportPrivate': 'Qt::LinuxAccessibilitySupportPrivate', 'mtdev': 'PkgConfig::Mtdev', 'odbc': 'ODBC::ODBC', 'pcre2': 'PCRE2', @@ -239,7 +237,6 @@ libray_mapping = { 'sqlite': 'SQLite::SQLite3', 'SQLite3': 'SQLite::SQLite3', 'tslib': 'PkgConfig::Tslib', - 'vulkan_supportPrivate': 'Qt::VulkanSupportPrivate', 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', 'xcb_icccm': 'XCB::ICCCM', 'xcb_image': 'XCB::IMAGE', -- cgit v1.2.3 From 511124bf1d297f8561cbca3b768ef24a12eb36cb Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 9 Apr 2019 11:35:18 +0200 Subject: CMake: pro2cmake.py: Fix plugin type only prints first letter Change-Id: Ib8989d2c13199d804d0c069903ca1c5eb61763e6 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 34921b4e37..9399be5f33 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -976,7 +976,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, plugin_type = scope.get_string('PLUGIN_TYPE') if plugin_type: - cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0])) + cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type)) source_keys: typing.List[str] = [] write_source_file_list(cm_fh, scope, 'SOURCES', -- cgit v1.2.3 From 677a7a3066c91f93f523aca7d630af679b329eba Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Tue, 9 Apr 2019 13:20:19 +0200 Subject: CMake: Fix mappings in helper.py - WUIAS This amends 9afc605eb5dea636d3805b0a4e14294030595da3. Change-Id: If246811d267d091c692875a1645c96767f781010 Reviewed-by: Tobias Hunger --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 5a22ff523d..f5573c3fac 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -148,7 +148,7 @@ def map_qt_base_library(lib: str) -> str: 'webview': 'Qt::WebView', 'widgets': 'Qt::Widgets', 'window-lib': 'Qt::AppManWindow', - 'windowsuiautomation': 'Qt::WindowsUIAutomationSupport', + 'windowsuiautomation_support': 'Qt::WindowsUIAutomationSupport', 'winextras': 'Qt::WinExtras', 'x11extras': 'Qt::X11Extras', 'xcb_qpa_lib': 'Qt::XcbQpa', -- cgit v1.2.3 From 92b0d7b9110dde7db56f10d7c34a21876fb27fe3 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 28 Mar 2019 15:25:17 +0100 Subject: CMake: Support /nolink libraries Add a helper function to QtBuild that generates Foo_nolink versions of Foo library targets. Map 'Foo/nolink' libs found in qmake to Foo_nolink. Automatically run helper function to create _nolink targets as part of extend_target. Change-Id: I4c23ea68b3037d23c9a31d4ac272a6bd0565f7c0 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 7 ++++++- util/cmake/pro2cmake.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index f5573c3fac..9de01df32f 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -232,6 +232,7 @@ libray_mapping = { 'libudev': 'PkgConfig::Libudev', 'mtdev': 'PkgConfig::Mtdev', 'odbc': 'ODBC::ODBC', + 'openssl': 'OpenSSL::SSL', 'pcre2': 'PCRE2', 'psql': 'PostgreSQL::PostgreSQL', 'sqlite': 'SQLite::SQLite3', @@ -263,4 +264,8 @@ libray_mapping = { def substitute_libs(lib: str) -> str: - return libray_mapping.get(lib, lib) + libpostfix = '' + if lib.endswith('/nolink'): + lib = lib[:-7] + libpostfix = '_nolink' + return libray_mapping.get(lib, lib) + libpostfix diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9399be5f33..7f4af835ff 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1012,7 +1012,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, write_library_section(cm_fh, scope, ['QMAKE_USE', 'LIBS'], - ['QT_FOR_PRIVATE', 'QMAKE_USE_PRIVATE', 'LIBS_PRIVATE'], + ['QT_FOR_PRIVATE', 'QMAKE_USE_PRIVATE', 'QMAKE_USE_FOR_PRIVATE', 'LIBS_PRIVATE'], ['QT',], indent=indent, known_libraries=known_libraries) -- cgit v1.2.3 From 8f0eb6557970a2407afa7190e617afdc5d3ef691 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 11 Apr 2019 17:04:11 +0200 Subject: CMake: Configurejson2cmake: Always enable system-pcre2 and handle dlopen Handle dlopen properly. Code is ifdef-ed on it, so we need it:-/ Change-Id: I7f35d24b97530796a4cdcdc1acbe139757170215 Reviewed-by: Albert Astals Cid --- util/cmake/configurejson2cmake.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 1edb4b44a1..f870637e15 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -288,10 +288,10 @@ def map_condition(condition): assert isinstance(condition, str) mapped_features = { - "dlopen": "ON", 'gbm': 'gbm_FOUND', "system-xcb": "ON", "system-freetype": "ON", + 'system-pcre2': 'ON', } # Turn foo != "bar" into (NOT foo STREQUAL 'bar') @@ -694,7 +694,9 @@ def parseFeature(ctx, feature, data, cm_fh): 'cross_compile': None, 'debug_and_release': None, 'debug': None, - 'dlopen': None, # handled by CMAKE_DL_LIBS + 'dlopen': { + 'condition': 'UNIX', + }, 'doubleconversion': None, 'enable_gdb_index': None, 'enable_new_dtags': None, -- cgit v1.2.3 From 2ec3f492a7964648e838ef663fe41e4e7351c9df Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 11 Apr 2019 17:06:01 +0200 Subject: CMake: pro2cmake.py: Handle setting a key with $$key in the value Change-Id: I86552ed2a30f07f8c6060b2bad04fd5489b1d482 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7f4af835ff..44326e488e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -189,7 +189,7 @@ class Operation: else: self._value = [str(value), ] - def process(self, input, transformer): + def process(self, key, input, transformer): assert(False) def __repr__(self): @@ -212,7 +212,7 @@ class Operation: class AddOperation(Operation): - def process(self, input, transformer): + def process(self, key, input, transformer): return input + transformer(self._value) def __repr__(self): @@ -220,7 +220,7 @@ class AddOperation(Operation): class UniqueAddOperation(Operation): - def process(self, input, transformer): + def process(self, key, input, transformer): result = input for v in transformer(self._value): if v not in result: @@ -232,11 +232,18 @@ class UniqueAddOperation(Operation): class SetOperation(Operation): - def process(self, input, transformer): + def process(self, key, input, transformer): + values = [] # typing.List[str] + for v in self._value: + if v != '$$' + key: + values.append(v) + else: + values += input + if transformer: - return list(transformer(self._value)) + return list(transformer(values)) else: - return self._value + return values def __repr__(self): return '=({})'.format(self._dump()) @@ -246,7 +253,7 @@ class RemoveOperation(Operation): def __init__(self, value): super().__init__(value) - def process(self, input, transformer): + def process(self, key, input, transformer): input_set = set(input) value_set = set(self._value) result = [] @@ -513,7 +520,7 @@ class Scope(object): op_transformer = lambda files: files for op in self._operations.get(key, []): - result = op.process(result, op_transformer) + result = op.process(key, result, op_transformer) for ic in self._included_children: result = list(ic._evalOps(key, transformer, result)) @@ -540,7 +547,8 @@ class Scope(object): expanded_files = [] # typing.List[str] for f in files: - expanded_files += self._expand_value(f) + 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)) @@ -568,7 +576,7 @@ class Scope(object): if match.group(0) == value: return self.get(match.group(1)) - replacement = self.expand(match.group(1)) + replacement = self.get(match.group(1)) replacement_str = replacement[0] if replacement else '' result = result[:match.start()] \ + replacement_str \ -- cgit v1.2.3 From 95cdb0d1ae62b4aa801eec32949ad18c61032e49 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 12 Apr 2019 11:45:43 +0200 Subject: CMake: pro2cmake.py: Inherrit VPATH from parent scopes Change-Id: I95b62fdf3a4cba674bef5a58f0d414464daa3b0c Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 44326e488e..75d52957a9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -510,10 +510,14 @@ class Scope(object): def _evalOps(self, key: str, transformer: typing.Optional[typing.Callable[[Scope, typing.List[str]], typing.List[str]]], - result: typing.List[str]) \ + result: typing.List[str], *, inherrit: bool = False) \ -> typing.List[str]: self._visited_keys.add(key) + # Inherrit values from above: + if self._parent and inherrit: + result = self._parent._evalOps(key, transformer, result) + if transformer: op_transformer = lambda files: transformer(self, files) else: @@ -527,13 +531,13 @@ class Scope(object): return result - def get(self, key: str, *, ignore_includes: bool = False) -> typing.List[str]: + def get(self, key: str, *, ignore_includes: bool = False, inherrit: bool = False) -> typing.List[str]: if key == 'PWD': return ['${CMAKE_CURRENT_SOURCE_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] if key == 'OUT_PWD': return ['${CMAKE_CURRENT_BUILD_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] - return self._evalOps(key, None, []) + return self._evalOps(key, None, [], inherrit=inherrit) def get_string(self, key: str, default: str = '') -> str: v = self.get(key) @@ -553,7 +557,7 @@ class Scope(object): 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')), mapped_files)) + result = list(map(lambda f: handle_vpath(f, self.basedir, self.get('VPATH', inherrit=True)), mapped_files)) else: result = mapped_files -- cgit v1.2.3 From ce809cab29ff1d5c99fdbc6fdcab0960eaecdd4a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 12 Apr 2019 11:53:38 +0200 Subject: CMake: pro2cmake.py: Fix handling of libraries in QT Change-Id: I5737a285ca0575a454e60fad231435d96b2f1be7 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 75d52957a9..e7bb4f3fa2 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -961,17 +961,15 @@ def write_library_section(cm_fh: typing.IO[str], scope: Scope, if map_qt_library(q) not in known_libraries] for key in mixed: for lib in scope.expand(key): - if map_qt_library(lib) in known_libraries: + mapped_lib = map_qt_library(lib) + if mapped_lib in known_libraries: continue - if lib.endswith('-private'): - mapped_lib_name = map_qt_base_library(lib[0:-8]) - if mapped_lib_name: - private_dependencies.append(mapped_lib_name + 'Private') - public_dependencies.append(mapped_lib_name) - continue - - public_dependencies.append(lib) + if mapped_lib.endswith('Private'): + private_dependencies.append(mapped_lib) + public_dependencies.append(mapped_lib[:-7]) + else: + public_dependencies.append(mapped_lib) write_library_list(cm_fh, 'LIBRARIES', private_dependencies, indent=indent) write_library_list(cm_fh, 'PUBLIC_LIBRARIES', public_dependencies, indent=indent) -- cgit v1.2.3 From 37ed6dae00640f9cc980ffda05347c12a7eb5d7e Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 11 Apr 2019 17:05:06 +0200 Subject: CMake: Map xkbcommon_support Qt 5.12 comes with xkbcommon_support now. Map that accordingly. Change-Id: Id10708349d377f6bdfed654428ebcef0b533bd69 Reviewed-by: Albert Astals Cid --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 9de01df32f..13c1677b52 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -152,6 +152,7 @@ def map_qt_base_library(lib: str) -> str: 'winextras': 'Qt::WinExtras', 'x11extras': 'Qt::X11Extras', 'xcb_qpa_lib': 'Qt::XcbQpa', + 'xkbcommon_support': 'Qt::XkbCommonSupport', 'xmlpatterns': 'Qt::XmlPatterns', 'xml': 'Qt::Xml', } -- cgit v1.2.3 From 5668522413c831d6f1c607e1c87a0b1e1ee3cbc5 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 1 May 2019 14:40:32 +0200 Subject: Make freetype a required package With qmake if we don't find a system package, we use the bundled one. With CMake we don't provide a bundle freetype, hence it's required to find a system one (or custom provided one). Change-Id: I00a5e2ac55459957dae0729f89bafa792a102152 Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 58ab106555..fe853bb910 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -54,7 +54,7 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'drm': 'Libdrm', 'egl': 'EGL', 'fontconfig': LibraryMapping(package='Fontconfig', resultVariable="FONTCONFIG"), - 'freetype': 'Freetype', + 'freetype': ['Freetype', 'REQUIRED'], 'gbm': 'gbm', 'glib': 'GLIB2', 'gnu_iconv': None, -- cgit v1.2.3 From 9b0b464e82071338134700edfa190bf998846e4e Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 24 Apr 2019 17:14:25 +0200 Subject: Write find_dependency() calls in Qt Module config files This change introduces a new function called qt_find_package() which can take an extra option called PROVIDED_TARGETS, which associates targets with the package that defines those targets. This is done by setting the INTERFACE_QT_PACKAGE_NAME and INTERFACE_QT_PACKAGE_VERSION properties on the imported targets. This information allows us to generate appropriate find_dependency() calls in a module's Config file for third party libraries. For example when an application links against QtCore, it should also link against zlib and atomic libraries. In order to do that, the library locations first have to be found by CMake. This is achieved by embedding find_dependency(ZLIB) and find_dependency(Atomic) in Qt5CoreDependencies.cmake which is included by Qt5CoreConfig.cmake. The latter is picked up when an application project contains find_package(Qt5Core), and thus all linking dependencies are resolved. The information 'which package provides which targets' is contained in the python json2cmake conversion script. The generated output of the script contains qt_find_package() calls that represent that information. The Qt5CoreDependencies.cmake file and which which dependencies it contains is generated at the QtPostProcess stop. Note that for non-static Qt builds, we only need to propagate public 3rd party libraries. For static builds, we need all third party libraries. In order for the INTERFACE_QT_PACKAGE_NAME property to be read in any scope, the targets on which the property is set, have to be GLOBAL. Also for applications and other modules to find all required third party libraries, we have to install all our custom Find modules, and make sure they define INTERFACE IMPORTED libraries, and not just IMPORTED libraries. Change-Id: I694d6e32d05b96d5e241df0156fc79d0029426aa Reviewed-by: Tobias Hunger --- util/cmake/configurejson2cmake.py | 13 +++++++++---- util/cmake/helper.py | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index fe853bb910..15cd9bab47 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -33,7 +33,7 @@ import re import sys from typing import Set, Union, List, Dict -from helper import map_qt_library, featureName, substitute_platform +from helper import map_qt_library, featureName, substitute_platform, qmake_library_to_cmake_target_mapping knownTests = set() # type: Set[str] @@ -68,7 +68,6 @@ def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: 'libinput': 'Libinput', 'libjpeg': 'JPEG', 'libpng': 'PNG', - 'libpng': 'PNG', 'libproxy': 'Libproxy', 'librt': 'WrapRt', 'libudev': 'Libudev', @@ -260,10 +259,16 @@ def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): isRequired = True extra.remove("REQUIRED") + # If we have a mapping from a qmake library to a CMake target name, + # encode that in the qt_find_package call(). + cmake_target_name = qmake_library_to_cmake_target_mapping.get(lib, None) + if cmake_target_name: + extra += ['PROVIDED_TARGETS', cmake_target_name] + if extra: - cm_fh.write('find_package({} {})\n'.format(newlib, ' '.join(extra))) + cm_fh.write('qt_find_package({} {})\n'.format(newlib, ' '.join(extra))) else: - cm_fh.write('find_package({})\n'.format(newlib)) + cm_fh.write('qt_find_package({})\n'.format(newlib)) cm_fh.write('set_package_properties({} PROPERTIES TYPE {})\n' .format(newlib, 'REQUIRED' if isRequired else 'OPTIONAL') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 13c1677b52..1e13a5a1f3 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -211,7 +211,7 @@ def substitute_platform(platform: str) -> str: return platform_mapping.get(platform, platform) -libray_mapping = { +qmake_library_to_cmake_target_mapping = { 'atspi': 'PkgConfig::ATSPI2', 'cups': 'Cups::Cups', 'drm': 'Libdrm::Libdrm', @@ -228,7 +228,7 @@ libray_mapping = { 'libdl': '${CMAKE_DL_LIBS}', 'libinput': 'Libinput::Libinput', 'libpng' : 'PNG::PNG', - 'libproxy': 'LibProxy::LibProxy', + 'libproxy': 'PkgConfig::Libproxy', 'librt': 'WrapRt', 'libudev': 'PkgConfig::Libudev', 'mtdev': 'PkgConfig::Mtdev', @@ -269,4 +269,4 @@ def substitute_libs(lib: str) -> str: if lib.endswith('/nolink'): lib = lib[:-7] libpostfix = '_nolink' - return libray_mapping.get(lib, lib) + libpostfix + return qmake_library_to_cmake_target_mapping.get(lib, lib) + libpostfix -- cgit v1.2.3 From 64b31cafa102c788a25a3d83975da84938a7432b Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Fri, 3 May 2019 09:46:47 +0200 Subject: cmake: Fix xcb_glx build Use the actual cmake library target name Change-Id: Icda1d232e3b920bf33ebceb00f39682050f7dedd Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 1e13a5a1f3..eb30aa02dd 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -240,6 +240,7 @@ qmake_library_to_cmake_target_mapping = { 'SQLite3': 'SQLite::SQLite3', 'tslib': 'PkgConfig::Tslib', 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', + 'xcb_glx': 'XCB::GLX', 'xcb_icccm': 'XCB::ICCCM', 'xcb_image': 'XCB::IMAGE', 'xcb_keysyms': 'XCB::KEYSYMS', -- cgit v1.2.3 From 9618434de56644fb61a76d4a3eb2176d629023fc Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 3 May 2019 14:08:17 +0200 Subject: CMake: configurejson2cmake: Do not generate useless OPTIONALs Do not set properties of packages to type OPTIONAL. That is the default anyway. Update generator script and generated files. Change-Id: I7a4d043b69c93ce8c2929a2e27ac6a07e4e6d8cc Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 15cd9bab47..fc6f9845a6 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -270,9 +270,8 @@ def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): else: cm_fh.write('qt_find_package({})\n'.format(newlib)) - cm_fh.write('set_package_properties({} PROPERTIES TYPE {})\n' - .format(newlib, 'REQUIRED' if isRequired else 'OPTIONAL') - ) + if isRequired: + cm_fh.write('set_package_properties({} PROPERTIES TYPE REQUIRED)\n'.format(newlib)) def lineify(label, value, quote=True): if value: -- cgit v1.2.3 From 7874ce780130cdecd6188662f03703ff49387ab0 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 3 May 2019 15:02:32 +0200 Subject: CMake: Improve mapping of libraries Merge all data related to mapping libraries into one data structure in helper.py. Use that data for everything related to library mapping. This change enables way more features now like e.g. adding find_package calls into generated files. Change-Id: Ibbd2a1063cbeb65277582d434a6a672d62fc170b Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 125 +++-------- util/cmake/helper.py | 427 ++++++++++++++++++++++---------------- util/cmake/pro2cmake.py | 2 +- 3 files changed, 272 insertions(+), 282 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index fc6f9845a6..b9ec0f9449 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -33,7 +33,7 @@ import re import sys from typing import Set, Union, List, Dict -from helper import map_qt_library, featureName, substitute_platform, qmake_library_to_cmake_target_mapping +from helper import map_qt_library, featureName, substitute_platform, find_library_mapping knownTests = set() # type: Set[str] @@ -44,70 +44,6 @@ class LibraryMapping: self.resultVariable = resultVariable self.appendFoundSuffix = appendFoundSuffix - -def map_library(lib: str) -> Union[str, LibraryMapping, List[str]]: - libmap = { - 'atspi': 'ATSPI2', - 'corewlan': None, # Framework - 'cups': 'Cups', - 'double-conversion': None, - 'drm': 'Libdrm', - 'egl': 'EGL', - 'fontconfig': LibraryMapping(package='Fontconfig', resultVariable="FONTCONFIG"), - 'freetype': ['Freetype', 'REQUIRED'], - 'gbm': 'gbm', - 'glib': 'GLIB2', - 'gnu_iconv': None, - 'gtk3': 'GTK3', - 'harfbuzz': 'harfbuzz', - 'host_dbus': None, - 'icu': ['ICU', 'COMPONENTS', 'i18n', 'uc', 'data'], - 'journald': 'Libsystemd', - 'libatomic': 'Atomic', - 'libdl': None, # handled by CMAKE_DL_LIBS - 'libinput': 'Libinput', - 'libjpeg': 'JPEG', - 'libpng': 'PNG', - 'libproxy': 'Libproxy', - 'librt': 'WrapRt', - 'libudev': 'Libudev', - 'lttng-ust': LibraryMapping(package='LTTngUST', resultVariable="LTTNGUST"), - 'mtdev': 'Mtdev', - 'odbc': 'ODBC', - 'opengl': LibraryMapping(package="OpenGL", resultVariable="OpenGL_OpenGL"), - 'openssl': 'OpenSSL', - 'openssl_headers': LibraryMapping(package="OpenSSL", resultVariable="OPENSSL_INCLUDE_DIR", appendFoundSuffix=False), - 'pcre2': ['PCRE2', 'REQUIRED'], - 'posix_iconv': None, - 'pps': 'PPS', - 'psql': 'PostgreSQL', - 'slog2': 'Slog2', - 'sqlite3': 'SQLite3', - 'sun_iconv': None, - 'tslib': 'Tslib', - 'udev': 'Libudev', - 'vulkan': 'Vulkan', - 'wayland_server': 'Wayland', - 'x11sm': LibraryMapping(package="X11", resultVariable="X11_SM"), - 'xcb_glx': LibraryMapping(package="XCB", resultVariable="XCB_GLX"), - 'xcb_render': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), - 'xcb': ['XCB', '1.9'], - 'xcb_xinput': LibraryMapping(package="XCB", resultVariable="XCB_XINPUT"), - 'xcb_xkb': LibraryMapping(package="XCB", resultVariable="XCB_XKB"), - 'xcb_xlib': 'X11_XCB', - 'xkbcommon': ['XKB', '0.4.1'], - 'xlib': 'X11', - 'xrender': LibraryMapping(package="XCB", resultVariable="XCB_RENDER"), - 'zlib': 'ZLIB', - 'zstd': 'ZSTD', - 'opengl_es2': 'GLESv2', - } # type: Dict[str, Union[str, List[str], LibraryMapping]] - if lib not in libmap: - raise Exception(' XXXX Unknown library "{}".'.format(lib)) - - return libmap[lib] - - def map_tests(test: str) -> str: testmap = { 'c++11': '$', @@ -229,49 +165,48 @@ def processFiles(ctx, data): return ctx def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): - extra = [] - try: - newlib = map_library(lib) - if isinstance(newlib, list): - extra = newlib[1:] - newlib = newlib[0] - elif isinstance(newlib, LibraryMapping): - newlib = newlib.package - except Exception: - return ctx - - if newlib is None: + newlib = find_library_mapping(lib) + if not newlib: + print(' XXXX Unknown library "{}".'.format(lib)) + return + + if newlib.packageName is None: print(' **** Skipping library "{}" -- was masked.'.format(lib)) return print(' mapped library {} to {}.'.format(lib, newlib)) # Avoid duplicate find_package calls. - if newlib in cmake_find_packages_set: + if newlib.targetName in cmake_find_packages_set: return - cmake_find_packages_set.add(newlib) + cmake_find_packages_set.add(newlib.targetName) isRequired = False + extra = newlib.extra.copy() + if extra: if "REQUIRED" in extra: isRequired = True extra.remove("REQUIRED") - # If we have a mapping from a qmake library to a CMake target name, - # encode that in the qt_find_package call(). - cmake_target_name = qmake_library_to_cmake_target_mapping.get(lib, None) + cmake_target_name = newlib.targetName + + # _nolink or not does not matter at this point: + if cmake_target_name.endswith('_nolink') or cmake_target_name.endswith('/nolink'): + cmake_target_name = cmake_target_name[:-7] + if cmake_target_name: extra += ['PROVIDED_TARGETS', cmake_target_name] if extra: - cm_fh.write('qt_find_package({} {})\n'.format(newlib, ' '.join(extra))) + cm_fh.write('qt_find_package({} {})\n'.format(newlib.packageName, ' '.join(extra))) else: - cm_fh.write('qt_find_package({})\n'.format(newlib)) + cm_fh.write('qt_find_package({})\n'.format(newlib.packageName)) if isRequired: - cm_fh.write('set_package_properties({} PROPERTIES TYPE REQUIRED)\n'.format(newlib)) + cm_fh.write('set_package_properties({} PROPERTIES TYPE REQUIRED)\n'.format(newlib.packageName)) def lineify(label, value, quote=True): if value: @@ -316,18 +251,14 @@ def map_condition(condition): substitution = None appendFoundSuffix = True if match.group(1) == 'libs': - try: - substitution = map_library(match.group(2)) - if isinstance(substitution, list): - substitution = substitution[0] - elif isinstance(substitution, LibraryMapping): - appendFoundSuffix = substitution.appendFoundSuffix - substitution = substitution.resultVariable - except Exception: - substitution = None - - if substitution is not None and appendFoundSuffix: - substitution += '_FOUND' + libmapping = find_library_mapping(match.group(2)) + + if libmapping and libmapping.packageName: + substitution = libmapping.packageName + if libmapping.resultVariable: + substitution = libmapping.resultVariable + if libmapping.appendFoundSuffix: + substitution += '_FOUND' elif match.group(1) == 'features': feature = match.group(2) diff --git a/util/cmake/helper.py b/util/cmake/helper.py index eb30aa02dd..99a518bf0c 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -27,136 +27,241 @@ ############################################################################# import re +import typing +class LibraryMapping: + def __init__(self, soName: typing.Optional[str], + packageName: str, targetName: str, *, + resultVariable: typing.Optional[str] = None, + extra: typing.List[str] = [], + appendFoundSuffix: bool = True) -> None: + self.soName = soName + self.packageName = packageName + self.resultVariable = resultVariable + self.appendFoundSuffix = appendFoundSuffix + self.extra = extra + self.targetName = targetName -def featureName(input: str) -> str: - return re.sub(r'[^a-zA-Z0-9_]', '_', input) + def is_qt() -> bool: + return self.packageName == 'Qt' \ + or self.packageName == 'Qt5' \ + or self.packageName == 'Qt6' + +_qt_library_map = [ + # Qt: + LibraryMapping('accessibility_support', 'Qt5', 'Qt::AccessibilitySupport', extra = ['COMPONENTS', 'AccessibilitySupport']), + LibraryMapping('androidextras', 'Qt5', 'Qt::AndroidExtras', extra = ['COMPONENTS', 'AndroidExtras']), + LibraryMapping('animation', 'Qt5', 'Qt::3DAnimation', extra = ['COMPONENTS', '3DAnimation']), + LibraryMapping('application-lib', 'Qt5', 'Qt::AppManApplication', extra = ['COMPONENTS', 'AppManApplication']), + LibraryMapping('bluetooth', 'Qt5', 'Qt::Bluetooth', extra = ['COMPONENTS', 'Bluetooth']), + LibraryMapping('bootstrap', 'Qt5', 'Qt::Bootstrap', extra = ['COMPONENTS', 'Bootstrap']), + # bootstrap-dbus: Not needed in Qt6! + LibraryMapping('client', 'Qt5', 'Qt::WaylandClient', extra = ['COMPONENTS', 'WaylandClient']), + LibraryMapping('clipboard_support', 'Qt5', 'Qt::ClipboardSupport', extra = ['COMPONENTS', 'ClipboardSupport']), + LibraryMapping('common-lib', 'Qt5', 'Qt::AppManCommon', extra = ['COMPONENTS', 'AppManCommon']), + LibraryMapping('compositor', 'Qt5', 'Qt::WaylandCompositor', extra = ['COMPONENTS', 'WaylandCompositor']), + LibraryMapping('concurrent', 'Qt5', 'Qt::Concurrent', extra = ['COMPONENTS', 'Concurrent']), + LibraryMapping('container', 'Qt5', 'Qt::AxContainer', extra = ['COMPONENTS', 'AxContainer']), + LibraryMapping('control', 'Qt5', 'Qt::AxServer', extra = ['COMPONENTS', 'AxServer']), + LibraryMapping('core_headers', 'Qt5', 'Qt::WebEngineCore', extra = ['COMPONENTS', 'WebEngineCore']), + LibraryMapping('core', 'Qt5', 'Qt::Core', extra = ['COMPONENTS', 'Core']), + LibraryMapping('coretest', 'Qt5', 'Qt::3DCoreTest', extra = ['COMPONENTS', '3DCoreTest']), + LibraryMapping('crypto-lib', 'Qt5', 'Qt::AppManCrypto', extra = ['COMPONENTS', 'AppManCrypto']), + LibraryMapping('dbus', 'Qt5', 'Qt::Dbus', extra = ['COMPONENTS', 'DBus']), + LibraryMapping('devicediscovery', 'Qt5', 'Qt::DeviceDiscoverySupport', extra = ['COMPONENTS', 'DeviceDiscoverySupport']), + LibraryMapping('devicediscovery_support', 'Qt5', 'Qt::DeviceDiscoverySupport', extra = ['COMPONENTS', 'DeviceDiscoverySupport']), + LibraryMapping('edid', 'Qt5', 'Qt::EdidSupport', extra = ['COMPONENTS', 'EdidSupport']), + LibraryMapping('edid_support', 'Qt5', 'Qt::EdidSupport', extra = ['COMPONENTS', 'EdidSupport']), + LibraryMapping('eglconvenience', 'Qt5', 'Qt::EglSupport', extra = ['COMPONENTS', 'EglSupport']), + LibraryMapping('eglfsdeviceintegration', 'Qt5', 'Qt::EglFSDeviceIntegration', extra = ['COMPONENTS', 'EglFSDeviceIntegration']), + LibraryMapping('eglfs_kms_support', 'Qt5', 'Qt::EglFsKmsSupport', extra = ['COMPONENTS', 'EglFsKmsSupport']), + LibraryMapping('egl_support', 'Qt5', 'Qt::EglSupport', extra = ['COMPONENTS', 'EglSupport']), + # enginio: Not needed in Qt6! + LibraryMapping('eventdispatchers', 'Qt5', 'Qt::EventDispatcherSupport', extra = ['COMPONENTS', 'EventDispatcherSupport']), + LibraryMapping('eventdispatcher_support', 'Qt5', 'Qt::EventDispatcherSupport', extra = ['COMPONENTS', 'EventDispatcherSupport']), + LibraryMapping('extras', 'Qt5', 'Qt::3DExtras', extra = ['COMPONENTS', '3DExtras']), + LibraryMapping('fbconvenience', 'Qt5', 'Qt::FbSupport', extra = ['COMPONENTS', 'FbSupport']), + LibraryMapping('fb_support', 'Qt5', 'Qt::FbSupport', extra = ['COMPONENTS', 'FbSupport']), + LibraryMapping('fontdatabase_support', 'Qt5', 'Qt::FontDatabaseSupport', extra = ['COMPONENTS', 'FontDatabaseSupport']), + LibraryMapping('gamepad', 'Qt5', 'Qt::Gamepad', extra = ['COMPONENTS', 'Gamepad']), + LibraryMapping('global', 'Qt5', 'Qt::Core', extra = ['COMPONENTS', 'Core']), # manually added special case + LibraryMapping('glx_support', 'Qt5', 'Qt::GlxSupport', extra = ['COMPONENTS', 'GlxSupport']), + LibraryMapping('graphics_support', 'Qt5', 'Qt::GraphicsSupport', extra = ['COMPONENTS', 'GraphicsSupport']), + LibraryMapping('gsttools', 'Qt5', 'Qt::MultimediaGstTools', extra = ['COMPONENTS', 'MultimediaGstTools']), + LibraryMapping('gui', 'Qt5', 'Qt::Gui', extra = ['COMPONENTS', 'Gui']), + LibraryMapping('help', 'Qt5', 'Qt::Help', extra = ['COMPONENTS', 'Help']), + LibraryMapping('hunspellinputmethod', 'Qt5', 'Qt::HunspellInputMethod', extra = ['COMPONENTS', 'HunspellInputMethod']), + LibraryMapping('input', 'Qt5', 'Qt::InputSupport', extra = ['COMPONENTS', 'InputSupport']), + LibraryMapping('input_support', 'Qt5', 'Qt::InputSupport', extra = ['COMPONENTS', 'InputSupport']), + LibraryMapping('installer-lib', 'Qt5', 'Qt::AppManInstaller', extra = ['COMPONENTS', 'AppManInstaller']), + LibraryMapping('kmsconvenience', 'Qt5', 'Qt::KmsSupport', extra = ['COMPONENTS', 'KmsSupport']), + LibraryMapping('kms_support', 'Qt5', 'Qt::KmsSupport', extra = ['COMPONENTS', 'KmsSupport']), + LibraryMapping('launcher-lib', 'Qt5', 'Qt::AppManLauncher', extra = ['COMPONENTS', 'AppManLauncher']), + LibraryMapping('lib', 'Qt5', 'Qt::Designer', extra = ['COMPONENTS', 'Designer']), + LibraryMapping('linuxaccessibility_support', 'Qt5', 'Qt::LinuxAccessibilitySupport', extra = ['COMPONENTS', 'LinuxAccessibilitySupport']), + LibraryMapping('location', 'Qt5', 'Qt::Location', extra = ['COMPONENTS', 'Location']), + LibraryMapping('logic', 'Qt5', 'Qt::3DLogic', extra = ['COMPONENTS', '3DLogic']), + LibraryMapping('macextras', 'Qt5', 'Qt::MacExtras', extra = ['COMPONENTS', 'MacExtras']), + LibraryMapping('main-lib', 'Qt5', 'Qt::AppManMain', extra = ['COMPONENTS', 'AppManMain']), + LibraryMapping('manager-lib', 'Qt5', 'Qt::AppManManager', extra = ['COMPONENTS', 'AppManManager']), + LibraryMapping('monitor-lib', 'Qt5', 'Qt::AppManMonitor', extra = ['COMPONENTS', 'AppManMonitor']), + LibraryMapping('multimedia', 'Qt5', 'Qt::Multimedia', extra = ['COMPONENTS', 'Multimedia']), + LibraryMapping('multimediawidgets', 'Qt5', 'Qt::MultimediaWidgets', extra = ['COMPONENTS', 'MultimediaWidgets']), + LibraryMapping('network', 'Qt5', 'Qt::Network', extra = ['COMPONENTS', 'Network']), + LibraryMapping('nfc', 'Qt5', 'Qt::Nfc', extra = ['COMPONENTS', 'Nfc']), + LibraryMapping('oauth', 'Qt5', 'Qt::NetworkAuth', extra = ['COMPONENTS', 'NetworkAuth']), + LibraryMapping('openglextensions', 'Qt5', 'Qt::OpenGLExtensions', extra = ['COMPONENTS', 'OpenGLExtensions']), + LibraryMapping('opengl', 'Qt5', 'Qt::OpenGL', extra = ['COMPONENTS', 'OpenGL']), + LibraryMapping('package-lib', 'Qt5', 'Qt::AppManPackage', extra = ['COMPONENTS', 'AppManPackage']), + LibraryMapping('packetprotocol', 'Qt5', 'Qt::PacketProtocol', extra = ['COMPONENTS', 'PacketProtocol']), + LibraryMapping('particles', 'Qt5', 'Qt::QuickParticles', extra = ['COMPONENTS', 'QuickParticles']), + LibraryMapping('platformcompositor', 'Qt5', 'Qt::PlatformCompositorSupport', extra = ['COMPONENTS', 'PlatformCompositorSupport']), + LibraryMapping('platformcompositor_support', 'Qt5', 'Qt::PlatformCompositorSupport', extra = ['COMPONENTS', 'PlatformCompositorSupport']), + LibraryMapping('plugin-interfaces', 'Qt5', 'Qt::AppManPluginInterfaces', extra = ['COMPONENTS', 'AppManPluginInterfaces']), + LibraryMapping('positioning', 'Qt5', 'Qt::Positioning', extra = ['COMPONENTS', 'Positioning']), + LibraryMapping('positioningquick', 'Qt5', 'Qt::PositioningQuick', extra = ['COMPONENTS', 'PositioningQuick']), + LibraryMapping('printsupport', 'Qt5', 'Qt::PrintSupport', extra = ['COMPONENTS', 'PrintSupport']), + LibraryMapping('purchasing', 'Qt5', 'Qt::Purchasing', extra = ['COMPONENTS', 'Purchasing']), + LibraryMapping('qmldebug', 'Qt5', 'Qt::QmlDebug', extra = ['COMPONENTS', 'QmlDebug']), + LibraryMapping('qmldevtools', 'Qt5', 'Qt::QmlDevTools', extra = ['COMPONENTS', 'QmlDevTools']), + LibraryMapping('qml', 'Qt5', 'Qt::Qml', extra = ['COMPONENTS', 'Qml']), + LibraryMapping('qmltest', 'Qt5', 'Qt::QuickTest', extra = ['COMPONENTS', 'QuickTest']), + LibraryMapping('qtmultimediaquicktools', 'Qt5', 'Qt::MultimediaQuick', extra = ['COMPONENTS', 'MultimediaQuick']), + LibraryMapping('quick3danimation', 'Qt5', 'Qt::3DQuickAnimation', extra = ['COMPONENTS', '3DQuickAnimation']), + LibraryMapping('quick3dextras', 'Qt5', 'Qt::3DQuickExtras', extra = ['COMPONENTS', '3DQuickExtras']), + LibraryMapping('quick3dinput', 'Qt5', 'Qt::3DQuickInput', extra = ['COMPONENTS', '3DQuickInput']), + LibraryMapping('quick3d', 'Qt5', 'Qt::3DQuick', extra = ['COMPONENTS', '3DQuick']), + LibraryMapping('quick3drender', 'Qt5', 'Qt::3DQuickRender', extra = ['COMPONENTS', '3DQuickRender']), + LibraryMapping('quick3dscene2d', 'Qt5', 'Qt::3DQuickScene2D', extra = ['COMPONENTS', '3DQuickScene2D']), + LibraryMapping('quickcontrols2', 'Qt5', 'Qt::QuickControls2', extra = ['COMPONENTS', 'QuickControls2']), + LibraryMapping('quick', 'Qt5', 'Qt::Quick', extra = ['COMPONENTS', 'Quick']), + LibraryMapping('quickshapes', 'Qt5', 'Qt::QuickShapes', extra = ['COMPONENTS', 'QuickShapes']), + LibraryMapping('quicktemplates2', 'Qt5', 'Qt::QuickTemplates2', extra = ['COMPONENTS', 'QuickTemplates2']), + LibraryMapping('quickwidgets', 'Qt5', 'Qt::QuickWidgets', extra = ['COMPONENTS', 'QuickWidgets']), + LibraryMapping('render', 'Qt5', 'Qt::3DRender', extra = ['COMPONENTS', '3DRender']), + LibraryMapping('script', 'Qt5', 'Qt::Script', extra = ['COMPONENTS', 'Script']), + LibraryMapping('scripttools', 'Qt5', 'Qt::ScriptTools', extra = ['COMPONENTS', 'ScriptTools']), + LibraryMapping('sensors', 'Qt5', 'Qt::Sensors', extra = ['COMPONENTS', 'Sensors']), + LibraryMapping('serialport', 'Qt5', 'Qt::SerialPort', extra = ['COMPONENTS', 'SerialPort']), + LibraryMapping('services', 'Qt5', 'Qt::ServiceSupport', extra = ['COMPONENTS', 'ServiceSupport']), + LibraryMapping('service_support', 'Qt5', 'Qt::ServiceSupport', extra = ['COMPONENTS', 'ServiceSupport']), + LibraryMapping('sql', 'Qt5', 'Qt::Sql', extra = ['COMPONENTS', 'Sql']), + LibraryMapping('svg', 'Qt5', 'Qt::Svg', extra = ['COMPONENTS', 'Svg']), + LibraryMapping('testlib', 'Qt5', 'Qt::Test', extra = ['COMPONENTS', 'Test']), + LibraryMapping('theme_support', 'Qt5', 'Qt::ThemeSupport', extra = ['COMPONENTS', 'ThemeSupport']), + LibraryMapping('tts', 'Qt5', 'Qt::TextToSpeech', extra = ['COMPONENTS', 'TextToSpeech']), + LibraryMapping('uiplugin', 'Qt5', 'Qt::UiPlugin', extra = ['COMPONENTS', 'UiPlugin']), + LibraryMapping('uitools', 'Qt5', 'Qt::UiTools', extra = ['COMPONENTS', 'UiTools']), + LibraryMapping('virtualkeyboard', 'Qt5', 'Qt::VirtualKeyboard', extra = ['COMPONENTS', 'VirtualKeyboard']), + LibraryMapping('vulkan_support', 'Qt5', 'Qt::VulkanSupport', extra = ['COMPONENTS', 'VulkanSupport']), + LibraryMapping('webchannel', 'Qt5', 'Qt::WebChannel', extra = ['COMPONENTS', 'WebChannel']), + LibraryMapping('webengine', 'Qt5', 'Qt::WebEngine', extra = ['COMPONENTS', 'WebEngine']), + LibraryMapping('webenginewidgets', 'Qt5', 'Qt::WebEngineWidgets', extra = ['COMPONENTS', 'WebEngineWidgets']), + LibraryMapping('websockets', 'Qt5', 'Qt::WebSockets', extra = ['COMPONENTS', 'WebSockets']), + LibraryMapping('webview', 'Qt5', 'Qt::WebView', extra = ['COMPONENTS', 'WebView']), + LibraryMapping('widgets', 'Qt5', 'Qt::Widgets', extra = ['COMPONENTS', 'Widgets']), + LibraryMapping('window-lib', 'Qt5', 'Qt::AppManWindow', extra = ['COMPONENTS', 'AppManWindow']), + LibraryMapping('windowsuiautomation_support', 'Qt5', 'Qt::WindowsUIAutomationSupport', extra = ['COMPONENTS', 'WindowsUIAutomationSupport']), + LibraryMapping('winextras', 'Qt5', 'Qt::WinExtras', extra = ['COMPONENTS', 'WinExtras']), + LibraryMapping('x11extras', 'Qt5', 'Qt::X11Extras', extra = ['COMPONENTS', 'X11Extras']), + LibraryMapping('xkbcommon_support', 'Qt5', 'Qt::XkbCommonSupport', extra = ['COMPONENTS', 'XkbCommonSupport']), + LibraryMapping('xmlpatterns', 'Qt5', 'Qt::XmlPatterns', extra = ['COMPONENTS', 'XmlPatterns']), + LibraryMapping('xml', 'Qt5', 'Qt::Xml', extra = ['COMPONENTS', 'Xml']), + # qtzlib: No longer supported. +] + +_library_map = [ + # 3rd party: + LibraryMapping('atspi', 'ATSPI2', 'PkgConfig::ATSPI2'), + LibraryMapping('corewlan', None, None), + LibraryMapping('cups', 'Cups', 'Cups::Cups'), + LibraryMapping('dbus', 'DBus1', 'dbus-1'), + LibraryMapping('doubleconversion', None, None), + LibraryMapping('drm', 'Libdrm', 'Libdrm::Libdrm'), + LibraryMapping('egl', 'EGL', 'EGL::EGL'), + LibraryMapping('fontconfig', 'Fontconfig', 'Fontconfig::Fontconfig', resultVariable="FONTCONFIG"), + LibraryMapping('freetype', 'Freetype', 'Freetype::Freetype', 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('libatomic', 'Atomic', 'Atomic'), + LibraryMapping('libdl', None, None), + LibraryMapping('libinput', 'Libinput', 'Libinput::Libinput'), + LibraryMapping('libjpeg', 'JPEG', 'JPEG::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('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('pcre2', 'PCRE2', 'PCRE2', extra = ['REQUIRED']), + LibraryMapping('posix_iconv', None, None), + LibraryMapping('pps', 'PPS', 'PPS::PPS'), + LibraryMapping('psql', 'PostgreSQL', 'PostgreSQL::PostgreSQL'), + LibraryMapping('slog2', 'Slog2', 'Slog2::Slog2'), + 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('x11sm', 'X11', '${X11_SM_LIB} ${X11_ICE_LIB}', resultVariable="X11_SM"), + LibraryMapping('xcb_glx', 'XCB', 'XCB::GLX', resultVariable='XCB_GLX'), + LibraryMapping('xcb_render', 'XCB', 'XCB::RENDER', resultVariable='XCB_RENDER'), + LibraryMapping('xcb', 'XCB', 'XCB::XCB', extra = ['1.9']), + 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'), + LibraryMapping('zstd', 'ZSTD', 'ZSTD::ZSTD'), +] -def map_qt_base_library(lib: str) -> str: - library_map = { - 'global': 'Qt::Core', # manually added special case - 'accessibility_support': 'Qt::AccessibilitySupport', - 'androidextras': 'Qt::AndroidExtras', - 'animation': 'Qt::3DAnimation', - 'application-lib': 'Qt::AppManApplication', - 'bluetooth': 'Qt::Bluetooth', - 'bootstrap-dbus': 'Qt::BootstrapDBus', - 'bootstrap': 'Qt::Bootstrap', - 'client': 'Qt::WaylandClient', - 'clipboard_support': 'Qt::ClipboardSupport', - 'common-lib': 'Qt::AppManCommon', - 'compositor': 'Qt::WaylandCompositor', - 'concurrent': 'Qt::Concurrent', - 'container': 'Qt::AxContainer', - 'control': 'Qt::AxServer', - 'core_headers': 'Qt::WebEngineCore', - 'core': 'Qt::Core', - 'coretest': 'Qt::3DCoreTest', - 'crypto-lib': 'Qt::AppManCrypto', - 'dbus': 'Qt::DBus', - 'devicediscovery': 'Qt::DeviceDiscoverySupport', - 'devicediscovery_support': 'Qt::DeviceDiscoverySupport', - 'edid': 'Qt::EdidSupport', - 'eglconvenience': 'Qt::EglSupport', - 'eglfsdeviceintegration': 'Qt::EglFSDeviceIntegration', - 'eglfs_kms_support': 'Qt::EglFsKmsSupport', - 'egl_support': 'Qt::EglSupport', - 'enginio_client': 'Enginio', - 'eventdispatchers': 'Qt::EventDispatcherSupport', - 'extras': 'Qt::3DExtras', - 'fb_support': 'Qt::FbSupport', - 'fbconvenience': 'Qt::FbSupport', - 'fontdatabase_support': 'Qt::FontDatabaseSupport', - 'gamepad': 'Qt::Gamepad', - 'glx_support': 'Qt::GlxSupport', - 'graphics_support': 'Qt::GraphicsSupport', - 'gsttools': 'Qt::MultimediaGstTools', - 'gui': 'Qt::Gui', - 'help': 'Qt::Help', - 'hunspellinputmethod': 'Qt::HunspellInputMethod', - 'input': 'Qt::InputSupport', - 'input_support': 'Qt::InputSupport', - 'installer-lib': 'Qt::AppManInstaller', - 'kmsconvenience': 'Qt::KmsSupport', - 'kms_support': 'Qt::KmsSupport', - 'launcher-lib': 'Qt::AppManLauncher', - 'lib': 'Qt::Designer', - 'linuxaccessibility_support': 'Qt::LinuxAccessibilitySupport', - 'location': 'Qt::Location', - 'logic': 'Qt::3DLogic', - 'macextras': 'Qt::MacExtras', - 'main-lib': 'Qt::AppManMain', - 'manager-lib': 'Qt::AppManManager', - 'monitor-lib': 'Qt::AppManMonitor', - 'multimedia': 'Qt::Multimedia', - 'multimediawidgets': 'Qt::MultimediaWidgets', - 'network': 'Qt::Network', - 'nfc': 'Qt::Nfc', - 'oauth': 'Qt::NetworkAuth', - 'openglextensions': 'Qt::OpenGLExtensions', - 'opengl': 'Qt::OpenGL', - 'package-lib': 'Qt::AppManPackage', - 'packetprotocol': 'Qt::PacketProtocol', - 'particles': 'Qt::QuickParticles', - 'platformcompositor': 'Qt::PlatformCompositorSupport', - 'platformcompositor_support': 'Qt::PlatformCompositorSupport', - 'plugin-interfaces': 'Qt::AppManPluginInterfaces', - 'positioning': 'Qt::Positioning', - 'positioningquick': 'Qt::PositioningQuick', - 'printsupport': 'Qt::PrintSupport', - 'purchasing': 'Qt::Purchasing', - 'qmldebug': 'Qt::QmlDebug', - 'qmldevtools': 'Qt::QmlDevTools', - 'qml': 'Qt::Qml', - 'qmltest': 'Qt::QuickTest', - 'qtmultimediaquicktools': 'Qt::MultimediaQuick', - 'qtzlib': 'Qt::Zlib', - 'quick3danimation': 'Qt::3DQuickAnimation', - 'quick3dextras': 'Qt::3DQuickExtras', - 'quick3dinput': 'Qt::3DQuickInput', - 'quick3d': 'Qt::3DQuick', - 'quick3drender': 'Qt::3DQuickRender', - 'quick3dscene2d': 'Qt::3DQuickScene2D', - 'quickcontrols2': 'Qt::QuickControls2', - 'quick': 'Qt::Quick', - 'quickshapes': 'Qt::QuickShapes', - 'quicktemplates2': 'Qt::QuickTemplates2', - 'quickwidgets': 'Qt::QuickWidgets', - 'render': 'Qt::3DRender', - 'script': 'Qt::Script', - 'scripttools': 'Qt::ScriptTools', - 'sensors': 'Qt::Sensors', - 'serialport': 'Qt::SerialPort', - 'services': 'Qt::ServiceSupport', - 'sql': 'Qt::Sql', - 'svg': 'Qt::Svg', - 'testlib': 'Qt::Test', - 'theme_support': 'Qt::ThemeSupport', - 'service_support': 'Qt::ServiceSupport', - 'eventdispatcher_support': 'Qt::EventDispatcherSupport', - 'edid_support': 'Qt::EdidSupport', - 'tts': 'Qt::TextToSpeech', - 'uiplugin': 'Qt::UiPlugin', - 'uitools': 'Qt::UiTools', - 'virtualkeyboard': 'Qt::VirtualKeyboard', - 'vulkan_support': 'Qt::VulkanSupport', - 'webchannel': 'Qt::WebChannel', - 'webengine': 'Qt::WebEngine', - 'webenginewidgets': 'Qt::WebEngineWidgets', - 'websockets': 'Qt::WebSockets', - 'webview': 'Qt::WebView', - 'widgets': 'Qt::Widgets', - 'window-lib': 'Qt::AppManWindow', - 'windowsuiautomation_support': 'Qt::WindowsUIAutomationSupport', - 'winextras': 'Qt::WinExtras', - 'x11extras': 'Qt::X11Extras', - 'xcb_qpa_lib': 'Qt::XcbQpa', - 'xkbcommon_support': 'Qt::XkbCommonSupport', - 'xmlpatterns': 'Qt::XmlPatterns', - 'xml': 'Qt::Xml', - } - return library_map.get(lib, lib) +def find_library_mapping(soName: str) -> typing.Optional[LibraryMapping]: + for i in _library_map: + if i.soName == soName: + return i + return None + + +def find_qt_library_mapping(soName: str) -> typing.Optional[LibraryMapping]: + for i in _qt_library_map: + if i.soName == soName: + return i + return None + + +def featureName(input: str) -> str: + return re.sub(r'[^a-zA-Z0-9_]', '_', input) def map_qt_library(lib: str) -> str: @@ -164,10 +269,13 @@ def map_qt_library(lib: str) -> str: if lib.endswith('-private'): private = True lib = lib[:-8] - mapped = map_qt_base_library(lib) + mapped = find_qt_library_mapping(lib) + qt_name = lib + if mapped: + qt_name = mapped.targetName if private: - mapped += 'Private' - return mapped + qt_name += 'Private' + return qt_name platform_mapping = { @@ -211,63 +319,14 @@ def substitute_platform(platform: str) -> str: return platform_mapping.get(platform, platform) -qmake_library_to_cmake_target_mapping = { - 'atspi': 'PkgConfig::ATSPI2', - 'cups': 'Cups::Cups', - 'drm': 'Libdrm::Libdrm', - 'doubleconversion': 'double-conversion', - 'fontconfig': 'Fontconfig::Fontconfig', - 'freetype': 'Freetype::Freetype', - 'gbm': 'gbm::gbm', - 'glib': 'GLIB2::GLIB2', - 'glx_support': 'Qt::GlxSupport', - 'glx_supportPrivate': 'Qt::GlxSupportPrivate', - 'harfbuzz': 'harfbuzz::harfbuzz', - 'icu': 'ICU::i18n ICU::uc ICU::data', - 'libatomic': 'Atomic', - 'libdl': '${CMAKE_DL_LIBS}', - 'libinput': 'Libinput::Libinput', - 'libpng' : 'PNG::PNG', - 'libproxy': 'PkgConfig::Libproxy', - 'librt': 'WrapRt', - 'libudev': 'PkgConfig::Libudev', - 'mtdev': 'PkgConfig::Mtdev', - 'odbc': 'ODBC::ODBC', - 'openssl': 'OpenSSL::SSL', - 'pcre2': 'PCRE2', - 'psql': 'PostgreSQL::PostgreSQL', - 'sqlite': 'SQLite::SQLite3', - 'SQLite3': 'SQLite::SQLite3', - 'tslib': 'PkgConfig::Tslib', - 'x11sm': '${X11_SM_LIB} ${X11_ICE_LIB}', - 'xcb_glx': 'XCB::GLX', - 'xcb_icccm': 'XCB::ICCCM', - 'xcb_image': 'XCB::IMAGE', - 'xcb_keysyms': 'XCB::KEYSYMS', - 'xcb_randr': 'XCB::RANDR', - 'xcb_renderutil': 'XCB::RENDERUTIL', - 'xcb_render': 'XCB::RENDER', - 'xcb_shape': 'XCB::SHAPE', - 'xcb_shm': 'XCB::SHM', - 'xcb_sync': 'XCB::SYNC', - 'xcb': 'XCB::XCB', - 'xcb_xfixes': 'XCB::XFIXES', - 'xcb_xinerama': 'XCB::XINERAMA', - 'xcb_xinput': 'XCB::XINPUT', - 'xcb_xkb': 'XCB::XKB', - 'xcb_xlib': 'X11::XCB', - 'xkbcommon_evdev': 'XKB::XKB', - 'xkbcommon_x11': 'XKB::XKB', - 'xkbcommon': 'XKB::XKB', - 'xrender': 'XCB::RENDER', - 'zlib': 'ZLIB::ZLIB', - 'zstd': 'ZSTD::ZSTD', -} - - def substitute_libs(lib: str) -> str: libpostfix = '' if lib.endswith('/nolink'): lib = lib[:-7] libpostfix = '_nolink' - return qmake_library_to_cmake_target_mapping.get(lib, lib) + libpostfix + mapping = find_qt_library_mapping(lib) + if not mapping: + mapping = find_library_mapping(lib) + if not mapping: + return lib + libpostfix + return mapping.targetName + libpostfix diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7801c4d103..493517dbc7 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -42,7 +42,7 @@ import typing from sympy.logic import (simplify_logic, And, Or, Not,) import pyparsing as pp -from helper import map_qt_library, map_qt_base_library, featureName, \ +from helper import map_qt_library, featureName, \ substitute_platform, substitute_libs -- cgit v1.2.3 From 6b2de61bf973dc8c0f245ba4dfdb791faa15b428 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 3 May 2019 16:03:45 +0200 Subject: Make zlib a required package for QtCore Change-Id: Ifbb969fafe05e355d6874d8bebe2e8f1e80510ff Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 99a518bf0c..b58c73dae8 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -241,7 +241,7 @@ _library_map = [ 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'), + LibraryMapping('zlib', 'ZLIB', 'ZLIB::ZLIB', extra=['REQUIRED']), LibraryMapping('zstd', 'ZSTD', 'ZSTD::ZSTD'), ] -- cgit v1.2.3 From 059f4ade7fd08b782ecb888f8ee9f0e553c07e9a Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 6 May 2019 12:26:31 +0200 Subject: CMake: pro2cmake.py: Fix library mapping Fix library substitution again which broke when I merged all the library related pieces of information. Keep Qt and 3rdparty libraries separate so that dbus does not get mapped into Qt::DBus (or the other way around). Make names in helper.py more consistent while at it. Change-Id: I5e5bf02bdabf8bafe991c5701deca76bde4df2c3 Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 10 +++++----- util/cmake/helper.py | 30 +++++++++++++++++++----------- util/cmake/pro2cmake.py | 34 +++++++++++++++++++++------------- 3 files changed, 45 insertions(+), 29 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index b9ec0f9449..8b9251f8a7 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -33,7 +33,7 @@ import re import sys from typing import Set, Union, List, Dict -from helper import map_qt_library, featureName, substitute_platform, find_library_mapping +from helper import map_qt_library, featureName, map_platform, find_3rd_party_library_mapping knownTests = set() # type: Set[str] @@ -165,7 +165,7 @@ def processFiles(ctx, data): return ctx def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): - newlib = find_library_mapping(lib) + newlib = find_3rd_party_library_mapping(lib) if not newlib: print(' XXXX Unknown library "{}".'.format(lib)) return @@ -174,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)) + print(' mapped library {} to {}.'.format(lib, newlib.targetName)) # Avoid duplicate find_package calls. if newlib.targetName in cmake_find_packages_set: @@ -251,7 +251,7 @@ def map_condition(condition): substitution = None appendFoundSuffix = True if match.group(1) == 'libs': - libmapping = find_library_mapping(match.group(2)) + libmapping = find_3rd_party_library_mapping(match.group(2)) if libmapping and libmapping.packageName: substitution = libmapping.packageName @@ -282,7 +282,7 @@ def map_condition(condition): substitution = 'INPUT_{}'.format(featureName(match.group(2))) elif match.group(1) == 'config': - substitution = substitute_platform(match.group(2)) + substitution = map_platform(match.group(2)) elif match.group(1) == 'arch': if match.group(2) == 'i386': diff --git a/util/cmake/helper.py b/util/cmake/helper.py index b58c73dae8..0a78230703 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -67,7 +67,7 @@ _qt_library_map = [ LibraryMapping('core', 'Qt5', 'Qt::Core', extra = ['COMPONENTS', 'Core']), LibraryMapping('coretest', 'Qt5', 'Qt::3DCoreTest', extra = ['COMPONENTS', '3DCoreTest']), LibraryMapping('crypto-lib', 'Qt5', 'Qt::AppManCrypto', extra = ['COMPONENTS', 'AppManCrypto']), - LibraryMapping('dbus', 'Qt5', 'Qt::Dbus', extra = ['COMPONENTS', 'DBus']), + LibraryMapping('dbus', 'Qt5', 'Qt::DBus', extra = ['COMPONENTS', 'DBus']), LibraryMapping('devicediscovery', 'Qt5', 'Qt::DeviceDiscoverySupport', extra = ['COMPONENTS', 'DeviceDiscoverySupport']), LibraryMapping('devicediscovery_support', 'Qt5', 'Qt::DeviceDiscoverySupport', extra = ['COMPONENTS', 'DeviceDiscoverySupport']), LibraryMapping('edid', 'Qt5', 'Qt::EdidSupport', extra = ['COMPONENTS', 'EdidSupport']), @@ -189,10 +189,11 @@ _library_map = [ 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('libdl', None, None), + LibraryMapping('libdl', None, '${CMAKE_DL_LIBS}'), LibraryMapping('libinput', 'Libinput', 'Libinput::Libinput'), - LibraryMapping('libjpeg', 'JPEG', 'JPEG::JPEG'), + LibraryMapping('libjpeg', 'JPEG', 'JPEG::JPEG'), # see also jpeg LibraryMapping('libpng', 'PNG', 'PNG::PNG'), LibraryMapping('libproxy', 'Libproxy', 'PkgConfig::Libproxy'), LibraryMapping('librt', 'WrapRt','WrapRt'), @@ -246,7 +247,7 @@ _library_map = [ ] -def find_library_mapping(soName: str) -> typing.Optional[LibraryMapping]: +def find_3rd_party_library_mapping(soName: str) -> typing.Optional[LibraryMapping]: for i in _library_map: if i.soName == soName: return i @@ -272,6 +273,7 @@ def map_qt_library(lib: str) -> str: mapped = find_qt_library_mapping(lib) qt_name = lib if mapped: + assert mapped.targetName # Qt libs must have a target name set qt_name = mapped.targetName if private: qt_name += 'Private' @@ -314,19 +316,25 @@ platform_mapping = { } -def substitute_platform(platform: str) -> str: +def map_platform(platform: str) -> str: """ Return the qmake platform as cmake platform or the unchanged string. """ return platform_mapping.get(platform, platform) -def substitute_libs(lib: str) -> str: +def is_known_3rd_party_library(lib: str) -> bool: + if lib.endswith('/nolink') or lib.endswith('_nolink'): + lib = lib[:-7] + mapping = find_3rd_party_library_mapping(lib) + + return mapping is not None and mapping.targetName is not None + + +def map_3rd_party_library(lib: str) -> str: libpostfix = '' if lib.endswith('/nolink'): lib = lib[:-7] libpostfix = '_nolink' - mapping = find_qt_library_mapping(lib) - if not mapping: - mapping = find_library_mapping(lib) - if not mapping: - return lib + libpostfix + mapping = find_3rd_party_library_mapping(lib) + if not mapping or not mapping.targetName: + return lib return mapping.targetName + libpostfix diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 493517dbc7..23ba9d7fc8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -42,8 +42,8 @@ import typing from sympy.logic import (simplify_logic, And, Or, Not,) import pyparsing as pp -from helper import map_qt_library, featureName, \ - substitute_platform, substitute_libs +from helper import map_qt_library, map_3rd_party_library, is_known_3rd_party_library, \ + featureName, map_platform def _parse_commandline(): @@ -810,15 +810,14 @@ def map_condition(condition: str) -> str: part = 'TARGET {}'.format(map_qt_library(feature.group(2))) else: feature = featureName(feature.group(2)) - if feature.startswith('system_') and substitute_libs(feature[7:]) != feature[7:]: - # Qt6 always uses system libraries! + if feature.startswith('system_') and is_known_3rd_party_library(feature[7:]): part = 'ON' elif feature == 'dlopen': part = 'ON' else: part = 'QT_FEATURE_' + feature else: - part = substitute_platform(part) + part = map_platform(part) part = part.replace('true', 'ON') part = part.replace('false', 'OFF') @@ -939,7 +938,9 @@ def write_library_list(cm_fh: typing.IO[str], cmake_keyword: str, if d.startswith('-'): d = '# Remove: {}'.format(d[1:]) else: - d = substitute_libs(d) + d = map_3rd_party_library(d) + if not d or d in dependencies_to_print: + continue dependencies_to_print.append(d) is_framework = False @@ -954,18 +955,24 @@ def write_library_list(cm_fh: typing.IO[str], cmake_keyword: str, def write_library_section(cm_fh: typing.IO[str], scope: Scope, public: typing.List[str], private: typing.List[str], - mixed: typing.List[str], *, + qt_private: typing.List[str], + qt_mixed: typing.List[str], *, indent: int = 0, known_libraries=set()): - public_dependencies = [] # typing.List[str] - private_dependencies = [] # typing.List[str] + public_dependencies = [] # type: typing.List[str] + private_dependencies = [] # type: typing.List[str] for key in public: - public_dependencies += [map_qt_library(q) for q in scope.expand(key) - if map_qt_library(q) not in known_libraries] + public_dependencies += [q for q in scope.expand(key) + if q not in known_libraries] for key in private: + private_dependencies += [q for q in scope.expand(key) + if q not in known_libraries] + + for key in qt_private: private_dependencies += [map_qt_library(q) for q in scope.expand(key) if map_qt_library(q) not in known_libraries] - for key in mixed: + + for key in qt_mixed: for lib in scope.expand(key): mapped_lib = map_qt_library(lib) if mapped_lib in known_libraries: @@ -1028,7 +1035,8 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, write_library_section(cm_fh, scope, ['QMAKE_USE', 'LIBS'], - ['QT_FOR_PRIVATE', 'QMAKE_USE_PRIVATE', 'QMAKE_USE_FOR_PRIVATE', 'LIBS_PRIVATE'], + ['QMAKE_USE_PRIVATE', 'QMAKE_USE_FOR_PRIVATE', 'LIBS_PRIVATE'], + ['QT_FOR_PRIVATE',], ['QT',], indent=indent, known_libraries=known_libraries) -- cgit v1.2.3 From 9ffcc761887f0088ae6226098723818843f0d287 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 6 May 2019 15:49:37 +0200 Subject: CMake: Scripts: Fix double entries in 3rd party library mapping Change-Id: I35f29876874d6083d19382800d194e417d57bca1 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 0a78230703..d47c65ffa6 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -219,8 +219,6 @@ _library_map = [ LibraryMapping('vulkan', 'Vulkan', 'Vulkan::Vulkan'), LibraryMapping('wayland_server', 'Wayland', 'Wayland::Server'), LibraryMapping('x11sm', 'X11', '${X11_SM_LIB} ${X11_ICE_LIB}', resultVariable="X11_SM"), - LibraryMapping('xcb_glx', 'XCB', 'XCB::GLX', resultVariable='XCB_GLX'), - LibraryMapping('xcb_render', 'XCB', 'XCB::RENDER', resultVariable='XCB_RENDER'), LibraryMapping('xcb', 'XCB', 'XCB::XCB', extra = ['1.9']), LibraryMapping('xcb_glx', 'XCB', 'XCB::GLX', extra = ['COMPONENTS', 'GLX'], resultVariable='XCB_GLX'), LibraryMapping('xcb_icccm', 'XCB', 'XCB::ICCCM', extra = ['COMPONENTS', 'ICCCM'], resultVariable='XCB_ICCCM'), -- cgit v1.2.3 From d5018720db03224885244ab4af42da29bba45aa4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 6 May 2019 16:44:31 +0200 Subject: CMake: scripts: Treat libraries mapped to None as known This detects doubleconversion as a 3rd party library. This fixes defaulting QT_FEATURE_system_doubleconversion to 'ON'. Change-Id: I9d18dbbb6f7a99f6a5c674bed3013b96f19bf6e0 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index d47c65ffa6..f688d0e477 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -324,7 +324,7 @@ def is_known_3rd_party_library(lib: str) -> bool: lib = lib[:-7] mapping = find_3rd_party_library_mapping(lib) - return mapping is not None and mapping.targetName is not None + return mapping is not None def map_3rd_party_library(lib: str) -> str: -- cgit v1.2.3 From 5ec3baa67a4d12b63ae11752f498d777e3ea229e Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 7 May 2019 16:37:33 +0200 Subject: CMake: scripts: Make xcb_qpa_lib known as a Qt module Change-Id: I65f48d86c4ec946b38004b945078f29625e32d93 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index f688d0e477..1833df0bd3 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -164,6 +164,7 @@ _qt_library_map = [ LibraryMapping('windowsuiautomation_support', 'Qt5', 'Qt::WindowsUIAutomationSupport', extra = ['COMPONENTS', 'WindowsUIAutomationSupport']), LibraryMapping('winextras', 'Qt5', 'Qt::WinExtras', extra = ['COMPONENTS', 'WinExtras']), LibraryMapping('x11extras', 'Qt5', 'Qt::X11Extras', extra = ['COMPONENTS', 'X11Extras']), + LibraryMapping('xcb_qpa_lib', 'Qt5', 'Qt::XcbQpa', extra = ['COMPONENTS', 'XcbQpa']), LibraryMapping('xkbcommon_support', 'Qt5', 'Qt::XkbCommonSupport', extra = ['COMPONENTS', 'XkbCommonSupport']), LibraryMapping('xmlpatterns', 'Qt5', 'Qt::XmlPatterns', extra = ['COMPONENTS', 'XmlPatterns']), LibraryMapping('xml', 'Qt5', 'Qt::Xml', extra = ['COMPONENTS', 'Xml']), -- cgit v1.2.3 From aed2c1f5ae002df7cc92bd4ceaa65c173757c40a Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 7 May 2019 16:46:28 +0200 Subject: Force pro2cmake.py to always change the dir to the converted pro file Otherwise if you call the script from a different directory, path handling becomes broken and certain files are not found. Change-Id: Ia2f60abbd312a771330b3d5e928e1ccd0b4a845b Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 23ba9d7fc8..a6ebc1094c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1566,8 +1566,14 @@ def main() -> None: debug_parsing = args.debug_parser or args.debug + backup_current_dir = os.getcwd() + for file in args.files: - parseresult = parseProFile(file, debug=debug_parsing) + new_current_dir = os.path.dirname(file) + file_relative_path = os.path.basename(file) + os.chdir(new_current_dir) + + parseresult = parseProFile(file_relative_path, debug=debug_parsing) if args.debug_parse_result or args.debug: print('\n\n#### Parser result:') @@ -1578,7 +1584,7 @@ def main() -> None: print(parseresult.asDict()) print('\n#### End of parser result dictionary.\n') - file_scope = Scope.FromDict(None, file, + file_scope = Scope.FromDict(None, file_relative_path, parseresult.asDict().get('statements')) if args.debug_pro_structure or args.debug: @@ -1594,6 +1600,7 @@ def main() -> None: print('\n#### End of full .pro/.pri file structure.\n') generate_cmakelists(file_scope) + os.chdir(backup_current_dir) if __name__ == '__main__': -- cgit v1.2.3 From 9d96c8da78125531c64916291e03867e752a97af Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 7 May 2019 20:20:39 +0200 Subject: Fix freetype target not being found when using vcpkg vcpkg and upstream CMake find module define different target names for the same package. To circumvent this, create our own Wrap find module, and link against it. Inside the find module, try both target names. Change-Id: Iba488bce0fb410ddb83f6414244f86ad367de72b Reviewed-by: Liang Qi Reviewed-by: Tobias Hunger --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 1833df0bd3..1873b5bfe0 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -181,7 +181,7 @@ _library_map = [ LibraryMapping('drm', 'Libdrm', 'Libdrm::Libdrm'), LibraryMapping('egl', 'EGL', 'EGL::EGL'), LibraryMapping('fontconfig', 'Fontconfig', 'Fontconfig::Fontconfig', resultVariable="FONTCONFIG"), - LibraryMapping('freetype', 'Freetype', 'Freetype::Freetype', extra=['REQUIRED']), + LibraryMapping('freetype', 'WrapFreetype', 'WrapFreetype::WrapFreetype', extra=['REQUIRED']), LibraryMapping('gbm', 'gbm', 'gbm::gbm'), LibraryMapping('glib', 'GLIB2', 'GLIB2::GLIB2'), LibraryMapping('gnu_iconv', None, None), -- cgit v1.2.3 From 0c498ef4ffde8c410ee2d34ef977779e22070122 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 8 May 2019 13:14:37 +0200 Subject: CMake: pro2cmake.py: Do not fail when run from .pro-file directory Change-Id: I285b05986e3a58efc060ca0b5732f6e3f5121476 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index a6ebc1094c..820a95d728 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1571,7 +1571,8 @@ def main() -> None: for file in args.files: new_current_dir = os.path.dirname(file) file_relative_path = os.path.basename(file) - os.chdir(new_current_dir) + if new_current_dir: + os.chdir(new_current_dir) parseresult = parseProFile(file_relative_path, debug=debug_parsing) -- cgit v1.2.3 From 5608bf3cbadefb2c73b23e4b90b8211d034f2221 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 7 May 2019 11:27:33 +0200 Subject: CMake: pro2cmake.py: Add basic support for examples Examples need to be built stand-alone and as part of Qt, so they need a special CMakeLists.txt file that supports both use-cases. Add an --is-example switch to pro2cmake to make it generate these special CMakeLists.txt files. This is basic support only and is currently still missing the necessary find_package calls. Change-Id: Ie770287350fb8a41e872cb0ea607923caa33073d Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 157 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 118 insertions(+), 39 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 820a95d728..556bc59c2b 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -67,6 +67,11 @@ def _parse_commandline(): dest='debug_full_pro_structure', action='store_true', help='Dump the full structure of the qmake .pro-file ' '(with includes).') + + parser.add_argument('--example', action='store_true', + dest="is_example", + help='Treat the input .pro file as an example.') + parser.add_argument('files', metavar='<.pro/.pri file>', type=str, nargs='+', help='The .pro/.pri file to process') @@ -826,7 +831,7 @@ def map_condition(condition: str) -> str: def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, - indent: int = 0) -> None: + indent: int = 0, is_example: bool=False) -> None: ind = ' ' * indent for sd in scope.get_files('SUBDIRS'): if os.path.isdir(sd): @@ -839,7 +844,7 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, '', scope.basedir) do_include(subdir_scope) - cmakeify_scope(subdir_scope, cm_fh, indent=indent) + cmakeify_scope(subdir_scope, cm_fh, indent=indent, is_example=is_example) elif sd.startswith('-'): cm_fh.write('{}### remove_subdirectory' '("{}")\n'.format(ind, sd[1:])) @@ -853,7 +858,7 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, elif cond: cm_fh.write('\n{}if({})\n'.format(ind, cond)) - handle_subdir(c, cm_fh, indent=indent + 1) + handle_subdir(c, cm_fh, indent=indent + 1, is_example=is_example) if cond: cm_fh.write('{}endif()\n'.format(ind)) @@ -914,10 +919,10 @@ def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str, cm_fh.write(header) extra_indent = '' if cmake_parameter: - cm_fh.write('{} {}\n'.format(ind, cmake_parameter)) + cm_fh.write('{}{}\n'.format(ind, cmake_parameter)) extra_indent = ' ' for s in sort_sources(sources): - cm_fh.write('{} {}{}\n'.format(ind, extra_indent, s)) + cm_fh.write('{}{}{}\n'.format(ind, extra_indent, s)) cm_fh.write(footer) @@ -951,6 +956,54 @@ def write_library_list(cm_fh: typing.IO[str], cmake_keyword: str, cm_fh.write('{} {}\n'.format(ind, d)) +def write_all_source_file_lists(cm_fh: typing.IO[str], scope: Scope, header: str, *, + indent: int = 0, footer: str = ''): + write_source_file_list(cm_fh, scope, header, + ['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'], + indent) + + +def write_defines(cm_fh: typing.IO[str], scope: Scope, header: str, *, + indent: int = 0, footer: str = ''): + ind = spaces(indent) + + defines = scope.expand('DEFINES') + defines += [d[2:] for d in scope.expand('QMAKE_CXXFLAGS') if d.startswith('-D')] + if defines: + cm_fh.write('{}{}\n'.format(ind, header)) + for d in defines: + d = d.replace('=\\\\\\"$$PWD/\\\\\\"', + '="${CMAKE_CURRENT_SOURCE_DIR}/"') + cm_fh.write('{} {}\n'.format(ind, d)) + if footer: + cm_fh.write('{}{}\n'.format(ind, footer)) + +def write_include_paths(cm_fh: typing.IO[str], scope: Scope, header: str, *, + indent: int = 0, footer: str = ''): + ind = spaces(indent) + + includes = scope.get_files('INCLUDEPATH') + if includes: + cm_fh.write('{}{}\n'.format(ind, header)) + for i in includes: + i = i.rstrip('/') or ('/') + cm_fh.write('{} {}\n'.format(ind, i)) + if footer: + cm_fh.write('{}{}\n'.format(ind, footer)) + + +def write_compile_options(cm_fh: typing.IO[str], scope: Scope, header: str, *, + indent: int = 0, footer: str = ''): + ind = spaces(indent) + + compile_options = [d for d in scope.expand('QMAKE_CXXFLAGS') if not d.startswith('-D')] + if compile_options: + cm_fh.write('{}{}\n'.format(ind, header)) + for co in compile_options: + cm_fh.write('{} "{}"\n'.format(ind, co)) + if footer: + cm_fh.write('{}{}\n'.format(ind, footer)) + def write_library_section(cm_fh: typing.IO[str], scope: Scope, public: typing.List[str], @@ -1001,37 +1054,23 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, if plugin_type: cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type)) - source_keys: typing.List[str] = [] - write_source_file_list(cm_fh, scope, 'SOURCES', - ['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'], - indent) + 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) + 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))) - write_source_file_list(cm_fh, scope, 'DBUS_INTERFACE_SOURCES', ['DBUS_INTERFACES',], indent) + 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))) - defines = scope.expand('DEFINES') - defines += [d[2:] for d in scope.expand('QMAKE_CXXFLAGS') if d.startswith('-D')] - if defines: - cm_fh.write('{} DEFINES\n'.format(ind)) - for d in defines: - d = d.replace('=\\\\\\"$$PWD/\\\\\\"', - '="${CMAKE_CURRENT_SOURCE_DIR}/"') - cm_fh.write('{} {}\n'.format(ind, d)) - includes = scope.get_files('INCLUDEPATH') - if includes: - cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind)) - for i in includes: - i = i.rstrip('/') or ('/') - cm_fh.write('{} {}\n'.format(ind, i)) + write_defines(cm_fh, scope, 'DEFINES', indent=indent + 1) + + write_include_paths(cm_fh, scope, 'INCLUDE_DIRECTORIES', indent=indent + 1) write_library_section(cm_fh, scope, ['QMAKE_USE', 'LIBS'], @@ -1040,11 +1079,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, ['QT',], indent=indent, known_libraries=known_libraries) - compile_options = [d for d in scope.expand('QMAKE_CXXFLAGS') if not d.startswith('-D')] - if compile_options: - cm_fh.write('{} COMPILE_OPTIONS\n'.format(ind)) - for co in compile_options: - cm_fh.write('{} "{}"\n'.format(ind, co)) + write_compile_options(cm_fh, scope, 'COMPILE_OPTIONS', indent=indent + 1) link_options = scope.get('QMAKE_LFLAGS') if link_options: @@ -1482,6 +1517,43 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope, known_libraries={'Qt::Core', }, extra_keys=['target.path', 'INSTALLS']) +def write_example(cm_fh: typing.IO[str], scope: Scope, + gui: bool = False, *, indent: int = 0) -> None: + binary_name = scope.TARGET + assert binary_name + +#find_package(Qt5 COMPONENTS Widgets REQUIRED) +#target_link_libraries(mimetypebrowser Qt::Widgets) + + cm_fh.write('cmake_minimum_required(VERSION 3.14)\n' + + 'project(mimetypebrowser 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') + + add_executable = 'add_executable({}'.format(binary_name); + if gui: + add_executable += ' WIN32_EXECUTABLE MACOSX_BUNDLE' + + write_all_source_file_lists(cm_fh, scope, add_executable, indent=0) + + cm_fh.write(')\n') + + write_include_paths(cm_fh, scope, 'target_include_directories({}'.format(binary_name), + indent=0, footer=')') + write_defines(cm_fh, scope, 'target_compile_definitions({}'.format(binary_name), + indent=0, footer=')') + write_compile_options(cm_fh, scope, 'target_compile_options({}'.format(binary_name), + indent=0, footer=')') + + cm_fh.write('\ninstall(TARGETS mimetypebrowser\n' + + ' RUNTIME_DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + + ' BUNDLE_DESTINATION "${INSTALL_EXAMPLESDIR}"\n' + + ')\n') + + def write_plugin(cm_fh, scope, *, indent: int = 0): plugin_name = scope.TARGET assert plugin_name @@ -1491,52 +1563,59 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, - indent: int = 0) -> None: + indent: int = 0, is_example: bool=False) -> None: assert scope.TEMPLATE in ('app', 'lib') is_lib = scope.TEMPLATE == 'lib' is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED')) if is_lib or 'qt_module' in scope.get('_LOADED'): + assert not is_example write_module(cm_fh, scope, indent=indent) elif is_plugin: + assert not is_example write_plugin(cm_fh, scope, indent=indent) elif 'qt_tool' in scope.get('_LOADED'): + assert not is_example write_tool(cm_fh, scope, indent=indent) else: if 'testcase' in scope.get('CONFIG') \ or 'testlib' in scope.get('CONFIG'): + assert not is_example write_test(cm_fh, scope, indent=indent) else: gui = 'console' not in scope.get('CONFIG') - write_binary(cm_fh, scope, gui, indent=indent) + if is_example: + write_example(cm_fh, scope, gui, indent=indent) + else: + write_binary(cm_fh, scope, gui, indent=indent) ind = spaces(indent) write_source_file_list(cm_fh, scope, '', ['QMAKE_DOCS',], - indent, + indent + 1, header = '{}add_qt_docs(\n'.format(ind), footer = '{})\n'.format(ind)) def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, - indent: int = 0) -> None: + indent: int = 0, is_example: bool=False) -> None: template = scope.TEMPLATE if template == 'subdirs': - handle_subdir(scope, cm_fh, indent=indent) + handle_subdir(scope, cm_fh, indent=indent, is_example=is_example) elif template in ('app', 'lib'): - handle_app_or_lib(scope, cm_fh, indent=indent) + handle_app_or_lib(scope, cm_fh, indent=indent, is_example=is_example) else: print(' XXXX: {}: Template type {} not yet supported.' .format(scope.file, template)) -def generate_cmakelists(scope: Scope) -> None: +def generate_cmakelists(scope: Scope, *, is_example: bool=False) -> None: with open(scope.cMakeListsFile, 'w') as cm_fh: assert scope.file cm_fh.write('# Generated from {}.\n\n' .format(os.path.basename(scope.file))) - cmakeify_scope(scope, cm_fh) + cmakeify_scope(scope, cm_fh, is_example=is_example) def do_include(scope: Scope, *, debug: bool = False) -> None: @@ -1600,7 +1679,7 @@ def main() -> None: print(file_scope.dump()) print('\n#### End of full .pro/.pri file structure.\n') - generate_cmakelists(file_scope) + generate_cmakelists(file_scope, is_example=args.is_example) os.chdir(backup_current_dir) -- cgit v1.2.3 From ee3d9a8b67183f7ccaa7267e49d30bf9165f6168 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 8 May 2019 16:45:25 +0200 Subject: CMake: scripts: Fix mypy issues Change-Id: I706740be79eccd6bf08213fdaf747dde08cd053a Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 7 ++++--- util/cmake/pro2cmake.py | 23 ++++++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 1873b5bfe0..0aac634984 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -30,8 +30,9 @@ import re import typing class LibraryMapping: - def __init__(self, soName: typing.Optional[str], - packageName: str, targetName: str, *, + 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) -> None: @@ -42,7 +43,7 @@ class LibraryMapping: self.extra = extra self.targetName = targetName - def is_qt() -> bool: + def is_qt(self) -> bool: return self.packageName == 'Qt' \ or self.packageName == 'Qt5' \ or self.packageName == 'Qt6' diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 556bc59c2b..6388afd9f0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -332,7 +332,7 @@ class Scope(object): @property def scope_debug(self) -> bool: merge = self.get_string('PRO2CMAKE_SCOPE_DEBUG').lower() - return merge and (merge == '1' or merge == 'on' or merge == 'yes' or merge == 'true') + return merge == '1' or merge == 'on' or merge == 'yes' or merge == 'true' @property def parent(self) -> typing.Optional[Scope]: @@ -557,7 +557,7 @@ class Scope(object): def _map_files(self, files: typing.List[str], *, use_vpath: bool = True, is_include: bool = False) -> typing.List[str]: - expanded_files = [] # typing.List[str] + expanded_files = [] # type: typing.List[str] for f in files: r = self._expand_value(f) expanded_files += r @@ -814,13 +814,13 @@ def map_condition(condition: str) -> str: if (feature.group(1) == "qtHaveModule"): part = 'TARGET {}'.format(map_qt_library(feature.group(2))) else: - feature = featureName(feature.group(2)) - if feature.startswith('system_') and is_known_3rd_party_library(feature[7:]): + 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' else: - part = 'QT_FEATURE_' + feature + part = 'QT_FEATURE_' + feature_name else: part = map_platform(part) @@ -928,7 +928,7 @@ def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str, def write_library_list(cm_fh: typing.IO[str], cmake_keyword: str, dependencies: typing.List[str], *, indent: int = 0): - dependencies_to_print = [] + dependencies_to_print = [] # type: typing.List[str] is_framework = False for d in dependencies: @@ -1301,11 +1301,11 @@ def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', return current_condition -def map_to_cmake_condition(condition: str) -> str: +def map_to_cmake_condition(condition: typing.Optional[str]) -> str: condition = re.sub(r'\bQT_ARCH___equals___([a-zA-Z_0-9]*)', - r'(TEST_architecture_arch STREQUAL "\1")', condition) + 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) + r'(TEST_architecture_arch STREQUAL "\1")', condition or '') return condition @@ -1366,6 +1366,7 @@ def merge_scopes(scopes: typing.List[Scope]) -> typing.List[Scope]: known_scopes = {} # type: typing.Mapping[str, Scope] for scope in scopes: total_condition = scope.total_condition + assert total_condition if total_condition == 'OFF': # ignore this scope entirely! pass @@ -1669,14 +1670,14 @@ def main() -> None: if args.debug_pro_structure or args.debug: print('\n\n#### .pro/.pri file structure:') - print(file_scope.dump()) + file_scope.dump() 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(file_scope.dump()) + file_scope.dump() print('\n#### End of full .pro/.pri file structure.\n') generate_cmakelists(file_scope, is_example=args.is_example) -- cgit v1.2.3 From c4dd1a8fe3f412a1045e43d2e507351fc2d18fc4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 8 May 2019 17:00:48 +0200 Subject: CMake: pro2cmake.py: Extract writing lists into cmake files Extract the actual functionality to write a list of "things" below headers and/or cmake parameter and followed by a footer. Reuse this functionality everywhere we write a list of things. Change-Id: Ia7647be465b4788a2b1e6a5dbede1ca868f24ae2 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 108 +++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 61 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6388afd9f0..0a9f32267d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -903,27 +903,37 @@ def write_scope_header(cm_fh: typing.IO[str], *, indent: int = 0): '##########################\n'.format(spaces(indent))) +def write_list(cm_fh: typing.IO[str], entries: typing.List[str], + cmake_parameter: str, + indent: int = 0, *, + header: str = '', footer: str = ''): + if not entries: + return + + ind = spaces(indent) + extra_indent = '' + + if header: + cm_fh.write('{}{}'.format(ind, header)) + extra_indent += ' ' + if cmake_parameter: + cm_fh.write('{}{}{}\n'.format(ind, extra_indent, cmake_parameter)) + extra_indent += ' ' + for s in sort_sources(entries): + cm_fh.write('{}{}{}\n'.format(ind, extra_indent, s)) + if footer: + cm_fh.write('{}{}'.format(ind, footer)) + + 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 = ''): - ind = spaces(indent) - # collect sources sources: typing.List[str] = [] for key in keys: sources += scope.get_files(key, use_vpath=True) - if not sources: - return - - cm_fh.write(header) - extra_indent = '' - if cmake_parameter: - cm_fh.write('{}{}\n'.format(ind, cmake_parameter)) - extra_indent = ' ' - for s in sort_sources(sources): - cm_fh.write('{}{}{}\n'.format(ind, extra_indent, s)) - cm_fh.write(footer) + write_list(cm_fh, sources, cmake_parameter, indent, header=header, footer=footer) def write_library_list(cm_fh: typing.IO[str], cmake_keyword: str, @@ -949,11 +959,7 @@ def write_library_list(cm_fh: typing.IO[str], cmake_keyword: str, dependencies_to_print.append(d) is_framework = False - if dependencies_to_print: - ind = spaces(indent) - cm_fh.write('{} {}\n'.format(ind, cmake_keyword)) - for d in sorted(list(set(dependencies_to_print))): - cm_fh.write('{} {}\n'.format(ind, d)) + write_list(cm_fh, dependencies_to_print, cmake_keyword, indent + 1) def write_all_source_file_lists(cm_fh: typing.IO[str], scope: Scope, header: str, *, @@ -963,46 +969,28 @@ def write_all_source_file_lists(cm_fh: typing.IO[str], scope: Scope, header: str indent) -def write_defines(cm_fh: typing.IO[str], scope: Scope, header: str, *, +def write_defines(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: str, *, indent: int = 0, footer: str = ''): - ind = spaces(indent) - defines = scope.expand('DEFINES') defines += [d[2:] for d in scope.expand('QMAKE_CXXFLAGS') if d.startswith('-D')] - if defines: - cm_fh.write('{}{}\n'.format(ind, header)) - for d in defines: - d = d.replace('=\\\\\\"$$PWD/\\\\\\"', - '="${CMAKE_CURRENT_SOURCE_DIR}/"') - cm_fh.write('{} {}\n'.format(ind, d)) - if footer: - cm_fh.write('{}{}\n'.format(ind, footer)) - -def write_include_paths(cm_fh: typing.IO[str], scope: Scope, header: str, *, + defines = [d.replace('=\\\\\\"$$PWD/\\\\\\"', + '="${CMAKE_CURRENT_SOURCE_DIR}/"') for d in defines] + + write_list(cm_fh, defines, cmake_parameter, indent) + + +def write_include_paths(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: str, *, indent: int = 0, footer: str = ''): - ind = spaces(indent) + includes = [i.rstrip('/') or ('/') for i in scope.get_files('INCLUDEPATH')] - includes = scope.get_files('INCLUDEPATH') - if includes: - cm_fh.write('{}{}\n'.format(ind, header)) - for i in includes: - i = i.rstrip('/') or ('/') - cm_fh.write('{} {}\n'.format(ind, i)) - if footer: - cm_fh.write('{}{}\n'.format(ind, footer)) + write_list(cm_fh, includes, cmake_parameter, indent) -def write_compile_options(cm_fh: typing.IO[str], scope: Scope, header: str, *, +def write_compile_options(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: str, *, indent: int = 0, footer: str = ''): - ind = spaces(indent) - compile_options = [d for d in scope.expand('QMAKE_CXXFLAGS') if not d.startswith('-D')] - if compile_options: - cm_fh.write('{}{}\n'.format(ind, header)) - for co in compile_options: - cm_fh.write('{} "{}"\n'.format(ind, co)) - if footer: - cm_fh.write('{}{}\n'.format(ind, footer)) + + write_list(cm_fh, compile_options, cmake_parameter, indent) def write_library_section(cm_fh: typing.IO[str], scope: Scope, @@ -1386,18 +1374,16 @@ def write_simd_part(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in 'avx512vl', 'avx512ifma', 'avx512vbmi', 'f16c', 'rdrnd', 'neon', 'mips_dsp', 'mips_dspr2', 'arch_haswell', 'avx512common', 'avx512core']; - ind = spaces(indent) - 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(ind, target, simd), - footer = '{})\n\n'.format(ind)) + ['{}_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') def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, @@ -1594,9 +1580,9 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, ind = spaces(indent) write_source_file_list(cm_fh, scope, '', ['QMAKE_DOCS',], - indent + 1, - header = '{}add_qt_docs(\n'.format(ind), - footer = '{})\n'.format(ind)) + indent, + header = 'add_qt_docs(\n', + footer = ')\n') def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, -- cgit v1.2.3 From 80e0c615a9b8c7498ecdfda1de5b98410ecfd0e7 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 9 May 2019 10:02:37 +0200 Subject: CMake: pro2cmake.py: Separate library extraction logic from writing out data Separate the logic to find all used libraries from the code that writes out the link_library information into the CMakeLists(.gen)?.txt files. This patch will remove some "PUBLIC_LIBRARIES Qt::Core" from generated files. This is due to us handling some Qt libraries in special ways in some of our add_qt_* helpers. These special libraries were added to the LIBRARIES section, but actually they should be added to the PUBLIC_LIBRARIES section instead. Do so now, so that the newly generated files do not break things again. Change-Id: I588781087a8aecc4d879e949735671d8085f0698 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 129 ++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 65 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0a9f32267d..47d1bbc439 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -888,6 +888,63 @@ def sort_sources(sources: typing.List[str]) -> typing.List[str]: return lines +def _map_libraries_to_cmake(libraries: typing.List[str], + known_libraries: typing.Set[str]) -> typing.List[str]: + result = [] # type: typing.List[str] + is_framework = False + + for l in libraries: + if l == '-framework': + is_framework = True + continue + if is_framework: + l = '${FW%s}' % l + if l.startswith('-l'): + l = l[2:] + + if l.startswith('-'): + l = '# Remove: {}'.format(l[1:]) + else: + l = map_3rd_party_library(l) + + if not l or l in result or l in known_libraries: + continue + + result.append(l) + 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] + + for key in ['QMAKE_USE', 'LIBS',]: + public_dependencies += scope.expand(key) + for key in ['QMAKE_USE_PRIVATE', 'QMAKE_USE_FOR_PRIVATE', 'LIBS_PRIVATE',]: + private_dependencies += scope.expand(key) + + for key in ['QT_FOR_PRIVATE',]: + private_dependencies += [map_qt_library(q) for q in scope.expand(key)] + + 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'): + 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)) + + def write_header(cm_fh: typing.IO[str], name: str, typename: str, *, indent: int = 0): cm_fh.write('{}###########################################' @@ -936,32 +993,6 @@ def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str, write_list(cm_fh, sources, cmake_parameter, indent, header=header, footer=footer) -def write_library_list(cm_fh: typing.IO[str], cmake_keyword: str, - dependencies: typing.List[str], *, indent: int = 0): - dependencies_to_print = [] # type: typing.List[str] - is_framework = False - - for d in dependencies: - if d == '-framework': - is_framework = True - continue - if is_framework: - d = '${FW%s}' % d - if d.startswith('-l'): - d = d[2:] - - if d.startswith('-'): - d = '# Remove: {}'.format(d[1:]) - else: - d = map_3rd_party_library(d) - if not d or d in dependencies_to_print: - continue - dependencies_to_print.append(d) - is_framework = False - - write_list(cm_fh, dependencies_to_print, cmake_keyword, indent + 1) - - def write_all_source_file_lists(cm_fh: typing.IO[str], scope: Scope, header: str, *, indent: int = 0, footer: str = ''): write_source_file_list(cm_fh, scope, header, @@ -993,40 +1024,13 @@ def write_compile_options(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: write_list(cm_fh, compile_options, cmake_parameter, indent) -def write_library_section(cm_fh: typing.IO[str], scope: Scope, - public: typing.List[str], - private: typing.List[str], - qt_private: typing.List[str], - qt_mixed: typing.List[str], *, - indent: int = 0, known_libraries=set()): - public_dependencies = [] # type: typing.List[str] - private_dependencies = [] # type: typing.List[str] - - for key in public: - public_dependencies += [q for q in scope.expand(key) - if q not in known_libraries] - for key in private: - private_dependencies += [q for q in scope.expand(key) - if q not in known_libraries] - - for key in qt_private: - private_dependencies += [map_qt_library(q) for q in scope.expand(key) - if map_qt_library(q) not in known_libraries] - - for key in qt_mixed: - for lib in scope.expand(key): - mapped_lib = map_qt_library(lib) - if mapped_lib in known_libraries: - continue - - if mapped_lib.endswith('Private'): - private_dependencies.append(mapped_lib) - public_dependencies.append(mapped_lib[:-7]) - else: - public_dependencies.append(mapped_lib) +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) - write_library_list(cm_fh, 'LIBRARIES', private_dependencies, indent=indent) - write_library_list(cm_fh, 'PUBLIC_LIBRARIES', public_dependencies, indent=indent) + write_list(cm_fh, private_dependencies, 'LIBRARIES', indent + 1) + write_list(cm_fh, public_dependencies, 'PUBLIC_LIBRARIES', indent + 1) @@ -1060,12 +1064,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, write_include_paths(cm_fh, scope, 'INCLUDE_DIRECTORIES', indent=indent + 1) - write_library_section(cm_fh, scope, - ['QMAKE_USE', 'LIBS'], - ['QMAKE_USE_PRIVATE', 'QMAKE_USE_FOR_PRIVATE', 'LIBS_PRIVATE'], - ['QT_FOR_PRIVATE',], - ['QT',], - indent=indent, known_libraries=known_libraries) + write_library_section(cm_fh, scope, indent=indent, known_libraries=known_libraries) write_compile_options(cm_fh, scope, 'COMPILE_OPTIONS', indent=indent + 1) -- cgit v1.2.3 From 862ebbf7ea28b36521d46a5e466cc1310de800c7 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 9 May 2019 10:51:27 +0200 Subject: CMake: scripts: Extract code to write find_package lines Extract code to write find_package lines from configurejson2cmake.py and move this over into helper.py. Change-Id: Iefd313b2a56cb78a99a7f3151c3f6c6284482f79 Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 28 +++------------------------- util/cmake/helper.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 25 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 8b9251f8a7..040e8ee7b4 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -33,7 +33,8 @@ import re import sys from typing import Set, Union, List, Dict -from helper import map_qt_library, featureName, map_platform, find_3rd_party_library_mapping +from helper import map_qt_library, featureName, map_platform, \ + find_3rd_party_library_mapping, generate_find_package_info knownTests = set() # type: Set[str] @@ -182,31 +183,8 @@ def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): cmake_find_packages_set.add(newlib.targetName) - isRequired = False + cm_fh.write(generate_find_package_info(newlib)) - extra = newlib.extra.copy() - - if extra: - if "REQUIRED" in extra: - isRequired = True - extra.remove("REQUIRED") - - cmake_target_name = newlib.targetName - - # _nolink or not does not matter at this point: - if cmake_target_name.endswith('_nolink') or cmake_target_name.endswith('/nolink'): - cmake_target_name = cmake_target_name[:-7] - - if cmake_target_name: - extra += ['PROVIDED_TARGETS', cmake_target_name] - - if extra: - cm_fh.write('qt_find_package({} {})\n'.format(newlib.packageName, ' '.join(extra))) - else: - cm_fh.write('qt_find_package({})\n'.format(newlib.packageName)) - - if isRequired: - cm_fh.write('set_package_properties({} PROPERTIES TYPE REQUIRED)\n'.format(newlib.packageName)) def lineify(label, value, quote=True): if value: diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 0aac634984..17e25ca0b0 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -338,3 +338,42 @@ def map_3rd_party_library(lib: str) -> str: 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) -> str: + isRequired = False + + extra = lib.extra.copy() + + if "REQUIRED" in extra and use_qt_find_package: + isRequired = True + extra.remove("REQUIRED") + + cmake_target_name = lib.targetName + + # _nolink or not does not matter at this point: + 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] + + result = '' + ind = ' ' * indent + + if use_qt_find_package: + if extra: + result = '{}qt_find_package({} {})\n'.format(ind, lib.packageName, ' '.join(extra)) + else: + result = '{}qt_find_package({})\n'.format(ind, lib.packageName) + + if isRequired: + 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)) + else: + result = '{}find_package({})\n'.format(ind, lib.packageName) + + return result -- cgit v1.2.3 From bc4687f5bbf5c2662a0de1e7c033105969a8451f Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sat, 4 May 2019 13:08:19 +0200 Subject: Keep "special case" modifications when regenerating project files The pro2cmake.py file is now smarter, and can reapply "special case" modifications after regenerating a CMakeLists.txt file. This substantially lowers the maintenance burden when regenerating many files at once. See the special_case_helper.py file for details on how it works. Make sure to commit the generated .prev_CMakeLists.txt file alongside your CMakeLists.txt changes. To disable the preservation behavior, you can pass -s or --skip-special-case-preservation to the script. To keep around temporary files that are created during this process, you can pass -k or --keep-temporary-files. To get more debug output, pass --debug-special-case-preservation. Fixes: QTBUG-75619 Change-Id: I6d8ba52ac5feb5020f31d47841203104c2a061d8 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 51 +++++- util/cmake/special_case_helper.py | 347 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 394 insertions(+), 4 deletions(-) create mode 100644 util/cmake/special_case_helper.py (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 47d1bbc439..4701b096c1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -45,6 +45,9 @@ import pyparsing as pp from helper import map_qt_library, map_3rd_party_library, is_known_3rd_party_library, \ featureName, map_platform +from shutil import copyfile +from special_case_helper import SpecialCaseHandler + def _parse_commandline(): parser = ArgumentParser(description='Generate CMakeLists.txt files from .' @@ -67,10 +70,21 @@ def _parse_commandline(): 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('--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') @@ -448,7 +462,12 @@ class Scope(object): return self._file or '' @property - def cMakeListsFile(self) -> str: + def generated_cmake_lists_path(self) -> str: + assert self.basedir + 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') @@ -1596,8 +1615,9 @@ def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, .format(scope.file, template)) -def generate_cmakelists(scope: Scope, *, is_example: bool=False) -> None: - with open(scope.cMakeListsFile, '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))) @@ -1626,6 +1646,14 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: scope.merge(include_scope) +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)) + copyfile(scope.generated_cmake_lists_path, scope.original_cmake_lists_path) + if not keep_temporary_files: + os.remove(scope.generated_cmake_lists_path) + + def main() -> None: args = _parse_commandline() @@ -1665,7 +1693,22 @@ def main() -> None: file_scope.dump() print('\n#### End of full .pro/.pri file structure.\n') - generate_cmakelists(file_scope, is_example=args.is_example) + 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) + + 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) os.chdir(backup_current_dir) diff --git a/util/cmake/special_case_helper.py b/util/cmake/special_case_helper.py new file mode 100644 index 0000000000..8777b9c4db --- /dev/null +++ b/util/cmake/special_case_helper.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +""" +This is a helper script that takes care of reapplying special case +modifications when regenerating a CMakeLists.txt file using +pro2cmake.py. + +It has two modes of operation: +1) Dumb "special case" block removal and re-application. +2) Smart "special case" diff application, using a previously generated + "clean" CMakeLists.txt as a source. "clean" in this case means a + generated file which has no "special case" modifications. + +Both modes use a temporary git repository to compute and reapply +"special case" diffs. + +For the first mode to work, the developer has to mark changes +with "# special case" markers on every line they want to keep. Or +enclose blocks of code they want to keep between "# special case begin" +and "# special case end" markers. + +For example: + +SOURCES + foo.cpp + bar.cpp # special case + +SOURCES + foo1.cpp + foo2.cpp + # special case begin + foo3.cpp + foo4.cpp + # special case end + +The second mode, as mentioned, requires a previous "clean" +CMakeLists.txt file. + +The script can then compute the exact diff between +a "clean" and "modified" (with special cases) file, and reapply that +diff to a newly generated "CMakeLists.txt" file. + +This implies that we always have to keep a "clean" file alongside the +"modified" project file for each project (corelib, gui, etc.) So we +have to commit both files to the repository. + +If there is no such "clean" file, we can use the first operation mode +to generate one. After that, we only have to use the second operation +mode for the project file in question. + +When the script is used, the developer only has to take care of fixing +the newly generated "modified" file. The "clean" file is automatically +handled and git add'ed by the script, and will be committed together +with the "modified" file. + + +""" + +import re +import os +import subprocess +import filecmp + +from shutil import copyfile +from shutil import rmtree + + +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) + + # Remove individual lines that have the "# special case" marker. + 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: + 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: + file_fd.write(content) + + +def resolve_simple_git_conflicts(file_path: str, debug=False) -> None: + content = read_content_from_file(file_path) + # 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) + write_content_to_file(file_path, replaced) + + +def copyfile_log(src: str, dst: str, debug=False): + if debug: + print('Copying {} to {}.'.format(src, dst)) + copyfile(src, dst) + + +def check_if_git_in_path() -> bool: + for path in os.environ['PATH'].split(os.pathsep): + git_path = os.path.join(path, 'git') + if os.path.isfile(git_path) and os.access(git_path, os.X_OK): + return True + return False + + +def run_process_quiet(args_string: str, debug=False) -> None: + if debug: + print('Running command: "{}\"'.format(args_string)) + args_list = args_string.split() + subprocess.run(args_list, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + +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)) + 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)) + return True + return 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 + new file. + """ + content_actual = read_content_from_file(original_file_path) + if debug: + print('Removing special case blocks from {}.'.format(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)) + write_content_to_file(no_special_cases_file_path, content_no_special_cases) + + +class SpecialCaseHandler(object): + + 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 + self.keep_temporary_files = keep_temporary_files + self.use_heuristic = False + self.debug = debug + + @property + def prev_file_path(self) -> str: + 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') + + @property + def no_special_file_path(self) -> str: + 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_absolute_path = os.path.abspath(repo) + 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)) + raise e + + generated_file_path = os.path.join("..", self.generated_file_path) + original_file_path = os.path.join("..", self.original_file_path) + no_special_cases_file_path = os.path.join("..", no_special_cases_file_path) + post_merge_file_path = os.path.join("..", self.post_merge_file_path) + + try: + # Create new repo with the "clean" CMakeLists.txt file. + run_process_quiet('git init .', 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) + + # Copy the original "modified" file (with the special cases) + # and make a new commit. + 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) + + # Checkout the commit with "clean" file again, and create a + # new branch. + run_process_quiet('git checkout HEAD~', 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) + + # Merge the "old" branch with modifications into the "new" + # branch with the newly generated file. + run_process_quiet('git merge master', debug=self.debug) + + # Resolve some simple conflicts (just remove the markers) + # for cases that don't need intervention. + resolve_simple_git_conflicts(txt, debug=self.debug) + + # 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)) + raise e + finally: + # Remove the temporary repo. + try: + if not self.keep_temporary_files: + rmtree(repo_absolute_path) + except Exception as e: + print(e) + + os.chdir(current_dir) + + def save_next_clean_file(self): + files_are_equivalent = filecmp.cmp(self.generated_file_path, self.post_merge_file_path) + + if not files_are_equivalent: + # Before overriding the generated file with the post + # merge result, save the new "clean" file for future + # regenerations. + copyfile_log(self.generated_file_path, self.prev_file_path, debug=self.debug) + run_process_quiet("git add {}".format(self.prev_file_path), debug=self.debug) + + def handle_special_cases_helper(self) -> bool: + """ + Uses git to reapply special case modifications to the "new" + generated CMakeLists.gen.txt file. + + If use_heuristic is True, a new file is created from the + original file, with special cases removed. + + If use_heuristic is False, an existing "clean" file with no + special cases is used from a previous conversion. The "clean" + file is expected to be in the same folder as the original one. + """ + try: + if does_file_have_conflict_markers(self.original_file_path): + return False + + if self.use_heuristic: + 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)) + + self.apply_git_merge_magic(no_special_cases_file_path) + self.save_next_clean_file() + + copyfile_log(self.post_merge_file_path, self.generated_file_path) + 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.') + + except Exception as e: + print('Error occurred while trying to reapply special case modifications: {}'.format(e)) + return False + finally: + if not self.keep_temporary_files and self.use_heuristic: + os.remove(self.no_special_file_path) + + return True + + def handle_special_cases(self) -> bool: + original_file_exists = os.path.isfile(self.original_file_path) + prev_file_exists = os.path.isfile(self.prev_file_path) + self.use_heuristic = not prev_file_exists + + git_available = check_if_git_in_path() + 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.') + + copy_generated_file = True + + if keep_special_cases: + copy_generated_file = self.handle_special_cases_helper() + + return copy_generated_file -- cgit v1.2.3 From 35ed41e547421f6594f76576c5074b1fe18e37d7 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 9 May 2019 10:59:13 +0200 Subject: CMake: pro2cmake.py: Generate find_package information into examples Change-Id: I6dab13ebea4386019f14be5f29a143d194268aac Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 16 ++++++++++++++++ util/cmake/pro2cmake.py | 40 ++++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 10 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 17e25ca0b0..0520e4fdb2 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -261,6 +261,22 @@ def find_qt_library_mapping(soName: str) -> typing.Optional[LibraryMapping]: return None +def find_library_info_for_target(targetName: str) -> typing.Optional[LibraryMapping]: + qt_target = targetName + if targetName.endswith('Private'): + qt_target = qt_target[:-7] + + for i in _qt_library_map: + if i.targetName == qt_target: + return i + + for i in _library_map: + if i.targetName == targetName: + return i + + return None + + def featureName(input: str) -> str: return re.sub(r'[^a-zA-Z0-9_]', '_', input) diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4701b096c1..fec76f286c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -43,7 +43,7 @@ from sympy.logic import (simplify_logic, And, Or, Not,) import pyparsing as pp from helper import map_qt_library, map_3rd_party_library, is_known_3rd_party_library, \ - featureName, map_platform + featureName, map_platform, find_library_info_for_target, generate_find_package_info from shutil import copyfile from special_case_helper import SpecialCaseHandler @@ -74,7 +74,7 @@ def _parse_commandline(): dest='debug_special_case_preservation', action='store_true', help='Show all git commands and file copies.') - parser.add_argument('--example', action='store_true', + 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', @@ -1522,25 +1522,45 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope, known_libraries={'Qt::Core', }, extra_keys=['target.path', 'INSTALLS']) +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] + all_libs = public_libs + private_libs + + for l in all_libs: + info = find_library_info_for_target(l) + if info and info not in packages: + packages.append(info) + + 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') + + def write_example(cm_fh: typing.IO[str], scope: Scope, gui: bool = False, *, indent: int = 0) -> None: binary_name = scope.TARGET assert binary_name -#find_package(Qt5 COMPONENTS Widgets REQUIRED) -#target_link_libraries(mimetypebrowser Qt::Widgets) - cm_fh.write('cmake_minimum_required(VERSION 3.14)\n' + - 'project(mimetypebrowser LANGUAGES CXX)\n\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') + (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(binary_name); if gui: - add_executable += ' WIN32_EXECUTABLE MACOSX_BUNDLE' + add_executable += ' WIN32 MACOSX_BUNDLE' write_all_source_file_lists(cm_fh, scope, add_executable, indent=0) @@ -1553,9 +1573,9 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, write_compile_options(cm_fh, scope, 'target_compile_options({}'.format(binary_name), indent=0, footer=')') - cm_fh.write('\ninstall(TARGETS mimetypebrowser\n' + - ' RUNTIME_DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + - ' BUNDLE_DESTINATION "${INSTALL_EXAMPLESDIR}"\n' + + cm_fh.write('\ninstall(TARGETS {}\n'.format(binary_name) + + ' RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + + ' BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + ')\n') -- cgit v1.2.3 From e9085f4162ec172b9a5a5e3f9148e058fe1787cb Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Thu, 9 May 2019 11:07:02 +0200 Subject: CMake: pro2cmake.py: Add target_link_libraries to examples Change-Id: Ic7054f0c88e228496b7f4855bffa5620ed717f9b Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index fec76f286c..0851181a25 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1570,6 +1570,10 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, indent=0, footer=')') write_defines(cm_fh, scope, 'target_compile_definitions({}'.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=')') -- cgit v1.2.3 From 400f94109dfb0b8fc28ee1e852b59891d3084624 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 8 May 2019 14:33:58 +0200 Subject: CMake: Wrap DBus1 find_package call to fix xproto not being picked up DBus1 (1.12) configuration file breaks PKG_CONFIG environment variables and will thus prevent other libraries to be picked up by pkgconfig. Main sympthom is that xproto is not getting picked up anymore, which results in hundreds of lines of warnings about this being printed. Work around that by wrapping the call to find_package(DBus1) and restoring the environment. Change-Id: Ia69f10b014dddc32045b40972500a843e5d29b38 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 0520e4fdb2..99d9242eba 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -177,7 +177,7 @@ _library_map = [ LibraryMapping('atspi', 'ATSPI2', 'PkgConfig::ATSPI2'), LibraryMapping('corewlan', None, None), LibraryMapping('cups', 'Cups', 'Cups::Cups'), - LibraryMapping('dbus', 'DBus1', 'dbus-1'), + LibraryMapping('dbus', 'WrapDBus1', 'dbus-1', resultVariable="DBus1"), LibraryMapping('doubleconversion', None, None), LibraryMapping('drm', 'Libdrm', 'Libdrm::Libdrm'), LibraryMapping('egl', 'EGL', 'EGL::EGL'), -- cgit v1.2.3 From 5fe8a38af34d1530f14c3b695dee5a33e4a85554 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 17 May 2019 15:05:49 +0200 Subject: CMake: Fix test_operations Fix test_operations and do some small mypy cleanups along the way Change-Id: I6586b5d3491e5dcf44252c098516f0922fa60420 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + util/cmake/pro2cmake.py | 31 +++++++++++++++++-------------- util/cmake/tests/test_operations.py | 8 ++++---- 3 files changed, 22 insertions(+), 18 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 99d9242eba..ee4274abd7 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -367,6 +367,7 @@ def generate_find_package_info(lib: LibraryMapping, use_qt_find_package: bool=Tr extra.remove("REQUIRED") cmake_target_name = lib.targetName + 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'): diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0851181a25..5b6b3847d8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -43,7 +43,8 @@ from sympy.logic import (simplify_logic, And, Or, Not,) import pyparsing as 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 + featureName, map_platform, find_library_info_for_target, generate_find_package_info, \ + LibraryMapping from shutil import copyfile from special_case_helper import SpecialCaseHandler @@ -205,13 +206,11 @@ def handle_vpath(source: str, base_dir: str, vpath: typing.List[str]) -> str: class Operation: - def __init__(self, value): - if isinstance(value, list): - self._value = value - else: - self._value = [str(value), ] + def __init__(self, value: typing.List[str]): + self._value = value - def process(self, key, input, transformer): + def process(self, key: str, input: typing.List[str], + transformer: typing.Callable[[typing.List[str]], typing.List[str]]) -> typing.List[str]: assert(False) def __repr__(self): @@ -234,7 +233,8 @@ class Operation: class AddOperation(Operation): - def process(self, key, input, transformer): + def process(self, key: str, input: typing.List[str], + transformer: typing.Callable[[typing.List[str]], typing.List[str]]) -> typing.List[str]: return input + transformer(self._value) def __repr__(self): @@ -242,7 +242,8 @@ class AddOperation(Operation): class UniqueAddOperation(Operation): - def process(self, key, input, transformer): + def process(self, key: str, input: typing.List[str], + transformer: typing.Callable[[typing.List[str]], typing.List[str]]) -> typing.List[str]: result = input for v in transformer(self._value): if v not in result: @@ -254,10 +255,11 @@ class UniqueAddOperation(Operation): class SetOperation(Operation): - def process(self, key, input, transformer): + 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] for v in self._value: - if v != '$$' + key: + if v != '$${}'.format(key): values.append(v) else: values += input @@ -275,7 +277,8 @@ class RemoveOperation(Operation): def __init__(self, value): super().__init__(value) - def process(self, key, input, transformer): + def process(self, key: str, input: typing.List[str], + transformer: typing.Callable[[typing.List[str]], typing.List[str]]) -> typing.List[str]: input_set = set(input) value_set = set(self._value) result = [] @@ -305,8 +308,8 @@ class Scope(object): file: typing.Optional[str] = None, condition: str = '', base_dir: str = '', operations: typing.Mapping[str, typing.List[Operation]] = { - 'QT_SOURCE_TREE': [SetOperation('${PROJECT_SOURCE_DIR}')], - 'QT_BUILD_TREE': [SetOperation('${PROJECT_BUILD_DIR}')], + 'QT_SOURCE_TREE': [SetOperation(['${PROJECT_SOURCE_DIR}'])], + 'QT_BUILD_TREE': [SetOperation(['${PROJECT_BUILD_DIR}'])], }) -> None: if parent_scope: parent_scope._add_child(self) diff --git a/util/cmake/tests/test_operations.py b/util/cmake/tests/test_operations.py index 3ea2f76a43..c1e5f1b250 100755 --- a/util/cmake/tests/test_operations.py +++ b/util/cmake/tests/test_operations.py @@ -32,26 +32,26 @@ from pro2cmake import AddOperation, SetOperation, UniqueAddOperation, RemoveOper def test_add_operation(): op = AddOperation(['bar', 'buz']) - result = op.process(['foo', 'bar']) + result = op.process(['foo', 'bar'], ['foo', 'bar'], lambda x: x) assert ['foo', 'bar', 'bar', 'buz'] == result def test_uniqueadd_operation(): op = UniqueAddOperation(['bar', 'buz']) - result = op.process(['foo', 'bar']) + result = op.process(['foo', 'bar'], ['foo', 'bar'], lambda x: x) assert ['foo', 'bar', 'buz'] == result def test_set_operation(): op = SetOperation(['bar', 'buz']) - result = op.process(['foo', 'bar']) + result = op.process(['foo', 'bar'], ['foo', 'bar'], lambda x: x) assert ['bar', 'buz'] == result def test_remove_operation(): op = RemoveOperation(['bar', 'buz']) - result = op.process(['foo', 'bar']) + result = op.process(['foo', 'bar'], ['foo', 'bar'], lambda x: x) assert ['foo', '-buz'] == result -- cgit v1.2.3 From 14bf7e952e529f5656514295960fa050aa3a8e69 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 17 May 2019 15:14:31 +0200 Subject: CMake: Fix test_scope_handling Adapt to updated APIs in pro2cmake.py Change-Id: I39898b675e27d6295ef6cfa049c82b245d71188a Reviewed-by: Alexandru Croitor --- util/cmake/tests/test_scope_handling.py | 62 ++++++++++++++++----------------- 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/tests/test_scope_handling.py b/util/cmake/tests/test_scope_handling.py index 8bca8c8ec5..c0b553fabd 100755 --- a/util/cmake/tests/test_scope_handling.py +++ b/util/cmake/tests/test_scope_handling.py @@ -37,7 +37,7 @@ ScopeList = typing.List[Scope] def _map_to_operation(**kwargs): result = {} # type: typing.Mapping[str, typing.List[SetOperation]] for (key, value) in kwargs.items(): - result[key] = [SetOperation(value)] + result[key] = [SetOperation([value])] return result @@ -75,13 +75,13 @@ def test_evaluate_child_scope(): assert scope.total_condition == 'QT_FEATURE_foo' assert len(scope.children) == 1 - assert scope.getString('test1') == 'bar' - assert scope.getString('test2', 'not found') == 'not found' + assert scope.get_string('test1') == 'bar' + assert scope.get_string('test2', 'not found') == 'not found' child = scope.children[0] assert child.total_condition == 'QT_FEATURE_bar AND QT_FEATURE_foo' - assert child.getString('test1', 'not found') == 'not found' - assert child.getString('test2') == 'bar' + assert child.get_string('test1', 'not found') == 'not found' + assert child.get_string('test2') == 'bar' def test_evaluate_two_child_scopes(): @@ -94,21 +94,21 @@ def test_evaluate_two_child_scopes(): assert scope.total_condition == 'QT_FEATURE_foo' assert len(scope.children) == 2 - assert scope.getString('test1') == 'bar' - assert scope.getString('test2', 'not found') == 'not found' - assert scope.getString('test3', 'not found') == 'not found' + assert scope.get_string('test1') == 'bar' + assert scope.get_string('test2', 'not found') == 'not found' + assert scope.get_string('test3', 'not found') == 'not found' child1 = scope.children[0] assert child1.total_condition == 'QT_FEATURE_bar AND QT_FEATURE_foo' - assert child1.getString('test1', 'not found') == 'not found' - assert child1.getString('test2') == 'bar' - assert child1.getString('test3', 'not found') == 'not found' + assert child1.get_string('test1', 'not found') == 'not found' + assert child1.get_string('test2') == 'bar' + assert child1.get_string('test3', 'not found') == 'not found' child2 = scope.children[1] assert child2.total_condition == 'QT_FEATURE_buz AND QT_FEATURE_foo' - assert child2.getString('test1', 'not found') == 'not found' - assert child2.getString('test2') == '' - assert child2.getString('test3', 'not found') == 'buz' + assert child2.get_string('test1', 'not found') == 'not found' + assert child2.get_string('test2') == '' + assert child2.get_string('test3', 'not found') == 'buz' def test_evaluate_else_child_scopes(): @@ -121,21 +121,21 @@ def test_evaluate_else_child_scopes(): assert scope.total_condition == 'QT_FEATURE_foo' assert len(scope.children) == 2 - assert scope.getString('test1') == 'bar' - assert scope.getString('test2', 'not found') == 'not found' - assert scope.getString('test3', 'not found') == 'not found' + assert scope.get_string('test1') == 'bar' + assert scope.get_string('test2', 'not found') == 'not found' + assert scope.get_string('test3', 'not found') == 'not found' child1 = scope.children[0] assert child1.total_condition == 'QT_FEATURE_bar AND QT_FEATURE_foo' - assert child1.getString('test1', 'not found') == 'not found' - assert child1.getString('test2') == 'bar' - assert child1.getString('test3', 'not found') == 'not found' + assert child1.get_string('test1', 'not found') == 'not found' + assert child1.get_string('test2') == 'bar' + assert child1.get_string('test3', 'not found') == 'not found' child2 = scope.children[1] assert child2.total_condition == 'QT_FEATURE_foo AND NOT QT_FEATURE_bar' - assert child2.getString('test1', 'not found') == 'not found' - assert child2.getString('test2') == '' - assert child2.getString('test3', 'not found') == 'buz' + assert child2.get_string('test1', 'not found') == 'not found' + assert child2.get_string('test2') == '' + assert child2.get_string('test3', 'not found') == 'buz' def test_evaluate_invalid_else_child_scopes(): @@ -196,8 +196,8 @@ def test_merge_two_scopes_with_same_condition(): assert len(result) == 1 r0 = result[0] assert r0.total_condition == 'QT_FEATURE_bar' - assert r0.getString('test') == 'foo' - assert r0.getString('test2') == 'bar' + assert r0.get_string('test') == 'foo' + assert r0.get_string('test2') == 'bar' def test_merge_three_scopes_two_with_same_condition(): @@ -214,8 +214,8 @@ def test_merge_three_scopes_two_with_same_condition(): assert len(result) == 2 r0 = result[0] assert r0.total_condition == 'QT_FEATURE_bar' - assert r0.getString('test') == 'foo' - assert r0.getString('test2') == 'bar' + assert r0.get_string('test') == 'foo' + assert r0.get_string('test2') == 'bar' assert result[1] == scopes[1] @@ -261,8 +261,8 @@ def test_merge_parent_child_scopes_with_same_conditions(): r0 = result[0] assert r0.parent == None assert r0.total_condition == 'FOO AND bar' - assert r0.getString('test1') == 'parent' - assert r0.getString('test2') == 'child' + assert r0.get_string('test1') == 'parent' + assert r0.get_string('test2') == 'child' def test_merge_parent_child_scopes_with_on_child_condition(): @@ -277,8 +277,8 @@ def test_merge_parent_child_scopes_with_on_child_condition(): r0 = result[0] assert r0.parent == None assert r0.total_condition == 'FOO AND bar' - assert r0.getString('test1') == 'parent' - assert r0.getString('test2') == 'child' + assert r0.get_string('test1') == 'parent' + assert r0.get_string('test2') == 'child' # Real world examples: -- cgit v1.2.3 From e0a6e9f3aa66da3018b8362d949e288a2c801daa Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 17 May 2019 14:56:11 +0200 Subject: Fix parsing qmake assignments that have comments in between For some reason the python comment regex that we used does not ignore the line break at the end of a comment line. This caused issues when parsing multi line assignments with comments in between. Use our own regex for comments to circumvent the issue. It was found while trying to port the qtimageformats repo. Added a pytest as well. Change-Id: Ie4bbdac2d1e1c133bc787a995224d0bbd8238204 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 7 ++++++- util/cmake/tests/data/lc_with_comment.pro | 4 ++++ util/cmake/tests/test_parsing.py | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 util/cmake/tests/data/lc_with_comment.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5b6b3847d8..71053d288b 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -779,7 +779,12 @@ class QmakeParser: expr.setDebug() Grammar = StatementGroup('statements') - Grammar.ignore(pp.pythonStyleComment()) + + # Ignore comment lines, including the final line break, + # otherwise parsing fails when looking at multi line assignments + # with comments in between. + Comment = pp.Regex(r"#.*\n").setName("qmake style comment") + Grammar.ignore(Comment()) return Grammar diff --git a/util/cmake/tests/data/lc_with_comment.pro b/util/cmake/tests/data/lc_with_comment.pro new file mode 100644 index 0000000000..c087dadacc --- /dev/null +++ b/util/cmake/tests/data/lc_with_comment.pro @@ -0,0 +1,4 @@ +SUBDIRS = \ +# dds \ + tga \ + wbmp diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index 79ad0a4945..4b6f48b931 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -305,3 +305,7 @@ def test_realworld_lc(): result = parse_file(_tests_path + '/data/lc.pro') assert len(result) == 3 + +def test_realworld_lc_with_comment_in_between(): + result = parse_file(_tests_path + '/data/lc_with_comment.pro') + assert len(result) == 1 -- cgit v1.2.3 From c454e622a1d50f4e6a879390241034b910e5ee90 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 17 May 2019 17:41:59 +0200 Subject: Don't exclude QtCore from dependencies when generating modules and plugins. src/network/network.pro for instance depends on core-private, but because we ignore adding QtCore as a public dependency, the exported Config file for Network doesn't depend on QtCore anymore, so if a user only links against Network, they won't automatically link against Core. Change-Id: I4a60ffae7e071927360b8ccf6b1b7479ab391060 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 71053d288b..ddfa7d05c3 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1482,7 +1482,7 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, write_main_part(cm_fh, module_name[2:], 'Module', 'add_qt_module', scope, extra_lines=extra, indent=indent, - known_libraries={'Qt::Core', }, extra_keys=[]) + known_libraries={}, extra_keys=[]) if 'qt_tracepoints' in scope.get('CONFIG'): tracepoints = scope.get_files('TRACEPOINT_PROVIDER') @@ -1596,7 +1596,7 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): assert plugin_name write_main_part(cm_fh, plugin_name, 'Plugin', 'add_qt_plugin', scope, - indent=indent, known_libraries={'QtCore', }, extra_keys=[]) + indent=indent, known_libraries={}, extra_keys=[]) def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, -- cgit v1.2.3 From 826821658df16b18a51ec0c0a6e54f7e3e31795c Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 17 May 2019 18:09:21 +0200 Subject: Workaround fix in pro2cmake Operation __init__ method There are still call sites that call Operation.__init__ with a string instead of a list. Restore the handling of such a case. Amends 5fe8a38af34d1530f14c3b695dee5a33e4a85554 Change-Id: I2a4d5c5cb5b460bf02b6da02d42d8cc8d5eb4192 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ddfa7d05c3..5c5569ae30 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -206,8 +206,11 @@ def handle_vpath(source: str, base_dir: str, vpath: typing.List[str]) -> str: class Operation: - def __init__(self, value: typing.List[str]): - self._value = value + def __init__(self, value: typing.Union[typing.List[str], str]): + if isinstance(value, list): + self._value = value + else: + 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]: -- cgit v1.2.3 From 846adfe079fd57410fbb8e9c96eae010cf81dd26 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 17 May 2019 17:24:15 +0200 Subject: Handle the new cmdline config feature in pro2cmake cmdline implies console, so use that also to determine if a binary should get a GUI flag. Change-Id: I084e0a45785df96a7dc2c101af5305fbb39efbc3 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5c5569ae30..ba77e0e51f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1624,7 +1624,8 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, assert not is_example write_test(cm_fh, scope, indent=indent) else: - gui = 'console' not in scope.get('CONFIG') + config = scope.get('CONFIG') + gui = all(val not in config for val in ['console', 'cmdline']) if is_example: write_example(cm_fh, scope, gui, indent=indent) else: -- cgit v1.2.3 From e4b8c488bd8b350f1a19874c4859ae2699afc747 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 17 May 2019 14:22:57 +0200 Subject: Improve configure2cmake to find_package only in certain conditions It doesn't make much sense to look for X11 related packages on macOS and Windows by default. Usually they would not be there, and as a result the configuration step would show a long list of scary not found packages, and also eat precious configure time. Change the conversion script to allow putting conditions around generated find_package calls. These conditions can be manually set in the conversion script library mapping, using the emit_if argument, which we do for the X11 and Wayland related packages. They are also computed by checking which features use a given library, and if the feature is protected by a simple emitIf condition like config.linux, the relevant library find_package call will be protected by the same condition. If a developer still wishes to look for all packages, they can define the CACHE variable QT_FIND_ALL_PACKAGES_ALWAYS to ON. The relevant configure.cmake files are regenerated in this patch. Change-Id: I6f918a94f50257ec41d6216305dae9774933389a Reviewed-by: Tobias Hunger --- util/cmake/configurejson2cmake.py | 24 ++++++++++++++++++++++-- util/cmake/helper.py | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 040e8ee7b4..d1646ed082 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -181,9 +181,29 @@ def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): if newlib.targetName in cmake_find_packages_set: return + # If certain libraries are used within a feature, but the feature + # is only emitted conditionally with a simple condition (like + # 'on Windows' or 'on Linux'), we should enclose the find_package + # call for the library into the same condition. + emit_if = newlib.emit_if + + # Only look through features if a custom emit_if wasn't provided. + if not emit_if: + for feature in data['features']: + feature_data = data['features'][feature] + if 'condition' in feature_data and \ + '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: + emit_if = map_condition(emit_if) + cmake_find_packages_set.add(newlib.targetName) - cm_fh.write(generate_find_package_info(newlib)) + cm_fh.write(generate_find_package_info(newlib, emit_if=emit_if)) def lineify(label, value, quote=True): @@ -890,7 +910,7 @@ def processLibraries(ctx, data, cm_fh): return for lib in data['libraries']: - parseLib(ctx, lib, data['libraries'][lib], cm_fh, cmake_find_packages_set) + parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set) def processSubconfigs(dir, ctx, data): diff --git a/util/cmake/helper.py b/util/cmake/helper.py index ee4274abd7..d9e9fd16e3 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -35,7 +35,8 @@ class LibraryMapping: targetName: typing.Optional[str], *, resultVariable: typing.Optional[str] = None, extra: typing.List[str] = [], - appendFoundSuffix: bool = True) -> None: + appendFoundSuffix: bool = True, + emit_if: str = '') -> None: self.soName = soName self.packageName = packageName self.resultVariable = resultVariable @@ -43,6 +44,10 @@ class LibraryMapping: self.extra = extra self.targetName = targetName + # if emit_if is non-empty, the generated find_package call + # for a library will be surrounded by this condition. + self.emit_if = emit_if + def is_qt(self) -> bool: return self.packageName == 'Qt' \ or self.packageName == 'Qt5' \ @@ -172,6 +177,7 @@ _qt_library_map = [ # qtzlib: No longer supported. ] +# Note that the library map is adjusted dynamically further down. _library_map = [ # 3rd party: LibraryMapping('atspi', 'ATSPI2', 'PkgConfig::ATSPI2'), @@ -247,6 +253,20 @@ _library_map = [ ] +def _adjust_library_map(): + # Assign a Linux condition on all x and wayland related packages. + # 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'] + 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' + + +_adjust_library_map() + + def find_3rd_party_library_mapping(soName: str) -> typing.Optional[LibraryMapping]: for i in _library_map: if i.soName == soName: @@ -356,8 +376,10 @@ def map_3rd_party_library(lib: str) -> str: return mapping.targetName + libpostfix -def generate_find_package_info(lib: LibraryMapping, use_qt_find_package: bool=True, *, - indent: int = 0) -> 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() @@ -377,7 +399,8 @@ def generate_find_package_info(lib: LibraryMapping, use_qt_find_package: bool=Tr extra += ['PROVIDED_TARGETS', cmake_target_name] result = '' - ind = ' ' * indent + one_ind = ' ' + ind = one_ind * indent if use_qt_find_package: if extra: @@ -393,4 +416,12 @@ def generate_find_package_info(lib: LibraryMapping, use_qt_find_package: bool=Tr else: 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) + return result -- cgit v1.2.3 From 76f5b784ce54730ed8d6ad4bb9c39c9a05c5d81d Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 17 May 2019 18:31:19 +0200 Subject: Fix the fix to correctly handle comments in multi-line assignments The previous fix where the Grammar comment style was changed to remove the newlines was incorrect, because if you have foo=1#comment bar=2 after the Grammar comment ignoring, it would transform into foo=1bar=2 which will clearly fail to parse, so the new line has to stay. But we would still have the following case which would fail: foo=a \ # comment b Apparently qmake things that's the equivalent of foo=a b but the grammar parses it as foo=a \ \n (newline) b Thus the parsing fails because there's a newline and then some weird 'b' token which the grammar does not expect. The best fix I found is to preprocess the source, to remove completely commented out lines. So: foo=a \ # comment b gets transformed into foo=a \ b Change-Id: I2487a0dbf94a6ad4d917d0a0ce05247341e9b7da Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 29 +++++++++++++++++++++++------ util/cmake/tests/data/lc_with_comment.pro | 18 ++++++++++++++++++ util/cmake/tests/test_parsing.py | 2 +- 3 files changed, 42 insertions(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ba77e0e51f..0036c658d6 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -155,6 +155,27 @@ def fixup_linecontinuation(contents: str) -> str: return contents +def fixup_comments(contents: str) -> str: + # Get rid of completely commented out lines. + # So any line which starts with a '#' char and ends with a new line + # will be replaced by a single new line. + # + # This is needed because qmake syntax is weird. In a multi line + # assignment (separated by backslashes and newlines aka + # # \\\n ), if any of the lines are completely commented out, in + # principle the assignment should fail. + # + # It should fail because you would have a new line separating + # the previous value from the next value, and the next value would + # not be interpreted as a value, but as a new token / operation. + # qmake is lenient though, and accepts that, so we need to take + # 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) + return contents + + def spaces(indent: int) -> str: return ' ' * indent @@ -782,12 +803,7 @@ class QmakeParser: expr.setDebug() Grammar = StatementGroup('statements') - - # Ignore comment lines, including the final line break, - # otherwise parsing fails when looking at multi line assignments - # with comments in between. - Comment = pp.Regex(r"#.*\n").setName("qmake style comment") - Grammar.ignore(Comment()) + Grammar.ignore(pp.pythonStyleComment()) return Grammar @@ -799,6 +815,7 @@ class QmakeParser: old_contents = contents contents = fixup_linecontinuation(contents) + contents = fixup_comments(contents) if old_contents != contents: print('Warning: Fixed line continuation in .pro-file!\n' diff --git a/util/cmake/tests/data/lc_with_comment.pro b/util/cmake/tests/data/lc_with_comment.pro index c087dadacc..176913dfc8 100644 --- a/util/cmake/tests/data/lc_with_comment.pro +++ b/util/cmake/tests/data/lc_with_comment.pro @@ -2,3 +2,21 @@ SUBDIRS = \ # dds \ tga \ wbmp + +MYVAR = foo # comment +MYVAR = foo2# comment +MYVAR = foo3# comment # + +MYVAR = foo4# comment # + +## +# +# +## + + # + # +# + # # + +MYVAR = foo5# comment # # diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index 4b6f48b931..c8feeb1811 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -308,4 +308,4 @@ def test_realworld_lc(): def test_realworld_lc_with_comment_in_between(): result = parse_file(_tests_path + '/data/lc_with_comment.pro') - assert len(result) == 1 + assert len(result) == 6 -- cgit v1.2.3 From 4e7756a6cd48716ff37d071fe660bb0e9edfa89f Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 21 May 2019 16:51:10 +0200 Subject: Add a few more 3rd party libraries to helper.py Handle a few more libraries that are used in qtimageformats repi. Change-Id: Ia3b9a845bc6cb8ce98a477b9355011bbadc32c1a Reviewed-by: Liang Qi Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index d9e9fd16e3..682e2ec15f 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -250,6 +250,9 @@ _library_map = [ 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'), ] -- cgit v1.2.3 From 0425ee06102410026f436adfd440aa2f0b3efd61 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 22 May 2019 15:09:39 +0200 Subject: Change the default enabled AUTOGEN tools list to contain only moc Before this patch we enabled AUTOMOC, AUTORCC, AUTOUIC for all targets that did not opt out. Aside from being wasteful from a performance point of view, this also caused issues when trying to build qtimageformats which does not depend on Widgets which is the package that exposes uic. To avoid this, enable only AUTOMOC for all targets by default, and UIC and RCC can be opted in via the ENABLE_AUTOGEN_TOOLS option. To facilitate this some refactoring had to be done, like moving some common setup for all autogen tools into a separate call, and making sure that extend_target understands the autogen options, because some ui files are only added conditionally. Also the conversion script has been adapted to output the ENABLE_AUTOGEN_TOOLS option whenever a .pro file contains at least one FORMS += foo assignment. Note that we don't really use AUTORCC while building Qt, so nothing opts into that at the moment. Task-number: QTBUG-75875 Change-Id: I889c4980e9fb1b74ba361abed4044737f8842ea4 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0036c658d6..3f9b4ccb53 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1080,6 +1080,12 @@ def write_library_section(cm_fh: typing.IO[str], scope: Scope, *, 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') + if forms: + 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()): @@ -1115,6 +1121,8 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, 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') if link_options: cm_fh.write('{} LINK_OPTIONS\n'.format(ind)) -- cgit v1.2.3 From 80271e82807b4ac51d158398d08554925b9fcdd5 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 23 May 2019 10:59:05 +0200 Subject: Improve qmake parser debug output in pro2cmake Override the default debug actions to be decorated with proper indentation for easier reading. The setup only has to be done once, and not on each QMakeParser creation. Change-Id: If5f965b462c782c654ee8ebfdd33570e8f94b084 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3f9b4ccb53..ea68b57a19 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -683,6 +683,33 @@ class QmakeParser: def __init__(self, *, debug: bool = False) -> None: self._Grammar = self._generate_grammar(debug) + @staticmethod + def set_up_py_parsing_nicer_debug_output(): + indent = -1 + + def increase_indent(fn): + def wrapper_function(*args): + nonlocal indent + indent += 1 + print("> " * indent, end="") + return fn(*args) + + return wrapper_function + + def decrease_indent(fn): + def wrapper_function(*args): + nonlocal indent + print("> " * indent, end="") + indent -= 1 + return fn(*args) + + return wrapper_function + + pp._defaultStartDebugAction = increase_indent(pp._defaultStartDebugAction) + pp._defaultSuccessDebugAction = decrease_indent(pp._defaultSuccessDebugAction) + pp._defaultExceptionDebugAction = decrease_indent(pp._defaultExceptionDebugAction) + + def _generate_grammar(self, debug: bool): # Define grammar: pp.ParserElement.setDefaultWhitespaceChars(' \t') @@ -829,6 +856,9 @@ class QmakeParser: return result +QmakeParser.set_up_py_parsing_nicer_debug_output() + + def parseProFile(file: str, *, debug=False): parser = QmakeParser(debug=debug) return parser.parseFile(file) -- cgit v1.2.3 From 6dfeb81bcb05360e83a55b519886014c55e8e771 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 23 May 2019 11:53:41 +0200 Subject: Improve qmake syntax parser in pro2cmake.py Some qtdeclarative pro files caused exceptions when trying to parse them using the script. This included the following: - handling conditions divided by newlines and backslashes - handling conditions that have no scope The parser has been fixed to deal with those cases and relevant tests were added. After the change, all qtdeclarative project files are parseable by the script. Change-Id: Ib9736423f7fb3bcc1944b26cfb3114306b4db9a7 Reviewed-by: Qt CMake Build Bot Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 25 ++++++++++++++++------ util/cmake/tests/data/condition_without_scope.pro | 2 ++ .../tests/data/multi_condition_divided_by_lc.pro | 3 +++ util/cmake/tests/data/nested_function_calls.pro | 2 ++ util/cmake/tests/test_parsing.py | 15 +++++++++++++ 5 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 util/cmake/tests/data/condition_without_scope.pro create mode 100644 util/cmake/tests/data/multi_condition_divided_by_lc.pro create mode 100644 util/cmake/tests/data/nested_function_calls.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ea68b57a19..fbf153b057 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -794,13 +794,26 @@ class QmakeParser: + pp.Optional(LC) + (pp.Literal(':') \ | pp.Literal('{') \ | pp.Literal('|')))) - ConditionPart = ((pp.Optional('!') + Identifier + pp.Optional(BracedValue)) \ - ^ pp.CharsNotIn('#{}|:=\\\n')) + pp.Optional(LC) + ConditionEnd - Condition = pp.Combine(ConditionPart \ - + pp.ZeroOrMore((pp.Literal('|') ^ pp.Literal(':')) \ - + ConditionPart)) + + ConditionPart1 = (pp.Optional('!') + Identifier + pp.Optional(BracedValue)) + ConditionPart2 = pp.CharsNotIn('#{}|:=\\\n') + ConditionPart = (ConditionPart1 ^ ConditionPart2) + pp.Optional(LC) + ConditionEnd + + ConditionOp = pp.Literal('|') ^ pp.Literal(':') + ConditionLC = pp.Suppress(pp.Optional(pp.White(' ') + LC + pp.White(' '))) + + ConditionRepeated = pp.ZeroOrMore((ConditionOp) + ConditionLC + ConditionPart) + + 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 = pp.Suppress(ConditionOp) + FunctionCall \ + + pp.Empty().setParseAction(lambda x: [[]])\ + .setResultsName('statements') + SingleLineScope = pp.Suppress(pp.Literal(':')) + pp.Optional(LC) \ + pp.Group(Block | (Statement + EOL))('statements') MultiLineScope = pp.Optional(LC) + Block('statements') @@ -811,7 +824,7 @@ class QmakeParser: ElseBranch = pp.Suppress(Else) + (SingleLineElse | MultiLineElse) Scope <<= pp.Optional(LC) \ + pp.Group(Condition('condition') \ - + (SingleLineScope | MultiLineScope) \ + + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) \ + pp.Optional(ElseBranch)('else_statements')) if debug: diff --git a/util/cmake/tests/data/condition_without_scope.pro b/util/cmake/tests/data/condition_without_scope.pro new file mode 100644 index 0000000000..2aa1237c12 --- /dev/null +++ b/util/cmake/tests/data/condition_without_scope.pro @@ -0,0 +1,2 @@ +write_file("a", contents)|error() + diff --git a/util/cmake/tests/data/multi_condition_divided_by_lc.pro b/util/cmake/tests/data/multi_condition_divided_by_lc.pro new file mode 100644 index 0000000000..23254231df --- /dev/null +++ b/util/cmake/tests/data/multi_condition_divided_by_lc.pro @@ -0,0 +1,3 @@ +equals(a): \ + greaterThan(a):flags += 1 + diff --git a/util/cmake/tests/data/nested_function_calls.pro b/util/cmake/tests/data/nested_function_calls.pro new file mode 100644 index 0000000000..5ecc53f1cc --- /dev/null +++ b/util/cmake/tests/data/nested_function_calls.pro @@ -0,0 +1,2 @@ +requires(qtConfig(dlopen)) + diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index c8feeb1811..11d1ed093f 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -309,3 +309,18 @@ def test_realworld_lc(): def test_realworld_lc_with_comment_in_between(): result = parse_file(_tests_path + '/data/lc_with_comment.pro') assert len(result) == 6 + + +def test_condition_without_scope(): + result = parse_file(_tests_path + '/data/condition_without_scope.pro') + assert len(result) == 1 + + +def test_multi_condition_divided_by_lc(): + result = parse_file(_tests_path + '/data/multi_condition_divided_by_lc.pro') + assert len(result) == 1 + + +def test_nested_function_calls(): + result = parse_file(_tests_path + '/data/nested_function_calls.pro') + assert len(result) == 1 -- cgit v1.2.3 From a2e0f19b61453dc472bca657935310dcfbf9fc86 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 23 May 2019 15:20:41 +0200 Subject: Make debug token output actually work for the pro2cmake qmake parser setName() and setDebug() need to be called on a parser element before the parser element is used as a sub-element in another parser element, otherwise the debug output is not shown. Hence the "iterate over all locals" approach works only partially. Instead add a new decorating function add_element() for the construction of each parser element, and make sure to enable debugging inside that function. Unfortunately there is no clean way to avoid duplicating the parser element name both in the local variable and in the function argument. Change-Id: Iaa9ed9b7dbb22ec084070b9c049cf51c841d442c Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 252 +++++++++++++++++++++++++++--------------------- 1 file changed, 143 insertions(+), 109 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index fbf153b057..0f3f796227 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -681,7 +681,8 @@ class Scope(object): class QmakeParser: def __init__(self, *, debug: bool = False) -> None: - self._Grammar = self._generate_grammar(debug) + self.debug = debug + self._Grammar = self._generate_grammar() @staticmethod def set_up_py_parsing_nicer_debug_output(): @@ -709,54 +710,72 @@ class QmakeParser: pp._defaultSuccessDebugAction = decrease_indent(pp._defaultSuccessDebugAction) pp._defaultExceptionDebugAction = decrease_indent(pp._defaultExceptionDebugAction) - - def _generate_grammar(self, debug: bool): + def _generate_grammar(self): # Define grammar: pp.ParserElement.setDefaultWhitespaceChars(' \t') - LC = pp.Suppress(pp.Literal('\\\n')) - EOL = pp.Suppress(pp.LineEnd()) - Else = pp.Keyword('else') - Identifier = pp.Word(pp.alphas + '_', bodyChars=pp.alphanums+'_-./') - BracedValue = pp.nestedExpr(ignoreExpr=pp.quotedString \ - | pp.QuotedString(quoteChar='$(', - endQuoteChar=')', - escQuote='\\', - unquoteResults=False) - ).setParseAction(lambda s, l, t: ['(', *t[0], ')']) + def add_element(name: str, value: pp.ParserElement): + nonlocal self + if self.debug: + value.setName(name) + value.setDebug() + return value + + LC = add_element('LC', pp.Suppress(pp.Literal('\\\n'))) + + 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], ')'])) 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 = pp.Word(pp.printables, excludeChars='$#{}()') + = 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 \ - = pp.Combine(pp.OneOrMore(Substitution | LiteralValuePart - | pp.Literal('$'))) - Value = pp.NotAny(Else | pp.Literal('}') | EOL) \ - + (pp.QuotedString(quoteChar='"', escChar='\\') - | SubstitutionValue - | BracedValue) - - Values = pp.ZeroOrMore(Value + pp.Optional(LC))('value') - - Op = pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') \ - | pp.Literal('*=') - - Key = Identifier - - Operation = Key('key') + pp.Optional(LC) \ - + Op('operation') + pp.Optional(LC) \ - + Values('value') - CallArgs = pp.Optional(LC) + pp.nestedExpr()\ + = add_element('SubstitutionValue', + pp.Combine(pp.OneOrMore(Substitution + | LiteralValuePart + | pp.Literal('$')))) + Value \ + = add_element('Value', + pp.NotAny(Else | pp.Literal('}') | EOL) \ + + (pp.QuotedString(quoteChar='"', escChar='\\') + | SubstitutionValue + | BracedValue)) + + Values = add_element('Values', pp.ZeroOrMore(Value + pp.Optional(LC))('value')) + + Op = add_element('OP', + pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') \ + | pp.Literal('*=')) + + Key = add_element('Key', Identifier) + + Operation = add_element('Operation', + Key('key') + pp.Optional(LC) \ + + Op('operation') + pp.Optional(LC) \ + + Values('value')) + CallArgs = add_element('CallArgs', pp.Optional(LC) + pp.nestedExpr()) def parse_call_args(results): out = '' @@ -768,80 +787,95 @@ class QmakeParser: return out CallArgs.setParseAction(parse_call_args) - Load = pp.Keyword('load') + CallArgs('loaded') - Include = pp.Keyword('include') + CallArgs('included') - Option = pp.Keyword('option') + CallArgs('option') - DefineTestDefinition = pp.Suppress(pp.Keyword('defineTest') + CallArgs - + pp.nestedExpr(opener='{', closer='}', ignoreExpr=pp.LineEnd())) # ignore the whole thing... - ForLoop = pp.Suppress(pp.Keyword('for') + CallArgs - + pp.nestedExpr(opener='{', closer='}', ignoreExpr=pp.LineEnd())) # ignore the whole thing... - ForLoopSingleLine = pp.Suppress(pp.Keyword('for') + CallArgs - + pp.Literal(':') + pp.SkipTo(EOL, ignore=LC)) # ignore the whole thing... - FunctionCall = pp.Suppress(Identifier + pp.nestedExpr()) - - Scope = pp.Forward() - - Statement = pp.Group(Load | Include | Option | ForLoop | ForLoopSingleLine \ - | DefineTestDefinition | FunctionCall | Operation) - StatementLine = Statement + (EOL | pp.FollowedBy('}')) - StatementGroup = pp.ZeroOrMore(StatementLine | Scope | pp.Suppress(EOL)) - - Block = pp.Suppress('{') + pp.Optional(LC | EOL) \ - + StatementGroup + pp.Optional(LC | EOL) \ - + pp.Suppress('}') + pp.Optional(LC | EOL) - - ConditionEnd = pp.FollowedBy((pp.Optional(pp.White()) - + pp.Optional(LC) + (pp.Literal(':') \ - | pp.Literal('{') \ - | pp.Literal('|')))) - - ConditionPart1 = (pp.Optional('!') + Identifier + pp.Optional(BracedValue)) - ConditionPart2 = pp.CharsNotIn('#{}|:=\\\n') - ConditionPart = (ConditionPart1 ^ ConditionPart2) + pp.Optional(LC) + ConditionEnd - - ConditionOp = pp.Literal('|') ^ pp.Literal(':') - ConditionLC = pp.Suppress(pp.Optional(pp.White(' ') + LC + pp.White(' '))) - - ConditionRepeated = pp.ZeroOrMore((ConditionOp) + ConditionLC + ConditionPart) - - Condition = pp.Combine(ConditionPart + ConditionRepeated) + + 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()))) + + # ignore the whole thing... + ForLoop = add_element( + '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, ignore=LC))) + + # ignore the whole thing... + FunctionCall = add_element('FunctionCall', pp.Suppress(Identifier + pp.nestedExpr())) + + 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))) + + Block = add_element('Block', + pp.Suppress('{') + pp.Optional(LC | EOL) + + StatementGroup + pp.Optional(LC | EOL) + + pp.Suppress('}') + pp.Optional(LC | EOL)) + + ConditionEnd = add_element('ConditionEnd', + pp.FollowedBy((pp.Optional(pp.White()) + + pp.Optional(LC) + (pp.Literal(':') + | pp.Literal('{') + | pp.Literal('|'))))) + + ConditionPart1 = add_element('ConditionPart1', + (pp.Optional('!') + Identifier + pp.Optional(BracedValue))) + ConditionPart2 = add_element('ConditionPart2', pp.CharsNotIn('#{}|:=\\\n')) + ConditionPart = add_element( + 'ConditionPart', + (ConditionPart1 ^ ConditionPart2) + pp.Optional(LC) + ConditionEnd) + + ConditionOp = add_element('ConditionOp', pp.Literal('|') ^ pp.Literal(':')) + ConditionLC = add_element('ConditionLC', + pp.Suppress(pp.Optional(pp.White(' ') + LC + pp.White(' ')))) + + ConditionRepeated = add_element('ConditionRepeated', + pp.ZeroOrMore((ConditionOp) + ConditionLC + ConditionPart)) + + 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 = pp.Suppress(ConditionOp) + FunctionCall \ - + pp.Empty().setParseAction(lambda x: [[]])\ - .setResultsName('statements') - - SingleLineScope = pp.Suppress(pp.Literal(':')) + pp.Optional(LC) \ - + pp.Group(Block | (Statement + EOL))('statements') - MultiLineScope = pp.Optional(LC) + Block('statements') - - SingleLineElse = pp.Suppress(pp.Literal(':')) + pp.Optional(LC) \ - + (Scope | Block | (Statement + pp.Optional(EOL))) - MultiLineElse = Block - ElseBranch = pp.Suppress(Else) + (SingleLineElse | MultiLineElse) + ConditionEndingInFunctionCall = add_element( + 'ConditionEndingInFunctionCall', pp.Suppress(ConditionOp) + FunctionCall + + pp.Empty().setParseAction(lambda x: [[]]) + .setResultsName('statements')) + + SingleLineScope = add_element('SingleLineScope', + pp.Suppress(pp.Literal(':')) + pp.Optional(LC) + + pp.Group(Block | (Statement + EOL))('statements')) + MultiLineScope = add_element('MultiLineScope', + pp.Optional(LC) + Block('statements')) + + SingleLineElse = add_element('SingleLineElse', + pp.Suppress(pp.Literal(':')) + pp.Optional(LC) + + (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.Optional(LC) \ - + pp.Group(Condition('condition') \ - + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) \ + + pp.Group(Condition('condition') + + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) + pp.Optional(ElseBranch)('else_statements')) - if debug: - for ename in 'LC EOL ' \ - 'Condition ConditionPart ConditionEnd ' \ - 'Else ElseBranch SingleLineElse MultiLineElse ' \ - 'SingleLineScope MultiLineScope ' \ - 'Identifier ' \ - 'Key Op Values Value BracedValue ' \ - 'Scope Block ' \ - 'StatementGroup StatementLine Statement '\ - 'Load Include Option DefineTestDefinition ForLoop ' \ - 'FunctionCall CallArgs Operation'.split(): - expr = locals()[ename] - expr.setName(ename) - expr.setDebug() - Grammar = StatementGroup('statements') Grammar.ignore(pp.pythonStyleComment()) -- cgit v1.2.3 From bfed22e3b7b6de27359d41f1a51ea345f7284ce2 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 23 May 2019 15:57:59 +0200 Subject: Remove all line continuations when processing qmake syntax We constantly had to adjust the qmake grammar to handle line continuations (\\\n) in weird places. Instead of doing that, just do a preprocess step to remove all the LCs like we do with comments, and simplify the grammar not to take into account the LCs. From some manual testing it doesn't look like we get any regressions. Change-Id: I2017d59396004cf67b6cb54977583db65c65e7d3 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 59 +++++++++++++++++++-------------------- util/cmake/tests/test_lc_fixup.py | 8 +++--- 2 files changed, 32 insertions(+), 35 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0f3f796227..5a2db68e67 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -149,9 +149,12 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '') -> str: def fixup_linecontinuation(contents: str) -> str: - contents = re.sub(r'([^\t ])\\[ \t]*\n', '\\1 \\\n', contents) - contents = re.sub(r'\\[ \t]*\n', '\\\n', contents) - + # Remove all line continuations, aka a backslash followed by + # 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) return contents @@ -721,8 +724,6 @@ class QmakeParser: value.setDebug() return value - LC = add_element('LC', pp.Suppress(pp.Literal('\\\n'))) - EOL = add_element('EOL', pp.Suppress(pp.LineEnd())) Else = add_element('Else', pp.Keyword('else')) Identifier = add_element('Identifier', pp.Word(pp.alphas + '_', @@ -763,7 +764,7 @@ class QmakeParser: | SubstitutionValue | BracedValue)) - Values = add_element('Values', pp.ZeroOrMore(Value + pp.Optional(LC))('value')) + Values = add_element('Values', pp.ZeroOrMore(Value)('value')) Op = add_element('OP', pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') \ @@ -771,11 +772,8 @@ class QmakeParser: Key = add_element('Key', Identifier) - Operation = add_element('Operation', - Key('key') + pp.Optional(LC) \ - + Op('operation') + pp.Optional(LC) \ - + Values('value')) - CallArgs = add_element('CallArgs', pp.Optional(LC) + pp.nestedExpr()) + Operation = add_element('Operation', Key('key') + Op('operation') + Values('value')) + CallArgs = add_element('CallArgs', pp.nestedExpr()) def parse_call_args(results): out = '' @@ -807,8 +805,7 @@ class QmakeParser: # ignore the whole thing... ForLoopSingleLine = add_element( 'ForLoopSingleLine', - pp.Suppress(pp.Keyword('for') + CallArgs - + pp.Literal(':') + pp.SkipTo(EOL, ignore=LC))) + pp.Suppress(pp.Keyword('for') + CallArgs + pp.Literal(':') + pp.SkipTo(EOL))) # ignore the whole thing... FunctionCall = add_element('FunctionCall', pp.Suppress(Identifier + pp.nestedExpr())) @@ -823,29 +820,30 @@ class QmakeParser: pp.ZeroOrMore(StatementLine | Scope | pp.Suppress(EOL))) Block = add_element('Block', - pp.Suppress('{') + pp.Optional(LC | EOL) - + StatementGroup + pp.Optional(LC | EOL) - + pp.Suppress('}') + pp.Optional(LC | EOL)) + 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.Optional(LC) + (pp.Literal(':') - | pp.Literal('{') - | pp.Literal('|'))))) + + (pp.Literal(':') + | pp.Literal('{') + | pp.Literal('|'))))) ConditionPart1 = add_element('ConditionPart1', (pp.Optional('!') + Identifier + pp.Optional(BracedValue))) ConditionPart2 = add_element('ConditionPart2', pp.CharsNotIn('#{}|:=\\\n')) ConditionPart = add_element( 'ConditionPart', - (ConditionPart1 ^ ConditionPart2) + pp.Optional(LC) + ConditionEnd) + (ConditionPart1 ^ ConditionPart2) + ConditionEnd) ConditionOp = add_element('ConditionOp', pp.Literal('|') ^ pp.Literal(':')) - ConditionLC = add_element('ConditionLC', - pp.Suppress(pp.Optional(pp.White(' ') + LC + pp.White(' ')))) + ConditionWhiteSpace = add_element('ConditionWhiteSpace', + pp.Suppress(pp.Optional(pp.White(' ')))) ConditionRepeated = add_element('ConditionRepeated', - pp.ZeroOrMore((ConditionOp) + ConditionLC + ConditionPart)) + pp.ZeroOrMore(ConditionOp + + ConditionWhiteSpace + ConditionPart)) Condition = add_element('Condition', pp.Combine(ConditionPart + ConditionRepeated)) Condition.setParseAction(lambda x: ' '.join(x).strip().replace(':', ' && ').strip(' && ')) @@ -859,22 +857,21 @@ class QmakeParser: .setResultsName('statements')) SingleLineScope = add_element('SingleLineScope', - pp.Suppress(pp.Literal(':')) + pp.Optional(LC) + pp.Suppress(pp.Literal(':')) + pp.Group(Block | (Statement + EOL))('statements')) - MultiLineScope = add_element('MultiLineScope', - pp.Optional(LC) + Block('statements')) + MultiLineScope = add_element('MultiLineScope', Block('statements')) SingleLineElse = add_element('SingleLineElse', - pp.Suppress(pp.Literal(':')) + pp.Optional(LC) + 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.Optional(LC) \ - + 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.ignore(pp.pythonStyleComment()) diff --git a/util/cmake/tests/test_lc_fixup.py b/util/cmake/tests/test_lc_fixup.py index d3680e895d..841e11615e 100755 --- a/util/cmake/tests/test_lc_fixup.py +++ b/util/cmake/tests/test_lc_fixup.py @@ -29,18 +29,18 @@ from pro2cmake import fixup_linecontinuation -from textwrap import dedent - def test_no_change(): input = "test \\\nline2\n line3" + output = "test line2\n line3" result = fixup_linecontinuation(input) - assert input == result + assert output == result def test_fix(): input = "test \\\t\nline2\\\n line3\\ \nline4 \\ \t\nline5\\\n\n\n" + output = "test line2 line3 line4 line5 \n\n" result = fixup_linecontinuation(input) - assert 'test \\\nline2 \\\n line3 \\\nline4 \\\nline5 \\\n\n\n' == result + assert output == result -- cgit v1.2.3 From cbb143e9f1396c8180401c4f8a5b1fe579902559 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 24 May 2019 17:29:21 +0200 Subject: Improve configurejson2cmake.py to handle non-compliant qmake JSON Some configure.json files contain new lines inside quoted strings, which is not conformant with the JSON spec. Add a new json_parser python module which uses pyparsing to preprocess the json files to remove the new lines inside the quoted strings, and then hands over the preprocessed content to the regular json module. Change-Id: I5f8938492068dda5640465cc78f5a7b6be0e709a Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/configurejson2cmake.py | 6 +-- util/cmake/helper.py | 26 ++++++++++ util/cmake/json_parser.py | 100 ++++++++++++++++++++++++++++++++++++++ util/cmake/pro2cmake.py | 31 +----------- 4 files changed, 131 insertions(+), 32 deletions(-) create mode 100644 util/cmake/json_parser.py (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index d1646ed082..09f76a272d 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -27,7 +27,7 @@ ## ############################################################################# -import json +import json_parser import os.path import re import sys @@ -154,8 +154,8 @@ def readJsonFromDir(dir): print('Reading {}...'.format(path)) assert os.path.exists(path) - with open(path, 'r') as fh: - return json.load(fh) + parser = json_parser.QMakeSpecificJSONParser() + return parser.parse(path) def processFiles(ctx, data): diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 682e2ec15f..b7d91921fa 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -428,3 +428,29 @@ def generate_find_package_info(lib: LibraryMapping, ind=one_ind) return result + + +def _set_up_py_parsing_nicer_debug_output(pp): + indent = -1 + + def increase_indent(fn): + def wrapper_function(*args): + nonlocal indent + indent += 1 + print("> " * indent, end="") + return fn(*args) + + return wrapper_function + + def decrease_indent(fn): + def wrapper_function(*args): + nonlocal indent + print("> " * indent, end="") + indent -= 1 + return fn(*args) + + return wrapper_function + + pp._defaultStartDebugAction = increase_indent(pp._defaultStartDebugAction) + pp._defaultSuccessDebugAction = decrease_indent(pp._defaultSuccessDebugAction) + pp._defaultExceptionDebugAction = decrease_indent(pp._defaultExceptionDebugAction) diff --git a/util/cmake/json_parser.py b/util/cmake/json_parser.py new file mode 100644 index 0000000000..6ead008f08 --- /dev/null +++ b/util/cmake/json_parser.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import 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) + + +class QMakeSpecificJSONParser: + def __init__(self, *, debug: bool = False) -> None: + self.debug = debug + self.grammar = self.create_py_parsing_grammar() + + def create_py_parsing_grammar(self): + # Keep around all whitespace. + pp.ParserElement.setDefaultWhitespaceChars('') + + def add_element(name: str, value: pp.ParserElement): + nonlocal self + if self.debug: + value.setName(name) + value.setDebug() + return value + + # Our grammar is pretty simple. We want to remove all newlines + # inside quoted strings, to make the quoted strings JSON + # compliant. So our grammar should skip to the first quote while + # keeping everything before it as-is, process the quoted string + # 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)) + + def remove_newlines_and_whitespace_in_quoted_string(tokens): + first_string = tokens[0] + replaced_string = re.sub(r'\n[ ]*', ' ', first_string) + return replaced_string + + 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) + + return Grammar + + def parse_file_using_py_parsing(self, file: str): + print('Pre processing "{}" using py parsing to remove incorrect newlines.'.format(file)) + try: + 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) + + return joined_string + except pp.ParseException as pe: + print(pe.line) + 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)) + json_parsed = json.loads(pre_processed_string) + return json_parsed diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5a2db68e67..7c2a625489 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -41,6 +41,8 @@ import typing 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, \ @@ -687,32 +689,6 @@ class QmakeParser: self.debug = debug self._Grammar = self._generate_grammar() - @staticmethod - def set_up_py_parsing_nicer_debug_output(): - indent = -1 - - def increase_indent(fn): - def wrapper_function(*args): - nonlocal indent - indent += 1 - print("> " * indent, end="") - return fn(*args) - - return wrapper_function - - def decrease_indent(fn): - def wrapper_function(*args): - nonlocal indent - print("> " * indent, end="") - indent -= 1 - return fn(*args) - - return wrapper_function - - pp._defaultStartDebugAction = increase_indent(pp._defaultStartDebugAction) - pp._defaultSuccessDebugAction = decrease_indent(pp._defaultSuccessDebugAction) - pp._defaultExceptionDebugAction = decrease_indent(pp._defaultExceptionDebugAction) - def _generate_grammar(self): # Define grammar: pp.ParserElement.setDefaultWhitespaceChars(' \t') @@ -900,9 +876,6 @@ class QmakeParser: return result -QmakeParser.set_up_py_parsing_nicer_debug_output() - - def parseProFile(file: str, *, debug=False): parser = QmakeParser(debug=debug) return parser.parseFile(file) -- cgit v1.2.3 From 499771f3eaf48a3a22769298efcb8dee7db6a04a Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 28 May 2019 14:16:01 +0200 Subject: Enhance the porting scripts with some new functionality These were some hard requirements while porting QtQml .pro files so that the generated CMake code is syntactically correct and the result buildable. This include handling of a few more different condition scopes and disabling the c++ make_unique feature test. Change-Id: Iae875ffaf8d100296e8b56b57d076455e5d72006 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 2 ++ util/cmake/pro2cmake.py | 11 +++++++++++ 2 files changed, 13 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 09f76a272d..148109e2a4 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -620,6 +620,8 @@ def parseFeature(ctx, feature, data, cm_fh): 'c++14': None, 'c++1y': None, 'c++1z': None, + # FIXME: used in qtdeclarative, drop when we require C++14 + 'cxx14_make_unique': None, 'c89': None, 'c99': None, 'ccache': None, diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7c2a625489..2f4c74b821 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -889,6 +889,17 @@ def map_condition(condition: str) -> str: condition = re.sub(r'\bequals\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\)' + 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) condition = condition.replace('*', '_x_') condition = condition.replace('.$$', '__ss_') -- cgit v1.2.3 From d843bceb0cf6ab5178fea43c194493d843665fdf Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 28 May 2019 15:42:21 +0200 Subject: Add Library Mapping for QtNetworkAuth Added library mapping information for any projects referencing the QtNetworkAuth project. Change-Id: I9c4309d26ee9895f94995d4844ffde4ee4444766 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index b7d91921fa..bb2b239aa0 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -114,6 +114,7 @@ _qt_library_map = [ LibraryMapping('multimedia', 'Qt5', 'Qt::Multimedia', extra = ['COMPONENTS', 'Multimedia']), LibraryMapping('multimediawidgets', 'Qt5', 'Qt::MultimediaWidgets', extra = ['COMPONENTS', 'MultimediaWidgets']), LibraryMapping('network', 'Qt5', 'Qt::Network', extra = ['COMPONENTS', 'Network']), + LibraryMapping('networkauth', 'Qt5', 'Qt::NetworkAuth', extra = ['COMPONENTS', 'NetworkAuth']), LibraryMapping('nfc', 'Qt5', 'Qt::Nfc', extra = ['COMPONENTS', 'Nfc']), LibraryMapping('oauth', 'Qt5', 'Qt::NetworkAuth', extra = ['COMPONENTS', 'NetworkAuth']), LibraryMapping('openglextensions', 'Qt5', 'Qt::OpenGLExtensions', extra = ['COMPONENTS', 'OpenGLExtensions']), -- cgit v1.2.3 From d908e0a47db862a43fbb11fe3f2beae01a42244b Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 29 May 2019 09:57:34 +0200 Subject: Fix uncessary relative dir suffix in pro2cmake.py When we replace the PWD key from qmake files and both the base and current directory are the same, there's no need to add an extra './' to the current working directory. This also fixes a unit test in qtsvg as it requires the passed in path to match exactly to the one outputed in the log files. Change-Id: Ide9ca6a70493e8039d3af84a9e576d8f6a313f2a Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 2f4c74b821..89bc844261 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -591,10 +591,19 @@ class Scope(object): return result def get(self, key: str, *, ignore_includes: bool = False, inherrit: bool = False) -> typing.List[str]: + + is_same_path = self.currentdir == self.basedir + if key == 'PWD': - return ['${CMAKE_CURRENT_SOURCE_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] + if is_same_path: + return ['${CMAKE_CURRENT_SOURCE_DIR}'] + else: + return ['${CMAKE_CURRENT_SOURCE_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] if key == 'OUT_PWD': - return ['${CMAKE_CURRENT_BUILD_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] + if is_same_path: + return ['${CMAKE_CURRENT_BUILD_DIR}'] + else: + return ['${CMAKE_CURRENT_BUILD_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] return self._evalOps(key, None, [], inherrit=inherrit) -- cgit v1.2.3 From 8fa646dbe2eba9674809042ff15e98f0eb1018d5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 29 May 2019 16:56:49 +0200 Subject: Add basic support for compiling applications with qmake * Generate module .pri files * Generate qconfig.pri * Propagate MODULE_CONFIG from the .pro files This enables the basic use-case of simple application builds that for example use the moc. Omitted from the patch is support for private module configurations, prl files (should we do this?) and possibly more hidden gems that need to be implemented to for example support building Qt modules with qmake. Task-number: QTBUG-75666 Change-Id: Icbf0d9ccea4cd683e4c38340b9a2320bf7951d0d Reviewed-by: Qt CMake Build Bot Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 89bc844261..e2625af266 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1575,6 +1575,10 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, if 'no_module_headers' in scope.get('CONFIG'): extra.append('NO_MODULE_HEADERS') + module_config = scope.get("MODULE_CONFIG") + if len(module_config): + extra.append('QMAKE_MODULE_CONFIG {}'.format(" ".join(module_config))) + write_main_part(cm_fh, module_name[2:], 'Module', 'add_qt_module', scope, extra_lines=extra, indent=indent, known_libraries={}, extra_keys=[]) -- cgit v1.2.3 From e88864578a881c0a14ef2dc958ee52e2aa036134 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 28 May 2019 16:19:42 +0200 Subject: Generate the c++xx standard features instead of skipping them The features are reused in qtdeclarative (and maybe somewhere else too), so they should be present. We can still map the conditions to proper CMake compile feature tests. Change-Id: I4d307d29d4d293cc23ab005b195ea346087c7162 Reviewed-by: Tobias Hunger Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 6 ------ util/cmake/helper.py | 5 ++++- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 148109e2a4..890d4dfc5f 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -615,13 +615,7 @@ def parseFeature(ctx, feature, data, cm_fh): 'alloc_malloc_h': None, 'alloc_stdlib_h': None, 'build_all': None, - 'c++11': None, # C and C++ versions 'c11': None, - 'c++14': None, - 'c++1y': None, - 'c++1z': None, - # FIXME: used in qtdeclarative, drop when we require C++14 - 'cxx14_make_unique': None, 'c89': None, 'c99': None, 'ccache': None, diff --git a/util/cmake/helper.py b/util/cmake/helper.py index bb2b239aa0..8f24a49d6b 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -302,7 +302,10 @@ def find_library_info_for_target(targetName: str) -> typing.Optional[LibraryMapp def featureName(input: str) -> str: - return re.sub(r'[^a-zA-Z0-9_]', '_', 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: -- cgit v1.2.3 From 0a96302dc12a968b3b5551c9d1168cffe5791668 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 29 May 2019 17:39:56 +0200 Subject: More fixes to conversion script while porting qtdeclarative Hardcode a few cases regarding scopes containing "qtConfig(opengl)". These arew few, and contain regular expressions, to just manually check and replace them. Add a new entry to _qt_library_map for handling QmlModels module. Fix Scope.children property to recursively access the .children property on included scopes (instead of just ._children) so that we get the full list of scopes from included children that include other scopes. This is needed for nested .pri files. Fix mapping of "win*" to WIN32. Change-Id: If949a8051f517683e56cda605733719daadb384a Reviewed-by: Simon Hausmann Reviewed-by: Tobias Hunger Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 2 ++ util/cmake/pro2cmake.py | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 8f24a49d6b..97397e67ec 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -132,6 +132,7 @@ _qt_library_map = [ LibraryMapping('qmldebug', 'Qt5', 'Qt::QmlDebug', extra = ['COMPONENTS', 'QmlDebug']), LibraryMapping('qmldevtools', 'Qt5', 'Qt::QmlDevTools', extra = ['COMPONENTS', 'QmlDevTools']), LibraryMapping('qml', 'Qt5', 'Qt::Qml', extra = ['COMPONENTS', 'Qml']), + LibraryMapping('qmlmodels', 'Qt5', 'Qt::QmlModels', extra = ['COMPONENTS', 'QmlModels']), LibraryMapping('qmltest', 'Qt5', 'Qt::QuickTest', extra = ['COMPONENTS', 'QuickTest']), LibraryMapping('qtmultimediaquicktools', 'Qt5', 'Qt::MultimediaQuick', extra = ['COMPONENTS', 'MultimediaQuick']), LibraryMapping('quick3danimation', 'Qt5', 'Qt::3DQuickAnimation', extra = ['COMPONENTS', '3DQuickAnimation']), @@ -325,6 +326,7 @@ def map_qt_library(lib: str) -> str: platform_mapping = { 'win32': 'WIN32', + 'win': 'WIN32', 'unix': 'UNIX', 'darwin': 'APPLE', 'linux': 'LINUX', diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e2625af266..b45f0e8805 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -523,7 +523,7 @@ class Scope(object): def children(self) -> typing.List['Scope']: result = list(self._children) for include_scope in self._included_children: - result += include_scope._children + result += include_scope.children return result def dump(self, *, indent: int = 0) -> None: @@ -891,6 +891,13 @@ 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'\bif\s*\((.*?)\)', r'\1', condition) condition = re.sub(r'\bisEmpty\s*\((.*?)\)', r'\1_ISEMPTY', condition) condition = re.sub(r'\bcontains\s*\((.*?),\s*"?(.*?)"?\)', -- cgit v1.2.3 From 962ee4ea37f23000bbc342c5d50838abf0bcdb8e Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 3 Jun 2019 17:42:43 +0200 Subject: Handle a few more condition types in pro2cmake qtdeclarative has a few conditions that check the gcc version via variables like QT_GCC_MAJOR_VERSION. To handle that, parse the CMake compiler version using qt_parse_version_string() into separate variables like QT_COMPILER_VERSION_MAJOR, QT_COMPILER_VERSION_MINOR and QT_COMPILER_VERSION_PATCH. We can then map the conditions appropriately. Also, handle isEqual(foo, bar), which is equivalent equals(foo,bar). Change-Id: I74575c733b44f1f42451e00038b3f113fd353915 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b45f0e8805..dfe407da6d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -898,12 +898,37 @@ def map_condition(condition: str) -> str: condition = re.sub(r'^qtConfig\(opengl\.\*\)$', r'QT_FEATURE_opengl', condition) condition = re.sub(r'^win\*$', r'win', 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' + + version = match_obj.group(3) + return '(QT_COMPILER_VERSION_{} {} {})'.format(version_type, operator, version) + + # TODO: Possibly fix for other compilers. + 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 + # unbalanced when there are nested expressions. + # 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'\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) -- cgit v1.2.3 From 9e6781b69e19f66cb56b1875c583da4682909a85 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 4 Jun 2019 09:19:17 +0200 Subject: cmake: Fix xcb build Need qt_find_package(X11_XCB) in src/gui/configure.cmake since we're using it in the file qt_feature("xcb_xlib" PRIVATE LABEL "XCB Xlib" CONDITION QT_FEATURE_xlib AND X11_XCB_FOUND ) Need qt_find_package(XRender PROVIDED_TARGETS PkgConfig::xrender) in src/plugins/platforms/xcb/CMakeLists.tx since we're using it in the file extend_target(XcbQpa CONDITION QT_FEATURE_xcb_native_painting AND QT_FEATURE_xrender PUBLIC_LIBRARIES PkgConfig::xrender ) Use capital XRender in pkgconfig to be more consistent on how XRender is called everywhere else Change-Id: I403ead2cc123b08f741c5142f20db88987657ba8 Reviewed-by: Alexandru Croitor Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 97397e67ec..60128a8b03 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -249,7 +249,7 @@ _library_map = [ 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('xrender', 'XRender', 'PkgConfig::XRender'), LibraryMapping('zlib', 'ZLIB', 'ZLIB::ZLIB', extra=['REQUIRED']), LibraryMapping('zstd', 'ZSTD', 'ZSTD::ZSTD'), LibraryMapping('tiff', 'TIFF', 'TIFF::TIFF'), -- cgit v1.2.3 From 0900298d466e819dd6d8fd39c0be333dc09af189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Fri, 3 May 2019 16:03:15 +0200 Subject: cmake: register plug-ins, create dependencies file This commit introduces infrastructure work to allow static builds of Qt to handle importing of plug-ins. Change-Id: Ife0ca3ca7276ea8ec96fe0eb6adf934fad7620ec Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index dfe407da6d..3b37991cba 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1193,11 +1193,6 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, # mark RESOURCES as visited: scope.get('RESOURCES') - plugin_type = scope.get_string('PLUGIN_TYPE') - - if plugin_type: - cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type)) - 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) @@ -1611,6 +1606,10 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, if len(module_config): extra.append('QMAKE_MODULE_CONFIG {}'.format(" ".join(module_config))) + module_plugin_types = scope.get_files('MODULE_PLUGIN_TYPES') + if module_plugin_types: + extra.append('PLUGIN_TYPES {}'.format(" ".join(module_plugin_types))) + write_main_part(cm_fh, module_name[2:], 'Module', 'add_qt_module', scope, extra_lines=extra, indent=indent, known_libraries={}, extra_keys=[]) @@ -1726,8 +1725,18 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): plugin_name = scope.TARGET assert plugin_name + extra = [] + + plugin_type = scope.get_string('PLUGIN_TYPE') + if plugin_type: + extra.append('TYPE {}'.format(plugin_type)) + + plugin_class_name = scope.get_string('PLUGIN_CLASS_NAME') + if plugin_class_name: + extra.append('CLASS_NAME {}'.format(plugin_class_name)) + write_main_part(cm_fh, plugin_name, 'Plugin', 'add_qt_plugin', scope, - indent=indent, known_libraries={}, extra_keys=[]) + indent=indent, extra_lines=extra, known_libraries={}, extra_keys=[]) def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, -- cgit v1.2.3 From 8fea3ec4e77dfebe2e84e10ae0f014d03b9098b3 Mon Sep 17 00:00:00 2001 From: Kevin Funk Date: Tue, 4 Jun 2019 16:19:46 +0200 Subject: pro2cmake: A couple more fixes regarding examples Changes: - Make sure the footer is passed along to callees - Make sure RESOURCES are part of add_executable(...) for AUTORCC Change-Id: I4546bacd2e41ca8acc1a9351af54d325afb715ab Reviewed-by: Alexandru Croitor Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3b37991cba..dc6c1fc873 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1140,10 +1140,10 @@ def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str, def write_all_source_file_lists(cm_fh: typing.IO[str], scope: Scope, header: str, *, - indent: int = 0, footer: str = ''): + indent: int = 0, footer: str = '', extraKeys: typing.List[str]): write_source_file_list(cm_fh, scope, header, - ['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'], - indent) + ['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'] + extraKeys, + indent, footer=footer) def write_defines(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: str, *, @@ -1153,21 +1153,21 @@ def write_defines(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: str, *, defines = [d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"') for d in defines] - write_list(cm_fh, defines, cmake_parameter, indent) + 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')] - write_list(cm_fh, includes, cmake_parameter, indent) + 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')] - write_list(cm_fh, compile_options, cmake_parameter, indent) + write_list(cm_fh, compile_options, cmake_parameter, indent, footer=footer) def write_library_section(cm_fh: typing.IO[str], scope: Scope, *, @@ -1700,13 +1700,13 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, if gui: add_executable += ' WIN32 MACOSX_BUNDLE' - write_all_source_file_lists(cm_fh, scope, add_executable, indent=0) + write_all_source_file_lists(cm_fh, scope, add_executable, indent=0, extraKeys = ['RESOURCES']) cm_fh.write(')\n') write_include_paths(cm_fh, scope, 'target_include_directories({}'.format(binary_name), indent=0, footer=')') - write_defines(cm_fh, scope, 'target_compile_definitions({}'.format(binary_name), + 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=')') -- cgit v1.2.3 From 772f5dbf1c30b9b0a26ac48a8ae1e1d5cf58b130 Mon Sep 17 00:00:00 2001 From: Kevin Funk Date: Tue, 4 Jun 2019 15:27:48 +0200 Subject: pro2cmake: Always add a newline after func calls Fixes the situation where we end up with e.g.: ``` target_link_libraries(... )target_link_libraries(... ) ``` Change-Id: I455563b24850db7f389746385af53cd606343274 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index dc6c1fc873..4b2b8dc5c7 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1125,7 +1125,7 @@ def write_list(cm_fh: typing.IO[str], entries: typing.List[str], for s in sort_sources(entries): cm_fh.write('{}{}{}\n'.format(ind, extra_indent, s)) if footer: - cm_fh.write('{}{}'.format(ind, footer)) + cm_fh.write('{}{}\n'.format(ind, footer)) def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str, -- cgit v1.2.3 From 75bb0b6cd12959618f91e11a8f69b1a0af04bb41 Mon Sep 17 00:00:00 2001 From: Kevin Funk Date: Tue, 4 Jun 2019 15:28:59 +0200 Subject: pro2cmake: Add PUBLIC to target_include_directories Change-Id: Ie4e882a5713022bfaec7182172be69616ff8b91c Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4b2b8dc5c7..072fd4a08b 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1704,7 +1704,7 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, cm_fh.write(')\n') - write_include_paths(cm_fh, scope, 'target_include_directories({}'.format(binary_name), + 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=')') -- cgit v1.2.3 From 8842cb5337c78b934e8da7752a9f23d36a671a1f Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 4 Jun 2019 14:28:14 +0200 Subject: Improve special_case_helper.py Make sure to set the author user and email for the temporary git repo, so that the commits succeed on systems that don't have a global git author. Also fix run_process_quiet to print stderr and stdout in case if the execution failed for some reason. Change-Id: I0ddb61730114b3e9c59e2d23480df0ced8d6e772 Reviewed-by: Albert Astals Cid Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/special_case_helper.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/special_case_helper.py b/util/cmake/special_case_helper.py index 8777b9c4db..57b91b6cb1 100644 --- a/util/cmake/special_case_helper.py +++ b/util/cmake/special_case_helper.py @@ -146,7 +146,13 @@ def run_process_quiet(args_string: str, debug=False) -> None: if debug: print('Running command: "{}\"'.format(args_string)) args_list = args_string.split() - subprocess.run(args_list, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + 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)) def does_file_have_conflict_markers(file_path: str, debug=False) -> bool: @@ -228,19 +234,23 @@ 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) 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) # Copy the original "modified" file (with the special cases) # and make a new commit. + 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) # Checkout the commit with "clean" file again, and create a # new branch. - run_process_quiet('git checkout HEAD~', 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. @@ -250,7 +260,7 @@ class SpecialCaseHandler(object): # Merge the "old" branch with modifications into the "new" # branch with the newly generated file. - run_process_quiet('git merge master', 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. -- cgit v1.2.3 From b64e8e721fd9117af585151b93a18fec1c285059 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 4 Jun 2019 15:17:33 +0200 Subject: Fix special_case_handler to handle git add prev_CMakeLists.txt better When using run_pro2cmake.py, it spawns multiple instances of pro2cmake.py. If more than 1 of the instances need to git add prev_CMakeLists.txt at the same time, git add might fail due to the acquired index.lock. The cleaner solution would be to use a file lock as a mutex, but that requires an external pypi package. Use a poor man solution of retrying the git add with a time delay for a finite amount of times. Change-Id: I2031f6e29ae499526cb4f1753e4387e7f4fab0ab Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/special_case_helper.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/special_case_helper.py b/util/cmake/special_case_helper.py index 57b91b6cb1..1cb25ac1e2 100644 --- a/util/cmake/special_case_helper.py +++ b/util/cmake/special_case_helper.py @@ -87,6 +87,7 @@ import re import os import subprocess import filecmp +import time from shutil import copyfile from shutil import rmtree @@ -142,7 +143,7 @@ def check_if_git_in_path() -> bool: return False -def run_process_quiet(args_string: str, debug=False) -> None: +def run_process_quiet(args_string: str, debug=False) -> bool: if debug: print('Running command: "{}\"'.format(args_string)) args_list = args_string.split() @@ -153,6 +154,8 @@ def run_process_quiet(args_string: str, debug=False) -> None: # an error for us. if 'git merge' not in args_string: print('Error while running: "{}"\n{}'.format(args_string, e.stdout)) + return False + return True def does_file_have_conflict_markers(file_path: str, debug=False) -> bool: @@ -289,7 +292,28 @@ class SpecialCaseHandler(object): # merge result, save the new "clean" file for future # regenerations. copyfile_log(self.generated_file_path, self.prev_file_path, debug=self.debug) - run_process_quiet("git add {}".format(self.prev_file_path), debug=self.debug) + + # Attempt to git add until we succeed. It can fail when + # run_pro2cmake executes pro2cmake in multiple threads, and git + # has acquired the index lock. + success = False + 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) + 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.') + if failed_once and success: + 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)) def handle_special_cases_helper(self) -> bool: """ -- cgit v1.2.3 From 9231204e2ccab611af0df9a18761cd601ba20e92 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 4 Jun 2019 22:40:42 +0200 Subject: Fix extra_keys in write_all_source_file_lists to be optional Initially it was added as a required argument, but not all usages of the function where adjusted, so the script failed. Make it optional. Also change the styling of the argument to be snake cased. Amends 8fea3ec4e77dfebe2e84e10ae0f014d03b9098b3. Change-Id: I568800401bb5af153b7bb5229f134c2f74b87468 Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 072fd4a08b..98706d1064 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1140,9 +1140,12 @@ def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str, def write_all_source_file_lists(cm_fh: typing.IO[str], scope: Scope, header: str, *, - indent: int = 0, footer: str = '', extraKeys: typing.List[str]): + indent: int = 0, footer: str = '', + extra_keys: typing.Union[typing.List[str], None] = None): + if extra_keys is None: + extra_keys = [] write_source_file_list(cm_fh, scope, header, - ['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'] + extraKeys, + ['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'] + extra_keys, indent, footer=footer) @@ -1700,7 +1703,7 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, if gui: add_executable += ' WIN32 MACOSX_BUNDLE' - write_all_source_file_lists(cm_fh, scope, add_executable, indent=0, extraKeys = ['RESOURCES']) + write_all_source_file_lists(cm_fh, scope, add_executable, indent=0, extra_keys=['RESOURCES']) cm_fh.write(')\n') -- cgit v1.2.3 From d0eb985f7413c62c2bff26caaf84bec2e6893131 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 28 May 2019 19:11:36 +0200 Subject: Improve run_pro2cmake.py Add two new options: If you pass --only-existing, the pro2cmake script will only be executed on .pro files that already have a CMakeLists.txt next to them. This is useful if you modify pro2cmake, and only want to regenerate existing files. If you pass --only-qtbase-main-modules, the script will be executed on the main modules in qtbase/src. This is useful if you want to check if your pro2cmake modification works correctly on the more complicated projects. Change-Id: I5228411a252dbef6d77f01ca742a7b98583c5a75 Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/run_pro2cmake.py | 137 +++++++++++++++++++++++++++++++++----------- 1 file changed, 104 insertions(+), 33 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index 47bc6b661f..bc64fb3fbb 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -31,18 +31,24 @@ import glob import os import subprocess import concurrent.futures -import sys import typing +import argparse +from argparse import ArgumentParser -script_path = os.path.dirname(os.path.abspath(__file__)) -base_path = os.path.dirname(script_path) -pro2cmake = os.path.join(script_path, 'pro2cmake.py') -if len(sys.argv) > 1: - base_path = os.path.abspath(sys.argv[1]) +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('path', metavar='', type=str, + help='The path where to look for .pro files.') + return parser.parse_args() -def find_all_pro_files(): + +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. """ @@ -54,9 +60,49 @@ def find_all_pro_files(): all_files = [] previous_dir_name: str = None - for pro_file in sorted(glob.glob(os.path.join(base_path, '**/*.pro'), - recursive=True), - key=sorter): + + 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')): + return True + return False + + def qtbase_main_modules_filter(path): + main_modules = [ + 'corelib', + 'network', + 'gui', + 'widgets', + 'testlib', + 'printsupport', + 'opengl', + 'sql', + 'dbus', + 'concurrent', + 'xml', + ] + path_suffixes = ['src/{}/{}.pro'.format(m, m, '.pro') for m in main_modules] + + for path_suffix in path_suffixes: + if path.endswith(path_suffix): + return True + return False + + filter_result = glob_result + filter_func = None + if args.only_existing: + filter_func = cmake_lists_exists_filter + elif args.only_qtbase_main_modules: + filter_func = qtbase_main_modules_filter + + if filter_func: + print('Filtering.') + filter_result = [p for p in filter_result if filter_func(p)] + + for pro_file in sorted(filter_result, key=sorter): dir_name = os.path.dirname(pro_file) if dir_name == previous_dir_name: print("Skipping:", pro_file) @@ -66,31 +112,56 @@ def find_all_pro_files(): return all_files -failed_files = [] -all_files = find_all_pro_files() -files_count = len(all_files) +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) + + if args.only_qtbase_main_modules: + # qtbase main modules take longer than usual to process. + workers = 2 + + with concurrent.futures.ThreadPoolExecutor(max_workers=workers, initializer=os.nice, initargs=(10,)) as pool: + 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 + result = subprocess.run((pro2cmake, os.path.basename(filename)), + cwd=os.path.dirname(filename), + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + stdout = 'Converted[{}/{}]: {}\n'.format(index, total, filename) + 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))): + if return_code: + failed_files.append(filename) + print(stdout) + + return failed_files + + +def main() -> None: + args = parse_command_line() -with concurrent.futures.ThreadPoolExecutor(initializer=os.nice, initargs=(10,)) as pool: + script_path = os.path.dirname(os.path.abspath(__file__)) + pro2cmake = os.path.join(script_path, 'pro2cmake.py') + base_path = args.path - def _process_a_file(data: typing.Tuple[str, int, int]) -> typing.Tuple[int, str, str]: - filename, index, total = data - result = subprocess.run((pro2cmake, os.path.basename(filename)), - cwd=os.path.dirname(filename), - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - stdout = 'Converted[{}/{}]: {}\n'.format(index, total, filename) - return result.returncode, filename, stdout + result.stdout.decode() + 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.') - 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) + if failed_files: + print('The following files were not successfully ' + 'converted ({} of {}):'.format(len(failed_files), files_count)) + for f in failed_files: + print(' "{}"'.format(f)) -if failed_files: - print('The following files were not successfully ' - 'converted ({} of {}):'.format(len(failed_files), files_count)) - for f in failed_files: - print(' "{}"'.format(f)) +if __name__ == '__main__': + main() -- cgit v1.2.3 From 35dc8f496dc324a816dc17e80b744bfe864ec261 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 4 Jun 2019 17:34:27 +0200 Subject: Map module.gui to if TARGET Qt::Gui in configure.cmake Some features check for module existence. Adapt conversion script and QtBuild feature condition parser to handle that. Change-Id: I063e49a6fe9f8e9cf3aec985fd78ed4430398586 Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 890d4dfc5f..2de2567803 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -281,6 +281,8 @@ def map_condition(condition): 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) == 'arch': if match.group(2) == 'i386': -- cgit v1.2.3 From 38b7ee3533f553f1bacd1a80ce15a5e604f82721 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 5 Jun 2019 10:30:49 +0200 Subject: Revert "Map module.gui to if TARGET Qt::Gui in configure.cmake" It breaks some conditions, and then the build fails not finding some private header files in qpa, etc. This reverts commit 35dc8f496dc324a816dc17e80b744bfe864ec261. Change-Id: I1b51eac06fe9186181d3f0a7c78f22da7be534e2 Reviewed-by: Tobias Hunger --- util/cmake/configurejson2cmake.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 2de2567803..890d4dfc5f 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -281,8 +281,6 @@ def map_condition(condition): 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) == 'arch': if match.group(2) == 'i386': -- cgit v1.2.3 From 9ad193cc3d3379587c283a2b2cc21f7e6a4fb0cf Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 5 Jun 2019 09:49:30 +0200 Subject: Fix write_all_source_file_lists type annotation Change-Id: I6e22d21562a0923079b90cf9b3e6d15dfe16ff29 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 98706d1064..fcf9b1060c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1141,7 +1141,7 @@ def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str, def write_all_source_file_lists(cm_fh: typing.IO[str], scope: Scope, header: str, *, indent: int = 0, footer: str = '', - extra_keys: typing.Union[typing.List[str], None] = None): + extra_keys: typing.Optional[typing.List[str]] = None): if extra_keys is None: extra_keys = [] write_source_file_list(cm_fh, scope, header, -- cgit v1.2.3 From bfa209dfa557e7c30d654add09f5c8f97cfc2492 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 4 Jun 2019 15:55:41 +0200 Subject: cmake: build with exceptions disabled by default Only re-enable exceptions for the modules that do CONFIG+=exceptions in qmake Change-Id: I9f19078adbdc1b8fa3d4102fb51a099e7e35522e Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index fcf9b1060c..bd26e5782a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1545,6 +1545,9 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, # Evaluate total condition of all scopes: recursive_evaluate_scope(scope) + 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) -- cgit v1.2.3 From 2401435d89c62be48ee7cf798aa3f47fb3faf670 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 4 Jun 2019 17:34:27 +0200 Subject: Map module.gui to if TARGET Qt::Gui in configure.cmake Some features check for module / target existence. Adapt conversion script to handle that. Reland after fixing it. Change-Id: If4fb942c2e0d16e76a0b9b767bf478527851b0f7 Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 890d4dfc5f..2de2567803 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -281,6 +281,8 @@ def map_condition(condition): 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) == 'arch': if match.group(2) == 'i386': -- cgit v1.2.3 From 527f3bb31ce066749741bd84145270336e4bbc7a Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Wed, 5 Jun 2019 10:48:56 +0200 Subject: CMake: Add WrapPCRE2 package The WrapPCRE2 package handles the PCRE2 packages that have targets, and reuse them. Change-Id: I24b0b51f507703cd8287f845f7e425f62dd2c3d6 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 60128a8b03..a184ce1f4d 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -215,7 +215,7 @@ _library_map = [ 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('pcre2', 'PCRE2', 'PCRE2', extra = ['REQUIRED']), + LibraryMapping('pcre2', 'WrapPCRE2', 'WrapPCRE2::WrapPCRE2', extra = ['REQUIRED']), LibraryMapping('posix_iconv', None, None), LibraryMapping('pps', 'PPS', 'PPS::PPS'), LibraryMapping('psql', 'PostgreSQL', 'PostgreSQL::PostgreSQL'), -- cgit v1.2.3 From a6c11d3e09f8ebc923eb043e6c090997d21f2ac9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 5 Jun 2019 13:49:08 +0200 Subject: Add support for private module .pri files Generate a pri file for public and private interfaces, but map CONFIG += internal_module to a cmake option and skip the former if set. Task-number: QTBUG-75666 Change-Id: I3f4baf1277094f4c22149a9e8769734baf9a235f Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index bd26e5782a..bfd791a196 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1605,6 +1605,8 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, extra = [] if 'static' in scope.get('CONFIG'): 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') -- cgit v1.2.3 From 82941a3f1bab6c385376761d53456c838b6cbaad Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 5 Jun 2019 22:41:38 +0200 Subject: Fix some configure.json conditions for qtimageformats The auto-generated add_subdirectory conditions in qtimageformats use QT_FEATURE_foo and not QT_FEATURE_system_foo. But the non-system ones don't actually check if the respective qt_find_package(TIFF) got found. Fix the conditions of the non system features to be the same as the system ones. Change-Id: I96f889cf7061721b829d562707c42aa0e29720df Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 2de2567803..f929ac142d 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -716,9 +716,15 @@ def parseFeature(ctx, feature, data, cm_fh): '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 } -- cgit v1.2.3 From 4e907f1f62e2dc5676aeb48b99494709b7bf9d50 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 6 Jun 2019 10:31:03 +0200 Subject: Fix comment fixup in pro2cmake Comments should be removed before line continuations, otherwise the semantics of an assignment change. Found this during reconversion of qtimageformats. Adjust test to specifically test for all the expected values. Amends 76f5b784ce54730ed8d6ad4bb9c39c9a05c5d81d. Change-Id: Iaa46bbc9cbd7b2390fe9b5f0078ac33d225a9258 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- util/cmake/tests/test_parsing.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index bfd791a196..2eda9b69d3 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -870,8 +870,8 @@ class QmakeParser: contents = file_fd.read() old_contents = contents - contents = fixup_linecontinuation(contents) contents = fixup_comments(contents) + contents = fixup_linecontinuation(contents) if old_contents != contents: print('Warning: Fixed line continuation in .pro-file!\n' diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index 11d1ed093f..f924b13913 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -308,6 +308,25 @@ def test_realworld_lc(): def test_realworld_lc_with_comment_in_between(): result = parse_file(_tests_path + '/data/lc_with_comment.pro') + + my_var = result[1]['value'][0] + assert my_var == 'foo' + + my_var = result[2]['value'][0] + assert my_var == 'foo2' + + my_var = result[3]['value'][0] + assert my_var == 'foo3' + + my_var = result[4]['value'][0] + assert my_var == 'foo4' + + my_var = result[5]['value'][0] + assert my_var == 'foo5' + + sub_dirs = result[0]['value'] + assert sub_dirs[0] == 'tga' + assert sub_dirs[1] == 'wbmp' assert len(result) == 6 -- cgit v1.2.3 From 38b1474c516810b7c1bef8d660ff594817f1cf08 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 7 Jun 2019 10:51:55 +0200 Subject: Handle minimal_syncqt as not running syncqt for now This is needed for QmlDevTools in qtdeclarative. Change-Id: I41adec15f292c91192e171b45d1e5d48764c37c4 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 2eda9b69d3..0de5fce559 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1609,6 +1609,8 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, 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') module_config = scope.get("MODULE_CONFIG") if len(module_config): -- cgit v1.2.3 From 44c9ad561799b51c1e4d10c8b9821fbff6143ef1 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 7 Jun 2019 18:13:53 +0200 Subject: Improve pro2cmake.py more Fix incorrect usage of CMAKE_CURRENT_BUILD_DIR, there is no such CMake variable, it's actually CMAKE_CURRENT_BINARY_DIR. Also if the host_build option is set when building a module, the library should be a static library. Change-Id: I9fb39905118dbd7f33d9821960eaed11f20b30c6 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0de5fce559..94c7965638 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -601,9 +601,9 @@ class Scope(object): return ['${CMAKE_CURRENT_SOURCE_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] if key == 'OUT_PWD': if is_same_path: - return ['${CMAKE_CURRENT_BUILD_DIR}'] + return ['${CMAKE_CURRENT_BINARY_DIR}'] else: - return ['${CMAKE_CURRENT_BUILD_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] + return ['${CMAKE_CURRENT_BINARY_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] return self._evalOps(key, None, [], inherrit=inherrit) @@ -1603,7 +1603,12 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, print('XXXXXX Module name {} does not start with Qt!'.format(module_name)) extra = [] - if 'static' in scope.get('CONFIG'): + + # 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') + + if is_static: extra.append('STATIC') if 'internal_module' in scope.get('CONFIG'): extra.append('INTERNAL_MODULE') -- cgit v1.2.3 From 118e04013ab35fd5b6607ca239a44ab80fb064ed Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 11 Jun 2019 15:13:59 +0200 Subject: Improve special case handler to work on Windows There were some issues with not finding the git executable and trying to remove read-only files / files that are still held by a process. Change-Id: I7f587c4e96cff763cc0b3438d9ed2249da8f122f Reviewed-by: Simon Hausmann --- util/cmake/special_case_helper.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/special_case_helper.py b/util/cmake/special_case_helper.py index 1cb25ac1e2..b9cb93dce0 100644 --- a/util/cmake/special_case_helper.py +++ b/util/cmake/special_case_helper.py @@ -88,6 +88,8 @@ import os import subprocess import filecmp import time +import typing +import stat from shutil import copyfile from shutil import rmtree @@ -136,8 +138,11 @@ def copyfile_log(src: str, dst: str, debug=False): 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') + if is_win: + git_path += '.exe' if os.path.isfile(git_path) and os.access(git_path, os.X_OK): return True return False @@ -187,6 +192,17 @@ def create_file_with_no_special_cases(original_file_path: str, no_special_cases_ 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): + # 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)) + + class SpecialCaseHandler(object): def __init__(self, @@ -275,14 +291,14 @@ class SpecialCaseHandler(object): print('Git merge conflict resolution process failed. Exception: {}'.format(e)) raise e finally: + os.chdir(current_dir) + # Remove the temporary repo. try: if not self.keep_temporary_files: - rmtree(repo_absolute_path) + rmtree(repo_absolute_path, onerror=rm_tree_on_error_handler) except Exception as e: - print(e) - - os.chdir(current_dir) + print('Error removing temporary repo. Exception: {}'.format(e)) def save_next_clean_file(self): files_are_equivalent = filecmp.cmp(self.generated_file_path, self.post_merge_file_path) -- cgit v1.2.3 From 6732fa3a291e77acad3ab6ba829d1026462dc139 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 11 Jun 2019 15:46:31 +0200 Subject: Fix linking of examples Provide add_qt_gui_executable() as function in our public API that takes care of automaticWinMain linkage. We can use this in the future to encapsulate similarplatform-specific behavior and adjustments, such as module generation onAndroid. In order for the examples to see the function in Qt5CoreMacros, three more additional fixes were required: * Do the build_repo_end() call _before_ attempting to build the examples, as we need the build_repo_end() to include QtPostProcess and complete the creation of all the target config files. Otherwise the find_package() calls in the examples see something incomplete. * Add more QT_NO_CREATE_TARGET guards * Always call find_dependency on the dependencies, regardless of the target creation mode. This way a find_package(Qt5 COMPONENTS Widgets) will still load Qt5CoreMacros. Change-Id: I03ce856e2f4312a050fe8043b8331cbe8a6c93e6 Reviewed-by: Qt CMake Build Bot Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 94c7965638..5e4e9abc9e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1711,7 +1711,7 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, (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(binary_name); + add_executable = 'add_{}executable({}'.format("qt_gui_" if gui else "", binary_name); if gui: add_executable += ' WIN32 MACOSX_BUNDLE' -- cgit v1.2.3 From efa9998521cb061051fe8b75d0df3206d0b32ec5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 12 Jun 2019 10:21:40 +0200 Subject: Fix compiling of examples on Android * Simplify add_qt_gui_executable() to not require WIN32/MACOSX_BUNDLE but provide it implicitly. It's redundant :) * When on Android, build a module (shared library), just like qmake. This requires an additional library destination in the install() call, but that's ignored on other platforms. * Fix typos in the android deployment generation settings function * Use the correct cache variable to determine whether we're inside a Qt build or not. Right now this only works inside Qt builds anyway as QtPlatformAndroid.cmake is not publically accessible. Change-Id: If1c763c31a7a83d0e0d854362ba7901657f63eb5 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5e4e9abc9e..28724b1fc0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1712,8 +1712,6 @@ def write_example(cm_fh: typing.IO[str], scope: 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); - if gui: - add_executable += ' WIN32 MACOSX_BUNDLE' write_all_source_file_lists(cm_fh, scope, add_executable, indent=0, extra_keys=['RESOURCES']) @@ -1733,6 +1731,7 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, 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') -- cgit v1.2.3 From a9d2c5b6d7fa6b7365db8690f57aa78002c8bc4b Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 24 Jun 2019 14:54:40 +0200 Subject: Android: Final changes for APK generation Generate the ${MODULE}-android-dependencies.xml for the androiddeployqt tool. This will ensure all the right plugins and dependencies are packaged when generating the apk. This change also changes the visibility for executable to default/public. Not having this will cause the application to crash as we can't locate main() in the executable (shared library). Additionally pro2cmake conversion script has been updated to perform the required conversions for the Android settings. Finally, the 6 projects in QtBase that have Android dependencies have been updated with the new script and the step that produces the xml files in run at the end in QtPostProcess.cmake. Change-Id: I9774ba1b123bc11cae972fa37054ef2c51988498 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 28724b1fc0..96e2bacae9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1536,6 +1536,32 @@ def write_simd_part(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in header = 'add_qt_simd_part({} SIMD {}\n'.format(target, simd), 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' ] + + has_no_values = True + for key in keys: + value = scope.get(key) + if len(value) != 0: + if has_no_values: + if scope.condition: + cm_fh.write('\n{}if(ANDROID AND ({}))\n'.format(spaces(indent), scope.condition)) + else: + cm_fh.write('\n{}if(ANDROID)\n'.format(spaces(indent))) + 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))) + indent -= 1 + + if not has_no_values: + cm_fh.write('{}endif()\n'.format(spaces(indent))) def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, cmake_function: str, scope: Scope, *, @@ -1577,6 +1603,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, write_simd_part(cm_fh, name, scope, indent) + write_android_part(cm_fh, name, scopes[0], indent) + ignored_keys_report = write_ignored_keys(scopes[0], spaces(indent)) if ignored_keys_report: cm_fh.write(ignored_keys_report) @@ -1590,12 +1618,12 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, for c in scopes[1:]: c.reset_visited_keys() + write_android_part(cm_fh, name, c, indent=indent) write_extend_target(cm_fh, name, c, indent=indent) ignored_keys_report = write_ignored_keys(c, spaces(indent)) if ignored_keys_report: cm_fh.write(ignored_keys_report) - def write_module(cm_fh: typing.IO[str], scope: Scope, *, indent: int = 0) -> None: module_name = scope.TARGET -- cgit v1.2.3 From 8c60782124aa7fe54ade1f67bd85b11068735314 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 26 Jun 2019 10:51:30 +0200 Subject: Fix XCB feature detection The xcb feature controls whether to build the xcb platform plugin. The feature depends among other things on qt_find_package(XCB 1.9 PROVIDED_TARGETS XCB::XCB) to succeed. This means at the moment setting XCB_FOUND to true, that's what the condition checks. Later on, in configure.cmake, we also check for other components in the XCB package, for example: qt_find_package(XCB COMPONENTS XINPUT PROVIDED_TARGETS XCB::XINPUT) If this component is not available, the xcb platform plugin has perhaps reduce functionality, but it should be built and the feature should be abled. However it isn't, because when that find_package call fails, XCB_FOUND will be set to false. And that in turn will disable the feature as the condition fails. Therefore this patch changes the condition to check for the presence of the XCB::XCB target instead. Change-Id: I534087d8c3b7ff5edf81a5ffcf16cc0bb78b9fb7 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index e80813a1f7..3be4a505a7 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -229,7 +229,7 @@ _library_map = [ LibraryMapping('vulkan', 'Vulkan', 'Vulkan::Vulkan'), LibraryMapping('wayland_server', 'Wayland', 'Wayland::Server'), LibraryMapping('x11sm', 'X11', '${X11_SM_LIB} ${X11_ICE_LIB}', resultVariable="X11_SM"), - LibraryMapping('xcb', 'XCB', 'XCB::XCB', extra = ['1.9']), + 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'), -- cgit v1.2.3 From faac0ef8c6f418aab019d7eb96d32178cddab4be Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 11 Jul 2019 14:43:49 +0200 Subject: Add missing expansion for $$_PRO_FILE_PWD_ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests in qtdeclarative were failing because the above variable was expanded to an empty string. This causes the tests to be unable to locate their test data when executed. Change-Id: Ibc3c094123f25d688a73c11886ac1673b6930c54 Reviewed-by: Jędrzej Nowacki Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 96e2bacae9..4e128c4c15 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -594,7 +594,7 @@ class Scope(object): is_same_path = self.currentdir == self.basedir - if key == 'PWD': + if key == '_PRO_FILE_PWD_' or key == 'PWD': if is_same_path: return ['${CMAKE_CURRENT_SOURCE_DIR}'] else: -- cgit v1.2.3 From 30b374637070581f04fc75287a32be943b00d422 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 5 Jun 2019 13:39:41 +0200 Subject: Add support for QML plugins Add the necessary code to both the QtBuild and pro2cmake to be able to handle qml plugins in qt declarative. Add condition replacement for QTDIR_build to QT_BUILDING_QT so that certain qml examples work correctly when being built in the build directory. Fix add_qt_resources not being updated during build after changes were made. Files list used as dependencies were not populated. Add missing module mappings for qtdeclarative. Change-Id: I0f71d0a3a0e7e97ba96807950d11cffaee04d9b2 Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 2 ++ util/cmake/pro2cmake.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 3be4a505a7..fcd38883c3 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -176,6 +176,8 @@ _qt_library_map = [ 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. ] diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4e128c4c15..495c0e14ce 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1441,6 +1441,7 @@ def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', def map_to_cmake_condition(condition: typing.Optional[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]*)', @@ -1490,7 +1491,6 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, write_resources(cm_fh, target, scope, indent) - def flatten_scopes(scope: Scope) -> typing.List[Scope]: result = [scope] # type: typing.List[Scope] for c in scope.children: @@ -1770,8 +1770,14 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): extra = [] plugin_type = scope.get_string('PLUGIN_TYPE') + is_qml_plugin = any('qml_plugin' == s for s in scope.get('_LOADED')) + target_path = scope.get_string('TARGETPATH') + if plugin_type: extra.append('TYPE {}'.format(plugin_type)) + elif is_qml_plugin: + extra.append('TYPE {}'.format('qml_plugin')) + extra.append('QML_TARGET_PATH "{}"'.format(target_path)) plugin_class_name = scope.get_string('PLUGIN_CLASS_NAME') if plugin_class_name: @@ -1780,13 +1786,56 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): write_main_part(cm_fh, plugin_name, 'Plugin', 'add_qt_plugin', scope, indent=indent, extra_lines=extra, known_libraries={}, extra_keys=[]) + if is_qml_plugin: + extra = [] + extra.append('TARGET_PATH "{}"'.format(target_path)) + + write_qml_plugin(cm_fh, plugin_name, scope, indent=indent, extra_lines=extra) + + +def write_qml_plugin(cm_fh: typing.IO[str], + target: str, + scope: Scope, *, + extra_lines: typing.List[str] = [], + indent: int = 0, + **kwargs: typing.Any): + # Collect other args if available + cxx_module = scope.get_string('CXX_MODULE') + if cxx_module: + extra_lines.append('CXX_MODULE "{}"'.format(cxx_module)) + 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('IMPORT_VERSION "{}"'.format(import_version)) + import_name = scope.get_string('IMPORT_NAME') + if import_name: + extra_lines.append('IMPORT_NAME "{}"'.format(import_name)) + plugindump_dep = scope.get_string('QML_PLUGINDUMP_DEPENDENCIES') + if plugindump_dep: + extra_lines.append('QML_PLUGINDUMP_DEPENDENCIES "{}"'.format(plugindump_dep)) + + cm_fh.write('\n{}{}({}\n'.format(spaces(indent), 'add_qml_module', target)) + indent += 1 + for extra_line in extra_lines: + cm_fh.write('{}{}\n'.format(spaces(indent), extra_line)) + + qml_files = scope.expand('QML_FILES') + if qml_files: + cm_fh.write('{}{}\n'.format(spaces(indent), 'QML_FILES')) + write_list(cm_fh, qml_files, '', indent=indent + 1) + + # Footer: + indent -= 1 + cm_fh.write('{})\n'.format(spaces(indent))) + 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') is_lib = scope.TEMPLATE == 'lib' - is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED')) + 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 if is_lib or 'qt_module' in scope.get('_LOADED'): assert not is_example -- cgit v1.2.3 From ec1546afc4a5e417d37c6a14e2909b063045bf39 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 12 Jul 2019 16:54:26 +0200 Subject: Handle TESTDATA for Qt Tests These changes enable the support to handle test data and install or package them as resources when appropriate. This change does not handle the GENERATED_TESTDATA or TEST_HELPER_INSTALLS since there are very few occurrences of these and we can handle those as special cases. Finally, in add_qt_test, only append CMAKE_CURRENT_SOURCE_DIR if the path is not absolute. Change-Id: Ic20b9749d10e2a09916f2797606116471c64850b Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 495c0e14ce..44768efa03 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1590,12 +1590,32 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, # Now write out the scopes: write_header(cm_fh, name, typename, indent=indent) + # collect all testdata and insert globbing commands + has_test_data = False + if typename == 'Test': + test_data = scope.expand('TESTDATA') + if test_data: + has_test_data = True + cm_fh.write('# Collect test data\n') + for data in test_data: + if data.endswith('*'): + cm_fh.write('{}file(GLOB test_data_glob \n{}LIST_DIRECTORIES' + ' true\n{}RELATIVE ${{CMAKE_CURRENT_SOURCE_DIR}}\n{}"{}")\n'\ + .format(spaces(indent), spaces(indent + 1), \ + spaces(indent + 1), spaces(indent + 1), data)) + cm_fh.write('{}list(APPEND test_data ${{test_data_glob}})\n'.format(spaces(indent))) + else: + cm_fh.write('{}list(APPEND test_data "{}")\n'.format(spaces(indent), data)) + cm_fh.write('\n') + cm_fh.write('{}{}({}\n'.format(spaces(indent), cmake_function, name)) for extra_line in extra_lines: cm_fh.write('{} {}\n'.format(spaces(indent), extra_line)) write_sources_section(cm_fh, scopes[0], indent=indent, **kwargs) + if has_test_data: + cm_fh.write('{} TESTDATA ${{test_data}}\n'.format(spaces(indent))) # Footer: cm_fh.write('{})\n'.format(spaces(indent))) -- cgit v1.2.3 From 341ccc3b590d0a06157f29ce9237bc111e8cebf8 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 15 Jul 2019 15:38:47 +0200 Subject: Enable recursive expansion of simple qmake variables Allow _expand_value to expand variables that may have more than one level of expansion when the regular expression covers all of the input. E.g.: A = Foo B = $$A/Bar scope.expand('$$B') While the original code was able to expand the string '$$B/source.cpp' to 'Foo/Bar/source.cpp', it could not expand the string '$$B' completely. The latter would always return '$$A/Bar' instead of the expected 'Foo/Bar' string. A test case has been added which coveres the above example. Change-Id: Ie3b5739c24ecbeb67d408dd204b0f54bab1d0f3f Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 28 ++++++++++++++++------------ util/cmake/tests/test_scope_handling.py | 8 ++++++++ 2 files changed, 24 insertions(+), 12 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 44768efa03..22b2d03219 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -569,12 +569,12 @@ class Scope(object): def _evalOps(self, key: str, transformer: typing.Optional[typing.Callable[[Scope, typing.List[str]], typing.List[str]]], - result: typing.List[str], *, inherrit: bool = False) \ + result: typing.List[str], *, inherit: bool = False) \ -> typing.List[str]: self._visited_keys.add(key) # Inherrit values from above: - if self._parent and inherrit: + if self._parent and inherit: result = self._parent._evalOps(key, transformer, result) if transformer: @@ -590,7 +590,7 @@ class Scope(object): return result - def get(self, key: str, *, ignore_includes: bool = False, inherrit: bool = False) -> typing.List[str]: + def get(self, key: str, *, ignore_includes: bool = False, inherit: bool = False) -> typing.List[str]: is_same_path = self.currentdir == self.basedir @@ -605,7 +605,7 @@ class Scope(object): else: return ['${CMAKE_CURRENT_BINARY_DIR}/' + os.path.relpath(self.currentdir, self.basedir),] - return self._evalOps(key, None, [], inherrit=inherrit) + return self._evalOps(key, None, [], inherit=inherit) def get_string(self, key: str, default: str = '') -> str: v = self.get(key) @@ -625,7 +625,7 @@ class Scope(object): 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', inherrit=True)), mapped_files)) + result = list(map(lambda f: handle_vpath(f, self.basedir, self.get('VPATH', inherit=True)), mapped_files)) else: result = mapped_files @@ -649,13 +649,17 @@ class Scope(object): while match: old_result = result if match.group(0) == value: - return self.get(match.group(1)) - - replacement = self.get(match.group(1)) - replacement_str = replacement[0] if replacement else '' - result = result[:match.start()] \ - + replacement_str \ - + result[match.end():] + get_result = self.get(match.group(1), inherit = True) + if len(get_result) == 1: + result = get_result[0] + else: + return get_result + 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():] if result == old_result: return [result,] # Do not go into infinite loop diff --git a/util/cmake/tests/test_scope_handling.py b/util/cmake/tests/test_scope_handling.py index c0b553fabd..14fd266c9c 100755 --- a/util/cmake/tests/test_scope_handling.py +++ b/util/cmake/tests/test_scope_handling.py @@ -336,3 +336,11 @@ def test_qstandardpaths_scopes(): assert scope10.total_condition == 'UNIX AND NOT APPLE_OSX AND (ANDROID_EMBEDDED OR NOT ANDROID)' assert scope11.total_condition == 'HAIKU AND (ANDROID_EMBEDDED OR NOT ANDROID)' assert scope12.total_condition == 'UNIX AND NOT APPLE_OSX AND NOT HAIKU AND (ANDROID_EMBEDDED OR NOT ANDROID)' + +def test_recursive_expansion(): + scope = _new_scope(A='Foo',B='$$A/Bar') + assert scope.get_string('A') == 'Foo' + assert scope.get_string('B') == '$$A/Bar' + assert scope._expand_value('$$B/Source.cpp') == ['Foo/Bar/Source.cpp'] + assert scope._expand_value('$$B') == ['Foo/Bar'] + -- cgit v1.2.3 From 3212f1b8662b5e5483a0185c22820e2d14593aba Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 16 Jul 2019 14:22:37 +0200 Subject: Add special case handling for corelib mimetypes resources We have to treat the resources from mimetypes in corelibs specially as they are reused for two test cases. Since we no longer use the qrc files, we have wrapped the relevant code in a function that can be called for every target that depends on it. This change also corrects formatting for the generate CMake code regarding resource commands. Change-Id: I50a05c81151d75aefc9ca165f5ffeb9f5cd77162 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 22b2d03219..5003faa370 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -132,18 +132,18 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '') -> str: if alias: full_source = os.path.join(base_dir, source) output += 'set_source_files_properties("{}"\n' \ - ' PROPERTIES alias "{}")\n'.format(full_source, alias) + ' PROPERTIES alias "{}"\n)\n'.format(full_source, alias) params = '' if lang: - params += ' LANG "{}"'.format(lang) + params += ' LANG\n "{}"\n'.format(lang) if prefix: - params += ' PREFIX "{}"'.format(prefix) + params += ' PREFIX\n "{}"\n'.format(prefix) if base_dir: - params += ' BASE "{}"'.format(base_dir) - output += 'add_qt_resource({} "{}"{} FILES\n {})\n'.format(target, full_resource_name, + params += ' BASE\n "{}"\n'.format(base_dir) + output += 'add_qt_resource({} "{}"\n{} FILES\n {}\n)\n'.format(target, full_resource_name, params, - '\n '.join(sorted_files)) + '\n '.join(sorted_files)) resource_count += 1 -- cgit v1.2.3 From fecefdd36978cf2562f0f16f5f79debff7d07f79 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Jul 2019 16:10:31 +0200 Subject: Fix mapping of features to private features When a feature generates a private feature, we should not just repeat the condition but also make it depend on the original feature. In qmake features had different outputs, while we have a 1:1 mapping. For example the developer_build feature had "private_tests" as an output feature. There's no condition attached to the feature and auto-detect is off, so we'd generate qt_feature("developer_build" AUTODETECT OFF) qt_feature("private_tests" AUTODETECT OFF) and that's wrong, because when the user enables the visible feature (developer_build) we want it to propagate to the private_tests feature. Change-Id: Id8408864802fa1e1ed9e67a5f47d1d2fde38d321 Reviewed-by: Leander Beernaert Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index f929ac142d..bede5934a5 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -788,7 +788,7 @@ def parseFeature(ctx, feature, data, cm_fh): cxxFeature = featureName(feature) - def writeFeature(name, publicFeature=False, privateFeature=False, labelAppend=''): + def writeFeature(name, publicFeature=False, privateFeature=False, labelAppend='', superFeature=None, autoDetect=''): if comment: cm_fh.write('# {}\n'.format(comment)) @@ -804,7 +804,11 @@ def parseFeature(ctx, feature, data, cm_fh): if purpose != label: cm_fh.write(lineify('PURPOSE', purpose)) cm_fh.write(lineify('AUTODETECT', autoDetect, quote=False)) - cm_fh.write(lineify('CONDITION', condition, quote=False)) + if superFeature: + feature_condition = "QT_FEATURE_{}".format(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)) @@ -814,7 +818,7 @@ def parseFeature(ctx, feature, data, cm_fh): # Default internal feature case. featureCalls = {} - featureCalls[cxxFeature] = {'name': cxxFeature, 'labelAppend': ''} + 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: @@ -836,6 +840,9 @@ def parseFeature(ctx, feature, data, cm_fh): if name not in featureCalls: featureCalls[name] = {'name': name, 'labelAppend': labelAppend} + if name != cxxFeature: + featureCalls[name]['superFeature'] = cxxFeature + if outputType in ['feature', 'publicFeature']: featureCalls[name]['publicFeature'] = True elif outputType == 'privateFeature': -- cgit v1.2.3 From ff5a75a60e5886e52b262f20750bd769ec5d88ac Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 22 Jul 2019 10:20:50 +0200 Subject: Process private libraries set in QT_PRIVATE The pro2cmake conversion was not processing private libraries specified with the QT_PRIVATE variable. Change-Id: I0c44595bb8e1ed9a748af51a2a859bee62e7d264 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5003faa370..35a0bba2cc 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1076,7 +1076,7 @@ def extract_cmake_libraries(scope: Scope, *, known_libraries: typing.Set[str]=se for key in ['QMAKE_USE_PRIVATE', 'QMAKE_USE_FOR_PRIVATE', 'LIBS_PRIVATE',]: private_dependencies += scope.expand(key) - for key in ['QT_FOR_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',]: -- cgit v1.2.3 From 31341ad63abbf67fc6a058284e5f48c33a6c4e8f Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 18 Jul 2019 09:38:37 +0200 Subject: Simplify add_qml_module code Reduce the amount of code required to add a qml plugin to cmake by making add_qml_module wrap the add_qt_plugin code where required. add_qml_module will also create a dummy target so that qml files will appear as source files in an IDE when no cpp files are present. CXX_MODULE qmake parameter has been dropped in favor of an explicit IMPORT_VERSION parameter, since it was only used to determine the version when the IMPORT_VERSION was not specified. Change-Id: I4a4b626566720d04c62d246ca521db8c4a95b10f Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 35a0bba2cc..64eb070282 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1795,28 +1795,20 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): plugin_type = scope.get_string('PLUGIN_TYPE') is_qml_plugin = any('qml_plugin' == s for s in scope.get('_LOADED')) - target_path = scope.get_string('TARGETPATH') - + plugin_function_name = 'add_qt_plugin' if plugin_type: extra.append('TYPE {}'.format(plugin_type)) elif is_qml_plugin: - extra.append('TYPE {}'.format('qml_plugin')) - extra.append('QML_TARGET_PATH "{}"'.format(target_path)) + 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') if plugin_class_name: extra.append('CLASS_NAME {}'.format(plugin_class_name)) - write_main_part(cm_fh, plugin_name, 'Plugin', 'add_qt_plugin', scope, + write_main_part(cm_fh, plugin_name, 'Plugin', plugin_function_name, scope, indent=indent, extra_lines=extra, known_libraries={}, extra_keys=[]) - if is_qml_plugin: - extra = [] - extra.append('TARGET_PATH "{}"'.format(target_path)) - - write_qml_plugin(cm_fh, plugin_name, scope, indent=indent, extra_lines=extra) - - def write_qml_plugin(cm_fh: typing.IO[str], target: str, scope: Scope, *, @@ -1824,9 +1816,10 @@ def write_qml_plugin(cm_fh: typing.IO[str], indent: int = 0, **kwargs: typing.Any): # Collect other args if available - cxx_module = scope.get_string('CXX_MODULE') - if cxx_module: - extra_lines.append('CXX_MODULE "{}"'.format(cxx_module)) + indent += 2 + target_path = scope.get_string('TARGETPATH') + if target_path: + extra_lines.append('TARGET_PATH "{}"'.format(target_path)) import_version = scope.get_string('IMPORT_VERSION') if import_version: import_version = import_version.replace("$$QT_MINOR_VERSION","${CMAKE_PROJECT_VERSION_MINOR}") @@ -1838,19 +1831,12 @@ def write_qml_plugin(cm_fh: typing.IO[str], if plugindump_dep: extra_lines.append('QML_PLUGINDUMP_DEPENDENCIES "{}"'.format(plugindump_dep)) - cm_fh.write('\n{}{}({}\n'.format(spaces(indent), 'add_qml_module', target)) - indent += 1 - for extra_line in extra_lines: - cm_fh.write('{}{}\n'.format(spaces(indent), extra_line)) - qml_files = scope.expand('QML_FILES') if qml_files: - cm_fh.write('{}{}\n'.format(spaces(indent), 'QML_FILES')) - write_list(cm_fh, qml_files, '', indent=indent + 1) + extra_lines.append('QML_FILES\n{}{}'.format( + spaces(indent), + '\n{}'.format(spaces(indent)).join(qml_files))) - # Footer: - indent -= 1 - cm_fh.write('{})\n'.format(spaces(indent))) def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, -- cgit v1.2.3 From e86509016c8940826774f7a803823b2dfd9b6944 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 23 Jul 2019 10:36:57 +0200 Subject: Fix mapping of Apple platforms in pro2cmake.py "mac" scope in qmake actually means all mac platforms, just like "darwin", aka macOS, iOS, watchOS, etc. Regenerate corelib, gui and testlib after this modification. This is a requirement for the iOS port. Change-Id: I029c7e907d13f6ec31816a08602a5930f9ac16a7 Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index fcd38883c3..e22284aab4 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -356,7 +356,7 @@ platform_mapping = { 'netbsd': 'NETBSD', 'haiku': 'HAIKU', 'netbsd': 'NETBSD', - 'mac': 'APPLE_OSX', + 'mac': 'APPLE', 'macx': 'APPLE_OSX', 'macos': 'APPLE_OSX', 'macx-icc': '(APPLE_OSX AND ICC)', -- cgit v1.2.3 From 2cc865b943f63c1aad630120c57e9aeccad66f57 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 22 Jul 2019 12:55:48 +0200 Subject: Extend add_qt_test to support qmltestcase Extend add_qt_test for qmltest by setting the option QMLTEST when we detect the config qmltestcase. We also forwards the GUI option to the tests when detected. This is a requirement for some QtQuickControls2 tests. Finally when doing a prefix build, we add the install directory to the QT_PLUGIN_PATH environment variable. Change-Id: I3b2ecb494955976e98abbcf3d03925c314336122 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 64eb070282..9619574566 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1698,14 +1698,26 @@ def write_tool(cm_fh: typing.IO[str], scope: Scope, *, extra_lines=extra, extra_keys=['CONFIG']) -def write_test(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0) -> None: +def write_test(cm_fh: typing.IO[str], scope: Scope, + gui: bool = False, *, indent: int = 0) -> None: test_name = scope.TARGET assert test_name + 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.get('IMPORTPATH') + if importpath: + qml_importpath = scope.expandString(importpath) + if qml_importpath: + extra.append('QML_IMPORTPATH "{}"'.format(qml_importpath)) + write_main_part(cm_fh, test_name, 'Test', 'add_qt_test', scope, - indent=indent, known_libraries={'Qt::Core', 'Qt::Test',}, - extra_keys=[]) + indent=indent, known_libraries=libraries, + extra_lines=extra, extra_keys=[]) def write_binary(cm_fh: typing.IO[str], scope: Scope, @@ -1713,7 +1725,7 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope, binary_name = scope.TARGET assert binary_name - extra = ['GUI',] if gui else[] + extra = ['GUI',] if gui else [] target_path = scope.get_string('target.path') if target_path: @@ -1857,13 +1869,14 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, assert not is_example write_tool(cm_fh, scope, indent=indent) else: - if 'testcase' in scope.get('CONFIG') \ - or 'testlib' in scope.get('CONFIG'): + config = scope.get('CONFIG') + gui = all(val not in config for val in ['console', 'cmdline']) + if 'testcase' in config \ + or 'testlib' in config \ + or 'qmltestcase' in config: assert not is_example - write_test(cm_fh, scope, indent=indent) + write_test(cm_fh, scope, gui, indent=indent) else: - config = scope.get('CONFIG') - gui = all(val not in config for val in ['console', 'cmdline']) if is_example: write_example(cm_fh, scope, gui, indent=indent) else: -- cgit v1.2.3 From a92f029e2a2601984b8291b79a90572dbb3f1d33 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 25 Jul 2019 11:32:38 +0200 Subject: Fix QML_FILES conversion Use a the more appropriate scope.get_files() to retrieve the qml file list from the qmake project. This makes it more consitent with the rest of the conversion script and fixes some issues with incorrect aliases in qrc files. Change-Id: I8907516be9457ba0d60d14af53d241197637aa9f Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9619574566..42beb34bcc 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1843,7 +1843,7 @@ def write_qml_plugin(cm_fh: typing.IO[str], if plugindump_dep: extra_lines.append('QML_PLUGINDUMP_DEPENDENCIES "{}"'.format(plugindump_dep)) - qml_files = scope.expand('QML_FILES') + qml_files = scope.get_files('QML_FILES', use_vpath=True) if qml_files: extra_lines.append('QML_FILES\n{}{}'.format( spaces(indent), -- cgit v1.2.3 From 88bf485f9198d13d69e3076f6d8924582624e88e Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 25 Jul 2019 11:22:52 +0200 Subject: Add EMBED_QML_FILES option to add_qml_module Some projects in QtQuickControls force the qml files to embedded into the binary. This change exposes an option to mimic that bevhavior. Change-Id: I4cbf0c21c05ca03b8dd1189eb7d81e43279c7157 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 42beb34bcc..c505a48e79 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1829,6 +1829,8 @@ def write_qml_plugin(cm_fh: typing.IO[str], **kwargs: typing.Any): # Collect other args if available indent += 2 + if 'builtin_resources' in scope.get('CONFIG'): + extra_lines.append('EMBED_QML_FILES') target_path = scope.get_string('TARGETPATH') if target_path: extra_lines.append('TARGET_PATH "{}"'.format(target_path)) -- cgit v1.2.3 From 1307736c7db1ff24e3b8282f4a7b14d24866feba Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 26 Jul 2019 18:15:41 +0200 Subject: Fix testdata handling Make sure to handle glob expressions in the entire path given, not just the end of the path. This handles tests like qsslkey and qnetworkreply. Also copy/install the testdata in the final test directory path under a "testdata" subdir. Previously INSTALL_TESTDIR was used, which was never set to anything. Change-Id: I2408e12f586cadeb524ffa249e851a4179324b23 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann Reviewed-by: Leander Beernaert --- util/cmake/pro2cmake.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index c505a48e79..23d459d765 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -31,6 +31,7 @@ from __future__ import annotations from argparse import ArgumentParser +from textwrap import dedent import copy import xml.etree.ElementTree as ET from itertools import chain @@ -1602,11 +1603,17 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, has_test_data = True cm_fh.write('# Collect test data\n') for data in test_data: - if data.endswith('*'): - cm_fh.write('{}file(GLOB test_data_glob \n{}LIST_DIRECTORIES' - ' true\n{}RELATIVE ${{CMAKE_CURRENT_SOURCE_DIR}}\n{}"{}")\n'\ - .format(spaces(indent), spaces(indent + 1), \ - spaces(indent + 1), spaces(indent + 1), data)) + if '*' in data: + cm_fh.write(dedent(""" + {indent}file(GLOB test_data_glob + {indent1}LIST_DIRECTORIES true + {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))) else: cm_fh.write('{}list(APPEND test_data "{}")\n'.format(spaces(indent), data)) -- cgit v1.2.3 From a285bcba26498f3a03da3891a253213e05dba621 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 26 Jul 2019 18:59:53 +0200 Subject: Ugly fix for handling QT_SOURCE_TREE QT_SOURCE_TREE is a variable that is set in qtbase/.qmake.conf. In qtbase, it's used throughout various projects to find cpp sources when building standalone tests (among other things). Everything works fine with qmake, because even if qmake is invoked on the tests subfolder, qmake searches up the source directory tree until it finds a .qmake.conf file, and uses that. When building qttools with qmake, the qdoc project expects to have a QT_SOURCE_TREE value, but it's not actually set in the qttools/.qmake.conf file, so the generated include paths that use that value are incorrect. Curiously the build still succeeds. Now in CMake land we replaced QT_SOURCE_TREE with CMAKE_SOURCE_DIR, but that does not work properly when doing a standalone tests build, because the project in that case is the tests one, and not the qtbase one, so configuration fails in a developer build when trying to configure some private tests. So far I've found that only qtbase actively uses this value. A temporary fix is to save the qtbase source directory into a QT_SOURCE_TREE variable inside the generated BuildInternalsExtra.cmake file. The pro2cmake script is changed to handle presence of QT_SOURCE_TREE in a qrc file path. This is handled by finding the location of a .qmake.conf file starting from the project file absolute path. This is needed to stop the script from crashing when handling the mimedatabase test projects for example. The change also regenerates the relevant failing test projects, and thus standalone tests (when doing developer builds aka private_tests enabled) now configure and build successfully. Change-Id: I15adc6f4ab6e3056c43ed850196204e2229c4d98 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 23d459d765..0beaa9a12f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -96,11 +96,48 @@ def _parse_commandline(): return parser.parse_args() -def process_qrc_file(target: str, filepath: str, base_dir: str = '') -> str: +def find_qmake_conf(project_file_path: str = '') -> typing.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)) + return None + + cwd = os.path.dirname(project_file_path) + file_name = '.qmake.conf' + + while os.path.isdir(cwd): + maybe_file = os.path.join(cwd, file_name) + if os.path.isfile(maybe_file): + return maybe_file + else: + cwd = os.path.dirname(cwd) + + return None + + +def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_file_path: str = '') -> 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}' + if qt_source_tree_literal in filepath: + qmake_conf = find_qmake_conf(project_file_path) + + if qmake_conf: + 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)) + + resource_name = os.path.splitext(os.path.basename(filepath))[0] base_dir = os.path.join('' if base_dir == '.' else base_dir, os.path.dirname(filepath)) + if not os.path.isfile(filepath): + raise RuntimeError('Invalid file path given to process_qrc_file: {}'.format(filepath)) + tree = ET.parse(filepath) root = tree.getroot() assert(root.tag == 'RCC') @@ -338,7 +375,7 @@ class Scope(object): file: typing.Optional[str] = None, condition: str = '', base_dir: str = '', operations: typing.Mapping[str, typing.List[Operation]] = { - 'QT_SOURCE_TREE': [SetOperation(['${PROJECT_SOURCE_DIR}'])], + 'QT_SOURCE_TREE': [SetOperation(['${QT_SOURCE_TREE}'])], 'QT_BUILD_TREE': [SetOperation(['${PROJECT_BUILD_DIR}'])], }) -> None: if parent_scope: @@ -357,6 +394,7 @@ class Scope(object): self._scope_id = Scope.SCOPE_ID Scope.SCOPE_ID += 1 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] @@ -494,6 +532,10 @@ class Scope(object): def file(self) -> str: return self._file or '' + @property + def file_absolute_path(self) -> str: + return self._file_absolute_path or '' + @property def generated_cmake_lists_path(self) -> str: assert self.basedir @@ -1464,7 +1506,7 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in qrc_only = True for r in resources: if r.endswith('.qrc'): - qrc_output += process_qrc_file(target, r, scope.basedir) + qrc_output += process_qrc_file(target, r, scope.basedir, scope.file_absolute_path) else: qrc_only = False -- cgit v1.2.3 From a920c16ef2f6e0bd26e136a92a4f4fe34a32eef0 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 25 Jul 2019 16:45:11 +0200 Subject: Add Qt Quick Compiler support This patch adds support for the qtquick compiler feature, which will embed the compiled qml files as resources along with the respective qml_loader. This patch also changes the add_qml_module call to require either EMBED_QML_FILES and/or INSTALL_QML_FILES to be specified. Change-Id: I32d29c9b554b8286ed3b980027a56dd4abe11c92 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0beaa9a12f..80e033caf8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1878,8 +1878,18 @@ def write_qml_plugin(cm_fh: typing.IO[str], **kwargs: typing.Any): # Collect other args if available indent += 2 - if 'builtin_resources' in scope.get('CONFIG'): + scope_config = scope.get('CONFIG') + is_embedding_qml_files = False + if 'builtin_resources' in scope_config or 'qt_quick_compiler' in scope_config: extra_lines.append('EMBED_QML_FILES') + is_embedding_qml_files = True + + if not is_embedding_qml_files: + extra_lines.append('INSTALL_QML_FILES') + + if 'install_qml_files' in scope_config and is_embedding_qml_files: + extra_lines.append('INSTALL_QML_FILES') + target_path = scope.get_string('TARGETPATH') if target_path: extra_lines.append('TARGET_PATH "{}"'.format(target_path)) -- cgit v1.2.3 From b26b1455d75709a53f50e1d3d41c384f8e90b576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Wed, 3 Jul 2019 14:39:03 +0200 Subject: Implement SUBDIR-= conversion in pro2cmake tool CMake doesn't support removing subdirectories therefore one need to convert all removal to conditional adds. The resulting code doesn't win a beauty contest. That is because handle_subdir works on already processed strings which means it doesn't have access to the boolean operations. As such it can not minimize the expressions, but it works and in the most simple cases it is pretty good. The patch re-generates CMakeLists.txt under tests/auto/corelib/kernel excluding qcoreapplication, qmetatype, qmimedata, qobject, qtimer, which are suffering from unrelated problems, like for example Gui, pthread linkage issues. Change-Id: I18a02f6eda7a3b41b1313211c8bc9ce277bb67be Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 51 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 12 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 80e033caf8..8dab147d54 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1026,9 +1026,35 @@ def map_condition(condition: str) -> str: def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, indent: int = 0, is_example: bool=False) -> None: ind = ' ' * indent + + def find_all_remove_subdir(scope: Scope, + current_conditions: typing.FrozenSet[str]=None, + rm_subdir_conditions: typing.Dict[str, typing.Set[typing.FrozenSet[str]]]=None) -> typing.Dict[str, typing.Set[typing.FrozenSet[str]]]: + rm_subdir_conditions = rm_subdir_conditions if rm_subdir_conditions is not None else dict() + for sd in scope.get_files('SUBDIRS'): + if sd.startswith('-') and current_conditions is not None: + conditions = rm_subdir_conditions.get(sd[1:], set()) + conditions.add(current_conditions) + rm_subdir_conditions[sd[1:]] = conditions + current_conditions = current_conditions if current_conditions is not None else frozenset() + for child_scope in scope.children: + assert child_scope.condition + find_all_remove_subdir(child_scope, frozenset((*current_conditions, child_scope.condition)), rm_subdir_conditions) + return rm_subdir_conditions + + rm_subdir_conditions = find_all_remove_subdir(scope) + for sd in scope.get_files('SUBDIRS'): if os.path.isdir(sd): - cm_fh.write('{}add_subdirectory({})\n'.format(ind, sd)) + conditions = rm_subdir_conditions.get(sd) + cond_ind = ind + if conditions: + conditions_str = " OR ".join(sorted("(" + " AND ".join(condition) + ")" for condition in conditions)) + cm_fh.write(f'{ind}if(NOT ({conditions_str}))\n') + cond_ind += " " + cm_fh.write(f'{cond_ind}add_subdirectory({sd})\n') + if conditions: + cm_fh.write(f'{ind}endif()\n') elif os.path.isfile(sd): subdir_result = parseProFile(sd, debug=False) subdir_scope \ @@ -1039,22 +1065,23 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, do_include(subdir_scope) cmakeify_scope(subdir_scope, cm_fh, indent=indent, is_example=is_example) elif sd.startswith('-'): - cm_fh.write('{}### remove_subdirectory' - '("{}")\n'.format(ind, sd[1:])) + pass else: print(' XXXX: SUBDIR {} in {}: Not found.'.format(sd, scope)) for c in scope.children: cond = c.condition - if cond == 'else': - cm_fh.write('\n{}else()\n'.format(ind)) - elif cond: - cm_fh.write('\n{}if({})\n'.format(ind, cond)) - - handle_subdir(c, cm_fh, indent=indent + 1, is_example=is_example) - - if cond: - cm_fh.write('{}endif()\n'.format(ind)) + temp_buf = io.StringIO('') # we do not want to print empty conditions + handle_subdir(c, temp_buf, indent=indent + 1, is_example=is_example) + sub_call_str = temp_buf.getvalue() + if sub_call_str: + if cond == 'else': + cm_fh.write('\n{}else()\n'.format(ind)) + elif cond: + cm_fh.write('\n{}if({})\n'.format(ind, cond)) + cm_fh.write(sub_call_str) + if cond: + cm_fh.write('{}endif()\n'.format(ind)) def sort_sources(sources: typing.List[str]) -> typing.List[str]: -- cgit v1.2.3 From 9e96c384261ca1329d3143295d013701eb85b186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 4 Jul 2019 14:48:32 +0200 Subject: Properly convert default QT directive If unset QT by default has value: "core gui". This patch adds this behavior by pre-defining the value in the root scope. qmimedata CMakeList.txt was re-generated. Change-Id: Ib8b6064bc46c72d829e0077d09f21bbfb494e137 Reviewed-by: Qt CMake Build Bot Reviewed-by: Liang Qi Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8dab147d54..80c975cec1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -378,10 +378,12 @@ class Scope(object): 'QT_SOURCE_TREE': [SetOperation(['${QT_SOURCE_TREE}'])], 'QT_BUILD_TREE': [SetOperation(['${PROJECT_BUILD_DIR}'])], }) -> None: + self._operations = copy.deepcopy(operations) if parent_scope: parent_scope._add_child(self) else: self._parent = None # type: typing.Optional[Scope] + self._operations['QT'] = [SetOperation(['core', 'gui'])] self._basedir = base_dir if file: @@ -398,7 +400,6 @@ class Scope(object): self._condition = map_condition(condition) self._children = [] # type: typing.List[Scope] self._included_children = [] # type: typing.List[Scope] - self._operations = copy.deepcopy(operations) self._visited_keys = set() # type: typing.Set[str] self._total_condition = None # type: typing.Optional[str] -- cgit v1.2.3 From 97b76704ea3b3a062a912656809b17d434f31ffe Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 1 Aug 2019 15:43:36 +0200 Subject: Refactor QML_FILES for add_qml_module It has been decided, that going forward all qml files are to be added to a module via the resource system. This patch does the ground work to make sure all qml modules in the qt codebase follow these new conventions. New properties on targets have been added so that we can't track all the qml related information for later use. To make sure it is still possible to install qml files we added the qt_install_qml_files() command. Pro2cmake has been adjusted to handle the special cases of versioned qml modules (e.g: QtQuick.2). It will now insert a TARGET_PATH override to avoid the default conversion from the URI parameter. Finally, this patch temporarliy disables the quick compiler by moving all relevant code into a dummy function. This will be removed in a follow up patch where the quick compiler will be enable for all qml files present in resource files. Change-Id: I09fe4517fad26ec96122d9c7c777dbfbd214905c Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 67 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 15 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 80c975cec1..a62b094ee9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -651,8 +651,8 @@ class Scope(object): return self._evalOps(key, None, [], inherit=inherit) - def get_string(self, key: str, default: str = '') -> str: - v = self.get(key) + 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 @@ -1646,6 +1646,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, # Evaluate total condition of all scopes: recursive_evaluate_scope(scope) + is_qml_plugin = any('qml_plugin' == s for s in scope.get('_LOADED')) + if 'exceptions' in scope.get('CONFIG'): extra_lines.append('EXCEPTIONS') @@ -1706,6 +1708,9 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, write_android_part(cm_fh, name, scopes[0], indent) + if is_qml_plugin: + write_qml_plugin_qml_files(cm_fh, name, scopes[0], indent) + ignored_keys_report = write_ignored_keys(scopes[0], spaces(indent)) if ignored_keys_report: cm_fh.write(ignored_keys_report) @@ -1898,6 +1903,7 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): write_main_part(cm_fh, plugin_name, 'Plugin', plugin_function_name, scope, indent=indent, extra_lines=extra, known_libraries={}, extra_keys=[]) + def write_qml_plugin(cm_fh: typing.IO[str], target: str, scope: Scope, *, @@ -1908,36 +1914,67 @@ def write_qml_plugin(cm_fh: typing.IO[str], indent += 2 scope_config = scope.get('CONFIG') is_embedding_qml_files = False - if 'builtin_resources' in scope_config or 'qt_quick_compiler' in scope_config: - extra_lines.append('EMBED_QML_FILES') - is_embedding_qml_files = True - if not is_embedding_qml_files: - extra_lines.append('INSTALL_QML_FILES') - if 'install_qml_files' in scope_config and is_embedding_qml_files: - extra_lines.append('INSTALL_QML_FILES') + sources = scope.get_files('SOURCES') + if len(sources) != 0: + extra_lines.append('CPP_PLUGIN') target_path = scope.get_string('TARGETPATH') if target_path: - extra_lines.append('TARGET_PATH "{}"'.format(target_path)) + uri = target_path.replace('/','.') + extra_lines.append('URI "{}"'.format(uri)) + # 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('.', '/') + if target_path != target_path_from_uri: + extra_lines.append('TARGET_PATH "{}"'.format(target_path)) + 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('IMPORT_VERSION "{}"'.format(import_version)) + extra_lines.append('VERSION "{}"'.format(import_version)) + import_name = scope.get_string('IMPORT_NAME') if import_name: - extra_lines.append('IMPORT_NAME "{}"'.format(import_name)) + extra_lines.append('NAME "{}"'.format(import_name)) plugindump_dep = scope.get_string('QML_PLUGINDUMP_DEPENDENCIES') + if plugindump_dep: extra_lines.append('QML_PLUGINDUMP_DEPENDENCIES "{}"'.format(plugindump_dep)) + # This is only required because of qmldir + extra_lines.append('RESOURCE_PREFIX "/qt-project.org/imports"') + +def write_qml_plugin_qml_files(cm_fh: typing.IO[str], + target: str, + scope: Scope, + indent: int = 0): + qml_files = scope.get_files('QML_FILES', use_vpath=True) if qml_files: - extra_lines.append('QML_FILES\n{}{}'.format( + 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 + prefix = '/qt-project.org/imports/' + target_path + cm_fh.write('\n# QML Files\n') + cm_fh.write('{}add_qt_resource({} {}\n{}PREFIX\n{}"{}"\n{}FILES\n{}{}\n)\n'.format( spaces(indent), - '\n{}'.format(spaces(indent)).join(qml_files))) - + target, + resource_name, + spaces(indent + 1), + spaces(indent + 2), + prefix, + spaces(indent + 1), + spaces(indent + 2), + '\n{}'.format(spaces(indent + 2)).join(qml_files))) + + if 'install_qml_files' in scope.get('CONFIG'): + cm_fh.write('\nqt_install_qml_files({}\n FILES\n {}\n)\n\n'.format( + target, + '\n '.join(qml_files))) def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, -- cgit v1.2.3 From cb370593df2d30c33afb804042f25944f5bca08d Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 1 Aug 2019 16:01:22 +0200 Subject: Small fix to correctly handle default arguments Dictionaries are mutable, and should not be assigned as a default parameter. Change-Id: Id08c17f89c17b404560241849603e1e1a0ec6562 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index a62b094ee9..843b2e7bac 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -374,10 +374,14 @@ class Scope(object): parent_scope: typing.Optional[Scope], file: typing.Optional[str] = None, condition: str = '', base_dir: str = '', - operations: typing.Mapping[str, typing.List[Operation]] = { - 'QT_SOURCE_TREE': [SetOperation(['${QT_SOURCE_TREE}'])], - 'QT_BUILD_TREE': [SetOperation(['${PROJECT_BUILD_DIR}'])], - }) -> None: + operations: typing.Union[ + typing.Mapping[str, typing.List[Operation]], None] = None) -> None: + if operations is None: + operations = { + '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) -- cgit v1.2.3 From 67bbb9179f28df8696548af52e6edf24c914ebd2 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 1 Aug 2019 16:39:43 +0200 Subject: Fix pro2cmake to handle QT default values properly Setting the "QT" SetOperation which defaults to "core" and "gui", should only be done once on the top-level .pro scope. To distinguish the top level scope, we need to check for both an empty parent_scope and an empty base_dir. Amends 9e96c384261ca1329d3143295d013701eb85b186 Change-Id: I9db1cbf0e6592c8c195d11b97b3eff40b1adbcbd Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 843b2e7bac..cf55bc76b6 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -387,7 +387,10 @@ class Scope(object): parent_scope._add_child(self) else: self._parent = None # type: typing.Optional[Scope] - self._operations['QT'] = [SetOperation(['core', 'gui'])] + # 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._basedir = base_dir if file: -- cgit v1.2.3 From e2ab42c2aada793f724326b811ea10b9ec493705 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 1 Aug 2019 17:30:26 +0200 Subject: Implement condition simplification for add_subdirectory conditions This makes the SUBDIRS -= foo conditions simplified and nice to look at. Amends b26b1455d75709a53f50e1d3d41c384f8e90b576. Change-Id: I9ffe3db1e358f94fb65a885cc90c44218482825b Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index cf55bc76b6..8eafaff0f8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1058,7 +1058,9 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, cond_ind = ind if conditions: conditions_str = " OR ".join(sorted("(" + " AND ".join(condition) + ")" for condition in conditions)) - cm_fh.write(f'{ind}if(NOT ({conditions_str}))\n') + conditions_str_wrapped = "NOT ({})".format(conditions_str) + conditions_simplified = simplify_condition(conditions_str_wrapped) + cm_fh.write(f'{ind}if({conditions_simplified})\n') cond_ind += " " cm_fh.write(f'{cond_ind}add_subdirectory({sd})\n') if conditions: @@ -1479,11 +1481,32 @@ def simplify_condition(condition: str) -> str: condition = condition.replace(' ON ', ' true ') condition = condition.replace(' OFF ', ' false ') + # SymPy chokes on expressions that contain two tokens one next to + # the other delimited by a space, which are not an operation. + # So a CMake condition like "TARGET Foo::Bar" fails the whole + # expression simplifying process. + # Turn these conditions into a single token so that SymPy can parse + # the expression, and thus simplify it. + # Do this by replacing and keeping a map of conditions to single + # token symbols. + 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_symbol_mapping[target_condition_symbol_name] = target_condition + condition = re.sub(target_condition, target_condition_symbol_name, condition) + try: # Generate and simplify condition using sympy: condition_expr = simplify_logic(condition) condition = str(_recursive_simplify(condition_expr)) + # Restore the target conditions. + for symbol_name in target_symbol_mapping: + condition = re.sub(symbol_name, target_symbol_mapping[symbol_name], condition) + # Map back to CMake syntax: condition = condition.replace('~', 'NOT ') condition = condition.replace('&', 'AND') -- cgit v1.2.3 From 5d88ba001ed44e21ae3456de2ce4d00eaac31e0c Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 5 Aug 2019 13:51:25 +0200 Subject: Fix $$_PRO_FILE_PWD substitution PRO_FILE_PWD should return CMAKE_CURRENT_SOURCE dir without any compenstation for mistmatching directory locations as we are doing with PWD. There are test that use a shared .pri project file which gets included into the .pro file of test. In this .pri file we have a statement such as DEFINES += QT_QMLTEST_DATADIR=\\\"$${_PRO_FILE_PWD_}/data\\\". Since the .pri project file is located in ../../shared/shared.pri the substitution would result in ${CMAKE_CURRENT_SOURCE_DIR}/../../shared/data instead of the expected ${CMAKE_CURRENT_SOURCE_DIR}/data. Change-Id: Id899d6a624acc45425af47ca9aa1b332cf63e659 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8eafaff0f8..83bca8487a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -645,7 +645,9 @@ class Scope(object): is_same_path = self.currentdir == self.basedir - if key == '_PRO_FILE_PWD_' or key == 'PWD': + if key == '_PRO_FILE_PWD_': + return ['${CMAKE_CURRENT_SOURCE_DIR}'] + if key == 'PWD': if is_same_path: return ['${CMAKE_CURRENT_SOURCE_DIR}'] else: -- cgit v1.2.3 From a3e149d92a4c60ef9d96836499dd43c728592983 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 5 Aug 2019 14:46:03 +0200 Subject: Fix qml plugin conversion: always install qml files Always install qml files regardless of whether the install_qml_files configuration is present in qmake. This was causing most unit tests to fail due to missing qml files. Change-Id: I53c7200cfa66805d0e459190ef105c1a73c34a5f Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 83bca8487a..f45de7edf3 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1986,13 +1986,17 @@ def write_qml_plugin_qml_files(cm_fh: typing.IO[str], qml_files = scope.get_files('QML_FILES', use_vpath=True) if 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))) + 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 prefix = '/qt-project.org/imports/' + target_path - cm_fh.write('\n# QML Files\n') - cm_fh.write('{}add_qt_resource({} {}\n{}PREFIX\n{}"{}"\n{}FILES\n{}{}\n)\n'.format( + cm_fh.write('\n{}add_qt_resource({} {}\n{}PREFIX\n{}"{}"\n{}FILES\n{}${{qml_files}}\n)\n'.format( spaces(indent), target, resource_name, @@ -2000,13 +2004,10 @@ def write_qml_plugin_qml_files(cm_fh: typing.IO[str], spaces(indent + 2), prefix, spaces(indent + 1), - spaces(indent + 2), - '\n{}'.format(spaces(indent + 2)).join(qml_files))) + spaces(indent + 2))) - if 'install_qml_files' in scope.get('CONFIG'): - cm_fh.write('\nqt_install_qml_files({}\n FILES\n {}\n)\n\n'.format( - target, - '\n '.join(qml_files))) + cm_fh.write('\nqt_install_qml_files({}\n FILES ${{qml_files}}\n)\n\n'.format( + target)) def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, -- cgit v1.2.3 From 709538096c9e52c0ab7a54a1fd2f63f7e42584f8 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 6 Aug 2019 14:06:47 +0200 Subject: Fix add_qml_module URI Make sure the URI name for qml modules correctly strips out the version number for instances such as QtQuick.Controls.2. Change-Id: I18e706b371323eeefdd6d7564b922265fa5cad3f Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f45de7edf3..905c2107d7 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1955,22 +1955,24 @@ def write_qml_plugin(cm_fh: typing.IO[str], target_path = scope.get_string('TARGETPATH') if target_path: uri = target_path.replace('/','.') - extra_lines.append('URI "{}"'.format(uri)) + 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('.', '/') if target_path != target_path_from_uri: extra_lines.append('TARGET_PATH "{}"'.format(target_path)) + if import_name: + extra_lines.append('URI "{}"'.format(import_name)) + else: + uri = re.sub('\\.\\d+\\.', '.',uri) + extra_lines.append('URI "{}"'.format(uri)) 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_name = scope.get_string('IMPORT_NAME') - if import_name: - extra_lines.append('NAME "{}"'.format(import_name)) plugindump_dep = scope.get_string('QML_PLUGINDUMP_DEPENDENCIES') if plugindump_dep: -- cgit v1.2.3 From d9432527414f66c5eaa8d44697ad307f9b37bd66 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 6 Aug 2019 14:51:04 +0200 Subject: Fix shared resource paths in tests Resources shared by tests were incorrectly setup. Resources would always be registered with BASE/filename as alias. In these cases the fix is to set the alias with the original file name. Change-Id: I667ce7b74ae5f86740c8bb8a040cc2895a3dc116 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 905c2107d7..8a002244ef 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -133,8 +133,12 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil resource_name = os.path.splitext(os.path.basename(filepath))[0] - base_dir = os.path.join('' if base_dir == '.' else base_dir, os.path.dirname(filepath)) + dir_name = os.path.dirname(filepath) + base_dir = os.path.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('..') if not os.path.isfile(filepath): raise RuntimeError('Invalid file path given to process_qrc_file: {}'.format(filepath)) @@ -159,6 +163,11 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil # 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 + if not alias and is_parent_path: + alias = path files[path] = alias sorted_files = sorted(files.keys()) -- cgit v1.2.3 From 9ca1d795391a07ae34b289e0ee4239023b8d58d6 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 6 Aug 2019 15:15:38 +0200 Subject: Fix regular expression for add_qml_module Values such as Foo.2 would no longer be converted into Foo due to an error in the regular expression. Change-Id: I6a11d02662782094f5b264f86a930d9aaf002c77 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8a002244ef..71887bb908 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1974,7 +1974,7 @@ def write_qml_plugin(cm_fh: typing.IO[str], if import_name: extra_lines.append('URI "{}"'.format(import_name)) else: - uri = re.sub('\\.\\d+\\.', '.',uri) + uri = re.sub('\\.\\d+', '', uri) extra_lines.append('URI "{}"'.format(uri)) import_version = scope.get_string('IMPORT_VERSION') -- cgit v1.2.3 From b4ef975a0a6e8c22d3c0851f9d7b11738d37b662 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 1 Aug 2019 19:14:58 +0200 Subject: Handle conditions for SUBDIRS *more* correctly There are project files that not only do SUBDIRS -= foo depending on some condition, but also SUBDIRS += foo. To handle these cases correctly we need to do a whole pass across all scopes and collect all addition and subtraction conditions. Once we have that information, we can compute a final condition for a particular subdirectory. After we have the condition information for all subdirectories, we can simplify all those conditions using SimPy, and group all subdirectories that have the same simplified condition. Finally we print out the subdirectories grouped by the simplified condition. The end result is similar to how we have extend_target() calls with multiple sources grouped into a single conditional call, except in this case it's subdirectories. Amends b26b1455d75709a53f50e1d3d41c384f8e90b576. Change-Id: I5b648ac67264ba9a940561baee5b49c43c13b721 Reviewed-by: Leander Beernaert Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 180 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 124 insertions(+), 56 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 71887bb908..c8a6ba7f88 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1042,67 +1042,135 @@ def map_condition(condition: str) -> str: return cmake_condition.strip() -def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *, - indent: int = 0, is_example: bool=False) -> None: - ind = ' ' * indent - - def find_all_remove_subdir(scope: Scope, - current_conditions: typing.FrozenSet[str]=None, - rm_subdir_conditions: typing.Dict[str, typing.Set[typing.FrozenSet[str]]]=None) -> typing.Dict[str, typing.Set[typing.FrozenSet[str]]]: - rm_subdir_conditions = rm_subdir_conditions if rm_subdir_conditions is not None else dict() +def handle_subdir(scope: Scope, + cm_fh: typing.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]]]] = {} + + # 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('-') + 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()) + if current_conditions: + if subtraction: + subtractions.add(current_conditions) + else: + additions.add(current_conditions) + if additions: + sub_dirs[subdir_name]['additions'] = additions + if 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'): - if sd.startswith('-') and current_conditions is not None: - conditions = rm_subdir_conditions.get(sd[1:], set()) - conditions.add(current_conditions) - rm_subdir_conditions[sd[1:]] = conditions - current_conditions = current_conditions if current_conditions is not None else frozenset() - for child_scope in scope.children: - assert child_scope.condition - find_all_remove_subdir(child_scope, frozenset((*current_conditions, child_scope.condition)), rm_subdir_conditions) - return rm_subdir_conditions - - rm_subdir_conditions = find_all_remove_subdir(scope) - - for sd in scope.get_files('SUBDIRS'): - if os.path.isdir(sd): - conditions = rm_subdir_conditions.get(sd) + # Collect info about conditions and SUBDIR assignments in the + # current scope. + 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): + subdir_result = parseProFile(sd, debug=False) + 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)) + + # Collect info about conditions and SUBDIR assignments in child + # scopes, aka recursively call the same function, but with an + # updated current_conditions frozen set. + for c in scope.children: + handle_subdir_helper(c, cm_fh, + indent=indent + 1, + is_example=is_example, + current_conditions=frozenset((*current_conditions, c.condition))) + + def group_and_print_sub_dirs(indent: int = 0): + # Simplify conditions, and group + # subdirectories with the same conditions. + grouped_sub_dirs = {} + for subdir_name in sub_dirs: + 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 = '' + if additions or subtractions: + addition_str = '' + subtraction_str = '' + if additions: + addition_str = " OR ".join(sorted("(" + " AND ".join(condition) + ")" + for condition in additions)) + if addition_str: + addition_str = '({})'.format(addition_str) + if subtractions: + subtraction_str = " OR ".join(sorted("(" + " AND ".join(condition) + ")" + for condition in subtractions)) + if subtraction_str: + subtraction_str = 'NOT ({})'.format(subtraction_str) + + condition_str = addition_str + if condition_str and subtraction_str: + condition_str += ' AND ' + condition_str += subtraction_str + condition_simplified = simplify_condition(condition_str) + condition_key = condition_simplified + + sub_dir_list_by_key = grouped_sub_dirs.get(condition_key, []) + sub_dir_list_by_key.append(subdir_name) + grouped_sub_dirs[condition_key] = sub_dir_list_by_key + + # Print the groups. + ind = ' ' * indent + for condition_key in grouped_sub_dirs: cond_ind = ind - if conditions: - conditions_str = " OR ".join(sorted("(" + " AND ".join(condition) + ")" for condition in conditions)) - conditions_str_wrapped = "NOT ({})".format(conditions_str) - conditions_simplified = simplify_condition(conditions_str_wrapped) - cm_fh.write(f'{ind}if({conditions_simplified})\n') + if condition_key: + cm_fh.write(f'{ind}if({condition_key})\n') cond_ind += " " - cm_fh.write(f'{cond_ind}add_subdirectory({sd})\n') - if conditions: + + 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') + if condition_key: cm_fh.write(f'{ind}endif()\n') - elif os.path.isfile(sd): - subdir_result = parseProFile(sd, debug=False) - 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) - elif sd.startswith('-'): - pass - else: - print(' XXXX: SUBDIR {} in {}: Not found.'.format(sd, scope)) - for c in scope.children: - cond = c.condition - temp_buf = io.StringIO('') # we do not want to print empty conditions - handle_subdir(c, temp_buf, indent=indent + 1, is_example=is_example) - sub_call_str = temp_buf.getvalue() - if sub_call_str: - if cond == 'else': - cm_fh.write('\n{}else()\n'.format(ind)) - elif cond: - cm_fh.write('\n{}if({})\n'.format(ind, cond)) - cm_fh.write(sub_call_str) - if cond: - cm_fh.write('{}endif()\n'.format(ind)) + # A set of conditions which will be ANDed together. The set is recreated with more conditions + # as the scope deepens. + current_conditions = frozenset() + + # Do the work. + 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]: -- cgit v1.2.3 From f9db9a22e9f0d35d8ecaf60b744e663efdd9ea3f Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 7 Aug 2019 14:25:50 +0200 Subject: Handle qmake's CONFIG=plugin In some tests in qtdeclarative we have projects that are built as a lib with CONFIG=plugin. Without these changes they would be translated to an add_qt_module call. Change-Id: I208d31d43b087ed0b87eb4715f6c49b218fcc2c5 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index c8a6ba7f88..f700ef8dff 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2093,21 +2093,21 @@ 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') + 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 + is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED')) or is_qml_plugin or 'plugin' in config - if is_lib or 'qt_module' in scope.get('_LOADED'): - assert not is_example - write_module(cm_fh, scope, indent=indent) - elif is_plugin: + if is_plugin: assert not is_example write_plugin(cm_fh, scope, indent=indent) + elif is_lib or 'qt_module' in scope.get('_LOADED'): + assert not is_example + write_module(cm_fh, scope, indent=indent) elif 'qt_tool' in scope.get('_LOADED'): assert not is_example write_tool(cm_fh, scope, indent=indent) else: - config = scope.get('CONFIG') gui = all(val not in config for val in ['console', 'cmdline']) if 'testcase' in config \ or 'testlib' in config \ -- cgit v1.2.3 From d8a7c0f40fff382fb4dbe6088b6f86ca4f4d03f5 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 7 Aug 2019 14:45:17 +0200 Subject: Handle DESTDIR override Set OUTPUT_DIRECTORY on a target when DESTDIR is specified. Change-Id: I72061ae8156356fcb2aa9ba6cb87049fcef600c7 Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f700ef8dff..c9fd604b2e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1800,6 +1800,11 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, cm_fh.write('{}list(APPEND test_data "{}")\n'.format(spaces(indent), data)) cm_fh.write('\n') + # Check for DESTDIR override + destdir = scope.get_string('DESTDIR') + if destdir: + extra_lines.append('OUTPUT_DIRECTORY "{}"'.format(destdir)) + cm_fh.write('{}{}({}\n'.format(spaces(indent), cmake_function, name)) for extra_line in extra_lines: cm_fh.write('{} {}\n'.format(spaces(indent), extra_line)) -- cgit v1.2.3 From 6396840182dd80518493c66fe025a4f032954dbd Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 8 Aug 2019 09:52:36 +0200 Subject: Fix conversion of SUBDIR pro files not in current directory This patch fixes the processing of pro files that do not reside in the current directory. Before this patch statements such as SUBDIRS += Foo/Bar/z.pro would cause z.pro to be parsed in the current CMakeLists.txt. What we want instead is a call to add_subdirectory(Foo/Bar). Failing to do so leads to issues with relative paths specified in z.pro being invalid. Change-Id: I7bfa7837d54877e5340081d1c9aebe855cf6796d Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index c9fd604b2e..3ebb6c7e1c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1091,14 +1091,22 @@ def handle_subdir(scope: Scope, collect_subdir_info(sd, current_conditions=current_conditions) # For the file case, directly write into the file handle. elif os.path.isfile(sd): - subdir_result = parseProFile(sd, debug=False) - 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) + # Handle cases with SUBDIRS += Foo/bar/z.pro. We want to be able + # to generate add_subdirectory(Foo/bar) instead of parsing the full + # .pro file in the current CMakeLists.txt. This causes issues + # with relative paths in certain projects otherwise. + dirname = os.path.dirname(sd) + if dirname: + 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) + + 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)) -- cgit v1.2.3 From 5d7bb2e4f084908a0e807ff8b0105f231bf74298 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 8 Aug 2019 13:21:23 +0200 Subject: Add support for converting qtTargetLibrary() value assignments Add support to pro2cmake to handle variable assignments via functions, e.g: TARGET = $$qtTargetLibrary($$TARGET). The evalulation of the functions happens during parsing and is very rudementary in nature. Currently it only covers the qtTargetLibrary(), required for certain projects, and quote(), required for passing unit tests. If we run into any unhanlded function an exception will be thrown. This patch also changes the TARGET property on Scope to expand the value. Change-Id: I678b7058067348a3972944bdba110f556cf22447 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 24 ++++++++++++++++++++++-- util/cmake/tests/data/value_function.pro | 2 ++ util/cmake/tests/test_parsing.py | 7 +++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 util/cmake/tests/data/value_function.pro (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3ebb6c7e1c..8e5e830783 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -278,6 +278,20 @@ def handle_vpath(source: str, base_dir: str, vpath: typing.List[str]) -> str: return '{}-NOTFOUND'.format(source) +def handle_function_value(group: pp.ParseResults): + function_name = group[0] + function_args = group[1] + if function_name == 'qtLibraryTarget': + if len(function_args) > 1: + raise RuntimeError('Don\'t know what to with more than one function argument for $$qtLibraryTarget().') + return str(function_args[0]) + + if function_name == 'quote': + # Do nothing, just return a string result + return str(group) + + 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]): if isinstance(value, list): @@ -751,9 +765,8 @@ class Scope(object): @property def TARGET(self) -> str: - return self.get_string('TARGET') \ + return self.expandString('TARGET') \ or os.path.splitext(os.path.basename(self.file))[0] - @property def _INCLUDED(self) -> typing.List[str]: return self.get('_INCLUDED') @@ -808,10 +821,17 @@ class QmakeParser: 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)) diff --git a/util/cmake/tests/data/value_function.pro b/util/cmake/tests/data/value_function.pro new file mode 100644 index 0000000000..598e4fadbd --- /dev/null +++ b/util/cmake/tests/data/value_function.pro @@ -0,0 +1,2 @@ +TARGET = Dummy +TARGET = $$qtLibraryTarget($$TARGET) diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index f924b13913..4019836ae1 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -343,3 +343,10 @@ def test_multi_condition_divided_by_lc(): def test_nested_function_calls(): result = parse_file(_tests_path + '/data/nested_function_calls.pro') assert len(result) == 1 + +def test_value_function(): + result = parse_file(_tests_path + '/data/value_function.pro') + target = result[0]['value'][0] + assert target == 'Dummy' + value = result[1]['value'] + assert value[0] == '$$TARGET' -- cgit v1.2.3 From 5424c72ab6e95a94ea6bc9e0ad80cae734bd3199 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 8 Aug 2019 14:34:56 +0200 Subject: Extend handling of special condtions Extend the hanlding of special condtions outside single line statements. Conditions such as 'qtHaveModule(gui):qtConfig(opengl(es1|es2)?)' would not be handled properly. Change-Id: I992f75311b82d84c574c9cb4ef6d7d648f425d81 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8e5e830783..589ac1d568 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -978,10 +978,10 @@ 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\)\?\)$', + 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'qtConfig\(opengl\.\*\)', r'QT_FEATURE_opengl', condition) condition = re.sub(r'^win\*$', r'win', condition) def gcc_version_handler(match_obj: re.Match): -- cgit v1.2.3 From 8ba882a0b39d1e16aa797f4b89f8796d22aa8bef Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 9 Aug 2019 11:01:41 +0200 Subject: Add support for QTQUICK_COMPILER_SKIPPED_RESOURCES Detect this in the conversion script and map it to a source file property. When that's the case, avoid repeating the file list but instead store it in a variable. Change-Id: If3119d83914bb798766e27351746b4e867bd3ab3 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 589ac1d568..78fd4fd1ce 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -115,7 +115,7 @@ 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 = '') -> str: +def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_file_path: str = '', skip_qtquick_compiler: bool = False) -> str: assert(target) # Hack to handle QT_SOURCE_TREE. Assume currently that it's the same @@ -181,6 +181,14 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil output += 'set_source_files_properties("{}"\n' \ ' PROPERTIES alias "{}"\n)\n'.format(full_source, alias) + if skip_qtquick_compiler: + file_list = '\n '.join(sorted_files) + output += 'set(resource_files\n {}\n)\n\n'.format(file_list) + file_list = "${resource_files}" + output += 'set_source_files_properties(${resource_files} QT_SKIP_QUICKCOMPILER 1)\n\n' + else: + file_list = '\n '.join(sorted_files) + params = '' if lang: params += ' LANG\n "{}"\n'.format(lang) @@ -190,7 +198,7 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil params += ' BASE\n "{}"\n'.format(base_dir) output += 'add_qt_resource({} "{}"\n{} FILES\n {}\n)\n'.format(target, full_resource_name, params, - '\n '.join(sorted_files)) + file_list) resource_count += 1 @@ -1666,12 +1674,13 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in # Handle QRC files by turning them into add_qt_resource: resources = scope.get_files('RESOURCES') + qtquickcompiler_skipped = scope.get_files('QTQUICK_COMPILER_SKIPPED_RESOURCES') qrc_output = '' if resources: qrc_only = True for r in resources: if r.endswith('.qrc'): - qrc_output += process_qrc_file(target, r, scope.basedir, scope.file_absolute_path) + qrc_output += process_qrc_file(target, r, scope.basedir, scope.file_absolute_path, r in qtquickcompiler_skipped) else: qrc_only = False -- cgit v1.2.3 From 73ba2ba2def56b9eba852b1f7e884925e713535d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 9 Aug 2019 11:42:37 +0200 Subject: Simplify resource embedding for qml modules * Add support for a QT_RESOURCE_PREFIX target property, that add_qt_resource respects. This makes it convenient to add files to the resource system for a project without the need to repeat prefixes. In qmake land with multiple resources they're repeated in the foo.prefix variables or in the prefix attribute in .qrc files. * Since /qt-project.org/imports is in the default QML import search path and the hierarchy under the import search paths is "regulated", we might as well make add_qml_module set QT_RESOURCE_PREFIX on the target. We can compute the correct value for that. This allows removing the redundant prefix from the add_qt_resource() calls for the qml files. Change-Id: Ic15130dc9e432340fc3edf93e35f2a803b4b40eb Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 78fd4fd1ce..794b2ec2ef 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2097,8 +2097,6 @@ def write_qml_plugin(cm_fh: typing.IO[str], if plugindump_dep: extra_lines.append('QML_PLUGINDUMP_DEPENDENCIES "{}"'.format(plugindump_dep)) - # This is only required because of qmldir - extra_lines.append('RESOURCE_PREFIX "/qt-project.org/imports"') def write_qml_plugin_qml_files(cm_fh: typing.IO[str], target: str, @@ -2116,15 +2114,11 @@ def write_qml_plugin_qml_files(cm_fh: typing.IO[str], target_path_mangled = target_path.replace('/', '_') target_path_mangled = target_path_mangled.replace('.', '_') resource_name = 'qmake_' + target_path_mangled - prefix = '/qt-project.org/imports/' + target_path - cm_fh.write('\n{}add_qt_resource({} {}\n{}PREFIX\n{}"{}"\n{}FILES\n{}${{qml_files}}\n)\n'.format( + 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), - prefix, - spaces(indent + 1), spaces(indent + 2))) cm_fh.write('\nqt_install_qml_files({}\n FILES ${{qml_files}}\n)\n\n'.format( -- cgit v1.2.3 From 95c27e325f47cff168a85c21b4b9e592168384f9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 9 Aug 2019 12:47:55 +0200 Subject: Fix add_qt_resource behavior with regards to unspecified prefixes This change makes the PREFIX parameter a required parameter if the target does not specify a default. This way the behavior is clear when reading the code: add_qt_resource() without PREFIX means it must come frmo the target. Change-Id: I79024e70e7b4d32a5164b93aa08ec9ff409b2d39 Reviewed-by: Alexandru Croitor Reviewed-by: Leander Beernaert --- util/cmake/pro2cmake.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 794b2ec2ef..097ed38f06 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -152,7 +152,7 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil for resource in root: assert(resource.tag == 'qresource') lang = resource.get('lang', '') - prefix = resource.get('prefix', '') + prefix = resource.get('prefix', '/') full_resource_name = resource_name + (str(resource_count) if resource_count > 0 else '') @@ -192,8 +192,7 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil params = '' if lang: params += ' LANG\n "{}"\n'.format(lang) - if prefix: - params += ' PREFIX\n "{}"\n'.format(prefix) + params += ' PREFIX\n "{}"\n'.format(prefix) if base_dir: params += ' BASE\n "{}"\n'.format(base_dir) output += 'add_qt_resource({} "{}"\n{} FILES\n {}\n)\n'.format(target, full_resource_name, -- cgit v1.2.3 From 1fc117e29de88e5a053eef2a2d11d9fd066ab80c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 9 Aug 2019 14:15:42 +0200 Subject: Add support for immediate resources Collect files passed to RESOURCES and write out an add_qt_resources() call. Similarly, for each "object" passed to RESOURCES (not a file), write add_qt_resources(), too. Change-Id: Idbb368ebb4d003a48e7c47ebaf53a78f65ebf9b9 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 94 +++++++++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 34 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 097ed38f06..efc57e26e9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -170,40 +170,46 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil alias = path files[path] = alias - sorted_files = sorted(files.keys()) - - assert(sorted_files) - - for source in sorted_files: - alias = files[source] - if alias: - full_source = os.path.join(base_dir, source) - output += 'set_source_files_properties("{}"\n' \ - ' PROPERTIES alias "{}"\n)\n'.format(full_source, alias) - - if skip_qtquick_compiler: - file_list = '\n '.join(sorted_files) - output += 'set(resource_files\n {}\n)\n\n'.format(file_list) - file_list = "${resource_files}" - output += 'set_source_files_properties(${resource_files} QT_SKIP_QUICKCOMPILER 1)\n\n' - else: - file_list = '\n '.join(sorted_files) - - params = '' - if lang: - params += ' LANG\n "{}"\n'.format(lang) - params += ' PREFIX\n "{}"\n'.format(prefix) - if base_dir: - params += ' BASE\n "{}"\n'.format(base_dir) - output += 'add_qt_resource({} "{}"\n{} FILES\n {}\n)\n'.format(target, full_resource_name, - params, - file_list) - + output += write_add_qt_resource_call(target, full_resource_name, prefix, base_dir, lang, files, skip_qtquick_compiler) 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) -> str: + output = '' + + sorted_files = sorted(files.keys()) + + assert(sorted_files) + + for source in sorted_files: + alias = files[source] + if alias: + full_source = os.path.join(base_dir, source) + output += 'set_source_files_properties("{}"\n' \ + ' PROPERTIES alias "{}"\n)\n'.format(full_source, alias) + + if skip_qtquick_compiler: + file_list = '\n '.join(sorted_files) + output += 'set(resource_files\n {}\n)\n\n'.format(file_list) + file_list = "${resource_files}" + output += 'set_source_files_properties(${resource_files} QT_SKIP_QUICKCOMPILER 1)\n\n' + else: + file_list = '\n '.join(sorted_files) + + params = '' + if lang: + params += ' LANG\n "{}"\n'.format(lang) + params += ' PREFIX\n "{}"\n'.format(prefix) + if base_dir: + params += ' BASE\n "{}"\n'.format(base_dir) + output += 'add_qt_resource({} "{}"\n{} FILES\n {}\n)\n'.format(target, resource_name, + params, file_list) + + return output + + def fixup_linecontinuation(contents: str) -> str: # Remove all line continuations, aka a backslash followed by # a newline character with an arbitrary amount of whitespace @@ -1676,15 +1682,35 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in qtquickcompiler_skipped = scope.get_files('QTQUICK_COMPILER_SKIPPED_RESOURCES') qrc_output = '' if resources: - qrc_only = True + standalone_files: typing.List[str] = [] for r in resources: + skip_qtquick_compiler = r in qtquickcompiler_skipped if r.endswith('.qrc'): - qrc_output += process_qrc_file(target, r, scope.basedir, scope.file_absolute_path, r in qtquickcompiler_skipped) + qrc_output += process_qrc_file(target, r, scope.basedir, scope.file_absolute_path, skip_qtquick_compiler) else: - qrc_only = False + immediate_files = {f:"" for f in scope.get_files(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_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) + else: + standalone_files.append(r) + + if standalone_files: + name = "qmake_immediate" + prefix = "/" + base = None + lang = None + 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) - if not qrc_only: - print(' XXXX Ignoring non-QRC file resources.') if qrc_output: cm_fh.write('\n# Resources:\n') -- cgit v1.2.3 From 3549f51c98fd6430ee09ceca78edd5b6e66660e1 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 9 Aug 2019 15:20:19 +0200 Subject: Fix cases where DESTDIR equals ./ or ../ When DESTDIR has relative paths as a parameter we should prefix those with ${CMAKE_CURRENT_BINARY_DIR} in order for them to be placed in the correct location. Change-Id: Ie9e9d656cbb54457bdf99425e3e1b05e09f20d7c Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index efc57e26e9..0b81cb10e1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1865,6 +1865,8 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, # Check for DESTDIR override 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)) cm_fh.write('{}{}({}\n'.format(spaces(indent), cmake_function, name)) -- cgit v1.2.3 From 1720970102836c20383b192fca6dbc17ee743392 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 9 Aug 2019 17:41:16 +0200 Subject: Fix resource prefix calculation tools/qml/qml.qrc uses prefix="something", which means that the leading slash is implied. Change-Id: Ia856fcef36873442a84b9162f1901cb31ca37b49 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0b81cb10e1..767a2b4371 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -153,6 +153,8 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil 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 '') -- cgit v1.2.3 From e614e837fafc927d64ef4d9b5866cbccd0b44281 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 12 Aug 2019 13:38:17 +0200 Subject: Add QT_QML_DEBUG define conversion Add QT_QML_DEBUG to target when we have qml_debug in qmake's CONFIG variable. Change-Id: I266c7313db12667498d4f770c73aec9e79c0b50e Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 767a2b4371..e47cba50ab 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1362,6 +1362,9 @@ def write_defines(cm_fh: typing.IO[str], scope: Scope, cmake_parameter: str, *, defines = [d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"') for d in defines] + if 'qml_debug' in scope.get('CONFIG'): + defines.append('QT_QML_DEBUG') + write_list(cm_fh, defines, cmake_parameter, indent, footer=footer) -- cgit v1.2.3 From 87078650a503477ddf63cf5656e4e82e9259ef56 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 12 Aug 2019 14:05:05 +0200 Subject: Wrap resource file paths in quotes This is required for paths that contain spaces, otherwise the .qrc file generated by the build system is invalid, and the RCC run fails at build time. Change-Id: I887100b08052b49dd7ea4dd92b94f357a7d9d16e Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e47cba50ab..591bb84aa4 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -192,6 +192,9 @@ def write_add_qt_resource_call(target: str, resource_name: str, prefix: typing.O output += 'set_source_files_properties("{}"\n' \ ' PROPERTIES alias "{}"\n)\n'.format(full_source, alias) + # Quote file paths in case there are spaces. + sorted_files = ['"{}"'.format(f) for f in sorted_files] + if skip_qtquick_compiler: file_list = '\n '.join(sorted_files) output += 'set(resource_files\n {}\n)\n\n'.format(file_list) @@ -2137,6 +2140,10 @@ def write_qml_plugin_qml_files(cm_fh: typing.IO[str], 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] + cm_fh.write('\n{}set(qml_files\n{}{}\n)\n'.format( spaces(indent), spaces(indent + 1), -- cgit v1.2.3 From 8630c5ac7ef9db033f96adbd5dec363f9f2796b9 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 12 Aug 2019 16:31:30 +0200 Subject: Add support for QtQuikCompiler retained resources Support translating the QTQUICK_COMPILER_RETAINED_RESOURCES variable. Fix missing PROPERTIES keyword for set_source_files_properties() commands. Apply source file properties to standalone source files as they appear in the project as it was possible for them to inherit values that were not meant for them. When creating resource lists, prefix them with the resource name to avoid collisions. Change-Id: I69ef85ea9414c0e7c07b1ebfb76d24bd878ce70f Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 591bb84aa4..b57a4beb1f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -115,7 +115,8 @@ 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) -> str: +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) -> str: assert(target) # Hack to handle QT_SOURCE_TREE. Assume currently that it's the same @@ -172,13 +173,14 @@ 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) + output += write_add_qt_resource_call(target, full_resource_name, prefix, base_dir, lang, files, skip_qtquick_compiler, retain_qtquick_compiler) 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) -> str: +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) -> str: output = '' sorted_files = sorted(files.keys()) @@ -195,13 +197,14 @@ def write_add_qt_resource_call(target: str, resource_name: str, prefix: typing.O # Quote file paths in case there are spaces. sorted_files = ['"{}"'.format(f) for f in sorted_files] + 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) if skip_qtquick_compiler: - file_list = '\n '.join(sorted_files) - output += 'set(resource_files\n {}\n)\n\n'.format(file_list) - file_list = "${resource_files}" - output += 'set_source_files_properties(${resource_files} QT_SKIP_QUICKCOMPILER 1)\n\n' - else: - file_list = '\n '.join(sorted_files) + output += 'set_source_files_properties(${{{}_resource_files}} PROPERTIES QT_SKIP_QUICKCOMPILER 1)\n\n'.format(resource_name) + + if retain_qtquick_compiler: + output += 'set_source_files_properties(${{{}_resource_files}} PROPERTIES QT_RETAIN_QUICKCOMPILER 1)\n\n'.format(resource_name) params = '' if lang: @@ -1688,13 +1691,16 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in # 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 = '' if resources: standalone_files: typing.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) + qrc_output += process_qrc_file(target, r, scope.basedir, scope.file_absolute_path, + skip_qtquick_compiler, retain_qtquick_compiler) else: immediate_files = {f:"" for f in scope.get_files(r + ".files")} if immediate_files: @@ -1706,8 +1712,16 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in immediate_base = scope.get(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) + qrc_output += write_add_qt_resource_call(target, immediate_name, immediate_prefix, immediate_base, immediate_lang, + immediate_files, skip_qtquick_compiler, retain_qtquick_compiler) else: + # stadalone source file properties need to be set as they + # are parsed. + if skip_qtquick_compiler: + output += 'set_source_files_properties("{}" PROPERTIES QT_SKIP_QUICKCOMPILER 1)\n\n'.format(r) + + if retain_qtquick_compiler: + output += 'set_source_files_properties("{}" PROPERTIES QT_RETAIN_QUICKCOMPILER 1)\n\n'.format(r) standalone_files.append(r) if standalone_files: @@ -1717,7 +1731,8 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in lang = None 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) + qrc_output += write_add_qt_resource_call(target, name, prefix, base, lang, files, + skip_qtquick_compiler = False, retain_qtquick_compiler = False) if qrc_output: -- cgit v1.2.3 From 7fda42ef9ad1d4980c905ff4415124adf5d06966 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 15 Aug 2019 10:42:47 +0200 Subject: Rename alias property on source files to QT_RESOURCE_ALIAS Rename the alias property used by add_qt_resource() to QT_RESOURCE_ALIAS to match property naming conventions. Change-Id: I97b12b0b794e158f03dabeed5ec23a3b7d56cfbb Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b57a4beb1f..29fd885370 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -192,7 +192,7 @@ def write_add_qt_resource_call(target: str, resource_name: str, prefix: typing.O if alias: full_source = os.path.join(base_dir, source) output += 'set_source_files_properties("{}"\n' \ - ' PROPERTIES alias "{}"\n)\n'.format(full_source, alias) + ' PROPERTIES QT_RESOURCE_ALIAS "{}"\n)\n'.format(full_source, alias) # Quote file paths in case there are spaces. sorted_files = ['"{}"'.format(f) for f in sorted_files] -- cgit v1.2.3 From b8dae2c617af4cceb886b5ae87ab3dbe79a4ecd0 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 12 Aug 2019 15:51:17 +0200 Subject: Handle test helpers better Teach pro2cmake to use add_qt_test_helper instead of add_qt_executable when the qmake 'qt_test_helper' feature is used. Don't use macOS bundles when building tests on macOS, because that breaks path assumptions for certain tests. Introduce a new OVERRIDE_OUTPUT_DIRECTORY option for add_qt_test_helper that allows placing the binary into a folder other than the test parent folder. I considered changing the default behavior not to place into the parent folder, but that would break all existing tests, so I opted for override approach instead. Ultimately we might want to revisit this later. Change-Id: I68fd1dea608333c2af0d3896050b40a6964dd87f Reviewed-by: Leander Beernaert Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 29fd885370..f6d354f19c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2008,7 +2008,14 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope, binary_name = scope.TARGET assert binary_name - extra = ['GUI',] if gui else [] + 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' + + if is_qt_test_helper: + binary_name += '_helper' + cmake_function_call = 'add_qt_test_helper' target_path = scope.get_string('target.path') if target_path: @@ -2017,7 +2024,7 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope, if 'target' in scope.get('INSTALLS'): extra.append('INSTALL_DIRECTORY "{}"'.format(target_path)) - write_main_part(cm_fh, binary_name, 'Binary', 'add_qt_executable', scope, + 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']) -- cgit v1.2.3 From 3a105b9d11ea80c76b381410d9a92fc4c57dc244 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 21 Aug 2019 16:19:16 +0200 Subject: Extend run_pro2cmake.py to pass --is-example to pro2cmake Add command line argument to make run_pro2cmake invoke pro2cmake with the --is-example option so we can convert examples in bulk. Change-Id: I162eddffc509f16a97de5517698e8daca5207b74 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/run_pro2cmake.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index bc64fb3fbb..f98dbf63a8 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -42,6 +42,8 @@ def parse_command_line(): 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.') @@ -126,7 +128,11 @@ def run(all_files: typing.List[str], pro2cmake: str, args: argparse.Namespace) - def _process_a_file(data: typing.Tuple[str, int, int]) -> typing.Tuple[int, str, str]: filename, index, total = data - result = subprocess.run((pro2cmake, os.path.basename(filename)), + pro2cmake_args = [pro2cmake] + if args.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) -- cgit v1.2.3 From f96faa95aa8a7c57e2d87c9097179ed7968f1e00 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 21 Aug 2019 15:47:38 +0200 Subject: Update pro2cmake example generation to use QT5_ADD_RESOURCES Update pro2cmake to generate CMake projects where resources are handled via the QT5_ADD_RESOURCES command. Change-Id: I66dc2174a45fc652fd8c9b7e3c0f46f58ae02c0c Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 26c346eef9..7c19c77ae8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -116,7 +116,7 @@ def find_qmake_conf(project_file_path: str = '') -> typing.Optional[str]: 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) -> str: + 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 @@ -173,14 +173,14 @@ 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) + 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) -> str: + lang: typing.Optional[str], files: typing.Dict[str, str], skip_qtquick_compiler: bool, retain_qtquick_compiler: bool, is_example :bool) -> str: output = '' sorted_files = sorted(files.keys()) @@ -212,8 +212,13 @@ def write_add_qt_resource_call(target: str, resource_name: str, prefix: typing.O params += ' PREFIX\n "{}"\n'.format(prefix) if base_dir: params += ' BASE\n "{}"\n'.format(base_dir) - output += 'add_qt_resource({} "{}"\n{} FILES\n {}\n)\n'.format(target, resource_name, - params, file_list) + add_resource_command = '' + if is_example: + add_resource_command = 'QT5_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) return output @@ -1689,7 +1694,7 @@ def map_to_cmake_condition(condition: typing.Optional[str]) -> str: return condition -def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: int = 0): +def write_resources(cm_fh: typing.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: @@ -1704,7 +1709,7 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in 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) + skip_qtquick_compiler, retain_qtquick_compiler, is_example) else: immediate_files = {f:"" for f in scope.get_files(r + ".files")} if immediate_files: @@ -1717,7 +1722,7 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in 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) + immediate_files, skip_qtquick_compiler, retain_qtquick_compiler, is_example) else: # stadalone source file properties need to be set as they # are parsed. @@ -1736,7 +1741,8 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in 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) + skip_qtquick_compiler = False, retain_qtquick_compiler = False, + is_example = is_example) if qrc_output: @@ -2071,7 +2077,7 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, add_executable = 'add_{}executable({}'.format("qt_gui_" if gui else "", binary_name); - write_all_source_file_lists(cm_fh, scope, add_executable, indent=0, extra_keys=['RESOURCES']) + write_all_source_file_lists(cm_fh, scope, add_executable, indent=0) cm_fh.write(')\n') @@ -2086,6 +2092,8 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, write_compile_options(cm_fh, scope, 'target_compile_options({}'.format(binary_name), indent=0, footer=')') + 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' + -- cgit v1.2.3 From 02074bf345995def88d4e658af7d709101c94110 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 19 Aug 2019 15:06:54 +0200 Subject: Fix more uses of functions in handle_function_value Change-Id: Ie6b6f79b50e9eaf605427f19a8c2ca86050e2f74 Reviewed-by: Tobias Hunger Reviewed-by: Qt CMake Build Bot Reviewed-by: Leander Beernaert --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7c19c77ae8..8c805ea481 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -317,7 +317,8 @@ def handle_function_value(group: pp.ParseResults): return str(group) # Return the whole expression as a string. - if function_name in ['join', 'cmakeRelativePath', 'shell_quote', 'shadowed']: + if function_name in ['join', 'cmakeRelativePath', 'shell_quote', 'shadowed', 'cmakeTargetPath', + 'shell_path']: return 'join({})'.format(''.join(function_args)) raise RuntimeError('No logic to handle function "{}", please add one in handle_function_value().'.format(function_name)) -- cgit v1.2.3 From 54aeb4ccd77e5660474ae282d153c30fffda596e Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 19 Aug 2019 15:07:22 +0200 Subject: Add support for Qt header_modules aka a header only INTERFACE library Also add support for modules that have no private module counterpart. Both are needed for Designer's QtUiPlugin in qttools. Change-Id: Ia7e9d8837140e1de5cd59e196b4f63481ab68298 Reviewed-by: Tobias Hunger Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8c805ea481..1d30cac4b6 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1962,6 +1962,10 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, 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): -- cgit v1.2.3 From 60ceb1b0c57c5f7edb33a3035cc48a830076d98b Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 20 Aug 2019 16:18:02 +0200 Subject: Handle contains(CONFIG, static) in pro2cmake Change-Id: I36717688dd3b2ff0f6730bf3d65be421e391895f Reviewed-by: Tobias Hunger Reviewed-by: Qt CMake Build Bot Reviewed-by: Leander Beernaert --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1d30cac4b6..0b03e2f690 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1014,6 +1014,7 @@ def map_condition(condition: str) -> str: condition) condition = re.sub(r'qtConfig\(opengl\.\*\)', r'QT_FEATURE_opengl', condition) condition = re.sub(r'^win\*$', r'win', condition) + condition = re.sub(r'contains\(CONFIG, static\)', r'NOT QT_BUILD_SHARED_LIBS', condition) def gcc_version_handler(match_obj: re.Match): operator = match_obj.group(1) -- cgit v1.2.3 From bf298499a058394cff1041580c29d30fc78568ef Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 23 Aug 2019 11:39:30 +0200 Subject: Update public CMake macros' version Update all public Qt macros to use qt6/QT6 instead of qt5/QT6. Change-Id: Ib178f4fa21f37dfb8da7d4d8c097aa0e96c9d9f9 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0b03e2f690..54521bab55 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -214,7 +214,7 @@ def write_add_qt_resource_call(target: str, resource_name: str, prefix: typing.O params += ' BASE\n "{}"\n'.format(base_dir) add_resource_command = '' if is_example: - add_resource_command = 'QT5_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, -- cgit v1.2.3 From 103d1aa91083ed5ab6268996fcadc6a55e9cc465 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 26 Aug 2019 10:11:27 +0200 Subject: Use GLOB_RECURSE for testdata Use GLOB_RECURSE for testdata instead of globbing the top level folder. Although RCC supports scanning directories, it will result in alias collosions and test not being able to find the files on platforms where test data is embedded into the binary. Change-Id: I35d3d46a5d9fcafa5c8dc833eafdd0044ffe355f Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 54521bab55..c72e27b9cf 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1883,8 +1883,7 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, for data in test_data: if '*' in data: cm_fh.write(dedent(""" - {indent}file(GLOB test_data_glob - {indent1}LIST_DIRECTORIES true + {indent}file(GLOB_RECURSE test_data_glob {indent1}RELATIVE ${{CMAKE_CURRENT_SOURCE_DIR}} {indent1}"{}") """).format( -- cgit v1.2.3 From 060784c981557aaf289631111c47e38c46b934d3 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 26 Aug 2019 10:52:17 +0200 Subject: Remove ../ from Targets Remove repeating '../' pattern from target names, since we generate the targets in the correct location with CMake. Change-Id: I89d527a9ad717f742f8d0e5921f378e6ac0a229d Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index c72e27b9cf..3a17ea05d9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -796,8 +796,10 @@ class Scope(object): @property def TARGET(self) -> str: - return self.expandString('TARGET') \ + target = self.expandString('TARGET') \ or os.path.splitext(os.path.basename(self.file))[0] + return re.sub('\.\./', '', target) + @property def _INCLUDED(self) -> typing.List[str]: return self.get('_INCLUDED') -- cgit v1.2.3 From 5a5dc6294b0eb5e32f99a903a85c66d0c871f274 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 26 Aug 2019 16:15:56 +0200 Subject: Augment conversion scripts with requirements for qttools The libclang test is implemented as a custom FindWrapLibClang module. The module does mostly the same things as the configure test in qmake land. Change-Id: I965f284baf7daef074e22f033047c35a917c8808 Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 15 +++++++++++++++ util/cmake/helper.py | 1 + 2 files changed, 16 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index bede5934a5..22e98a6864 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -32,6 +32,7 @@ import os.path import re import sys from typing import Set, Union, List, Dict +from textwrap import dedent from helper import map_qt_library, featureName, map_platform, \ find_3rd_party_library_mapping, generate_find_package_info @@ -80,6 +81,7 @@ def map_tests(test: str) -> str: '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', @@ -590,6 +592,19 @@ def parseTest(ctx, test, data, cm_fh): cm_fh.write(qmakeFixme) cm_fh.write(")\n\n") + elif data["type"] == "libclang": + knownTests.add(test) + + cm_fh.write("# {}\n".format(test)) + lib_clang_lib = find_3rd_party_library_mapping("libclang") + cm_fh.write(generate_find_package_info(lib_clang_lib)) + cm_fh.write(dedent(""" + if(TARGET WrapLibClang::WrapLibClang) + set(TEST_libclang "ON" CACHE BOOL "Required libclang version found." FORCE) + endif() + """)) + cm_fh.write("\n") + elif data["type"] == "x86Simd": knownTests.add(test) diff --git a/util/cmake/helper.py b/util/cmake/helper.py index e22284aab4..e9e0986cce 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -203,6 +203,7 @@ _library_map = [ 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 -- cgit v1.2.3 From 3416a83f0546449260268268d2d5e3026f38297e Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 26 Aug 2019 16:15:56 +0200 Subject: Augment conversion scripts with requirements for qttools part 2 Designer components target names need to be prefixed with Qt. Change-Id: I13037820b080d88cd670f2db97232509eb868daf Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index e9e0986cce..79473ef3c6 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -74,6 +74,8 @@ _qt_library_map = [ 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']), -- cgit v1.2.3 From 5c75f6a21aba9192743f060768c9f185708d4b64 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 27 Aug 2019 19:15:36 +0200 Subject: Fix some corner cases of condition mapping in pro2cmake no-png is an old unused scope that should be mapped to the png feature instead. Map contains(QT_CONFIG, shared) to correct variable check. Sympy doesn't like dashes in indentifiers, so replace all of them with a _dash_ token which will be replaced again at the end of the condition simplifying. When doing sympy preprocessing, fix mapping of target names that don't contain double colons. Change-Id: Id3d37800665c96505b7cbb1d80fdbed59c3ae9c0 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3a17ea05d9..2e80ef12fe 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1016,7 +1016,9 @@ def map_condition(condition: str) -> str: 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) @@ -1624,6 +1626,8 @@ def simplify_condition(condition: str) -> str: condition = condition.replace(' OR ', ' | ') condition = condition.replace(' ON ', ' true ') condition = condition.replace(' OFF ', ' false ') + # Replace dashes with a token + 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. @@ -1633,7 +1637,9 @@ def simplify_condition(condition: str) -> str: # the expression, and thus simplify it. # Do this by replacing and keeping a map of conditions to single # token symbols. - pattern = re.compile(r'(TARGET [a-zA-Z]+::[a-zA-Z]+)') + # Support both target names without double colons, and with double + # colons. + 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: @@ -1657,6 +1663,7 @@ def simplify_condition(condition: str) -> str: condition = condition.replace('|', 'OR') condition = condition.replace('True', 'ON') condition = condition.replace('False', 'OFF') + condition = condition.replace('_dash_', '-') except: # sympy did not like our input, so leave this condition alone: condition = input_condition -- cgit v1.2.3 From c3ef1a012b377b1cda1ade7de2db7785badfff53 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 28 Aug 2019 12:57:12 +0200 Subject: Fix handling of more functions in handle_function_value Essentially pass them through without failing the whole conversion process. Change-Id: I7bbd198203f79b45120a7254e2ecdf58e32f9525 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 2e80ef12fe..db7dfa9747 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -318,7 +318,8 @@ def handle_function_value(group: pp.ParseResults): # Return the whole expression as a string. if function_name in ['join', 'cmakeRelativePath', 'shell_quote', 'shadowed', 'cmakeTargetPath', - 'shell_path']: + 'shell_path', 'cmakeProcessLibs', 'cmakeTargetPaths', + 'cmakePortablePaths', 'escape_expand']: return 'join({})'.format(''.join(function_args)) raise RuntimeError('No logic to handle function "{}", please add one in handle_function_value().'.format(function_name)) -- cgit v1.2.3 From 0798ed8a83eca831515d9ea710a49dcaa32a675b Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 27 Aug 2019 19:17:44 +0200 Subject: Fix subdir handling in pro2cmake Conditions scopes that had "else" were not handled properly. The "else" condition should be expanded to its proper negative full form. Also every condition "piece" should be wrapped with parentheses before being passed to sympy simplification, to be sure that boolean operator precedence is applied properly. Refactored the code a bit to make it easier to read. Change-Id: I4726845b867f93c3afbc9362cdce1cd063ccff63 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index db7dfa9747..315efb1f8a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1171,15 +1171,39 @@ def handle_subdir(scope: Scope, # scopes, aka recursively call the same function, but with an # updated current_conditions frozen set. 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, c.condition))) + current_conditions=frozenset((*current_conditions, + child_condition))) def group_and_print_sub_dirs(indent: int = 0): # Simplify conditions, and group # subdirectories with the same conditions. grouped_sub_dirs = {} + + # 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] + + 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 = '' + 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)) + 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()) @@ -1188,18 +1212,12 @@ def handle_subdir(scope: Scope, # that should be added unconditionally. condition_key = '' if additions or subtractions: - addition_str = '' - subtraction_str = '' - if additions: - addition_str = " OR ".join(sorted("(" + " AND ".join(condition) + ")" - for condition in additions)) - if addition_str: - addition_str = '({})'.format(addition_str) - if subtractions: - subtraction_str = " OR ".join(sorted("(" + " AND ".join(condition) + ")" - for condition in subtractions)) - if subtraction_str: - subtraction_str = 'NOT ({})'.format(subtraction_str) + addition_str = join_all_conditions(additions) + if addition_str: + addition_str = '({})'.format(addition_str) + subtraction_str = join_all_conditions(subtractions) + if subtraction_str: + subtraction_str = 'NOT ({})'.format(subtraction_str) condition_str = addition_str if condition_str and subtraction_str: @@ -1230,6 +1248,10 @@ def handle_subdir(scope: Scope, # as the scope deepens. current_conditions = frozenset() + # Compute the total condition for scopes. Needed for scopes that + # have 'else' as a condition. + recursive_evaluate_scope(scope) + # Do the work. handle_subdir_helper(scope, cm_fh, indent=indent, -- cgit v1.2.3 From e5bf0290d9249846f385b05046e82b8261be6b6c Mon Sep 17 00:00:00 2001 From: Maurice Kalinowski Date: Wed, 28 Aug 2019 16:03:14 +0200 Subject: Add Qt Mqtt to the library mapping Change-Id: Ifa50161a579647002d3349b49da7012660c59f81 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 79473ef3c6..387b66fcb0 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -113,6 +113,7 @@ _qt_library_map = [ 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']), -- cgit v1.2.3 From 8da530510236061968e46c11f5d266ecb6f68286 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 30 Aug 2019 15:03:50 +0200 Subject: Fix Harfbuzz detection We use qt_find_package(harfbuzz PROVIDED_TARGETS harfbuzz::harfbuzz) which would set harfbuzz_FOUND. Since variable names are case-sensitive, this is also the name we must check for in the qt feature condition. Change-Id: I43420489c3310bc9c3e5cc798a005c8d5b0ab646 Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 22e98a6864..69e8627830 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -660,7 +660,7 @@ def parseFeature(ctx, feature, data, cm_fh): }, 'GNUmake': None, 'harfbuzz': { - 'condition': 'HARFBUZZ_FOUND' + 'condition': 'harfbuzz_FOUND' }, 'host-dbus': None, 'iconv': { -- cgit v1.2.3 From 3ff31ed020a38a2975e99a44303a70994f3c60f4 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Mon, 2 Sep 2019 17:37:26 +0300 Subject: Enable building of the mysql plugin Change-Id: I8cb97afaaed46ee64b5a133e797179d7ddecdeef Reviewed-by: Simon Hausmann Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 387b66fcb0..b1a07a0a11 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -216,6 +216,7 @@ _library_map = [ 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'), -- cgit v1.2.3 From 95c9c5a308a87d91dc1d0797ad186aee7bd7e012 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Tue, 3 Sep 2019 13:44:39 +0300 Subject: cmake: Enable building of the db2 plugin Change-Id: I26810cccba5f3128cb47e0bf53b5ec78de2eec8c Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index b1a07a0a11..77ef783965 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -190,6 +190,7 @@ _library_map = [ 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'), -- cgit v1.2.3 From b2a11daaa177805d36e33ca57e94093919107269 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Thu, 5 Sep 2019 10:07:24 +0300 Subject: cmake: Enable building of the oci plugin Change-Id: I003d6da10c7e47402fb373d3956817817328cf0f Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 77ef783965..5d1cfadace 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -223,6 +223,7 @@ _library_map = [ 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'), -- cgit v1.2.3 From 3215f5457a202a252be49a4ef41db501d7609cce Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 9 Sep 2019 10:52:27 +0200 Subject: Handle top level tests, examples and main qmake projects in pro2cmake The script now detects whether the project file given is a: - top level qmake project (qtdeclarative.pro) - top level tests project (qtdeclarative/tests.pro) - top level examples project (qtdeclarative/examples.pro) This is done by finding the .qmake.conf file in parent directories of the given project, and comparing the project's location to the .qmake.conf location. For the top level qmake project and the tests project, the script will now use a predefined block of code that we usually had to copy paste and change manually. Now only small bits will have to be adjusted manually (project name and dependencies). For the examples project, the content is surrounded by the required build examples commands. As a result, developers will have to worry less about knowing where to copy paste from. Change-Id: I4f751b371e74eeb86e070d58635c3d99b970ab18 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 151 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 315efb1f8a..dbf58dc558 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -39,6 +39,7 @@ import os.path import re import io import typing +import glob from sympy.logic import (simplify_logic, And, Or, Not,) import pyparsing as pp @@ -53,6 +54,8 @@ from shutil import copyfile from special_case_helper import SpecialCaseHandler +cmake_version_string = "3.15.0" + def _parse_commandline(): parser = ArgumentParser(description='Generate CMakeLists.txt files from .' 'pro files.') @@ -96,6 +99,40 @@ def _parse_commandline(): return parser.parse_args() +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 + + +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 + + +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 + + def find_qmake_conf(project_file_path: str = '') -> typing.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: {}' @@ -2274,17 +2311,125 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, footer = ')\n') +def handle_top_level_repo_project(scope: Scope, cm_fh: typing.IO[str]): + # qtdeclarative + project_file_name = os.path.splitext(os.path.basename(scope.file_absolute_path))[0] + + # declarative + file_name_without_qt_prefix = project_file_name[2:] + + # Qt::Declarative + qt_lib = map_qt_library(file_name_without_qt_prefix) + + # Found a mapping, adjust name. + if qt_lib != file_name_without_qt_prefix: + # QtDeclarative + qt_lib = re.sub(r':', r'', qt_lib) + + # Declarative + qt_lib_no_prefix = qt_lib[2:] + else: + qt_lib += "_FIXME" + qt_lib_no_prefix = qt_lib + + content = """cmake_minimum_required(VERSION {}) + +project({} + VERSION 6.0.0 + DESCRIPTION "Qt {} 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) + + cm_fh.write('{}'.format(content)) + + +def find_top_level_repo_project_file(project_file_path: str = '') -> typing.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')) + 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]): + top_level_project_path = find_top_level_repo_project_file(scope.file_absolute_path) + if top_level_project_path: + # qtdeclarative + file_name = os.path.splitext(os.path.basename(top_level_project_path))[0] + + # declarative + file_name_without_qt = file_name[2:] + + # Qt::Declarative + qt_lib = map_qt_library(file_name_without_qt) + + # Found a mapping, adjust name. + if qt_lib != file_name_without_qt: + # QtDeclarative + 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() + +qt_build_tests() +""".format(cmake_version_string, qt_lib) + + cm_fh.write('{}'.format(content)) + + def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *, indent: int = 0, is_example: bool=False) -> None: template = scope.TEMPLATE - if template == 'subdirs': - handle_subdir(scope, cm_fh, indent=indent, is_example=is_example) + + temp_buffer = io.StringIO() + + # Handle top level repo project in a special way. + if is_top_level_repo_project(scope.file_absolute_path): + handle_top_level_repo_project(scope, temp_buffer) + # 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': + handle_subdir(scope, temp_buffer, indent=indent, is_example=is_example) elif template in ('app', 'lib'): - handle_app_or_lib(scope, cm_fh, indent=indent, is_example=is_example) + 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)) + buffer_value = temp_buffer.getvalue() + + if is_top_level_repo_examples_project(scope.file_absolute_path): + # 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() + +{} +qt_examples_build_end() +""".format(buffer_value) + + cm_fh.write(buffer_value) + def generate_new_cmakelists(scope: Scope, *, is_example: bool=False) -> None: print('Generating CMakeLists.gen.txt') -- cgit v1.2.3 From e87677ad4f2dec8c6e5991de1180016ddc937342 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 9 Sep 2019 14:16:26 +0200 Subject: Try to detect if project given to pro2cmake is an example If the project path starts with "examples/" relative to the repo source directory (which is decided by location of .qmake.conf), consider the given project file to be an example. This makes the usage of run_pro2cmake.py easier, specifically by being able to point it to a repo source directory, and getting most project conversions correctly. Change-Id: I93de98f8fc97af509c1f96cdebad3681190a53d1 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index dbf58dc558..91e7f5f5f4 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -133,6 +133,18 @@ def is_top_level_repo_examples_project(project_file_path: str = '') -> bool: return False +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 + + def find_qmake_conf(project_file_path: str = '') -> typing.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: {}' @@ -2437,7 +2449,10 @@ def generate_new_cmakelists(scope: Scope, *, is_example: bool=False) -> None: assert scope.file cm_fh.write('# Generated from {}.\n\n' .format(os.path.basename(scope.file))) - cmakeify_scope(scope, cm_fh, is_example=is_example) + + is_example_heuristic = is_example_project(scope.file_absolute_path) + final_is_example_decision = is_example or is_example_heuristic + cmakeify_scope(scope, cm_fh, is_example=final_is_example_decision) def do_include(scope: Scope, *, debug: bool = False) -> None: -- cgit v1.2.3 From b0dbfc3094b9c3950643fc5acf7020e33825dd3e Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 9 Sep 2019 14:29:12 +0200 Subject: Skip converting certain projects when they are passed to pro2cmake Specifically skip cmake tests projects (because they are only needed when Qt is built with qmake). Also skip test qmake projects in qmake's test suite. We might find more tests to skip in the future. Change-Id: I4c3a42db669b31e81fd06206652371b6a70fd8ce Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 91e7f5f5f4..e2ac9ef4b0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2485,6 +2485,23 @@ def copy_generated_file_to_final_location(scope: Scope, keep_temporary_files=Fal os.remove(scope.generated_cmake_lists_path) +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'): + return False + + # Skip qmake testdata projects. + if project_relative_path.startswith('tests/auto/tools/qmake/testdata'): + return False + + return True + + def main() -> None: args = _parse_commandline() @@ -2498,6 +2515,11 @@ def main() -> None: if new_current_dir: os.chdir(new_current_dir) + project_file_absolute_path = os.path.abspath(file) + if not should_convert_project(project_file_absolute_path): + print('Skipping conversion of project: "{}"'.format(project_file_absolute_path)) + continue + parseresult = parseProFile(file_relative_path, debug=debug_parsing) if args.debug_parse_result or args.debug: -- cgit v1.2.3 From 2cf0ba1fba9293b3265a186527dbc90d395dfd20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Fri, 5 Jul 2019 10:47:26 +0200 Subject: Use pre-compiled headers when building Qt with cmake Some modules define their own manually-maintained lists, and we can rely on the headers generated by each module to include in the pch as well e.g. QtCore/QtCore. There's also e.g. QtWidgetDepends for QtWidgets, but this only works for modules, not for tools, examples or other applications. For now we'll use the Qt/Qt headers for the modules we depend on. Building with PCH can be disabled with -DBUILD_WITH_PCH=NO, and it only works for versions of CMake newer than 3.15.20190829. Change-Id: Iae52bd69acfdfd58f4cd20d3cfa3c7f42775f732 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e2ac9ef4b0..22e1b4b574 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1534,6 +1534,18 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *, for mo in moc_options: cm_fh.write('{} "{}"\n'.format(ind, mo)) + precompiled_header = scope.get('PRECOMPILED_HEADER') + if precompiled_header: + cm_fh.write('{} PRECOMPILED_HEADER\n'.format(ind)) + for header in precompiled_header: + cm_fh.write('{} "{}"\n'.format(ind, header)) + + no_pch_sources = scope.get('NO_PCH_SOURCES') + if no_pch_sources: + cm_fh.write('{} NO_PCH_SOURCES\n'.format(ind)) + for source in no_pch_sources: + cm_fh.write('{} "{}"\n'.format(ind, source)) + def is_simple_condition(condition: str) -> bool: return ' ' not in condition \ -- cgit v1.2.3 From 9d893641b920d1d11ee55dc42cec1517d228ddcd Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 11 Sep 2019 09:13:19 +0200 Subject: Minor fix path handling when considering whether to convert project MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should use the new relative path to the project, instead of the old specified on command line. Change-Id: I54cb1cefd4df079a95c364b7c7c66c36941add01 Reviewed-by: Leander Beernaert Reviewed-by: Jörg Bornemann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 22e1b4b574..1274d44741 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2527,7 +2527,7 @@ def main() -> None: if new_current_dir: os.chdir(new_current_dir) - project_file_absolute_path = os.path.abspath(file) + 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)) continue -- cgit v1.2.3 From 8489fe4ba4e1babd9e8c6dd2a5d92b6e947db341 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 11 Sep 2019 10:13:06 +0200 Subject: Use posix paths in pro2cmake and configurejson2cmake even on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the conversions script usable on Windows. Change-Id: Icb37f3ee8ae1c942556f524984ce3aed0d21cee0 Reviewed-by: Leander Beernaert Reviewed-by: Jörg Bornemann --- util/cmake/configurejson2cmake.py | 10 +++++----- util/cmake/pro2cmake.py | 15 ++++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 69e8627830..5676aa536b 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -28,7 +28,7 @@ ############################################################################# import json_parser -import os.path +import posixpath import re import sys from typing import Set, Union, List, Dict @@ -151,10 +151,10 @@ def cm(ctx, *output): def readJsonFromDir(dir): - path = os.path.join(dir, 'configure.json') + path = posixpath.join(dir, 'configure.json') print('Reading {}...'.format(path)) - assert os.path.exists(path) + assert posixpath.exists(path) parser = json_parser.QMakeSpecificJSONParser() return parser.parse(path) @@ -943,7 +943,7 @@ def processSubconfigs(dir, ctx, data): assert ctx is not None if 'subconfigs' in data: for subconf in data['subconfigs']: - subconfDir = os.path.join(dir, subconf) + subconfDir = posixpath.join(dir, subconf) subconfData = readJsonFromDir(subconfDir) subconfCtx = ctx processJson(subconfDir, subconfCtx, subconfData) @@ -954,7 +954,7 @@ def processJson(dir, ctx, data): ctx = processFiles(ctx, data) - with open(os.path.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) diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1274d44741..99b4279728 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -36,6 +36,7 @@ import copy import xml.etree.ElementTree as ET from itertools import chain import os.path +import posixpath import re import io import typing @@ -155,7 +156,7 @@ def find_qmake_conf(project_file_path: str = '') -> typing.Optional[str]: file_name = '.qmake.conf' while os.path.isdir(cwd): - maybe_file = os.path.join(cwd, file_name) + maybe_file = posixpath.join(cwd, file_name) if os.path.isfile(maybe_file): return maybe_file else: @@ -184,7 +185,7 @@ def process_qrc_file(target: str, filepath: str, base_dir: str = '', project_fil resource_name = os.path.splitext(os.path.basename(filepath))[0] dir_name = os.path.dirname(filepath) - base_dir = os.path.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. @@ -239,7 +240,7 @@ def write_add_qt_resource_call(target: str, resource_name: str, prefix: typing.O for source in sorted_files: alias = files[source] if alias: - full_source = os.path.join(base_dir, source) + full_source = posixpath.join(base_dir, source) output += 'set_source_files_properties("{}"\n' \ ' PROPERTIES QT_RESOURCE_ALIAS "{}"\n)\n'.format(full_source, alias) @@ -320,7 +321,7 @@ def map_to_file(f: str, scope: Scope, *, is_include: bool = False) -> str: return f base_dir = scope.currentdir if is_include else scope.basedir - f = os.path.join(base_dir, f) + f = posixpath.join(base_dir, f) return trim_leading_dot(f) @@ -345,9 +346,9 @@ def handle_vpath(source: str, base_dir: str, vpath: typing.List[str]) -> str: return source for v in vpath: - fullpath = os.path.join(v, source) + fullpath = posixpath.join(v, source) if os.path.exists(fullpath): - return trim_leading_dot(os.path.relpath(fullpath, base_dir)) + return trim_leading_dot(posixpath.relpath(fullpath, base_dir)) print(' XXXX: Source {}: Not found.'.format(source)) return '{}-NOTFOUND'.format(source) @@ -1319,7 +1320,7 @@ def sort_sources(sources: typing.List[str]) -> typing.List[str]: base = os.path.splitext(os.path.basename(s))[0] if base.endswith('_p'): base = base[:-2] - sort_name = os.path.join(dir, base) + sort_name = posixpath.join(dir, base) array = to_sort.get(sort_name, []) array.append(s) -- cgit v1.2.3 From 59b940ff418cf4c134face2a3c56a593bd75d5ca Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 16 Sep 2019 13:54:03 +0200 Subject: pro2cmake: Recursively expand $$FOO variables Given HEADERS = $$PUBLIC_HEADERS $$PRIVATE_HEADERS $$PUBLIC_HEADERS can be expanded into a list of source files which in turn contain $$PWD/foo.cpp. The $$PWD needs to be expanded as well. This is the case for qtwebsockets/src/websockets/websockets.pro project. Change-Id: I3aa14203ee8d177fadd12a7e3212c3250970e0a8 Reviewed-by: Liang Qi Reviewed-by: Leander Beernaert Reviewed-by: Maurice Kalinowski Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 99b4279728..cc5373c31a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -811,7 +811,12 @@ class Scope(object): if len(get_result) == 1: result = get_result[0] else: - return get_result + # Recursively expand each value from the result list + # returned from self.get(). + result_list = [] + for entry_value in get_result: + 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 '' -- cgit v1.2.3 From 4acb0b8582ed0e3c7ca3c8c82cb72c2124b58ab7 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 3 Sep 2019 15:25:55 +0200 Subject: Add expansion for $$files() in RESOURCES Expand $$files() when processing resources. Change-Id: I55a10dad65461db8640450609414fcfb0bb5d103 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index cc5373c31a..f7eb755c39 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -245,7 +245,13 @@ def write_add_qt_resource_call(target: str, resource_name: str, prefix: typing.O ' PROPERTIES QT_RESOURCE_ALIAS "{}"\n)\n'.format(full_source, alias) # Quote file paths in case there are spaces. - sorted_files = ['"{}"'.format(f) for f in sorted_files] + sorted_files_backup = sorted_files + sorted_files = [] + for source in sorted_files_backup: + if source.startswith('${'): + sorted_files.append(source) + else: + sorted_files.append('"{}"'.format(source)) file_list = '\n '.join(sorted_files) output += 'set({}_resource_files\n {}\n)\n\n'.format(resource_name, file_list) @@ -366,12 +372,18 @@ def handle_function_value(group: pp.ParseResults): # Do nothing, just return a string result return str(group) + if function_name == 'files': + if len(function_args) > 1: + raise RuntimeError('Don\'t know what to with more than one function argument for $$files().') + return str(function_args[0]) + # Return the whole expression as a string. - if function_name in ['join', 'cmakeRelativePath', 'shell_quote', 'shadowed', 'cmakeTargetPath', + if function_name in ['join', 'files', 'cmakeRelativePath', 'shell_quote', 'shadowed', 'cmakeTargetPath', 'shell_path', 'cmakeProcessLibs', 'cmakeTargetPaths', 'cmakePortablePaths', 'escape_expand']: return 'join({})'.format(''.join(function_args)) + raise RuntimeError('No logic to handle function "{}", please add one in handle_function_value().'.format(function_name)) class Operation: @@ -1795,6 +1807,7 @@ def map_to_cmake_condition(condition: typing.Optional[str]) -> str: 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') @@ -1825,14 +1838,24 @@ def write_resources(cm_fh: typing.IO[str], target: str, scope: Scope, indent: in 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: - # stadalone source file properties need to be set as they - # are parsed. - if skip_qtquick_compiler: - output += 'set_source_files_properties("{}" PROPERTIES QT_SKIP_QUICKCOMPILER 1)\n\n'.format(r) - - if retain_qtquick_compiler: - output += 'set_source_files_properties("{}" PROPERTIES QT_RETAIN_QUICKCOMPILER 1)\n\n'.format(r) - standalone_files.append(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)) + 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) + + if retain_qtquick_compiler: + qrc_output += 'set_source_files_properties("{}" PROPERTIES QT_RETAIN_QUICKCOMPILER 1)\n\n'.format(r) + standalone_files.append(r) if standalone_files: name = "qmake_immediate" -- cgit v1.2.3 From bc4f17e7dc52a65abb30d4c8aee999413106faca Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 17 Sep 2019 10:49:55 +0200 Subject: pro2cmake: Remove warning about line continuations Almost every non-trivial .pro file has line continuations. Remove the warning as there's nothing the user can do about it. Change-Id: Ic8a54e5e5cc39a31267800edde4b0ff2b0276a48 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f7eb755c39..defecf128f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1059,10 +1059,6 @@ class QmakeParser: old_contents = contents contents = fixup_comments(contents) contents = fixup_linecontinuation(contents) - - if old_contents != contents: - print('Warning: Fixed line continuation in .pro-file!\n' - ' Position information in Parsing output might be wrong!') result = self._Grammar.parseString(contents, parseAll=True) except pp.ParseException as pe: print(pe.line) -- cgit v1.2.3 From ce8337dbeb8560b47844ea2ad99e64f42102b73d Mon Sep 17 00:00:00 2001 From: Sona Kurazyan Date: Tue, 17 Sep 2019 11:53:26 +0200 Subject: Add Qt Coap to the library mapping Task-number: QTBUG-78308 Change-Id: I6ba6f84354d7d008e128c784e24db00ecd67647a Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 5d1cfadace..8eb34a8f31 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -64,6 +64,7 @@ _qt_library_map = [ # 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']), -- cgit v1.2.3 From 933d383a24fa4a98075b98fc9fe3ed04895a5883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCri=20Valdmann?= Date: Tue, 17 Sep 2019 11:27:47 +0200 Subject: pro2cmake: Support QML IMPORTPATH with multiple elements Change-Id: I8113d7dd4e7967d020d59a5b4104e8366d55283c Reviewed-by: Alexandru Croitor Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index defecf128f..ee68c1ca2e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2125,11 +2125,11 @@ def write_test(cm_fh: typing.IO[str], scope: Scope, if 'qmltestcase' in scope.get('CONFIG'): libraries.add('Qt::QmlTest') extra.append('QMLTEST') - importpath = scope.get('IMPORTPATH') + importpath = scope.expand('IMPORTPATH') if importpath: - qml_importpath = scope.expandString(importpath) - if qml_importpath: - extra.append('QML_IMPORTPATH "{}"'.format(qml_importpath)) + extra.append('QML_IMPORTPATH') + for path in importpath: + extra.append(' "{}"'.format(path)) write_main_part(cm_fh, test_name, 'Test', 'add_qt_test', scope, indent=indent, known_libraries=libraries, -- cgit v1.2.3 From 69caeada8d54d148dbfdc7d1306c8fb790696399 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 17 Sep 2019 12:48:39 +0200 Subject: run_pro2cmake.py: Run on Windows Prefix by python executable and drop os.nice. Change-Id: Idd1d0de6695887652db84261da1130a084e5af78 Reviewed-by: Cristian Adam Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/run_pro2cmake.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index f98dbf63a8..0d4cff2d67 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -31,6 +31,7 @@ import glob import os import subprocess import concurrent.futures +import sys import typing import argparse from argparse import ArgumentParser @@ -123,12 +124,15 @@ def run(all_files: typing.List[str], pro2cmake: str, args: argparse.Namespace) - # qtbase main modules take longer than usual to process. workers = 2 - with concurrent.futures.ThreadPoolExecutor(max_workers=workers, initializer=os.nice, initargs=(10,)) as pool: + with concurrent.futures.ThreadPoolExecutor(max_workers=workers, initargs=(10,)) as pool: 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 - pro2cmake_args = [pro2cmake] + pro2cmake_args = [] + if sys.platform == "win32": + pro2cmake_args.append(sys.executable) + pro2cmake_args.append(pro2cmake) if args.is_example: pro2cmake_args.append('--is-example') pro2cmake_args.append(os.path.basename(filename)) -- cgit v1.2.3 From 9b31bdacc6b4a68a1a238790a82d99d7289587d5 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 17 Sep 2019 13:25:17 +0200 Subject: pro2cmake.py: State dependencies Change-Id: I26234c68b781b983022c73a2d4206550f25b0232 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ee68c1ca2e..24fcd85fd0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -59,7 +59,8 @@ cmake_version_string = "3.15.0" def _parse_commandline(): parser = ArgumentParser(description='Generate CMakeLists.txt files from .' - 'pro files.') + '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', -- cgit v1.2.3 From b935fa8e08a0c7a41cc2ac0096060af38b5b40a3 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Tue, 17 Sep 2019 16:04:02 +0200 Subject: Add a mapping for QtSerialBus Change-Id: I3664dfe5a7aa064ea4fa8f9455242815a359f346 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 8eb34a8f31..b5190e6bca 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -155,6 +155,7 @@ _qt_library_map = [ 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']), -- cgit v1.2.3 From c3b79e79afd8dd15aaf6ca4a0a62fd30fcd2f11a Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 17 Sep 2019 10:39:13 +0200 Subject: pro2cmake: Handle nested lists in handle_function_value Needed to successfully parse qtconnectivity projects where there are expressions like: WINDOWS_SDK_VERSION = $$member($$list($$split(WINDOWS_SDK_VERSION_STRING, .)), 2) Also fix '$$member' to be handled in the function. Change-Id: I35b5cff8c39b3a35d485d1d72f5d668cccbe9b2f Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 24fcd85fd0..cea4e4259c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -41,6 +41,12 @@ import re import io import typing import glob +import collections + +try: + collectionsAbc = collections.abc +except AttributeError: + collectionsAbc = collections from sympy.logic import (simplify_logic, And, Or, Not,) import pyparsing as pp @@ -361,6 +367,15 @@ def handle_vpath(source: str, base_dir: str, vpath: typing.List[str]) -> str: return '{}-NOTFOUND'.format(source) +def flatten_list(l): + """ Flattens an irregular nested list into a simple list.""" + for el in l: + if isinstance(el, collectionsAbc.Iterable) and not isinstance(el, (str, bytes)): + yield from flatten_list(el) + else: + yield el + + def handle_function_value(group: pp.ParseResults): function_name = group[0] function_args = group[1] @@ -378,10 +393,13 @@ def handle_function_value(group: pp.ParseResults): 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']: + 'cmakePortablePaths', 'escape_expand', 'member']: return 'join({})'.format(''.join(function_args)) -- cgit v1.2.3 From 17e11745f1ec9611f44bf821cb08a926d1242a65 Mon Sep 17 00:00:00 2001 From: Karsten Heimrich Date: Tue, 17 Sep 2019 15:58:33 +0200 Subject: Add initial support for QtKnx cmake Change-Id: I419d15ca0bb0b02e11001b75d0c612dddfa3abec Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index b5190e6bca..42b1396dfe 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -103,6 +103,7 @@ _qt_library_map = [ 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']), -- cgit v1.2.3 From 08aba5ea0ab4196779c79a4e8d8ba6d510b14e12 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Tue, 17 Sep 2019 14:26:40 +0200 Subject: cmake: Add library mappings for qtwayland Change-Id: I9d394229073579df104e21c539bbbb614ef8efbd Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/configurejson2cmake.py | 1 + util/cmake/helper.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 5676aa536b..31e062580b 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -499,6 +499,7 @@ def parseTest(ctx, test, data, cm_fh): 'separate_debug_info', # FIXME: see if cmake can do this 'gc_binaries', 'libinput_axis_api', + 'wayland-scanner', 'xlib', } diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 42b1396dfe..de946a58db 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -168,6 +168,7 @@ _qt_library_map = [ 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']), @@ -239,7 +240,10 @@ _library_map = [ 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-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'), -- cgit v1.2.3 From bb95a030d99293b08e918894019fe2873f7eaad2 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 17 Sep 2019 11:06:46 +0200 Subject: CMake: add library mapping for QtSpeech Change-Id: I274e7382b1593dcb010f5f03ba0e2ccc9a37e5b8 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index de946a58db..6ab7deae6e 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -199,6 +199,8 @@ _library_map = [ 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'), @@ -233,6 +235,7 @@ _library_map = [ 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), -- cgit v1.2.3 From d6ca7306ad9f6eea56c6a97221db66bf6388c3dd Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 18 Sep 2019 09:23:34 +0200 Subject: CMake: add Qt::TextToSpeech to library mapping Change-Id: Ib682dede7d8e99461b146e9ef71e6d455daa8b6e Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 6ab7deae6e..6ad468485c 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -162,6 +162,7 @@ _qt_library_map = [ 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']), -- cgit v1.2.3 From cb5fbbde27a4cf217046085fc493ae076ba9bb02 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 18 Sep 2019 09:27:22 +0200 Subject: Fix up documentation integration placeholder In the future need will need to continue to tie qdoc runs still to targets, just like with qmake. This change prepares us for that by ensuring that add_docs takes two parameters and that any re-generated CMakeLists.txt from now on gets it right. Change-Id: Id0256dc1e2f2f59f3b4e4ca98f0d10d025d189fb Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index cea4e4259c..c4e383c10d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2080,7 +2080,7 @@ def write_main_part(cm_fh: typing.IO[str], name: str, typename: str, cm_fh.write(ignored_keys_report) def write_module(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0) -> None: + 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)) @@ -2112,7 +2112,8 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, if module_plugin_types: extra.append('PLUGIN_TYPES {}'.format(" ".join(module_plugin_types))) - write_main_part(cm_fh, module_name[2:], 'Module', 'add_qt_module', scope, + 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=[]) @@ -2121,9 +2122,11 @@ def write_module(cm_fh: typing.IO[str], scope: Scope, *, cm_fh.write('\n\n{}qt_create_tracepoints({} {})\n' .format(spaces(indent), module_name[2:], ' '.join(tracepoints))) + return target_name + def write_tool(cm_fh: typing.IO[str], scope: Scope, *, - indent: int = 0) -> None: + indent: int = 0) -> str: tool_name = scope.TARGET extra = ['BOOTSTRAP'] if 'force_bootstrap' in scope.get('CONFIG') else [] @@ -2132,9 +2135,11 @@ def write_tool(cm_fh: typing.IO[str], scope: 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) -> None: + gui: bool = False, *, indent: int = 0) -> str: test_name = scope.TARGET assert test_name @@ -2154,6 +2159,8 @@ def write_test(cm_fh: typing.IO[str], scope: 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: @@ -2180,6 +2187,8 @@ def write_binary(cm_fh: typing.IO[str], scope: 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], @@ -2202,7 +2211,7 @@ def write_find_package_section(cm_fh: typing.IO[str], def write_example(cm_fh: typing.IO[str], scope: Scope, - gui: bool = False, *, indent: int = 0) -> None: + gui: bool = False, *, indent: int = 0) -> str: binary_name = scope.TARGET assert binary_name @@ -2242,8 +2251,10 @@ def write_example(cm_fh: typing.IO[str], scope: Scope, ' LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + ')\n') + return binary_name + -def write_plugin(cm_fh, scope, *, indent: int = 0): +def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: plugin_name = scope.TARGET assert plugin_name @@ -2264,6 +2275,7 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): 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], @@ -2348,34 +2360,35 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, 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 - write_plugin(cm_fh, scope, indent=indent) + target = write_plugin(cm_fh, scope, indent=indent) elif is_lib or 'qt_module' in scope.get('_LOADED'): assert not is_example - write_module(cm_fh, scope, indent=indent) + target = write_module(cm_fh, scope, indent=indent) elif 'qt_tool' in scope.get('_LOADED'): assert not is_example - write_tool(cm_fh, scope, indent=indent) + target = write_tool(cm_fh, scope, indent=indent) else: gui = all(val not in config for val in ['console', 'cmdline']) if 'testcase' in config \ or 'testlib' in config \ or 'qmltestcase' in config: assert not is_example - write_test(cm_fh, scope, gui, indent=indent) + target = write_test(cm_fh, scope, gui, indent=indent) else: if is_example: - write_example(cm_fh, scope, gui, indent=indent) + target = write_example(cm_fh, scope, gui, indent=indent) else: - write_binary(cm_fh, scope, gui, indent=indent) + target = write_binary(cm_fh, scope, gui, indent=indent) ind = spaces(indent) write_source_file_list(cm_fh, scope, '', ['QMAKE_DOCS',], indent, - header = 'add_qt_docs(\n', + header = f"add_qt_docs({target},\n", footer = ')\n') -- cgit v1.2.3 From c1cf305be0f1f529b96a06dc70ce5ee7273006ef Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 18 Sep 2019 12:57:56 +0200 Subject: Don't create "gui" applications when linking against testlib In qmake land, testlib.pro has "console" in MODULE_CONFIG, so linking against testlib implicies CONFIG += console. The need for a console app is typically also covered by other cases, except in qtdeclarative's qqmldebugjsserver binary (and some others). They are not test helper binaries, they are not Qt tests themselves, but they must be console apps and due to their QT += testlib in the .pro they become console apps. So with cmake they also must be console apps so that the unit tests that launch them and read their stdout can pass. Change-Id: I687fdc910b550051750be74ecd176dd96626675c Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index c4e383c10d..28eb70454e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2372,7 +2372,8 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, assert not is_example target = write_tool(cm_fh, scope, indent=indent) else: - gui = all(val not in config for val in ['console', 'cmdline']) + 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: -- cgit v1.2.3 From 91634c3c9b8d4f68f0ebd2ac76a8b5b79e4b4c94 Mon Sep 17 00:00:00 2001 From: Cristian Maureira-Fredes Date: Tue, 17 Sep 2019 00:11:17 +0200 Subject: Improve styling of util/cmake scripts flake8 was used to evaluate the file, with a couple of exeptions: E501,E266,W503 black was used to reformat the code automatically The changes were: * Added a README that explains how to use pipenv and pip, * Remove unnecessary return statements, * Remove '\' from the end of the lines, * Use f-strings (>= 3.6) since we are requiring Python 3.7, * Commenting unused variables, * Adding assert when Python >= 3.7 is not being used, * Wrapping long lines to 100 (Qt Style), * Re-factoring some lines, * Re-ordering imports, * Naming `except` for sympy (SympifyError, TypeError) Change-Id: Ie05f754e7d8ee4bf427117c58e0eb1b903202933 Reviewed-by: Alexandru Croitor --- util/cmake/README.md | 54 + util/cmake/cmakeconversionrate.py | 84 +- util/cmake/configurejson2cmake.py | 955 +++++++------- util/cmake/helper.py | 795 +++++++----- util/cmake/json_parser.py | 31 +- util/cmake/pro2cmake.py | 2517 +++++++++++++++++++++---------------- util/cmake/pro_conversion_rate.py | 86 +- util/cmake/requirements.txt | 4 + util/cmake/run_pro2cmake.py | 109 +- util/cmake/special_case_helper.py | 165 +-- 10 files changed, 2752 insertions(+), 2048 deletions(-) create mode 100644 util/cmake/README.md create mode 100644 util/cmake/requirements.txt (limited to 'util/cmake') 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', - }, - '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', + "alloc_h": None, # handled by alloc target + "alloc_malloc_h": None, + "alloc_stdlib_h": None, + "build_all": None, + "c11": None, + "c89": None, + "c99": None, + "ccache": None, + "compiler-flags": None, + "cross_compile": None, + "debug_and_release": None, + "debug": None, + "dlopen": {"condition": "UNIX"}, + "doubleconversion": None, + "enable_gdb_index": None, + "enable_new_dtags": None, + "force_debug_info": None, + "framework": {"condition": "APPLE AND BUILD_SHARED_LIBS"}, + "gc_binaries": None, + "gcc-sysroot": None, + "gcov": None, + "gnu-libiconv": { + "condition": "NOT WIN32 AND NOT QNX AND NOT ANDROID AND NOT APPLE AND TEST_posix_iconv AND NOT TEST_iconv_needlib", + "enable": "TEST_posix_iconv AND NOT TEST_iconv_needlib", + "disable": "NOT TEST_posix_iconv OR TEST_iconv_needlib", }, - 'GNUmake': None, - 'harfbuzz': { - 'condition': 'harfbuzz_FOUND' + "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 )" }, - '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 != 'define': + if outputType == "feature": + outputType = "define" + outputArgs = { + "name": f"QT_NO_{cxxFeature.upper()}", + "negative": True, + "value": 1, + "type": "define", + } + + if outputType != "define": continue - if outputArgs.get('name') is None: - print(' 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.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 = 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( + "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], ')'])) - - 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('$')) + 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.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')) - - Op = add_element('OP', - pp.Literal('=') | pp.Literal('-=') | pp.Literal('+=') \ - | pp.Literal('*=')) - - Key = add_element('Key', Identifier) - - Operation = add_element('Operation', Key('key') + Op('operation') + Values('value')) - CallArgs = add_element('CallArgs', pp.nestedExpr()) + + 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")) + + Op = add_element( + "OP", pp.Literal("=") | pp.Literal("-=") | pp.Literal("+=") | pp.Literal("*=") + ) + + Key = add_element("Key", Identifier) + + 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())) - - 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))) - - 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('|'))))) - - ConditionPart1 = add_element('ConditionPart1', - (pp.Optional('!') + Identifier + pp.Optional(BracedValue))) - ConditionPart2 = add_element('ConditionPart2', pp.CharsNotIn('#{}|:=\\\n')) + FunctionCall = add_element("FunctionCall", pp.Suppress(Identifier + pp.nestedExpr())) + + 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)) + ) + + 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("|"))) + ), + ) + + 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')) - - 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)) + "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")) + + 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'\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\)' + 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) + + 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_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_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_list(cm_fh: typing.IO[str], entries: typing.List[str], - cmake_parameter: str, - indent: int = 0, *, - header: str = '', footer: str = ''): +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: 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)) - - -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 = ''): + cm_fh.write(f"{ind}{footer}\n") + + +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) - - -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] - - if 'qml_debug' in scope.get('CONFIG'): - defines.append('QT_QML_DEBUG') + 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: 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") 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') - - unix_expr = simplify_logic('UNIX') - win_expr = simplify_logic('WIN32') - false_expr = simplify_logic('false') - true_expr = simplify_logic('true') + # 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") 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') - -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' ] + 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: 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))) - -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): + cm_fh.write(f"{spaces(indent)}endif()\n") + + +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=[]) - - 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))) + 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( + 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)) - - write_main_part(cm_fh, test_name, 'Test', 'add_qt_test', scope, - indent=indent, known_libraries=libraries, - extra_lines=extra, extra_keys=[]) + 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=[], + ) 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)) - - 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']) + 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"], + ) 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') - - 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_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(")\n") + + 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) + + 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)) - - write_main_part(cm_fh, plugin_name, 'Plugin', plugin_function_name, scope, - indent=indent, extra_lines=extra, known_libraries={}, extra_keys=[]) + 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=[], + ) 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] - - cm_fh.write('\n{}set(qml_files\n{}{}\n)\n'.format( - spaces(indent), - spaces(indent + 1), - '\n{}'.format(spaces(indent + 1)).join(qml_files))) - - 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))) - - cm_fh.write('\nqt_install_qml_files({}\n FILES ${{qml_files}}\n)\n\n'.format( - target)) - - -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') - - 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 + qml_files = [f'"{f}"' for f in 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( + 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(f"\nqt_install_qml_files({target}\n FILES ${{qml_files}}\n)\n\n") + + +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 + ) 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(f"{content}") - cm_fh.write('{}'.format(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 -- cgit v1.2.3 From 255a6d91db2e2e7e9087f9675a930d1e3afc3ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=C3=A1n=20Maureira-Fredes?= Date: Wed, 18 Sep 2019 15:22:34 +0200 Subject: Fix indentation inside group_and_print_sub_dirs Change-Id: I34a737ae4914795f96900f2b72d0d15a3f5652d5 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7363ace586..bb30cfe3b1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1479,7 +1479,7 @@ def handle_subdir( grouped_sub_dirs[condition_key] = sub_dir_list_by_key # Print the groups. - ind = spaces(1) + ind = spaces(indent) for condition_key in grouped_sub_dirs: cond_ind = ind if condition_key: @@ -2838,14 +2838,7 @@ def cmakeify_scope( # Wrap top level examples project with some commands which # are necessary to build examples as part of the overall # build. - buffer_value = dedent( - """\ - qt_examples_build_begin() - - {buffer_value} - qt_examples_build_end() - """ - ) + buffer_value = f"\nqt_examples_build_begin()\n\n{buffer_value}\nqt_examples_build_end()" cm_fh.write(buffer_value) -- cgit v1.2.3 From b72adf8a911f4281c7c56b359f9ae1a35f766fcf Mon Sep 17 00:00:00 2001 From: Cristian Maureira-Fredes Date: Wed, 18 Sep 2019 15:04:44 +0200 Subject: Add option to generate pro2cmake on failed ones The option --only-missing was added to check only the cases where there is a pro file and not a CMakeLists.txt Change-Id: Ifc6233552757e0afe8e7242a79f958e8bc77f823 Reviewed-by: Alexandru Croitor --- util/cmake/run_pro2cmake.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index 5d5f50cc2a..16ad0534cd 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -47,6 +47,12 @@ def parse_command_line(): action="store_true", help="Run pro2cmake only on .pro files that already have a CMakeLists.txt.", ) + parser.add_argument( + "--only-missing", + dest="only_missing", + action="store_true", + help="Run pro2cmake only on .pro files that do not have a CMakeLists.txt.", + ) parser.add_argument( "--only-qtbase-main-modules", dest="only_qtbase_main_modules", @@ -87,6 +93,9 @@ def find_all_pro_files(base_path: str, args: argparse.Namespace): return True return False + def cmake_lists_missing_filter(path): + return not cmake_lists_exists_filter(path) + def qtbase_main_modules_filter(path): main_modules = [ "corelib", @@ -112,6 +121,8 @@ def find_all_pro_files(base_path: str, args: argparse.Namespace): filter_func = None if args.only_existing: filter_func = cmake_lists_exists_filter + elif args.only_missing: + filter_func = cmake_lists_missing_filter elif args.only_qtbase_main_modules: filter_func = qtbase_main_modules_filter -- cgit v1.2.3 From c58df80cf7926b07da9fe6515230bd4295c1fc6d Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 18 Sep 2019 15:17:09 +0200 Subject: pro2cmake: Handle qmake env variables in file paths Replace things like $$(FOO) to $ENV{FOO}. This is needed to stop the script from crashing when it sees $$(FOO) in a .pro file, like in qtmultimedia/src/plugins/directshow/directshow.pro A better approach in the future will be to write a Find module that uses the env var to find the relevant headers, and then instead use a CMake cache variable, to make the build more robust in case the env var is removed after first configuring the project. Change-Id: Ia5b3dc3a90e1d4159a8b90acdade85f34b5165f9 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann Reviewed-by: Cristian Maureira-Fredes --- util/cmake/pro2cmake.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index bb30cfe3b1..0ebc94d72e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -951,32 +951,49 @@ class Scope(object): return list(self._evalOps(key, transformer, [])) + @staticmethod + def _replace_env_var_value(value: Any) -> str: + if not isinstance(value, str): + return value + + pattern = re.compile(r"\$\$\(?([A-Za-z_][A-Za-z0-9_]*)\)?") + match = re.search(pattern, value) + if match: + value = re.sub(pattern, r"$ENV{\1}", value) + + return value + def _expand_value(self, value: str) -> List[str]: result = value 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: + match_group_0 = match.group(0) + if match_group_0 == value: get_result = self.get(match.group(1), inherit=True) if len(get_result) == 1: result = get_result[0] + result = self._replace_env_var_value(result) else: # Recursively expand each value from the result list # returned from self.get(). result_list = [] for entry_value in get_result: - result_list += self._expand_value(entry_value) + result_list += self._expand_value(self._replace_env_var_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() :] + result = self._replace_env_var_value(result) if result == old_result: return [result] # Do not go into infinite loop match = re.search(pattern, result) + + result = self._replace_env_var_value(result) return [result] def expand(self, key: str) -> List[str]: -- cgit v1.2.3 From 2214be5e5a7ff16b948f248510878fb11ca60ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Wed, 18 Sep 2019 16:47:47 +0200 Subject: Add SDL2 to 3rd-party library mapping Change-Id: I9f9df259340271b22a75ddfa6112ecba0f25bbbf Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index ab4f05113b..2d07cb8604 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -496,6 +496,7 @@ _library_map = [ LibraryMapping("tiff", "TIFF", "TIFF::TIFF"), LibraryMapping("webp", "WrapWebP", "WrapWebP::WrapWebP"), LibraryMapping("jasper", "WrapJasper", "WrapJasper::WrapJasper"), + LibraryMapping('sdl2', 'SDL2', 'SDL2::SDL2'), ] -- cgit v1.2.3 From ceb29374983456e95c64b31f12d1386729d4bb1d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 19 Sep 2019 09:34:46 +0200 Subject: Fix add_qt_docs calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brown paper bag for me, don't use a comma to separate parameters when calling a function. Change-Id: I21e16142fab4fd1c2714df13cd6a892b036e899a Reviewed-by: MĂĄrten Nordheim --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0ebc94d72e..c3e65c9de4 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2736,7 +2736,7 @@ def handle_app_or_lib( # ind = spaces(indent) write_source_file_list( - cm_fh, scope, "", ["QMAKE_DOCS"], indent, header=f"add_qt_docs({target},\n", footer=")\n" + cm_fh, scope, "", ["QMAKE_DOCS"], indent, header=f"add_qt_docs({target}\n", footer=")\n" ) -- cgit v1.2.3 From f4aa7255e6ea6de26f790266b0c7799f49c23212 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 19 Sep 2019 08:45:21 +0200 Subject: Fix casing of public API functions Use lower case for the function name as that's cmake style. Change-Id: I6c53166aa3a8cece4abe8a485e06bd359fc12a06 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index c3e65c9de4..4aa9accdd1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -353,7 +353,7 @@ def write_add_qt_resource_call( 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 += ( -- cgit v1.2.3 From d03d12a74f08973b272266810ee37514aaecbacc Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 3 Sep 2019 14:32:43 +0200 Subject: Add Qml plugin example support Extend the example writing section to handle qml plugins. Detection is based on whether the plugin config is detected an the target links against Qml. add_qt_qml_module() now uses the the public facing API underneath. Change-Id: I2ccd06548e7b5b18c9dc3338b835bc115fa97809 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 293 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 247 insertions(+), 46 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4aa9accdd1..b79e1f97cf 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -364,6 +364,113 @@ def write_add_qt_resource_call( return output +class QmlDirFileInfo: + def __init__(self, file_path: str, type_name: str): + self.file_path = file_path + self.version = "" + self.type_name = type_name + self.internal = False + self.singleton = False + + +class QmlDir: + def __init__(self): + self.module = "" + self.plugin_name = "" + self.plugin_path = "" + self.classname = "" + self.imports = [] # typing.List[str] + self.type_names = {} # typing.Dict[str, QmlDirFileInfo] + self.type_infos = [] # typing.List[str] + self.depends = [] # typing.List[[str,str]] + self.designer_supported = False + + def __str__(self): + str = "module: {}\n".format(self.module) + str += "plugin: {} {}\n".format(self.plugin_name, self.plugin_path) + str += "classname: {}\n".format(self.classname) + str += "type_infos:{}\n".format(" \n".join(self.type_infos)) + str += "imports:{}\n".format(" \n".join(self.imports)) + str += "dependends: \n" + for dep in self.depends: + str += " {} {}\n".format(dep[0], dep[1]) + str += "designer supported: {}\n".format(self.designer_supported) + str += "type_names:\n" + for key in self.type_names: + file_info = self.type_names[key] + str += " type:{} version:{} path:{} internal:{} singleton:{}\n".format( + file_info.type_name, + file_info.version, + file_info.type_name, + file_info.file_path, + file_info.internal, + file_info.singleton, + ) + return str + + def get_or_create_file_info(self, path: str, type_name: str) -> QmlDirFileInfo: + if not path in self.type_names: + self.type_names[path] = QmlDirFileInfo(path, type_name) + qmldir_file = self.type_names[path] + if qmldir_file.type_name != type_name: + raise RuntimeError("Registered qmldir file type_name does not match.") + return qmldir_file + + def handle_file_internal(self, type_name: str, path: str): + qmldir_file = self.get_or_create_file_info(path, type_name) + qmldir_file.internal = True + + def handle_file_singleton(self, type_name: str, version: str, path: str): + qmldir_file = self.handle_file(type_name, version, path) + qmldir_file.singleton = True + + def handle_file(self, type_name: str, version: str, path: str) -> QmlDirFileInfo: + qmldir_file = self.get_or_create_file_info(path, type_name) + qmldir_file.version = version + qmldir_file.type_name = type_name + qmldir_file.path = path + return qmldir_file + + def from_file(self, path: str): + f = open(path, "r") + if not f: + raise RuntimeError("Failed to open qmldir file at: {}".format(str)) + for line in f: + if line.startswith("#"): + continue + line = line.strip().replace("\n", "") + if len(line) == 0: + continue + + entries = line.split(" ") + if len(entries) == 0: + raise RuntimeError("Unexpected QmlDir file line entry") + if entries[0] == "module": + self.module = entries[1] + elif entries[0] == "[singleton]": + self.handle_file_singleton(entries[1], entries[2], entries[3]) + elif entries[0] == "internal": + self.handle_file_internal(entries[1], entries[2]) + elif entries[0] == "plugin": + self.plugin_name = entries[1] + if len(entries) > 2: + self.plugin_path = entries[2] + elif entries[0] == "classname": + self.classname = entries[1] + elif entries[0] == "typeinfo": + self.type_infos.append(entries[1]) + elif entries[0] == "depends": + self.depends.append((entries[1], entries[2])) + elif entries[0] == "designersupported": + self.designer_supported = True + elif entries[0] == "import": + self.imports.append(entries[1]) + elif len(entries) == 3: + self.handle_file(entries[0], entries[1], entries[2]) + else: + raise RuntimeError("Uhandled qmldir entry {}".format(line)) + + def fixup_linecontinuation(contents: str) -> str: # Remove all line continuations, aka a backslash followed by # a newline character with an arbitrary amount of whitespace @@ -2352,9 +2459,6 @@ def write_main_part( write_android_part(cm_fh, name, scopes[0], indent) - if is_qml_plugin: - write_qml_plugin_qml_files(cm_fh, name, scopes[0], indent) - ignored_keys_report = write_ignored_keys(scopes[0], spaces(indent)) if ignored_keys_report: cm_fh.write(ignored_keys_report) @@ -2534,7 +2638,9 @@ def write_find_package_section( cm_fh.write("\n") -def write_example(cm_fh: 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, is_plugin: bool = False +) -> str: binary_name = scope.TARGET assert binary_name @@ -2551,9 +2657,53 @@ def write_example(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: in (public_libs, private_libs) = extract_cmake_libraries(scope) write_find_package_section(cm_fh, public_libs, private_libs, indent=indent) - add_executable = f'add_{"qt_gui_" if gui else ""}executable({binary_name}' + add_target = "" + + qmldir = None + if is_plugin: + if "qml" in scope.get("QT"): + # Get the uri from the destination directory + dest_dir = scope.expandString("DESTDIR") + if not dest_dir: + dest_dir = "${CMAKE_CURRENT_BINARY_DIR}" + else: + uri = os.path.basename(dest_dir) + dest_dir = "${CMAKE_CURRENT_BINARY_DIR}/" + dest_dir - write_all_source_file_lists(cm_fh, scope, add_executable, indent=0) + add_target = f"qt6_add_qml_module({binary_name}\n" + add_target += f' OUTPUT_DIRECTORY "{dest_dir}"\n' + add_target += " VERSION 1.0\n" + add_target += ' URI "{}"\n'.format(uri) + + qmldir_file_path = scope.get_files("qmldir.files") + if qmldir_file_path: + qmldir_file_path = os.path.join(os.getcwd(), qmldir_file_path[0]) + else: + qmldir_file_path = os.path.join(os.getcwd(), "qmldir") + + if os.path.exists(qmldir_file_path): + qml_dir = QmlDir() + qml_dir.from_file(qmldir_file_path) + if qml_dir.designer_supported: + add_target += " DESIGNER_SUPPORTED\n" + if len(qml_dir.classname) != 0: + add_target += f" CLASSNAME {qml_dir.classname}\n" + if len(qml_dir.imports) != 0: + add_target += " IMPORTS\n{}".format(" \n".join(qml_dir.imports)) + if len(qml_dir.depends) != 0: + add_target += " DEPENDENCIES\n" + for dep in qml_dir.depends: + add_target += f" {dep[0]}/{dep[1]}\n" + + add_target += " INSTALL_LOCATION ${INSTALL_EXAMPLEDIR}\n)\n\n" + add_target += f"target_sources({binary_name} PRIVATE" + else: + add_target = f"add_library({binary_name} MODULE" + + else: + add_target = f'add_{"qt_gui_" if gui else ""}executable({binary_name}' + + write_all_source_file_lists(cm_fh, scope, add_target, indent=0) cm_fh.write(")\n") @@ -2561,14 +2711,18 @@ def write_example(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: in 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=")" + cm_fh, + scope, + "target_compile_definitions({} PUBLIC".format(binary_name), + indent=0, + footer=")", ) write_list( cm_fh, private_libs, "", indent=indent, - header=f"target_link_libraries({binary_name} PRIVATE\n", + header="target_link_libraries({} PRIVATE\n".format(binary_name), footer=")", ) write_list( @@ -2576,21 +2730,24 @@ def write_example(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: in public_libs, "", indent=indent, - header=f"target_link_libraries({binary_name} PUBLIC\n", + header="target_link_libraries({} PUBLIC\n".format(binary_name), footer=")", ) write_compile_options( - cm_fh, scope, f"target_compile_options({binary_name}", indent=0, footer=")" + cm_fh, scope, "target_compile_options({}".format(binary_name), indent=0, footer=")" ) write_resources(cm_fh, binary_name, scope, indent=indent, is_example=True) + if qmldir: + write_qml_plugin_epilogue(cm_fh, binary_name, scope, qmldir, indent) + 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" + "\ninstall(TARGETS {}\n".format(binary_name) + + ' RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + + ' BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + + ' LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"\n' + + ")\n" ) return binary_name @@ -2602,6 +2759,7 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: extra = [] + qmldir = None 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" @@ -2609,11 +2767,11 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: extra.append(f"TYPE {plugin_type}") elif is_qml_plugin: plugin_function_name = "add_qml_module" - write_qml_plugin(cm_fh, plugin_name, scope, indent=indent, extra_lines=extra) + qmldir = write_qml_plugin(cm_fh, plugin_name, scope, indent=indent, extra_lines=extra) plugin_class_name = scope.get_string("PLUGIN_CLASS_NAME") if plugin_class_name: - extra.append(f"CLASS_NAME {plugin_class_name}") + extra.append("CLASS_NAME {}".format(plugin_class_name)) write_main_part( cm_fh, @@ -2626,6 +2784,10 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: known_libraries={}, extra_keys=[], ) + + if qmldir: + write_qml_plugin_epilogue(cm_fh, plugin_name, scope, qmldir, indent) + return plugin_name @@ -2634,18 +2796,12 @@ def write_qml_plugin( target: str, scope: Scope, *, - extra_lines: List[str] = [], + extra_lines: typing.List[str] = [], indent: int = 0, - **kwargs: Any, -): + **kwargs: typing.Any, +) -> QmlDir: # Collect other args if available indent += 2 - # scope_config = scope.get('CONFIG') - # is_embedding_qml_files = False - - sources = scope.get_files("SOURCES") - if len(sources) != 0: - extra_lines.append("CPP_PLUGIN") target_path = scope.get_string("TARGETPATH") if target_path: @@ -2675,28 +2831,74 @@ def write_qml_plugin( if plugindump_dep: extra_lines.append(f'QML_PLUGINDUMP_DEPENDENCIES "{plugindump_dep}"') + qmldir_file_path = os.path.join(os.getcwd(), "qmldir") + if os.path.exists(qmldir_file_path): + qml_dir = QmlDir() + qml_dir.from_file(qmldir_file_path) + if qml_dir.designer_supported: + extra_lines.append("DESIGNER_SUPPORTED") + if len(qml_dir.classname) != 0: + extra_lines.append(f"CLASSNAME {qml_dir.classname}") + if len(qml_dir.imports) != 0: + extra_lines.append("IMPORTS\n {}".format("\n ".join(qml_dir.imports))) + if len(qml_dir.depends) != 0: + extra_lines.append("DEPENDENCIES") + for dep in qml_dir.depends: + extra_lines.append(f" {dep[0]}/{dep[1]}") + + return qml_dir + + return None + -def write_qml_plugin_qml_files(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): +def write_qml_plugin_epilogue( + cm_fh: typing.IO[str], target: str, scope: Scope, qmldir: QmlDir, indent: int = 0 +): qml_files = scope.get_files("QML_FILES", use_vpath=True) if qml_files: + indent_0 = spaces(indent) + indent_1 = spaces(indent + 1) # Quote file paths in case there are spaces. - qml_files = [f'"{f}"' for f in qml_files] + qml_files_quoted = ['"{}"'.format(f) for f in 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( - f"\n{spaces(indent)}add_qt_resource({target} {resource_name}\n" - f"{spaces(indent+1)}FILES\n{spaces(indent+2)}${{qml_files}}\n)\n" + "\n{}set(qml_files\n{}{}\n)\n".format( + indent_0, indent_1, "\n{}".format(indent_1).join(qml_files_quoted) + ) ) - cm_fh.write(f"\nqt_install_qml_files({target}\n FILES ${{qml_files}}\n)\n\n") + for qml_file in qml_files: + if qml_file in qmldir.type_names: + qmldir_file_info = qmldir.type_names[qml_file] + cm_fh.write( + "{}set_source_files_properties({} PROPERTIES\n".format(indent_0, qml_file) + ) + cm_fh.write( + '{}QT_QML_SOURCE_VERSION "{}"\n'.format(indent_1, qmldir_file_info.version) + ) + # Only write typename if they are different, CMake will infer + # the name by default + if ( + os.path.splitext(os.path.basename(qmldir_file_info.path))[0] + != qmldir_file_info.type_name + ): + cm_fh.write( + "{}QT_QML_SOURCE_TYPENAME {}\n".format(indent_1, qmldir_file_info.type_name) + ) + cm_fh.write("{}QT_QML_SOURCE_INSTALL TRUE\n".format(indent_1)) + if qmldir_file_info.singleton: + cm_fh.write("{}QT_QML_SINGLETON_TYPE TRUE\n".format(indent_1)) + if qmldir_file_info.internal: + cm_fh.write("{}QT_QML_INTERNAL_TYPE TRUE\n".format(indent_1)) + cm_fh.write("{})\n".format(indent_0)) + + cm_fh.write( + "\n{}qt6_target_qml_files({}\n{}FILES\n{}${{qml_files}}\n)\n".format( + indent_0, target, indent_1, spaces(indent + 2) + ) + ) def handle_app_or_lib( @@ -2711,8 +2913,13 @@ def handle_app_or_lib( any("qt_plugin" == s for s in scope.get("_LOADED")) or is_qml_plugin or "plugin" in config ) target = "" + gui = all( + val not in config for val in ["console", "cmdline"] + ) and "testlib" not in scope.expand("QT") - if is_plugin: + if is_example: + target = write_example(cm_fh, scope, gui, indent=indent, is_plugin=is_plugin) + elif is_plugin: assert not is_example target = write_plugin(cm_fh, scope, indent=indent) elif is_lib or "qt_module" in scope.get("_LOADED"): @@ -2722,17 +2929,11 @@ def handle_app_or_lib( 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: assert not is_example target = write_test(cm_fh, scope, gui, indent=indent) else: - if is_example: - target = write_example(cm_fh, scope, gui, indent=indent) - else: - target = write_binary(cm_fh, scope, gui, indent=indent) + target = write_binary(cm_fh, scope, gui, indent=indent) # ind = spaces(indent) write_source_file_list( -- cgit v1.2.3 From 7e91bd1f27ddd6949001b731f871660c2aba06e7 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Thu, 19 Sep 2019 11:04:16 +0200 Subject: cmake: Add scxml to the library mapping Change-Id: I86653af25fbbc2d269eb439ceefaa2f4d4ebde28 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 2d07cb8604..278ade406f 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -283,6 +283,7 @@ _qt_library_map = [ 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("scxml", "Qt6", "Qt::Scxml", extra=["COMPONENTS", "Scxml"]), LibraryMapping("sensors", "Qt6", "Qt::Sensors", extra=["COMPONENTS", "Sensors"]), LibraryMapping("serialport", "Qt6", "Qt::SerialPort", extra=["COMPONENTS", "SerialPort"]), LibraryMapping("serialbus", "Qt6", "Qt::SerialBus", extra=["COMPONENTS", "SerialBus"]), -- cgit v1.2.3 From cd88c30ce3ca81a1efb89aeb5fa7fc7f1287aa0f Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 19 Sep 2019 16:01:09 +0200 Subject: Fix formatting for test data Add missing f parameter and globbing expression. Change-Id: I0eedb59e47537f9cf42a0b9d9471ddfc87ee48f2 Reviewed-by: Cristian Maureira-Fredes --- util/cmake/pro2cmake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b79e1f97cf..2985b3a4b1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2423,10 +2423,10 @@ def write_main_part( if "*" in data: cm_fh.write( dedent( - """ + f"""\ {spaces(indent)}file(GLOB_RECURSE test_data_glob {spaces(indent+1)}RELATIVE ${{CMAKE_CURRENT_SOURCE_DIR}} - {spaces(indent+1)}"{}") + {spaces(indent+1)}{data}) """ ) ) -- cgit v1.2.3 From 9bc3968aae124cb5d9313a2dcae07deb9990f08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=C3=A1n=20Maureira-Fredes?= Date: Thu, 19 Sep 2019 14:21:43 +0200 Subject: pro2cmake: exclude 3rdparty libs from examples There are some libraries inside examples that cannot be properly translated due to the fact that the is_example variable is True. This condition excludes it, for example: qtmultimedia/examples/multimedia/spectrum/3rdparty/fftreal/ Change-Id: I68a1e7d1684ab4604f54b6d16bf88d04e3214ba9 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 2985b3a4b1..51cce8027c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -188,8 +188,11 @@ def is_example_project(project_file_path: str = "") -> bool: 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. - return project_relative_path.startswith("examples") + # relative to the repo source dir, then it must be an example, but + # some examples contain 3rdparty libraries that do not need to be + # built as examples. + return (project_relative_path.startswith("examples") + and "3rdparty" not in project_relative_path) def find_qmake_conf(project_file_path: str = "") -> Optional[str]: -- cgit v1.2.3 From 3b7aa8aee855a939984473a0fc32383c43ec7620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=C3=A1n=20Maureira-Fredes?= Date: Thu, 19 Sep 2019 14:27:18 +0200 Subject: pro2cmake: skip tuple declaration on condition str In some cases after cleaning and simplifying the conditions you can end up with the following line: ((())) Python interprets that as an empty tuple: >>> a = ((())) >>> a () and then the simplify_condition (sympy) method fails to continue with an error: File ".../util/cmake/pro2cmake.py", line 1993, in simplify_condition condition_expr = simplify_logic(condition) File ".../sympy/logic/boolalg.py", line 2292, in simplify_logic expr = expr.xreplace(dict(zip(variables, s))) AttributeError: 'tuple' object has no attribute 'xreplace' You can see this behavior with the file: qtmultimedia/tests/auto/unit/multimediaqml.pro Change-Id: Ied4285b67f1e567660be49f6610311199bc48a22 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 51cce8027c..df81974ef1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1598,6 +1598,8 @@ def handle_subdir( if condition_str and subtraction_str: condition_str += " AND " condition_str += subtraction_str + if not condition_str.rstrip("()").strip(): + continue condition_simplified = simplify_condition(condition_str) condition_key = condition_simplified -- cgit v1.2.3 From d6a491d607015a3d6981f90a01fb0cdf4490e2e8 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 18 Sep 2019 11:54:11 +0200 Subject: CMake: Use FindWaylandScanner.cmake from extra-cmake-modules Change-Id: Iaf78f215396671fc6f351ac20e686cfd2c39664f Reviewed-by: Leander Beernaert Reviewed-by: Qt CMake Build Bot Reviewed-by: Pier Luigi Fiorini Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index db56f26eda..846d800298 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -128,6 +128,8 @@ def map_tests(test: str) -> str: "reduce_exports": "CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY", "libinput_axis_api": "ON", "xlib": "X11_FOUND", + + 'wayland-scanner': 'WaylandScanner_FOUND', } if test in testmap: return testmap.get(test, None) -- cgit v1.2.3 From e619a387fbb1032c3fec825948647017fd5a9a0f Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 19 Sep 2019 16:29:14 +0200 Subject: Extend globbing support in resources Extend globbing support in resource in order to be able to handle globbing expression present in qmake expression such as RESOURCE.files = *.qml Change-Id: I55a38c0bdc231a6a1ddbd65be7f57ca6f7dc2932 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index df81974ef1..009cebdaf5 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2163,6 +2163,26 @@ def map_to_cmake_condition(condition: str = "") -> str: resource_file_expansion_counter = 0 +def expand_resource_glob(cm_fh: IO[str], expression: str) -> str: + global resource_file_expansion_counter + r = expression.replace('"', "") + + cm_fh.write( + 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() + """ + ) + ) + + expanded_var = f"${{resource_glob_{resource_file_expansion_counter}}}" + resource_file_expansion_counter += 1 + return expanded_var + + def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, is_example=False): # vpath = scope.expand('VPATH') @@ -2189,6 +2209,13 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, else: immediate_files = {f: "" for f in scope.get_files(f"{r}.files")} if immediate_files: + immediate_files_filtered = [] + for f in immediate_files: + if "*" in f: + immediate_files_filtered.append(expand_resource_glob(cm_fh, f)) + else: + immediate_files_filtered.append(f) + immediate_files = {f: "" for f in immediate_files_filtered} immediate_prefix = scope.get(r + ".prefix") if immediate_prefix: immediate_prefix = immediate_prefix[0] @@ -2210,22 +2237,7 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, ) else: if "*" in r: - global 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 + standalone_files.append(expand_resource_glob(cm_fh, r)) else: # stadalone source file properties need to be set as they # are parsed. -- cgit v1.2.3 From 6cefa950e9a87718b2acec8d7ab405ea1b09c444 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 20 Sep 2019 10:32:28 +0200 Subject: CMake: pro2cmake: Handle OBJECTIVE_HEADERS Change-Id: I9a7b9c96530e8c922cf4aa5b6580e5579829db87 Reviewed-by: Cristian Maureira-Fredes --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 009cebdaf5..f135b9d998 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1794,7 +1794,7 @@ def write_all_source_file_lists( cm_fh, scope, header, - ["SOURCES", "HEADERS", "OBJECTIVE_SOURCES", "NO_PCH_SOURCES", "FORMS"] + extra_keys, + ["SOURCES", "HEADERS", "OBJECTIVE_SOURCES", "OBJECTIVE_HEADERS", "NO_PCH_SOURCES", "FORMS"] + extra_keys, indent, footer=footer, ) -- cgit v1.2.3 From d245ba48e75cd3de08d7e3d2b2d5186794a4d1a9 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 20 Sep 2019 10:33:04 +0200 Subject: CMake: pro2cmake: Do not mis-report used values as ignored Do not mis-report "PLUGIN_TYPE", "PLUGIN_CLASS_NAME", "CLASS_NAME" and "MODULE_PLUGIN_TYPES" as ignored. The script evaluates these. Change-Id: I26546a7eff61a20afe68168a865fe3ac99f66319 Reviewed-by: Cristian Maureira-Fredes --- util/cmake/pro2cmake.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f135b9d998..1b58dc7f86 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1920,6 +1920,10 @@ def write_ignored_keys(scope: Scope, indent: str) -> str: "QT_SOURCE_TREE", "QT_BUILD_TREE", "TRACEPOINT_PROVIDER", + "PLUGIN_TYPE", + "PLUGIN_CLASS_NAME", + "CLASS_NAME", + "MODULE_PLUGIN_TYPES", }: # All these keys are actually reported already continue -- cgit v1.2.3 From bb8f8381c6f9ee38896917ecff9796472e293bb0 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Fri, 20 Sep 2019 11:50:22 +0200 Subject: pro2cmake: Translate STATECHARTS to qt6_add_statecharts Change-Id: I461768e67ce8fa7dae70a057df340e9c9a2bc14e Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1b58dc7f86..51932115c0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2276,6 +2276,16 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, for line in qrc_output.split("\n"): cm_fh.write(f"{' ' * indent}{line}\n") +def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): + sources = scope.get("STATECHARTS") + if not sources: + return + cm_fh.write("\n# Statecharts:\n") + cm_fh.write(f"qt6_add_statecharts({target}\n") + indent += 1 + for f in sources: + cm_fh.write(f"{spaces(indent)}{f}\n") + cm_fh.write(")\n") def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): ind = spaces(indent) @@ -2476,6 +2486,8 @@ def write_main_part( write_resources(cm_fh, name, scope, indent) + write_statecharts(cm_fh, name, scope, indent) + write_simd_part(cm_fh, name, scope, indent) write_android_part(cm_fh, name, scopes[0], indent) @@ -2759,6 +2771,7 @@ def write_example( ) write_resources(cm_fh, binary_name, scope, indent=indent, is_example=True) + write_statecharts(cm_fh, binary_name, scope, indent=indent) if qmldir: write_qml_plugin_epilogue(cm_fh, binary_name, scope, qmldir, indent) -- cgit v1.2.3 From be3287d779d6c97d321b908e2f078baec04cc999 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 20 Sep 2019 13:08:19 +0200 Subject: configurejson2cmake: Add special case handling for Windows BT config tests Change-Id: Id73c44f5b7faff7392e7a8245e5e26e7dfe78cd8 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/configurejson2cmake.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 846d800298..787b668dd0 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -572,6 +572,11 @@ def parseTest(ctx, test, data, cm_fh): cm_fh.write("if (LINUX)\n") cm_fh.write(" set(" + librariesCmakeName + " pthread rt)\n") cm_fh.write("endif()\n") + elif details["qmake"] == "!winrt: LIBS += runtimeobject.lib": + librariesCmakeName = format(featureName(test)) + "_TEST_LIBRARIES" + cm_fh.write("if (NOT WINRT)\n") + cm_fh.write(" set(" + librariesCmakeName + " runtimeobject)\n") + cm_fh.write("endif()\n") elif details["qmake"] == "CONFIG += c++11": # do nothing we're always in c++11 mode pass -- cgit v1.2.3 From 9ef4ffb1a45f85fb3b5f77320b15981b2b102f17 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 20 Sep 2019 14:27:35 +0200 Subject: Add platform mapping for emscripten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EMSCRIPTEN is the name in cmake land as per emscripten's toolchain file. Change-Id: I11fa444204003f25f14fbf993ecf6766bf37d884 Reviewed-by: MĂĄrten Nordheim --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 278ade406f..0fa1039572 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -585,6 +585,7 @@ platform_mapping = { "watchos": "APPLE_WATCHOS", "winrt": "WINRT", "wasm": "WASM", + "emscripten": "EMSCRIPTEN", "msvc": "MSVC", "clang": "CLANG", "gcc": "GCC", -- cgit v1.2.3 From 9fe82283c65f46b5d5a48b82c94eb7d405b35eee Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 20 Sep 2019 13:43:26 +0200 Subject: Add support for requires() statements in top-level .pro files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For example qtserialport has requires(!winrt) and with this patch it gets translated to project(...) if(WINRT) return() endif() find_package(...) qt_build_repo(...) Change-Id: Ic38b803b464037a42adc55930947f57c5efa7b45 Reviewed-by: MĂĄrten Nordheim Reviewed-by: Cristian Maureira-Fredes --- util/cmake/pro2cmake.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 51932115c0..cb728f1e8f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -865,6 +865,10 @@ class Scope(object): scope._append_operation("_INCLUDED", UniqueAddOperation(included)) continue + project_required_condition = statement.get("project_required_condition") + if project_required_condition: + scope._append_operation("_REQUIREMENTS", AddOperation(project_required_condition)) + scope.settle_condition() if scope.scope_debug: @@ -1239,6 +1243,7 @@ class QmakeParser: 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")) + Requires = add_element("Requires", pp.Keyword("requires") + CallArgs("project_required_condition")) # ignore the whole thing... DefineTestDefinition = add_element( @@ -1277,6 +1282,7 @@ class QmakeParser: Load | Include | Option + | Requires | ForLoop | ForLoopSingleLine | DefineTestDefinition @@ -2287,6 +2293,21 @@ def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0 cm_fh.write(f"{spaces(indent)}{f}\n") cm_fh.write(")\n") +def expand_project_requirements(scope: Scope) -> str: + requirements = "" + for requirement in scope.get("_REQUIREMENTS"): + original_condition = simplify_condition(map_condition(requirement)) + inverted_requirement = simplify_condition(f"NOT {map_condition(requirement)}") + requirements += dedent(f"""\ + if({inverted_requirement}) + message(NOTICE "Skipping the build as the condition \\"{original_condition}\\" is not met.") + return() + endif() + + """) + return requirements + + def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): ind = spaces(indent) extend_qt_io_string = io.StringIO() @@ -2996,7 +3017,7 @@ def handle_top_level_repo_project(scope: Scope, cm_fh: IO[str]): qt_lib += "_FIXME" qt_lib_no_prefix = qt_lib - content = dedent( + header = dedent( f"""\ cmake_minimum_required(VERSION {cmake_version_string}) @@ -3009,11 +3030,17 @@ def handle_top_level_repo_project(scope: Scope, cm_fh: IO[str]): 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) + + """ + ) + + build_repo = dedent( + f"""\ qt_build_repo() """ ) - cm_fh.write(f"{content}") + cm_fh.write(f"{header}{expand_project_requirements(scope)}{build_repo}") def find_top_level_repo_project_file(project_file_path: str = "") -> Optional[str]: -- cgit v1.2.3 From f771b8f6b14418508c110e1e0dcd31b903424fca Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Fri, 20 Sep 2019 16:24:57 +0200 Subject: cmake: Let pro2cmake use private statechart API for non-examples Change-Id: If6206f2377e90ba7f8502029a1cc74856a2e03a1 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index cb728f1e8f..73171bbcfe 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2282,12 +2282,15 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, for line in qrc_output.split("\n"): cm_fh.write(f"{' ' * indent}{line}\n") -def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): +def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, is_example=False): sources = scope.get("STATECHARTS") if not sources: return cm_fh.write("\n# Statecharts:\n") - cm_fh.write(f"qt6_add_statecharts({target}\n") + if is_example: + cm_fh.write(f"qt6_add_statecharts({target}\n") + else: + cm_fh.write(f"add_qt_statecharts({target} FILES\n") indent += 1 for f in sources: cm_fh.write(f"{spaces(indent)}{f}\n") -- cgit v1.2.3 From cfa3c646253c88cbda366d6a374b81a941caa241 Mon Sep 17 00:00:00 2001 From: Cristian Maureira-Fredes Date: Fri, 20 Sep 2019 11:34:16 +0200 Subject: Adapt string formatting in util/cmake Second and final batch of changes related to string formatting styling. Change-Id: Ifc0e999ee95fe52fd076ac2f001b4a58f82ab5be Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 33 ++++--- util/cmake/helper.py | 20 ++--- util/cmake/pro2cmake.py | 178 +++++++++++++++++++------------------- util/cmake/pro_conversion_rate.py | 8 +- 4 files changed, 118 insertions(+), 121 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 787b668dd0..2c3d98ddfb 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -134,7 +134,7 @@ def map_tests(test: str) -> str: if test in testmap: return testmap.get(test, None) if test in knownTests: - return "TEST_{}".format(featureName(test)) + return f"TEST_{featureName(test)}" return None @@ -151,7 +151,7 @@ def cm(ctx, *output): def readJsonFromDir(dir): path = posixpath.join(dir, "configure.json") - print("Reading {}...".format(path)) + print(f"Reading {path}...") assert posixpath.exists(path) parser = json_parser.QMakeSpecificJSONParser() @@ -169,14 +169,14 @@ def processFiles(ctx, data): def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): newlib = find_3rd_party_library_mapping(lib) if not newlib: - print(' XXXX Unknown library "{}".'.format(lib)) + print(f' XXXX Unknown library "{lib}".') return if newlib.packageName is None: - print(' **** Skipping library "{}" -- was masked.'.format(lib)) + print(f' **** Skipping library "{lib}" -- was masked.') return - print(" mapped library {} to {}.".format(lib, newlib.targetName)) + print(f" mapped library {lib} to {newlib.targetName}.") # Avoid duplicate find_package calls. if newlib.targetName in cmake_find_packages_set: @@ -194,7 +194,7 @@ def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): feature_data = data["features"][feature] if ( "condition" in feature_data - and "libs.{}".format(lib) in feature_data["condition"] + and f"libs.{lib}" in feature_data["condition"] and "emitIf" in feature_data and "config." in feature_data["emitIf"] ): @@ -212,8 +212,9 @@ def parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set): def lineify(label, value, quote=True): if value: if quote: - return ' {} "{}"\n'.format(label, value.replace('"', '\\"')) - return " {} {}\n".format(label, value) + escaped_value = value.replace('"', '\\"') + return f' {label} "{escaped_value}"\n' + return f" {label} {value}\n" return "" @@ -267,12 +268,10 @@ def map_condition(condition): if feature in mapped_features: substitution = mapped_features.get(feature) else: - substitution = "QT_FEATURE_{}".format(featureName(match.group(2))) + substitution = f"QT_FEATURE_{featureName(match.group(2))}" elif match.group(1) == "subarch": - substitution = "TEST_arch_{}_subarch_{}".format( - "${TEST_architecture_arch}", match.group(2) - ) + substitution = f"TEST_arch_{'${TEST_architecture_arch}'}_subarch_{match.group(2)}" elif match.group(1) == "call": if match.group(2) == "crossCompile": @@ -282,12 +281,12 @@ def map_condition(condition): substitution = map_tests(match.group(2)) elif match.group(1) == "input": - substitution = "INPUT_{}".format(featureName(match.group(2))) + substitution = f"INPUT_{featureName(match.group(2))}" 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))) + substitution = f"TARGET {map_qt_library(match.group(2))}" elif match.group(1) == "arch": if match.group(2) == "i386": @@ -306,7 +305,7 @@ def map_condition(condition): substitution = "(TEST_architecture_arch STREQUAL mips)" if substitution is None: - print(' XXXX Unknown condition "{}".'.format(match.group(0))) + print(f' XXXX Unknown condition "{match.group(0)}"') has_failed = True else: mapped_condition += condition[last_pos : match.start(1)] + substitution @@ -919,7 +918,7 @@ def parseFeature(ctx, feature, data, cm_fh): 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(f' VALUE "{outputArgs.get("value")}"') cm_fh.write(")\n") @@ -1015,7 +1014,7 @@ def main(): directory = sys.argv[1] - print("Processing: {}.".format(directory)) + print(f"Processing: {directory}.") data = readJsonFromDir(directory) processJson(directory, {}, data) diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 0fa1039572..011c277c11 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -40,7 +40,7 @@ class LibraryMapping: resultVariable: typing.Optional[str] = None, extra: typing.List[str] = [], appendFoundSuffix: bool = True, - emit_if: str = "" + emit_if: str = "", ) -> None: self.soName = soName self.packageName = packageName @@ -497,7 +497,7 @@ _library_map = [ LibraryMapping("tiff", "TIFF", "TIFF::TIFF"), LibraryMapping("webp", "WrapWebP", "WrapWebP::WrapWebP"), LibraryMapping("jasper", "WrapJasper", "WrapJasper::WrapJasper"), - LibraryMapping('sdl2', 'SDL2', 'SDL2::SDL2'), + LibraryMapping("sdl2", "SDL2", "SDL2::SDL2"), ] @@ -656,26 +656,22 @@ def generate_find_package_info( if use_qt_find_package: if extra: - result = "{}qt_find_package({} {})\n".format(ind, lib.packageName, " ".join(extra)) + result = f"{ind}qt_find_package({lib.packageName} {' '.join(extra)})\n" else: - result = "{}qt_find_package({})\n".format(ind, lib.packageName) + result = f"{ind}qt_find_package({lib.packageName})\n" if isRequired: - result += "{}set_package_properties({} PROPERTIES TYPE REQUIRED)\n".format( - ind, lib.packageName - ) + result += f"{ind}set_package_properties({lib.packageName} PROPERTIES TYPE REQUIRED)\n" else: if extra: - result = "{}find_package({} {})\n".format(ind, lib.packageName, " ".join(extra)) + result = f"{ind}find_package({lib.packageName} {' '.join(extra)})\n" else: - result = "{}find_package({})\n".format(ind, lib.packageName) + result = f"{ind}find_package({lib.packageName})\n" # 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 = f"if(({emit_if}) OR QT_FIND_ALL_PACKAGES_ALWAYS)\n{one_ind}{result}endif()\n" return result diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 73171bbcfe..39d6fdb624 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -191,8 +191,7 @@ def is_example_project(project_file_path: str = "") -> bool: # relative to the repo source dir, then it must be an example, but # some examples contain 3rdparty libraries that do not need to be # built as examples. - return (project_relative_path.startswith("examples") - and "3rdparty" not in project_relative_path) + return project_relative_path.startswith("examples") and "3rdparty" not in project_relative_path def find_qmake_conf(project_file_path: str = "") -> Optional[str]: @@ -264,7 +263,7 @@ def process_qrc_file( lang = resource.get("lang", "") prefix = resource.get("prefix", "/") if not prefix.startswith("/"): - prefix = "/" + prefix + prefix = f"/{prefix}" full_resource_name = resource_name + (str(resource_count) if resource_count > 0 else "") @@ -319,10 +318,11 @@ def write_add_qt_resource_call( alias = files[source] if alias: full_source = posixpath.join(base_dir, source) - output += ( - f'set_source_files_properties("{full_source}"\n' - f' PROPERTIES QT_RESOURCE_ALIAS "{alias}"\n)\n' - ) + output += dedent(f"""\ + set_source_files_properties("{full_source}" + PROPERTIES QT_RESOURCE_ALIAS "{alias}" + ) + """) # Quote file paths in case there are spaces. sorted_files_backup = sorted_files @@ -333,8 +333,12 @@ def write_add_qt_resource_call( else: sorted_files.append(f'"{source}"') - file_list = "\n ".join(sorted_files) - output += f"set({resource_name}_resource_files\n {file_list}\n)\n\n" + file_list = "\n ".join(sorted_files) + output += dedent(f"""\ + set({resource_name}_resource_files + {file_list} + )\n + """) file_list = f"${{{resource_name}_resource_files}}" if skip_qtquick_compiler: output += ( @@ -389,27 +393,30 @@ class QmlDir: self.designer_supported = False def __str__(self): - str = "module: {}\n".format(self.module) - str += "plugin: {} {}\n".format(self.plugin_name, self.plugin_path) - str += "classname: {}\n".format(self.classname) - str += "type_infos:{}\n".format(" \n".join(self.type_infos)) - str += "imports:{}\n".format(" \n".join(self.imports)) - str += "dependends: \n" + types_infos_line = " \n".join(self.types_infos) + imports_line = " \n".join(self.imports) + string = f"""\ + module: {self.module} + plugin: {self.plugin_name} {self.plugin_path} + classname: {self.classname} + type_infos:{type_infos_line} + imports:{imports_line} + dependends: + """ for dep in self.depends: - str += " {} {}\n".format(dep[0], dep[1]) - str += "designer supported: {}\n".format(self.designer_supported) - str += "type_names:\n" + string += f" {dep[0]} {dep[1]}\n" + string += f"designer supported: {self.designer_supported}\n" + string += "type_names:\n" for key in self.type_names: file_info = self.type_names[key] - str += " type:{} version:{} path:{} internal:{} singleton:{}\n".format( - file_info.type_name, - file_info.version, - file_info.type_name, - file_info.file_path, - file_info.internal, - file_info.singleton, + string += ( + f" type:{file_info.type_name} " + f"version:{file_info.version} " + f"path:{file_info.file_path} " + f"internal:{file_info.internal} " + f"singleton:{file_info.singleton}\n" ) - return str + return string def get_or_create_file_info(self, path: str, type_name: str) -> QmlDirFileInfo: if not path in self.type_names: @@ -437,7 +444,7 @@ class QmlDir: def from_file(self, path: str): f = open(path, "r") if not f: - raise RuntimeError("Failed to open qmldir file at: {}".format(str)) + raise RuntimeError(f"Failed to open qmldir file at: {path}") for line in f: if line.startswith("#"): continue @@ -471,7 +478,7 @@ class QmlDir: elif len(entries) == 3: self.handle_file(entries[0], entries[1], entries[2]) else: - raise RuntimeError("Uhandled qmldir entry {}".format(line)) + raise RuntimeError(f"Uhandled qmldir entry {line}") def fixup_linecontinuation(contents: str) -> str: @@ -640,9 +647,9 @@ class Operation: class AddOperation(Operation): def process( - self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] ) -> List[str]: - return input + transformer(self._value) + return sinput + transformer(self._value) def __repr__(self): return f"+({self._dump()})" @@ -650,9 +657,9 @@ class AddOperation(Operation): class UniqueAddOperation(Operation): def process( - self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] ) -> List[str]: - result = input + result = sinput for v in transformer(self._value): if v not in result: result.append(v) @@ -664,14 +671,14 @@ class UniqueAddOperation(Operation): class SetOperation(Operation): def process( - self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] ) -> List[str]: values = [] # List[str] for v in self._value: if v != f"$${key}": values.append(v) else: - values += input + values += sinput if transformer: return list(transformer(values)) @@ -687,20 +694,20 @@ class RemoveOperation(Operation): super().__init__(value) def process( - self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] ) -> List[str]: - input_set = set(input) + sinput_set = set(sinput) value_set = set(self._value) result: List[str] = [] # Add everything that is not going to get removed: - for v in input: + for v in sinput: if v not in value_set: result += [v] # Add everything else with removal marker: for v in transformer(self._value): - if v not in input_set: + if v not in sinput_set: result += [f"-{v}"] return result @@ -2226,7 +2233,7 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, else: immediate_files_filtered.append(f) immediate_files = {f: "" for f in immediate_files_filtered} - immediate_prefix = scope.get(r + ".prefix") + immediate_prefix = scope.get(f"{r}.prefix") if immediate_prefix: immediate_prefix = immediate_prefix[0] else: @@ -2252,10 +2259,16 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, # stadalone source file properties need to be set as they # are parsed. if skip_qtquick_compiler: - qrc_output += 'set_source_files_properties(f"{r}" PROPERTIES QT_SKIP_QUICKCOMPILER 1)\n\n' + qrc_output += ( + f'set_source_files_properties("{r}" PROPERTIES ' + f"QT_SKIP_QUICKCOMPILER 1)\n\n" + ) if retain_qtquick_compiler: - qrc_output += 'set_source_files_properties(f"{r}" PROPERTIES QT_RETAIN_QUICKCOMPILER 1)\n\n' + qrc_output += ( + f'set_source_files_properties("{r}" PROPERTIES ' + f"QT_RETAIN_QUICKCOMPILER 1)\n\n" + ) standalone_files.append(r) if standalone_files: @@ -2494,7 +2507,7 @@ def write_main_part( destdir = scope.get_string("DESTDIR") if destdir: if destdir.startswith("./") or destdir.startswith("../"): - destdir = "${CMAKE_CURRENT_BINARY_DIR}/" + destdir + destdir = f"${{CMAKE_CURRENT_BINARY_DIR}}/{destdir}" extra_lines.append(f'OUTPUT_DIRECTORY "{destdir}"') cm_fh.write(f"{spaces(indent)}{cmake_function}({name}\n") @@ -2725,12 +2738,14 @@ def write_example( dest_dir = "${CMAKE_CURRENT_BINARY_DIR}" else: uri = os.path.basename(dest_dir) - dest_dir = "${CMAKE_CURRENT_BINARY_DIR}/" + dest_dir + dest_dir = f"${{CMAKE_CURRENT_BINARY_DIR}}/{dest_dir}" - add_target = f"qt6_add_qml_module({binary_name}\n" - add_target += f' OUTPUT_DIRECTORY "{dest_dir}"\n' - add_target += " VERSION 1.0\n" - add_target += ' URI "{}"\n'.format(uri) + add_target = dedent(f"""\ + qt6_add_qml_module({binary_name} + OUTPUT_DIRECTORY "{dest_dir}" + VERSION 1.0 + URI "{uri}" + """) qmldir_file_path = scope.get_files("qmldir.files") if qmldir_file_path: @@ -2746,7 +2761,8 @@ def write_example( if len(qml_dir.classname) != 0: add_target += f" CLASSNAME {qml_dir.classname}\n" if len(qml_dir.imports) != 0: - add_target += " IMPORTS\n{}".format(" \n".join(qml_dir.imports)) + qml_dir_imports_line = " \n".join(qml_dir.imports) + add_target += f" IMPORTS\n{qml_dir_imports_line}" if len(qml_dir.depends) != 0: add_target += " DEPENDENCIES\n" for dep in qml_dir.depends: @@ -2768,18 +2784,14 @@ def write_example( cm_fh, scope, f"target_include_directories({binary_name} PUBLIC", indent=0, footer=")" ) write_defines( - cm_fh, - scope, - "target_compile_definitions({} PUBLIC".format(binary_name), - indent=0, - footer=")", + cm_fh, scope, f"target_compile_definitions({binary_name} PUBLIC", indent=0, footer=")" ) write_list( cm_fh, private_libs, "", indent=indent, - header="target_link_libraries({} PRIVATE\n".format(binary_name), + header=f"target_link_libraries({binary_name} PRIVATE\n", footer=")", ) write_list( @@ -2787,11 +2799,11 @@ def write_example( public_libs, "", indent=indent, - header="target_link_libraries({} PUBLIC\n".format(binary_name), + header=f"target_link_libraries({binary_name} PUBLIC\n", footer=")", ) write_compile_options( - cm_fh, scope, "target_compile_options({}".format(binary_name), indent=0, footer=")" + cm_fh, scope, f"target_compile_options({binary_name}", indent=0, footer=")" ) write_resources(cm_fh, binary_name, scope, indent=indent, is_example=True) @@ -2801,11 +2813,11 @@ def write_example( write_qml_plugin_epilogue(cm_fh, binary_name, scope, qmldir, indent) 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" + f"\ninstall(TARGETS {binary_name}\n" + f' RUNTIME DESTINATION "${{INSTALL_EXAMPLEDIR}}"\n' + f' BUNDLE DESTINATION "${{INSTALL_EXAMPLEDIR}}"\n' + f' LIBRARY DESTINATION "${{INSTALL_EXAMPLEDIR}}"\n' + f")\n" ) return binary_name @@ -2829,7 +2841,7 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: 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, @@ -2898,7 +2910,9 @@ def write_qml_plugin( if len(qml_dir.classname) != 0: extra_lines.append(f"CLASSNAME {qml_dir.classname}") if len(qml_dir.imports) != 0: - extra_lines.append("IMPORTS\n {}".format("\n ".join(qml_dir.imports))) + qml_dir_imports_line = "\n ".join(qml_dir.imports) + extra_lines.append("IMPORTS\n " + f"{qml_dir_imports_line}") if len(qml_dir.depends) != 0: extra_lines.append("DEPENDENCIES") for dep in qml_dir.depends: @@ -2919,43 +2933,33 @@ def write_qml_plugin_epilogue( indent_0 = spaces(indent) indent_1 = spaces(indent + 1) # Quote file paths in case there are spaces. - qml_files_quoted = ['"{}"'.format(f) for f in qml_files] + qml_files_quoted = [f'"{qf}"' for qf in qml_files] - cm_fh.write( - "\n{}set(qml_files\n{}{}\n)\n".format( - indent_0, indent_1, "\n{}".format(indent_1).join(qml_files_quoted) - ) - ) + indented_qml_files = f"\n{indent_1}".join(qml_files_quoted) + cm_fh.write(f"\n{indent_0}set(qml_files\n{indent_1}" f"{indented_qml_files}\n)\n") for qml_file in qml_files: if qml_file in qmldir.type_names: qmldir_file_info = qmldir.type_names[qml_file] - cm_fh.write( - "{}set_source_files_properties({} PROPERTIES\n".format(indent_0, qml_file) - ) - cm_fh.write( - '{}QT_QML_SOURCE_VERSION "{}"\n'.format(indent_1, qmldir_file_info.version) - ) + cm_fh.write(f"{indent_0}set_source_files_properties({qml_file} PROPERTIES\n") + cm_fh.write(f'{indent_1}QT_QML_SOURCE_VERSION "{qmldir_file_info.version}"\n') # Only write typename if they are different, CMake will infer # the name by default if ( os.path.splitext(os.path.basename(qmldir_file_info.path))[0] != qmldir_file_info.type_name ): - cm_fh.write( - "{}QT_QML_SOURCE_TYPENAME {}\n".format(indent_1, qmldir_file_info.type_name) - ) - cm_fh.write("{}QT_QML_SOURCE_INSTALL TRUE\n".format(indent_1)) + cm_fh.write(f"{indent_1}QT_QML_SOURCE_TYPENAME {qmldir_file_info.type_name}\n") + cm_fh.write(f"{indent_1}QT_QML_SOURCE_INSTALL TRUE\n") if qmldir_file_info.singleton: - cm_fh.write("{}QT_QML_SINGLETON_TYPE TRUE\n".format(indent_1)) + cm_fh.write(f"{indent_1}QT_QML_SINGLETON_TYPE TRUE\n") if qmldir_file_info.internal: - cm_fh.write("{}QT_QML_INTERNAL_TYPE TRUE\n".format(indent_1)) - cm_fh.write("{})\n".format(indent_0)) + cm_fh.write(f"{indent_1}QT_QML_INTERNAL_TYPE TRUE\n") + cm_fh.write(f"{indent_0})\n") cm_fh.write( - "\n{}qt6_target_qml_files({}\n{}FILES\n{}${{qml_files}}\n)\n".format( - indent_0, target, indent_1, spaces(indent + 2) - ) + f"\n{indent_0}qt6_target_qml_files({target}\n{indent_1}FILES\n" + f"{spaces(indent+2)}${{qml_files}}\n)\n" ) @@ -3073,7 +3077,7 @@ def handle_top_level_repo_tests_project(scope: Scope, cm_fh: 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 = f'{re.sub(r":", r"", qt_lib)}{"Tests"}' else: qt_lib += "Tests_FIXME" else: @@ -3120,7 +3124,7 @@ def cmakeify_scope( # Wrap top level examples project with some commands which # are necessary to build examples as part of the overall # build. - buffer_value = f"\nqt_examples_build_begin()\n\n{buffer_value}\nqt_examples_build_end()" + buffer_value = f"qt_examples_build_begin()\n\n{buffer_value}\nqt_examples_build_end()\n" cm_fh.write(buffer_value) diff --git a/util/cmake/pro_conversion_rate.py b/util/cmake/pro_conversion_rate.py index c2807434a6..a3ba8455c8 100755 --- a/util/cmake/pro_conversion_rate.py +++ b/util/cmake/pro_conversion_rate.py @@ -184,13 +184,11 @@ def print_stats( for stat in stats: if stats[stat]["value"] > 0: print( - "{:<40}: {} ({}%)".format( - stats[stat]["label"], stats[stat]["value"], stats[stat]["percentage"] - ) + f"{stats[stat]['label']:<40}: {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)) + print(f"\n{'Scan time':<40}: {scan_time:.10f} seconds") + print(f"{'Total script time':<40}: {script_time:.10f} seconds") def main(): -- cgit v1.2.3 From 900df4812278f8c9cde915690a2be355307f09dd Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sat, 21 Sep 2019 17:41:20 +0200 Subject: Skip converting projects under tests/auto/installed_cmake Change-Id: Id26320fb55f7f0ae4b18726b0794a4a1f169c80f Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 39d6fdb624..a8bd573368 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3177,6 +3177,8 @@ def should_convert_project(project_file_path: str = "") -> bool: # Skip cmake auto tests, they should not be converted. if project_relative_path.startswith("tests/auto/cmake"): return False + if project_relative_path.startswith("tests/auto/installed_cmake"): + return False # Skip qmake testdata projects. if project_relative_path.startswith("tests/auto/tools/qmake/testdata"): -- cgit v1.2.3 From f35cc5090a3d78ee7f843625d2c19e472e031cc7 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sat, 21 Sep 2019 16:22:36 +0200 Subject: configurejson2cmake: handle out-of-line config tests Generate appropriate qt_config_compile_test() calls for config tests that have CMake projects. These are protected by an if(EXISTS) check so that configuration doesn't fail for repos where the config tests have not been ported yet. Adjust the qt_config_compile_test() function to use try_compile for projects specified via new PROJECT_PATH argument. Change-Id: I83c061e384f68688a654b782fd7a9bede282d1e3 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 2c3d98ddfb..51d89d56ab 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -516,7 +516,16 @@ def parseTest(ctx, test, data, cm_fh): details = data["test"] if isinstance(details, str): - print(f" XXXX UNHANDLED TEST SUB-TYPE {details} in test description") + if not ctx['test_dir']: + print(f" XXXX UNHANDLED TEST SUB-TYPE {details} in test description") + return + + cm_fh.write(f""" +if(EXISTS "${{CMAKE_CURRENT_SOURCE_DIR}}/{ctx['test_dir']}/{data['test']}/CMakeLists.txt") + qt_config_compile_test("{data['test']}" + PROJECT_PATH "${{CMAKE_CURRENT_SOURCE_DIR}}/{ctx['test_dir']}/{data['test']}") +endif() +""") return head = details.get("head", "") @@ -975,6 +984,7 @@ def processSubconfigs(dir, ctx, data): def processJson(dir, ctx, data): ctx["module"] = data.get("module", "global") + ctx["test_dir"] = data.get("testDir", "") ctx = processFiles(ctx, data) -- cgit v1.2.3 From d71609610abbd628ef671b9b8577522eed9203e2 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sat, 21 Sep 2019 18:02:54 +0200 Subject: pro2cmake: Handle generation of config.tests Handle conversion of config.tests that are used as out-of-line tests in configure.json. The script should able to handle conversion of simple config tests including nested scopes. One thing that will probably be needed in the future is handling of pkconfig which is used in qtwebengine config.tests. There is also a hardcoded list of config tests which should not be automatically converted by pro2cmake, either because they were hand written, or because it doesn't make sense to convert them for now. Incidentally, we should fix example conversion to also handle nested scopes, similarly to how it's done for config.tests. Change-Id: I862e67c4d19d5ed8cb7fc7c9f9aaf6e3d503aa03 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 123 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index a8bd573368..9afa95b2a0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -50,6 +50,7 @@ import xml.etree.ElementTree as ET from argparse import ArgumentParser from textwrap import dedent from itertools import chain +from functools import lru_cache from shutil import copyfile from sympy.logic import simplify_logic, And, Or, Not from sympy.core.sympify import SympifyError @@ -194,6 +195,20 @@ def is_example_project(project_file_path: str = "") -> bool: return project_relative_path.startswith("examples") and "3rdparty" not in project_relative_path +def is_config_test_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) + dir_name_with_qmake_confg = os.path.basename(qmake_conf_dir_path) + + project_relative_path = os.path.relpath(project_file_path, qmake_conf_dir_path) + # If the project file is found in a subdir called 'config.tests' + # relative to the repo source dir, then it's probably a config test. + # Also if the .qmake.conf is found within config.tests dir (like in qtbase) + # then the project is probably a config .test + return project_relative_path.startswith("config.tests") or dir_name_with_qmake_confg == "config.tests" + + +@lru_cache(maxsize=None) def find_qmake_conf(project_file_path: str = "") -> Optional[str]: if not os.path.isabs(project_file_path): print( @@ -1073,7 +1088,7 @@ class Scope(object): return list(self._evalOps(key, transformer, [])) @staticmethod - def _replace_env_var_value(value: Any) -> str: + def _replace_env_var_value(value: Any) -> Any: if not isinstance(value, str): return value @@ -3098,6 +3113,95 @@ def handle_top_level_repo_tests_project(scope: Scope, cm_fh: IO[str]): cm_fh.write(f"{content}") +def write_regular_cmake_target_scope_section(scope: Scope, + cm_fh: IO[str], + indent: int = 0, + skip_sources: bool = False): + if not skip_sources: + target_sources = "target_sources(${PROJECT_NAME} PUBLIC" + write_all_source_file_lists(cm_fh, scope, target_sources, indent=indent, footer=")") + + write_include_paths( + cm_fh, scope, f"target_include_directories(${{PROJECT_NAME}} PUBLIC", indent=indent, footer=")" + ) + write_defines( + cm_fh, scope, f"target_compile_definitions(${{PROJECT_NAME}} PUBLIC", indent=indent, footer=")" + ) + (public_libs, private_libs) = extract_cmake_libraries(scope) + write_list( + cm_fh, + private_libs, + "", + indent=indent, + header=f"target_link_libraries(${{PROJECT_NAME}} PRIVATE\n", + footer=")", + ) + write_list( + cm_fh, + public_libs, + "", + indent=indent, + header=f"target_link_libraries(${{PROJECT_NAME}} PUBLIC\n", + footer=")", + ) + write_compile_options( + cm_fh, scope, f"target_compile_options(${{PROJECT_NAME}}", indent=indent, footer=")" + ) + + +def handle_config_test_project(scope: Scope, cm_fh: IO[str]): + project_name = os.path.splitext(os.path.basename(scope.file_absolute_path))[0] + content = ( + f"cmake_minimum_required(VERSION 3.14.0)\n" + f"project(config_test_{project_name} LANGUAGES CXX)\n" + ) + cm_fh.write(f"{content}\n") + + # Remove default QT libs. + scope._append_operation("QT", RemoveOperation(["core", "gui"])) + + config = scope.get("CONFIG") + gui = all(val not in config for val in ["console", "cmdline"]) + + add_target = f'add_executable(${{PROJECT_NAME}}' + + if gui: + add_target += " WIN32 MACOSX_BUNDLE" + + temp_buffer = io.StringIO() + write_all_source_file_lists(temp_buffer, scope, add_target, indent=0) + buffer_value = temp_buffer.getvalue() + + if buffer_value: + cm_fh.write(buffer_value) + else: + cm_fh.write(add_target) + cm_fh.write(")\n") + + indent = 0 + write_regular_cmake_target_scope_section(scope, cm_fh, indent, skip_sources=True) + + recursive_evaluate_scope(scope) + scopes = flatten_scopes(scope) + scopes = merge_scopes(scopes) + + assert len(scopes) + assert scopes[0].total_condition == "ON" + + for c in scopes[1:]: + extend_scope_io_string = io.StringIO() + write_regular_cmake_target_scope_section(c, extend_scope_io_string, indent=indent+1) + extend_string = extend_scope_io_string.getvalue() + + if extend_string: + extend_scope = ( + f"\nif({map_to_cmake_condition(c.total_condition)})\n" + f"{extend_string}" + f"endif()\n" + ) + cm_fh.write(extend_scope) + + def cmakeify_scope( scope: Scope, cm_fh: IO[str], *, indent: int = 0, is_example: bool = False ) -> None: @@ -3111,6 +3215,8 @@ def cmakeify_scope( # 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 is_config_test_project(scope.file_absolute_path): + handle_config_test_project(scope, temp_buffer) elif template == "subdirs": handle_subdir(scope, temp_buffer, indent=indent, is_example=is_example) elif template in ("app", "lib"): @@ -3184,6 +3290,21 @@ def should_convert_project(project_file_path: str = "") -> bool: if project_relative_path.startswith("tests/auto/tools/qmake/testdata"): return False + # Skip certain config tests. + config_tests = [ + # Relative to qtbase/config.tests + "arch/arch.pro", + "avx512/avx512.pro", + "stl/stl.pro", + "verifyspec/verifyspec.pro", + "x86_simd/x86_simd.pro", + # Relative to repo src dir + "config.tests/hostcompiler/hostcompiler.pro", + ] + skip_certain_tests = any(project_relative_path.startswith(c) for c in config_tests) + if skip_certain_tests: + return False + return True -- cgit v1.2.3 From d6bbf6944ce212a900c39a5f03c724c74bfba23a Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sat, 21 Sep 2019 20:09:36 +0200 Subject: Reformat python files using black Change-Id: I0cc8ed89e2057b65f4fa34294eccffae642f2c73 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 11 ++++--- util/cmake/pro2cmake.py | 64 ++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 26 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 51d89d56ab..33e37ded4b 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -128,8 +128,7 @@ def map_tests(test: str) -> str: "reduce_exports": "CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY", "libinput_axis_api": "ON", "xlib": "X11_FOUND", - - 'wayland-scanner': 'WaylandScanner_FOUND', + "wayland-scanner": "WaylandScanner_FOUND", } if test in testmap: return testmap.get(test, None) @@ -516,16 +515,18 @@ def parseTest(ctx, test, data, cm_fh): details = data["test"] if isinstance(details, str): - if not ctx['test_dir']: + if not ctx["test_dir"]: print(f" XXXX UNHANDLED TEST SUB-TYPE {details} in test description") return - cm_fh.write(f""" + cm_fh.write( + f""" if(EXISTS "${{CMAKE_CURRENT_SOURCE_DIR}}/{ctx['test_dir']}/{data['test']}/CMakeLists.txt") qt_config_compile_test("{data['test']}" PROJECT_PATH "${{CMAKE_CURRENT_SOURCE_DIR}}/{ctx['test_dir']}/{data['test']}") endif() -""") +""" + ) return head = details.get("head", "") diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9afa95b2a0..54b6730c83 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -205,7 +205,10 @@ def is_config_test_project(project_file_path: str = "") -> bool: # relative to the repo source dir, then it's probably a config test. # Also if the .qmake.conf is found within config.tests dir (like in qtbase) # then the project is probably a config .test - return project_relative_path.startswith("config.tests") or dir_name_with_qmake_confg == "config.tests" + return ( + project_relative_path.startswith("config.tests") + or dir_name_with_qmake_confg == "config.tests" + ) @lru_cache(maxsize=None) @@ -333,11 +336,13 @@ def write_add_qt_resource_call( alias = files[source] if alias: full_source = posixpath.join(base_dir, source) - output += dedent(f"""\ + output += dedent( + f"""\ set_source_files_properties("{full_source}" PROPERTIES QT_RESOURCE_ALIAS "{alias}" ) - """) + """ + ) # Quote file paths in case there are spaces. sorted_files_backup = sorted_files @@ -349,11 +354,13 @@ def write_add_qt_resource_call( sorted_files.append(f'"{source}"') file_list = "\n ".join(sorted_files) - output += dedent(f"""\ + output += dedent( + f"""\ set({resource_name}_resource_files {file_list} )\n - """) + """ + ) file_list = f"${{{resource_name}_resource_files}}" if skip_qtquick_compiler: output += ( @@ -1265,7 +1272,9 @@ class QmakeParser: 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")) - Requires = add_element("Requires", pp.Keyword("requires") + CallArgs("project_required_condition")) + Requires = add_element( + "Requires", pp.Keyword("requires") + CallArgs("project_required_condition") + ) # ignore the whole thing... DefineTestDefinition = add_element( @@ -1822,7 +1831,8 @@ def write_all_source_file_lists( cm_fh, scope, header, - ["SOURCES", "HEADERS", "OBJECTIVE_SOURCES", "OBJECTIVE_HEADERS", "NO_PCH_SOURCES", "FORMS"] + extra_keys, + ["SOURCES", "HEADERS", "OBJECTIVE_SOURCES", "OBJECTIVE_HEADERS", "NO_PCH_SOURCES", "FORMS"] + + extra_keys, indent, footer=footer, ) @@ -2310,6 +2320,7 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, for line in qrc_output.split("\n"): cm_fh.write(f"{' ' * indent}{line}\n") + def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, is_example=False): sources = scope.get("STATECHARTS") if not sources: @@ -2324,18 +2335,21 @@ def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0 cm_fh.write(f"{spaces(indent)}{f}\n") cm_fh.write(")\n") + def expand_project_requirements(scope: Scope) -> str: requirements = "" for requirement in scope.get("_REQUIREMENTS"): original_condition = simplify_condition(map_condition(requirement)) inverted_requirement = simplify_condition(f"NOT {map_condition(requirement)}") - requirements += dedent(f"""\ + requirements += dedent( + f"""\ if({inverted_requirement}) message(NOTICE "Skipping the build as the condition \\"{original_condition}\\" is not met.") return() endif() - """) + """ + ) return requirements @@ -2755,12 +2769,14 @@ def write_example( uri = os.path.basename(dest_dir) dest_dir = f"${{CMAKE_CURRENT_BINARY_DIR}}/{dest_dir}" - add_target = dedent(f"""\ + add_target = dedent( + f"""\ qt6_add_qml_module({binary_name} OUTPUT_DIRECTORY "{dest_dir}" VERSION 1.0 URI "{uri}" - """) + """ + ) qmldir_file_path = scope.get_files("qmldir.files") if qmldir_file_path: @@ -2926,8 +2942,7 @@ def write_qml_plugin( extra_lines.append(f"CLASSNAME {qml_dir.classname}") if len(qml_dir.imports) != 0: qml_dir_imports_line = "\n ".join(qml_dir.imports) - extra_lines.append("IMPORTS\n " - f"{qml_dir_imports_line}") + extra_lines.append("IMPORTS\n " f"{qml_dir_imports_line}") if len(qml_dir.depends) != 0: extra_lines.append("DEPENDENCIES") for dep in qml_dir.depends: @@ -3113,19 +3128,26 @@ def handle_top_level_repo_tests_project(scope: Scope, cm_fh: IO[str]): cm_fh.write(f"{content}") -def write_regular_cmake_target_scope_section(scope: Scope, - cm_fh: IO[str], - indent: int = 0, - skip_sources: bool = False): +def write_regular_cmake_target_scope_section( + scope: Scope, cm_fh: IO[str], indent: int = 0, skip_sources: bool = False +): if not skip_sources: target_sources = "target_sources(${PROJECT_NAME} PUBLIC" write_all_source_file_lists(cm_fh, scope, target_sources, indent=indent, footer=")") write_include_paths( - cm_fh, scope, f"target_include_directories(${{PROJECT_NAME}} PUBLIC", indent=indent, footer=")" + cm_fh, + scope, + f"target_include_directories(${{PROJECT_NAME}} PUBLIC", + indent=indent, + footer=")", ) write_defines( - cm_fh, scope, f"target_compile_definitions(${{PROJECT_NAME}} PUBLIC", indent=indent, footer=")" + cm_fh, + scope, + f"target_compile_definitions(${{PROJECT_NAME}} PUBLIC", + indent=indent, + footer=")", ) (public_libs, private_libs) = extract_cmake_libraries(scope) write_list( @@ -3163,7 +3185,7 @@ def handle_config_test_project(scope: Scope, cm_fh: IO[str]): config = scope.get("CONFIG") gui = all(val not in config for val in ["console", "cmdline"]) - add_target = f'add_executable(${{PROJECT_NAME}}' + add_target = f"add_executable(${{PROJECT_NAME}}" if gui: add_target += " WIN32 MACOSX_BUNDLE" @@ -3190,7 +3212,7 @@ def handle_config_test_project(scope: Scope, cm_fh: IO[str]): for c in scopes[1:]: extend_scope_io_string = io.StringIO() - write_regular_cmake_target_scope_section(c, extend_scope_io_string, indent=indent+1) + write_regular_cmake_target_scope_section(c, extend_scope_io_string, indent=indent + 1) extend_string = extend_scope_io_string.getvalue() if extend_string: -- cgit v1.2.3 From 6920acd894dd7253fecfc23451eade8c69b4c606 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sat, 21 Sep 2019 20:18:10 +0200 Subject: pro2cmake: Small fixes and adjustments reported by flake8 and IDE Change-Id: I13fd2f20414647b75c8da237857248808dddaa40 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 54b6730c83..2617677d07 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -54,7 +54,20 @@ from functools import lru_cache 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 typing import ( + List, + Optional, + Dict, + Set, + IO, + Union, + Mapping, + Any, + Callable, + FrozenSet, + Tuple, + Match, +) from special_case_helper import SpecialCaseHandler from helper import ( map_qt_library, @@ -415,7 +428,7 @@ class QmlDir: self.designer_supported = False def __str__(self): - types_infos_line = " \n".join(self.types_infos) + type_infos_line = " \n".join(self.type_infos) imports_line = " \n".join(self.imports) string = f"""\ module: {self.module} @@ -441,7 +454,7 @@ class QmlDir: return string def get_or_create_file_info(self, path: str, type_name: str) -> QmlDirFileInfo: - if not path in self.type_names: + if path not in self.type_names: self.type_names[path] = QmlDirFileInfo(path, type_name) qmldir_file = self.type_names[path] if qmldir_file.type_name != type_name: @@ -1434,7 +1447,7 @@ def map_condition(condition: str) -> str: 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): + def gcc_version_handler(match_obj: Match): operator = match_obj.group(1) version_type = match_obj.group(2) if operator == "equals": @@ -2488,8 +2501,6 @@ def write_main_part( # Evaluate total condition of all scopes: recursive_evaluate_scope(scope) - is_qml_plugin = any("qml_plugin" == s for s in scope.get("_LOADED")) - if "exceptions" in scope.get("CONFIG"): extra_lines.append("EXCEPTIONS") @@ -2682,7 +2693,7 @@ def write_test(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = return test_name -def write_binary(cm_fh: 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) -> str: binary_name = scope.TARGET assert binary_name @@ -2897,10 +2908,10 @@ def write_qml_plugin( target: str, scope: Scope, *, - extra_lines: typing.List[str] = [], + extra_lines: List[str] = [], indent: int = 0, - **kwargs: typing.Any, -) -> QmlDir: + **kwargs: Any, +) -> Optional[QmlDir]: # Collect other args if available indent += 2 @@ -2954,7 +2965,7 @@ def write_qml_plugin( def write_qml_plugin_epilogue( - cm_fh: typing.IO[str], target: str, scope: Scope, qmldir: QmlDir, indent: int = 0 + cm_fh: IO[str], target: str, scope: Scope, qmldir: QmlDir, indent: int = 0 ): qml_files = scope.get_files("QML_FILES", use_vpath=True) -- cgit v1.2.3 From 1144c08be878a09e7de497f7bdf2316df2592cdc Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sat, 21 Sep 2019 20:30:08 +0200 Subject: pro2cmake: Fix default mutable arguments usage Change-Id: I91f2632f46539a1e5009b598625d78f61a051588 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 2617677d07..472e4745de 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1738,8 +1738,10 @@ def _map_libraries_to_cmake(libraries: List[str], known_libraries: Set[str]) -> def extract_cmake_libraries( - scope: Scope, *, known_libraries: Set[str] = set() + scope: Scope, *, known_libraries: Optional[Set[str]] = None ) -> Tuple[List[str], List[str]]: + if known_libraries is None: + known_libraries = set() public_dependencies = [] # type: List[str] private_dependencies = [] # type: List[str] @@ -1883,8 +1885,10 @@ def write_compile_options( def write_library_section( - cm_fh: IO[str], scope: Scope, *, indent: int = 0, known_libraries: Set[str] = set() + cm_fh: IO[str], scope: Scope, *, indent: int = 0, known_libraries: Optional[Set[str]] = None ): + if known_libraries is None: + known_libraries = set() public_dependencies, private_dependencies = extract_cmake_libraries( scope, known_libraries=known_libraries ) @@ -1899,7 +1903,11 @@ def write_autogen_section(cm_fh: IO[str], scope: Scope, *, indent: int = 0): write_list(cm_fh, ["uic"], "ENABLE_AUTOGEN_TOOLS", indent) -def write_sources_section(cm_fh: 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: Optional[Set[str]] = None +): + if known_libraries is None: + known_libraries = set() ind = spaces(indent) # mark RESOURCES as visited: @@ -2493,12 +2501,14 @@ def write_main_part( cmake_function: str, scope: Scope, *, - extra_lines: List[str] = [], + extra_lines: Optional[List[str]] = None, indent: int = 0, extra_keys: List[str], **kwargs: Any, ): # Evaluate total condition of all scopes: + if extra_lines is None: + extra_lines = [] recursive_evaluate_scope(scope) if "exceptions" in scope.get("CONFIG"): @@ -2908,11 +2918,13 @@ def write_qml_plugin( target: str, scope: Scope, *, - extra_lines: List[str] = [], + extra_lines: Optional[List[str]] = None, indent: int = 0, **kwargs: Any, ) -> Optional[QmlDir]: # Collect other args if available + if extra_lines is None: + extra_lines = [] indent += 2 target_path = scope.get_string("TARGETPATH") -- cgit v1.2.3 From f0a22bd78dcd07834dcede4f25587eb619c684ba Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sun, 22 Sep 2019 16:42:16 +0200 Subject: pro2cmake: Fix regressions introduced by reformat change Sympy uses eval() inside its code, and that can raise an AttributeError exception, so we have to catch that. This happened when converting qml.pro in qtdeclarative. Also the formatting change removed the raising of an exception when an unhandled function was encountered in handle_function_value, which caused not returning any value in such a case. Instead just return the whole expression string, without checking if the function is in some whitelist. Also encountered in qml.pro. Amends 91634c3c9b8d4f68f0ebd2ac76a8b5b79e4b4c94. Change-Id: I47b73af1de0180becb9ef4fb78a8c35a04928d34 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 472e4745de..831bda0e6a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -631,22 +631,8 @@ def handle_function_value(group: pp.ParseResults): 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 f"join({''.join(function_args)})" + # For other functions, return the whole expression as a string. + return f"$${function_name}({' '.join(function_args)})" class Operation: @@ -2178,7 +2164,7 @@ def simplify_condition(condition: str) -> str: condition = condition.replace("True", "ON") condition = condition.replace("False", "OFF") condition = condition.replace("_dash_", "-") - except (SympifyError, TypeError): + except (SympifyError, TypeError, AttributeError): # sympy did not like our input, so leave this condition alone: condition = input_condition -- cgit v1.2.3 From 5f856a6d0c347bb8ed18be80e7ae3ebd5c9fe850 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sun, 22 Sep 2019 16:44:29 +0200 Subject: pro2cmake: Handle if() conditions with pyparsing Previously we used a regular expression that matched parentheses, but the regexp didn't match the parantheses in a balanced way if there was more than one pair. Instead use a helper function that uses pyparsing to match balanced parantheses, and removes the if keyword, leaving just the nested expression. Change-Id: Ie621e6a305e57aa411838288599366ccfba063cc Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 831bda0e6a..4411dc8c2c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1420,6 +1420,19 @@ def parseProFile(file: str, *, debug=False): return parser.parseFile(file) +# Given "if(a|b):c" returns "(a|b):c". Uses pyparsing to keep the parentheses +# balanced. +def unwrap_if(input_string): + # Compute the grammar only once. + if not hasattr(unwrap_if, "if_grammar"): + expr_with_parentheses = pp.originalTextFor(pp.nestedExpr()) + if_keyword = pp.Suppress(pp.Keyword("if")) + unwrap_if.if_grammar = if_keyword + expr_with_parentheses + + output_string = unwrap_if.if_grammar.transformString(input_string) + return output_string + + def map_condition(condition: str) -> str: # Some hardcoded cases that are too bothersome to generalize. condition = re.sub( @@ -1450,12 +1463,8 @@ def map_condition(condition: str) -> str: 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 - # unbalanced when there are nested expressions. - # 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) + # Handle if(...) conditions. + condition = unwrap_if(condition) condition = re.sub(r"\bisEmpty\s*\((.*?)\)", r"\1_ISEMPTY", condition) condition = re.sub(r'\bcontains\s*\((.*?),\s*"?(.*?)"?\)', r"\1___contains___\2", condition) -- cgit v1.2.3 From 303095e686bc647b0d6766fa70b18a8bd41f1caf Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sun, 22 Sep 2019 17:54:34 +0200 Subject: pro2cmake: Fix regexp for parsing env var expansion The qmake syntax for env var expansion is "$$()". The parantheses are not optional, so the optional "?" modifiers should be removed. This fixes the failing test_recursive_expansion pytest. Amends c58df80cf7926b07da9fe6515230bd4295c1fc6d. Change-Id: I5d7217555287ee7d96d6b38027964b1141af208a Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4411dc8c2c..aad0d621f6 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1098,7 +1098,7 @@ class Scope(object): if not isinstance(value, str): return 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, value) if match: value = re.sub(pattern, r"$ENV{\1}", value) -- cgit v1.2.3 From d8b18385e88df29dd0ce38db484d1d7912ddea15 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Sun, 22 Sep 2019 18:28:01 +0200 Subject: pro2cmake: Improve handling of requires clauses Change the grammar to parse and extract the whole expression that is present in the requires clause. Make sure to preprocess the parsed content as a condition, so that the value passed to map_condition and simplify_condition is valid and the functions can handle more complicated conditions like qtConfig() and if(). Wrap the final condition with an extra pair of parentheses, so that the negated condition is computed correctly. Handle the require clause in subdir projects, top level tests projects, regular test projects, as well as top level repo projects. Examples are not yet handled, because the would require some kind of CMake public api to e.g. query if a feature is enabled. Change-Id: I9af96cf022e47b66f24db3ca15d3803dda7a5d7c Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 53 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 11 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index aad0d621f6..797844f7d5 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -49,6 +49,7 @@ import xml.etree.ElementTree as ET from argparse import ArgumentParser from textwrap import dedent +from textwrap import indent as textwrap_indent from itertools import chain from functools import lru_cache from shutil import copyfile @@ -1271,8 +1272,22 @@ class QmakeParser: 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")) + RequiresCondition = add_element("RequiresCondition", pp.originalTextFor(pp.nestedExpr())) + + def parse_requires_condition(s, l, t): + # The following expression unwraps the condition via the additional info + # set by originalTextFor. + condition_without_parentheses = s[t._original_start + 1 : t._original_end - 1] + + # And this replaces the colons with '&&' similar how it's done for 'Condition'. + condition_without_parentheses = ( + condition_without_parentheses.strip().replace(":", " && ").strip(" && ") + ) + return condition_without_parentheses + + RequiresCondition.setParseAction(parse_requires_condition) Requires = add_element( - "Requires", pp.Keyword("requires") + CallArgs("project_required_condition") + "Requires", pp.Keyword("requires") + RequiresCondition("project_required_condition") ) # ignore the whole thing... @@ -1598,7 +1613,7 @@ def handle_subdir( current_conditions=frozenset((*current_conditions, child_condition)), ) - def group_and_print_sub_dirs(indent: int = 0): + def group_and_print_sub_dirs(scope: Scope, indent: int = 0): # Simplify conditions, and group # subdirectories with the same conditions. grouped_sub_dirs = {} @@ -1652,6 +1667,9 @@ def handle_subdir( sub_dir_list_by_key.append(subdir_name) grouped_sub_dirs[condition_key] = sub_dir_list_by_key + # Print any requires() blocks. + cm_fh.write(expand_project_requirements(scope)) + # Print the groups. ind = spaces(indent) for condition_key in grouped_sub_dirs: @@ -1678,7 +1696,7 @@ def handle_subdir( handle_subdir_helper( scope, cm_fh, indent=indent, current_conditions=current_conditions, is_example=is_example ) - group_and_print_sub_dirs(indent=indent) + group_and_print_sub_dirs(scope, indent=indent) def sort_sources(sources: List[str]) -> List[str]: @@ -2352,19 +2370,22 @@ def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0 cm_fh.write(")\n") -def expand_project_requirements(scope: Scope) -> str: +def expand_project_requirements(scope: Scope, skip_message: bool = False) -> str: requirements = "" for requirement in scope.get("_REQUIREMENTS"): original_condition = simplify_condition(map_condition(requirement)) - inverted_requirement = simplify_condition(f"NOT {map_condition(requirement)}") + inverted_requirement = simplify_condition(f"NOT ({map_condition(requirement)})") + if not skip_message: + message = f""" +{spaces(7)}message(NOTICE "Skipping the build as the condition \\"{original_condition}\\" is not met.")""" + else: + message = "" requirements += dedent( f"""\ - if({inverted_requirement}) - message(NOTICE "Skipping the build as the condition \\"{original_condition}\\" is not met.") + if({inverted_requirement}){message} return() endif() - - """ +""" ) return requirements @@ -2683,6 +2704,11 @@ def write_test(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = for path in importpath: extra.append(f' "{path}"') + requires_content = expand_project_requirements(scope, skip_message=True) + if requires_content: + requires_content += "\n" + cm_fh.write(requires_content) + write_main_part( cm_fh, test_name, @@ -3131,16 +3157,21 @@ def handle_top_level_repo_tests_project(scope: Scope, cm_fh: IO[str]): else: qt_lib = "Tests_FIXME" + requires_content = expand_project_requirements(scope, skip_message=True) + if requires_content: + requires_content = f"\n\n{textwrap_indent(requires_content, spaces(3))}" + 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) + find_package(Qt6 ${{PROJECT_VERSION}} OPTIONAL_COMPONENTS SET_ME_TO_SOMETHING_USEFUL){requires_content} qt_set_up_standalone_tests_build() endif() - qt_build_tests()""" + qt_build_tests() +""" ) cm_fh.write(f"{content}") -- cgit v1.2.3 From 6ff123be879bcb8d476c11af44a36624e2b626aa Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Mon, 23 Sep 2019 10:27:00 +0200 Subject: pro2cmake: Ignore generated .qrc files ...instead of complaining about non-existing files. qtscxml/tests/auto/scion/scion.pro has RESOURCES = $$OUT_PWD/scion.qrc Change-Id: I5737fe5078c87689152fc7fa0d8d25ddc2b1a00f Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 797844f7d5..649aacc28d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2273,6 +2273,9 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, skip_qtquick_compiler = r in qtquickcompiler_skipped retain_qtquick_compiler = r in qtquickcompiler_retained if r.endswith(".qrc"): + if "${CMAKE_CURRENT_BINARY_DIR}" in r: + cm_fh.write(f"#### Ignored generated resource: {r}") + continue qrc_output += process_qrc_file( target, r, -- cgit v1.2.3 From 8049ef3d4d1e6c458211a3c86ea35e9fe9b29ff5 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Mon, 23 Sep 2019 13:19:22 +0200 Subject: cmake: Implement the ~= operator in pro2cmake Change-Id: I3fadd0038d517d197cce898d10edd6ba452adea9 Reviewed-by: Cristian Maureira-Fredes Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 649aacc28d..8765c870f6 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -676,7 +676,6 @@ class AddOperation(Operation): def __repr__(self): return f"+({self._dump()})" - class UniqueAddOperation(Operation): def process( self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] @@ -690,6 +689,37 @@ class UniqueAddOperation(Operation): def __repr__(self): return f"*({self._dump()})" +class ReplaceOperation(Operation): + def process( + self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] + ) -> List[str]: + result = [] + for s in sinput: + for v in transformer(self._value): + pattern, replacement = self.split_rex(v) + result.append(re.sub(pattern, replacement, s)) + return result + + def split_rex(self, s): + pattern = "" + replacement = "" + if len(s) < 4: + return pattern, replacement + sep = s[1] + s = s[2:] + rex = re.compile(f"[^\\\\]{sep}") + m = rex.search(s) + if not m: + return pattern, replacement + pattern = s[:m.start() + 1] + replacement = s[m.end():] + m = rex.search(replacement) + if m: + replacement = replacement[:m.start() + 1] + return pattern, replacement + + def __repr__(self): + return f"*({self._dump()})" class SetOperation(Operation): def process( @@ -864,6 +894,8 @@ class Scope(object): scope._append_operation(key, AddOperation(value)) elif operation == "*=": scope._append_operation(key, UniqueAddOperation(value)) + elif operation == "~=": + scope._append_operation(key, ReplaceOperation(value)) else: print(f'Unexpected operation "{operation}" in scope "{scope}".') assert False @@ -1250,7 +1282,7 @@ class QmakeParser: Values = add_element("Values", pp.ZeroOrMore(Value)("value")) Op = add_element( - "OP", pp.Literal("=") | pp.Literal("-=") | pp.Literal("+=") | pp.Literal("*=") + "OP", pp.Literal("=") | pp.Literal("-=") | pp.Literal("+=") | pp.Literal("*=") | pp.Literal("~=") ) Key = add_element("Key", Identifier) -- cgit v1.2.3 From 0bcd50b40a747329932fb2823e494575082cd150 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 20 Sep 2019 09:23:51 +0200 Subject: pro2cmake.py: generate code for WAYLAND*SOURCES WAYLANDCLIENTSOURCES and WAYLANDSERVERSOURCES is used heavily throughout QtWayland. Task-number: QTBUG-78177 Change-Id: I4507447054c435f78e82c2ca3e9404288db6c1f6 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8765c870f6..84dc344d2b 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1839,6 +1839,7 @@ def write_list( *, header: str = "", footer: str = "", + prefix: str = "", ): if not entries: return @@ -1853,7 +1854,7 @@ def write_list( cm_fh.write(f"{ind}{extra_indent}{cmake_parameter}\n") extra_indent += " " for s in sort_sources(entries): - cm_fh.write(f"{ind}{extra_indent}{s}\n") + cm_fh.write(f"{ind}{extra_indent}{prefix}{s}\n") if footer: cm_fh.write(f"{ind}{footer}\n") @@ -2545,6 +2546,32 @@ def write_android_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = cm_fh.write(f"{spaces(indent)}endif()\n") +def write_wayland_part(cm_fh: typing.IO[str], target: str, scope:Scope, indent: int = 0): + client_sources = scope.get_files('WAYLANDCLIENTSOURCES', use_vpath=True) + server_sources = scope.get_files('WAYLANDSERVERSOURCES', use_vpath=True) + if len(client_sources) == 0 and len(server_sources) == 0: + return + + condition = map_to_cmake_condition(scope.total_condition) + if condition != "ON": + cm_fh.write(f"\n{spaces(indent)}if({condition})\n") + indent += 1 + + if len(client_sources) != 0: + cm_fh.write(f"\n{spaces(indent)}qt6_generate_wayland_protocol_client_sources({target}\n") + write_list(cm_fh, client_sources, 'FILES', indent + 1, prefix='${CMAKE_CURRENT_SOURCE_DIR}/') + cm_fh.write(f"{spaces(indent)})\n") + + if len(server_sources) != 0: + cm_fh.write(f"\n{spaces(indent)}qt6_generate_wayland_protocol_server_sources({target}\n") + write_list(cm_fh, server_sources, 'FILES', indent + 1, prefix='${CMAKE_CURRENT_SOURCE_DIR}/') + cm_fh.write(f"{spaces(indent)})\n") + + if condition != "ON": + indent -= 1 + cm_fh.write(f"\n{spaces(indent)}endif()\n") + + def write_main_part( cm_fh: IO[str], name: str, @@ -2630,6 +2657,8 @@ def write_main_part( write_android_part(cm_fh, name, scopes[0], indent) + write_wayland_part(cm_fh, name, scopes[0], indent) + ignored_keys_report = write_ignored_keys(scopes[0], spaces(indent)) if ignored_keys_report: cm_fh.write(ignored_keys_report) @@ -2643,6 +2672,7 @@ def write_main_part( for c in scopes[1:]: c.reset_visited_keys() write_android_part(cm_fh, name, c, indent=indent) + write_wayland_part(cm_fh, name, c, indent=indent) write_extend_target(cm_fh, name, c, indent=indent) ignored_keys_report = write_ignored_keys(c, spaces(indent)) if ignored_keys_report: -- cgit v1.2.3 From 9f6e1abfdb8bf3f0ff9484f1ef16f0b93b23a3c9 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 23 Sep 2019 14:08:43 +0200 Subject: pro2cmake: Skip requires "skip build" message for subdir projects It makes too much noise when configuring. Change-Id: I0a65cb99d0dc9a6c0d288cf548035cca1bea57bc Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 84dc344d2b..edd1b90caf 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1700,7 +1700,7 @@ def handle_subdir( grouped_sub_dirs[condition_key] = sub_dir_list_by_key # Print any requires() blocks. - cm_fh.write(expand_project_requirements(scope)) + cm_fh.write(expand_project_requirements(scope, skip_message=True)) # Print the groups. ind = spaces(indent) -- cgit v1.2.3 From 1d5e8996b0551633674d5f68c5d103ea7b3b9587 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 24 Sep 2019 10:57:05 +0200 Subject: configurejson2cmake: Generate label for out-of-line tests Change-Id: Iaaf8d2f19269043dfad34d4ec32ec8163e9616e4 Reviewed-by: Oliver Wolff Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 33e37ded4b..ae7c0328a8 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -523,6 +523,7 @@ def parseTest(ctx, test, data, cm_fh): f""" if(EXISTS "${{CMAKE_CURRENT_SOURCE_DIR}}/{ctx['test_dir']}/{data['test']}/CMakeLists.txt") qt_config_compile_test("{data['test']}" + LABEL "{data['label']}" PROJECT_PATH "${{CMAKE_CURRENT_SOURCE_DIR}}/{ctx['test_dir']}/{data['test']}") endif() """ -- cgit v1.2.3 From 0441e3743e7d2d7e809903ad5dec9e72f5317dea Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 24 Sep 2019 10:57:54 +0200 Subject: pro2cmake: Don't generate GUI applications for config.tests On macOS the config.tests executable should not be a bundle, and on Windows it should be a subsystem console application. Change-Id: I2c8078cc9537df42683f3ff3bcce409402824555 Reviewed-by: Liang Qi Reviewed-by: Oliver Wolff Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 6 ------ 1 file changed, 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index edd1b90caf..f149898ae5 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3296,14 +3296,8 @@ def handle_config_test_project(scope: Scope, cm_fh: IO[str]): # Remove default QT libs. scope._append_operation("QT", RemoveOperation(["core", "gui"])) - config = scope.get("CONFIG") - gui = all(val not in config for val in ["console", "cmdline"]) - add_target = f"add_executable(${{PROJECT_NAME}}" - if gui: - add_target += " WIN32 MACOSX_BUNDLE" - temp_buffer = io.StringIO() write_all_source_file_lists(temp_buffer, scope, add_target, indent=0) buffer_value = temp_buffer.getvalue() -- cgit v1.2.3 From c3131f9f5e78eb661eddb8d536b25ee8ed5516fc Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 24 Sep 2019 11:56:52 +0200 Subject: pro2cmake: Fix call of write_statecharts for examples Change-Id: I5417a20deda07b96499ac4f2707e7234b0f4eb7b Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f149898ae5..a2bbce53f0 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2945,7 +2945,7 @@ def write_example( ) write_resources(cm_fh, binary_name, scope, indent=indent, is_example=True) - write_statecharts(cm_fh, binary_name, scope, indent=indent) + write_statecharts(cm_fh, binary_name, scope, indent=indent, is_example=True) if qmldir: write_qml_plugin_epilogue(cm_fh, binary_name, scope, qmldir, indent) -- cgit v1.2.3 From 6e2ca1d6cb12d37a00d0c61b1aac317df02ac786 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 24 Sep 2019 11:39:26 +0200 Subject: Add SKIP_TYPE_REGISTRATION option to add_qml_module If the qml files are not listed in the qmldir file they do not need to be written again, since it is expected that they are registered by the c++ plugin code. Change-Id: I624aedb182583f942bf1d1a322861c47a64b5065 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index a2bbce53f0..4a88bf95b8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2905,6 +2905,8 @@ def write_example( add_target += " DEPENDENCIES\n" for dep in qml_dir.depends: add_target += f" {dep[0]}/{dep[1]}\n" + if len(qml_dir.type_names) == 0: + add_target += " SKIP_TYPE_REGISTRATION\n" add_target += " INSTALL_LOCATION ${INSTALL_EXAMPLEDIR}\n)\n\n" add_target += f"target_sources({binary_name} PRIVATE" @@ -3056,6 +3058,8 @@ def write_qml_plugin( extra_lines.append("DEPENDENCIES") for dep in qml_dir.depends: extra_lines.append(f" {dep[0]}/{dep[1]}") + if len(qml_dir.type_names) == 0: + extra_lines.append("SKIP_TYPE_REGISTRATION") return qml_dir -- cgit v1.2.3 From 4b592ba90c4a9da3bd5707e70c9d0e25891bda25 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Mon, 23 Sep 2019 13:27:49 +0200 Subject: pro2cmake: Do not error out if $$files is called with a 2nd parameter Do not stop the whole conversion if we encounter $$files(..., true). Ignore the 2nd parameter for now. Change-Id: If9334ce9719b98c33716dc7f18ba8aede05fe8b1 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4a88bf95b8..bb5c23f8da 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -623,10 +623,6 @@ def handle_function_value(group: pp.ParseResults): return str(group) if function_name == "files": - if len(function_args) > 1: - 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): -- cgit v1.2.3 From eaf0b2709192aaf1671b5ed126082bc194dc7822 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 18 Sep 2019 15:04:52 +0200 Subject: CMake: Add missing library-mappings for QtWayland I haven't checked if the config for wayland-kms actually works, as that requires building for a target that supports it (RCar M3 or similar). Task-number: QTBUG-78177 Change-Id: Ib30a63a4e5feb955d17763c37e32ad3f2c13d358 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 011c277c11..1188a93d4d 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -310,6 +310,9 @@ _qt_library_map = [ LibraryMapping( "waylandclient", "Qt6", "Qt::WaylandClient", extra=["COMPONENTS", "WaylandClient"] ), + LibraryMapping( + "waylandcompositor", "Qt6", "Qt::WaylandCompositor", extra=["COMPONENTS", "WaylandCompositor"] + ), LibraryMapping("webchannel", "Qt6", "Qt::WebChannel", extra=["COMPONENTS", "WebChannel"]), LibraryMapping("webengine", "Qt6", "Qt::WebEngine", extra=["COMPONENTS", "WebEngine"]), LibraryMapping( @@ -411,6 +414,8 @@ _library_map = [ LibraryMapping("wayland-client", "Wayland", "Wayland::Client"), LibraryMapping("wayland-cursor", "Wayland", "Wayland::Cursor"), LibraryMapping("wayland-egl", "Wayland", "Wayland::Egl"), + LibraryMapping('wayland-kms', 'Waylandkms', 'PkgConfig::Waylandkms'), #TODO: check if this actually works + LibraryMapping("x11", "X11", "X11::X11"), LibraryMapping("x11sm", "X11", "${X11_SM_LIB} ${X11_ICE_LIB}", resultVariable="X11_SM"), LibraryMapping( "xcb", @@ -487,6 +492,7 @@ _library_map = [ "xcb_xkb", "XCB", "XCB::XKB", extra=["COMPONENTS", "XKB"], resultVariable="XCB_XKB" ), LibraryMapping("xcb_xlib", "X11_XCB", "X11::XCB"), + LibraryMapping('xcomposite', 'XComposite', 'PkgConfig::XComposite'), 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"]), -- cgit v1.2.3 From 724281d1aa4588fd37d38d650b608cec3379e185 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Tue, 24 Sep 2019 08:27:25 +0200 Subject: cmake: Add BlueZ to the list of libraries used by Qt (connectivity) Task-number: QTBUG-78181 Change-Id: I4eda42c4e42ccb28ebb64eb060d02a33c3af6b03 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 1188a93d4d..54b6f815a2 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -349,6 +349,7 @@ _qt_library_map = [ _library_map = [ # 3rd party: LibraryMapping("atspi", "ATSPI2", "PkgConfig::ATSPI2"), + LibraryMapping("bluez", "BlueZ", "PkgConfig::BlueZ"), LibraryMapping("corewlan", None, None), LibraryMapping("cups", "Cups", "Cups::Cups"), LibraryMapping("db2", "DB2", "DB2::DB2"), -- cgit v1.2.3 From 7bed351c6c19cb6cd2b1ef8cf2695e1a120ab2c3 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 25 Sep 2019 13:47:01 +0200 Subject: Fix run_pro2cmake only_qtbase_main_modules option There was a missing f prefix for the f-string, and thus no replacements were done. Change-Id: If778d4ce25905c302de22a76c27af00a63f3c515 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/run_pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index 16ad0534cd..cb7181449a 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -110,7 +110,7 @@ def find_all_pro_files(base_path: str, args: argparse.Namespace): "concurrent", "xml", ] - path_suffixes = ["src/{m}/{m}.pro" for m in main_modules] + path_suffixes = [f"src/{m}/{m}.pro" for m in main_modules] for path_suffix in path_suffixes: if path.endswith(path_suffix): -- cgit v1.2.3 From 316353b3e00a08cbf9bb93d2d94e9e9693d3b5a5 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 25 Sep 2019 13:48:26 +0200 Subject: pro2cmake: Handle one more hardcoded condition CONFIG(osx) We can't really handle this in a generic way now, because the values of CONFIG() can not be directly mapped to features. It might be possible in the future if we make sure that all configure.json entries that have output publicConfig or privateConfig also generate a feature. For now just hardcode the case. Change-Id: Ie6b82b87973deb84012c3620b41920b343ffb2da Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index bb5c23f8da..46cce9b78b 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1488,6 +1488,7 @@ def map_condition(condition: str) -> str: 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"CONFIG\(osx\)", r"APPLE_OSX", condition) def gcc_version_handler(match_obj: Match): operator = match_obj.group(1) -- cgit v1.2.3 From 6f5b1dd9ab257542c9f1d0be324968a487e9a77b Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 25 Sep 2019 14:16:57 +0200 Subject: pro2cmake: Handle WINDOWS_SDK_VERSION and simplifications Add custom code to handle WINDOWS_SDK_VERSION comparisons, similar to how it was done for QT_GCC_MAJOR_VERSION. Also fix simplify_condition to do mapping of conditions to unified tokens, similar how it was done for target conditions, so that the simplification process does not fail because of whitespace. Change-Id: Ia0cec6e1ffeed4c7859bb2a44423d0160222f425 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 46cce9b78b..b842e9fcc3 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1507,6 +1507,21 @@ def map_condition(condition: str) -> str: pattern = r"(equals|greaterThan|lessThan)\(QT_GCC_([A-Z]+)_VERSION,[ ]*([0-9]+)\)" condition = re.sub(pattern, gcc_version_handler, condition) + def windows_sdk_version_handler(match_obj: Match): + operator = match_obj.group(1) + if operator == "equals": + operator = "STREQUAL" + elif operator == "greaterThan": + operator = "STRGREATER" + elif operator == "lessThan": + operator = "STRLESS" + + version = match_obj.group(2) + return f"(QT_WINDOWS_SDK_VERSION {operator} {version})" + + pattern = r"(equals|greaterThan|lessThan)\(WINDOWS_SDK_VERSION,[ ]*([0-9]+)\)" + condition = re.sub(pattern, windows_sdk_version_handler, condition) + # Handle if(...) conditions. condition = unwrap_if(condition) @@ -2205,6 +2220,16 @@ def simplify_condition(condition: str) -> str: target_symbol_mapping[target_condition_symbol_name] = target_condition condition = re.sub(target_condition, target_condition_symbol_name, condition) + # Do similar token mapping for comparison operators. + pattern = re.compile(r"([a-zA-Z_0-9]+ (?:STRLESS|STREQUAL|STRGREATER) [a-zA-Z_0-9]+)") + comparison_symbol_mapping = {} + all_comparisons = re.findall(pattern, condition) + for comparison in all_comparisons: + # Replace spaces and colons with underscores. + comparison_symbol_name = re.sub("[ ]", "_", comparison) + comparison_symbol_mapping[comparison_symbol_name] = comparison + condition = re.sub(comparison, comparison_symbol_name, condition) + try: # Generate and simplify condition using sympy: condition_expr = simplify_logic(condition) @@ -2214,6 +2239,10 @@ def simplify_condition(condition: str) -> str: for symbol_name in target_symbol_mapping: condition = re.sub(symbol_name, target_symbol_mapping[symbol_name], condition) + # Restore comparisons. + for comparison in comparison_symbol_mapping: + condition = re.sub(comparison, comparison_symbol_mapping[comparison], condition) + # Map back to CMake syntax: condition = condition.replace("~", "NOT ") condition = condition.replace("&", "AND") -- cgit v1.2.3 From 309f96ccb6a1e3e1c235148e04497c82b3d61390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Tue, 24 Sep 2019 11:35:56 +0200 Subject: Add CMake support for directfb plug-in Change-Id: I126545e1da54018ce081b42a29e62ca30ee04d64 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 54b6f815a2..802b674316 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -352,6 +352,7 @@ _library_map = [ LibraryMapping("bluez", "BlueZ", "PkgConfig::BlueZ"), LibraryMapping("corewlan", None, None), LibraryMapping("cups", "Cups", "Cups::Cups"), + LibraryMapping("directfb", "DirectFB", "PkgConfig::DirectFB"), LibraryMapping("db2", "DB2", "DB2::DB2"), LibraryMapping("dbus", "WrapDBus1", "dbus-1", resultVariable="DBus1"), LibraryMapping("doubleconversion", None, None), -- cgit v1.2.3 From 3886828c1fc038a1f10924e5eb704cdc47c7d435 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 1 Oct 2019 08:13:24 +0200 Subject: pro2cmake: Add qtremoteobjects Change-Id: I6cb2ee0c83e0dd3bf830a063f5790e35cbf73285 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 802b674316..4ababe3117 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -281,6 +281,7 @@ _qt_library_map = [ ), LibraryMapping("quickwidgets", "Qt6", "Qt::QuickWidgets", extra=["COMPONENTS", "QuickWidgets"]), LibraryMapping("render", "Qt6", "Qt::3DRender", extra=["COMPONENTS", "3DRender"]), + LibraryMapping("remoteobjects", "Qt6", "Qt::RemoteObjects", extra=["COMPONENTS", "RemoteObjects"]), LibraryMapping("script", "Qt6", "Qt::Script", extra=["COMPONENTS", "Script"]), LibraryMapping("scripttools", "Qt6", "Qt::ScriptTools", extra=["COMPONENTS", "ScriptTools"]), LibraryMapping("scxml", "Qt6", "Qt::Scxml", extra=["COMPONENTS", "Scxml"]), -- cgit v1.2.3 From 447c868a5df9aac84ae39989d09bc2b704940698 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 27 Sep 2019 15:10:50 +0200 Subject: CMake: Generate wayland protocol code for examples as well Task-number: QTBUG-78177 Change-Id: Id1c49013244e5ba51b99c1538b314181efe786fb Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b842e9fcc3..a2ea6ce0bf 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2578,7 +2578,10 @@ def write_wayland_part(cm_fh: typing.IO[str], target: str, scope:Scope, indent: if len(client_sources) == 0 and len(server_sources) == 0: return - condition = map_to_cmake_condition(scope.total_condition) + condition = "ON" + if scope.total_condition: + condition = map_to_cmake_condition(scope.total_condition) + if condition != "ON": cm_fh.write(f"\n{spaces(indent)}if({condition})\n") indent += 1 @@ -2946,6 +2949,8 @@ def write_example( cm_fh.write(")\n") + write_wayland_part(cm_fh, binary_name, scope, indent=0) + write_include_paths( cm_fh, scope, f"target_include_directories({binary_name} PUBLIC", indent=0, footer=")" ) -- cgit v1.2.3 From 590213e531f3a14f9b5365a10122f815d5fd6e8f Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 30 Sep 2019 18:11:15 +0200 Subject: Move sympy condition simplification code into separate file Change-Id: I3f062bf939b452bb41b7a27508a83cbf93abff8c Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/condition_simplifier.py | 236 +++++++++++++++++++++++++++++++++ util/cmake/pro2cmake.py | 207 +---------------------------- util/cmake/tests/test_logic_mapping.py | 2 +- 3 files changed, 239 insertions(+), 206 deletions(-) create mode 100644 util/cmake/condition_simplifier.py (limited to 'util/cmake') diff --git a/util/cmake/condition_simplifier.py b/util/cmake/condition_simplifier.py new file mode 100644 index 0000000000..c67b78ffad --- /dev/null +++ b/util/cmake/condition_simplifier.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + + +import re + +from sympy import simplify_logic, And, Or, Not, SympifyError + + +def _iterate_expr_tree(expr, op, matches): + assert expr.func == op + keepers = () + for arg in expr.args: + if arg in matches: + matches = tuple(x for x in matches if x != arg) + elif arg == op: + (matches, extra_keepers) = _iterate_expr_tree(arg, op, matches) + keepers = (*keepers, *extra_keepers) + else: + keepers = (*keepers, arg) + return matches, keepers + + +def _simplify_expressions(expr, op, matches, replacement): + for arg in expr.args: + expr = expr.subs(arg, _simplify_expressions(arg, op, matches, replacement)) + + if expr.func == op: + (to_match, keepers) = tuple(_iterate_expr_tree(expr, op, matches)) + if len(to_match) == 0: + # build expression with keepers and replacement: + if keepers: + start = replacement + current_expr = None + last_expr = keepers[-1] + for repl_arg in keepers[:-1]: + current_expr = op(start, repl_arg) + start = current_expr + top_expr = op(start, last_expr) + else: + top_expr = replacement + + expr = expr.subs(expr, top_expr) + + return expr + + +def _simplify_flavors_in_condition(base: str, flavors, expr): + """ Simplify conditions based on the knowledge of which flavors + belong to which OS. """ + base_expr = simplify_logic(base) + 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) + return expr + + +def _simplify_os_families(expr, family_members, other_family_members): + for family in family_members: + for other in other_family_members: + if other in family_members: + continue # skip those in the sub-family + + f_expr = simplify_logic(family) + o_expr = simplify_logic(other) + + 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")) + return expr + + +def _recursive_simplify(expr): + """ 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", + ) + + 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) + # UNIX [AND foo ]AND WIN32 -> OFF [AND foo] + 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) + + # Simplify families of OSes against other families: + expr = _simplify_os_families(expr, ("WIN32", "WINRT"), unixes) + expr = _simplify_os_families(expr, androids, unixes) + expr = _simplify_os_families(expr, ("BSD", *bsds), unixes) + + for family in ("HAIKU", "QNX", "INTEGRITY", "LINUX", "VXWORKS"): + expr = _simplify_os_families(expr, (family,), unixes) + + # Now simplify further: + expr = simplify_logic(expr) + + while expr != input_expr: + input_expr = expr + expr = _recursive_simplify(expr) + + return expr + + +def simplify_condition(condition: str) -> str: + input_condition = condition.strip() + + # Map to sympy syntax: + condition = " " + input_condition + " " + condition = condition.replace("(", " ( ") + condition = condition.replace(")", " ) ") + + 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 ") + # Replace dashes with a token + 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. + # So a CMake condition like "TARGET Foo::Bar" fails the whole + # expression simplifying process. + # Turn these conditions into a single token so that SymPy can parse + # the expression, and thus simplify it. + # Do this by replacing and keeping a map of conditions to single + # token symbols. + # Support both target names without double colons, and with double + # colons. + 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_symbol_mapping[target_condition_symbol_name] = target_condition + condition = re.sub(target_condition, target_condition_symbol_name, condition) + + # Do similar token mapping for comparison operators. + pattern = re.compile(r"([a-zA-Z_0-9]+ (?:STRLESS|STREQUAL|STRGREATER) [a-zA-Z_0-9]+)") + comparison_symbol_mapping = {} + all_comparisons = re.findall(pattern, condition) + for comparison in all_comparisons: + # Replace spaces and colons with underscores. + comparison_symbol_name = re.sub("[ ]", "_", comparison) + comparison_symbol_mapping[comparison_symbol_name] = comparison + condition = re.sub(comparison, comparison_symbol_name, condition) + + try: + # Generate and simplify condition using sympy: + condition_expr = simplify_logic(condition) + condition = str(_recursive_simplify(condition_expr)) + + # Restore the target conditions. + for symbol_name in target_symbol_mapping: + condition = re.sub(symbol_name, target_symbol_mapping[symbol_name], condition) + + # Restore comparisons. + for comparison in comparison_symbol_mapping: + condition = re.sub(comparison, comparison_symbol_mapping[comparison], 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 (SympifyError, TypeError, AttributeError): + # sympy did not like our input, so leave this condition alone: + condition = input_condition + + return condition or "ON" diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index a2ea6ce0bf..96d9712bb2 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -39,6 +39,8 @@ import io import glob import collections +from condition_simplifier import simplify_condition + try: collectionsAbc = collections.abc except AttributeError: @@ -53,8 +55,6 @@ from textwrap import indent as textwrap_indent from itertools import chain from functools import lru_cache from shutil import copyfile -from sympy.logic import simplify_logic, And, Or, Not -from sympy.core.sympify import SympifyError from typing import ( List, Optional, @@ -2054,209 +2054,6 @@ def write_ignored_keys(scope: Scope, indent: str) -> str: return result -def _iterate_expr_tree(expr, op, matches): - assert expr.func == op - keepers = () - for arg in expr.args: - if arg in matches: - matches = tuple(x for x in matches if x != arg) - elif arg == op: - (matches, extra_keepers) = _iterate_expr_tree(arg, op, matches) - keepers = (*keepers, *extra_keepers) - else: - keepers = (*keepers, arg) - return matches, keepers - - -def _simplify_expressions(expr, op, matches, replacement): - for arg in expr.args: - expr = expr.subs(arg, _simplify_expressions(arg, op, matches, replacement)) - - if expr.func == op: - (to_match, keepers) = tuple(_iterate_expr_tree(expr, op, matches)) - if len(to_match) == 0: - # build expression with keepers and replacement: - if keepers: - start = replacement - current_expr = None - last_expr = keepers[-1] - for repl_arg in keepers[:-1]: - current_expr = op(start, repl_arg) - start = current_expr - top_expr = op(start, last_expr) - else: - top_expr = replacement - - expr = expr.subs(expr, top_expr) - - return expr - - -def _simplify_flavors_in_condition(base: str, flavors, expr): - """ Simplify conditions based on the knownledge of which flavors - belong to which OS. """ - base_expr = simplify_logic(base) - 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) - return expr - - -def _simplify_os_families(expr, family_members, other_family_members): - for family in family_members: - for other in other_family_members: - if other in family_members: - continue # skip those in the sub-family - - f_expr = simplify_logic(family) - o_expr = simplify_logic(other) - - 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")) - return expr - - -def _recursive_simplify(expr): - """ 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", - ) - - 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) - # UNIX [AND foo ]AND WIN32 -> OFF [AND foo] - 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) - - # Simplify families of OSes against other families: - expr = _simplify_os_families(expr, ("WIN32", "WINRT"), unixes) - expr = _simplify_os_families(expr, androids, unixes) - expr = _simplify_os_families(expr, ("BSD", *bsds), unixes) - - for family in ("HAIKU", "QNX", "INTEGRITY", "LINUX", "VXWORKS"): - expr = _simplify_os_families(expr, (family,), unixes) - - # Now simplify further: - expr = simplify_logic(expr) - - while expr != input_expr: - input_expr = expr - expr = _recursive_simplify(expr) - - return expr - - -def simplify_condition(condition: str) -> str: - input_condition = condition.strip() - - # Map to sympy syntax: - condition = " " + input_condition + " " - condition = condition.replace("(", " ( ") - condition = condition.replace(")", " ) ") - - 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 ") - # Replace dashes with a token - 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. - # So a CMake condition like "TARGET Foo::Bar" fails the whole - # expression simplifying process. - # Turn these conditions into a single token so that SymPy can parse - # the expression, and thus simplify it. - # Do this by replacing and keeping a map of conditions to single - # token symbols. - # Support both target names without double colons, and with double - # colons. - 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_symbol_mapping[target_condition_symbol_name] = target_condition - condition = re.sub(target_condition, target_condition_symbol_name, condition) - - # Do similar token mapping for comparison operators. - pattern = re.compile(r"([a-zA-Z_0-9]+ (?:STRLESS|STREQUAL|STRGREATER) [a-zA-Z_0-9]+)") - comparison_symbol_mapping = {} - all_comparisons = re.findall(pattern, condition) - for comparison in all_comparisons: - # Replace spaces and colons with underscores. - comparison_symbol_name = re.sub("[ ]", "_", comparison) - comparison_symbol_mapping[comparison_symbol_name] = comparison - condition = re.sub(comparison, comparison_symbol_name, condition) - - try: - # Generate and simplify condition using sympy: - condition_expr = simplify_logic(condition) - condition = str(_recursive_simplify(condition_expr)) - - # Restore the target conditions. - for symbol_name in target_symbol_mapping: - condition = re.sub(symbol_name, target_symbol_mapping[symbol_name], condition) - - # Restore comparisons. - for comparison in comparison_symbol_mapping: - condition = re.sub(comparison, comparison_symbol_mapping[comparison], 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 (SympifyError, TypeError, AttributeError): - # sympy did not like our input, so leave this condition alone: - condition = input_condition - - return condition or "ON" - - def recursive_evaluate_scope( scope: Scope, parent_condition: str = "", previous_condition: str = "" ) -> str: diff --git a/util/cmake/tests/test_logic_mapping.py b/util/cmake/tests/test_logic_mapping.py index c477aa8351..c18c3ddc65 100755 --- a/util/cmake/tests/test_logic_mapping.py +++ b/util/cmake/tests/test_logic_mapping.py @@ -27,7 +27,7 @@ ## ############################################################################# -from pro2cmake import simplify_condition +from condition_simplifier import simplify_condition def validate_simplify(input: str, expected: str) -> None: -- cgit v1.2.3 From f23b7e1476ded2be43d5e3f03195f522d307b163 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 30 Sep 2019 18:16:20 +0200 Subject: Implement persistent caching for simplify_condition Store the results of simplify_condition in a json cache file, next to the pro2cmake.py script. Dramatically speeds up re-conversion of projects. The cache is cleared whenever the content of the condition_simplifier.py file changes, effectively presuming that the condition computing code has changed. Sample times. Initial corelib.pro conversion - 142.13 seconds. Next re-conversion with hot cache - 1.17 seconds. Change-Id: I8790b2736358236e4b23bcb5f10f45a36fdfa426 Reviewed-by: Tobias Hunger Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/condition_simplifier.py | 3 +- util/cmake/condition_simplifier_cache.py | 113 +++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 util/cmake/condition_simplifier_cache.py (limited to 'util/cmake') diff --git a/util/cmake/condition_simplifier.py b/util/cmake/condition_simplifier.py index c67b78ffad..de329be1d4 100644 --- a/util/cmake/condition_simplifier.py +++ b/util/cmake/condition_simplifier.py @@ -29,8 +29,8 @@ import re - from sympy import simplify_logic, And, Or, Not, SympifyError +from condition_simplifier_cache import simplify_condition_memoize def _iterate_expr_tree(expr, op, matches): @@ -160,6 +160,7 @@ def _recursive_simplify(expr): return expr +@simplify_condition_memoize def simplify_condition(condition: str) -> str: input_condition = condition.strip() diff --git a/util/cmake/condition_simplifier_cache.py b/util/cmake/condition_simplifier_cache.py new file mode 100644 index 0000000000..b405ef23f8 --- /dev/null +++ b/util/cmake/condition_simplifier_cache.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + + +import atexit +import hashlib +import json +import os +import sys +import time + +from typing import Callable + + +def get_current_file_path() -> str: + try: + this_file = __file__ + except NameError: + this_file = sys.argv[0] + this_file = os.path.abspath(this_file) + return this_file + + +def get_cache_location() -> str: + this_file = get_current_file_path() + dir_path = os.path.dirname(this_file) + cache_path = os.path.join(dir_path, ".pro2cmake_cache", "cache.json") + return cache_path + + +def get_file_checksum(file_path: str) -> str: + try: + with open(file_path, "r") as content_file: + content = content_file.read() + except IOError: + content = time.time() + checksum = hashlib.md5(content.encode("utf-8")).hexdigest() + return checksum + + +def get_condition_simplifier_checksum() -> str: + current_file_path = get_current_file_path() + dir_name = os.path.dirname(current_file_path) + condition_simplifier_path = os.path.join(dir_name, "condition_simplifier.py") + return get_file_checksum(condition_simplifier_path) + + +def init_cache_dict(): + return { + "checksum": get_condition_simplifier_checksum(), + "schema_version": "1", + "cache": {"conditions": {}}, + } + + +def simplify_condition_memoize(f: Callable[[str], str]): + cache_path = get_cache_location() + cache_file_content = None + + if os.path.exists(cache_path): + try: + with open(cache_path, "r") as cache_file: + cache_file_content = json.load(cache_file) + except (IOError, ValueError): + pass + + if not cache_file_content: + cache_file_content = init_cache_dict() + + current_checksum = get_condition_simplifier_checksum() + if cache_file_content["checksum"] != current_checksum: + cache_file_content = init_cache_dict() + + def update_cache_file(): + if not os.path.exists(cache_path): + os.makedirs(os.path.dirname(cache_path), exist_ok=True) + with open(cache_path, "w") as cache_file_write_handle: + json.dump(cache_file_content, cache_file_write_handle, indent=4) + + atexit.register(update_cache_file) + + def helper(condition: str) -> str: + if condition not in cache_file_content["cache"]["conditions"]: + cache_file_content["cache"]["conditions"][condition] = f(condition) + return cache_file_content["cache"]["conditions"][condition] + + return helper -- cgit v1.2.3 From 1b0710a39380c7f75bba2d777e137f247fcbd5b7 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 30 Sep 2019 17:02:08 +0200 Subject: pro2cmake: Handle SOURCES subtractions SOURCES -= foo.cpp statements should now be handled correctly by the script. The script uses the same principle as for subdir handling: go through all scopes to collect source removals, and then generate new scopes with total conditions that take into account both additions and subtractions and their conditions. Should handle NO_PCH_SOURCES case as well. Tested on gui.pro, network.pro and qtconnectivity bluetooth.pro. Change-Id: I0cc913ef64cecf09e8ada4e6a3ce3ccb4365b44e Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 96d9712bb2..b10df35ffb 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -68,6 +68,7 @@ from typing import ( FrozenSet, Tuple, Match, + Type, ) from special_case_helper import SpecialCaseHandler from helper import ( @@ -1886,6 +1887,12 @@ def write_source_file_list( for key in keys: sources += scope.get_files(key, use_vpath=True) + # Remove duplicates, like in the case when NO_PCH_SOURCES ends up + # adding the file to SOURCES, but SOURCES might have already + # contained it before. Preserves order in Python 3.7+ because + # dict keys are ordered. + sources = list(dict.fromkeys(sources)) + write_list(cm_fh, sources, cmake_parameter, indent, header=header, footer=footer) @@ -2398,6 +2405,144 @@ def write_wayland_part(cm_fh: typing.IO[str], target: str, scope:Scope, indent: cm_fh.write(f"\n{spaces(indent)}endif()\n") +def handle_source_subtractions(scopes: List[Scope]): + """ + Handles source subtractions like SOURCES -= painting/qdrawhelper.cpp + by creating a new scope with a new condition containing all addition + and subtraction conditions. + + Algorithm is as follows: + - Go through each scope and find files in SOURCES starting with "-" + - Save that file and the scope condition in modified_sources dict. + - Remove the file from the found scope (optionally remove the + NO_PCH_SOURCES entry for that file as well). + - Go through each file in modified_sources dict. + - Find scopes where the file is added, remove the file from that + scope and save the condition. + - Create a new scope just for that file with a new simplified + condition that takes all the other conditions into account. + """ + + def remove_file_from_operation( + scope: Scope, ops_key: str, file: str, op_type: Type[Operation] + ) -> bool: + """ + Remove a source file from an operation in a scope. + Example: remove foo.cpp from any operations that have + ops_key="SOURCES" in "scope", where the operation is of + type "op_type". + + The implementation is very rudimentary and might not work in + all cases. + + Returns True if a file was found and removed in any operation. + """ + file_removed = False + ops = scope._operations.get(ops_key, list()) + for op in ops: + if not isinstance(op, op_type): + continue + if file in op._value: + op._value.remove(file) + file_removed = True + for include_child_scope in scope._included_children: + file_removed = file_removed or remove_file_from_operation( + include_child_scope, ops_key, file, op_type + ) + return file_removed + + def join_all_conditions(set_of_alternatives: Set[str]): + final_str = "" + if set_of_alternatives: + alternatives = [f"({alternative})" for alternative in set_of_alternatives] + final_str = " OR ".join(sorted(alternatives)) + return final_str + + modified_sources: Dict[str, Dict[str, Union[Set[str], bool]]] = {} + + new_scopes = [] + top_most_scope = scopes[0] + + for scope in scopes: + sources = scope.get_files("SOURCES") + for file in sources: + # Find subtractions. + if file.startswith("-"): + file_without_minus = file[1:] + + if file_without_minus not in modified_sources: + modified_sources[file_without_minus] = {} + + subtractions = modified_sources[file_without_minus].get("subtractions", set()) + + # Add the condition to the set of conditions and remove + # the file subtraction from the processed scope, which + # will be later re-added in a new scope. + if scope.condition: + subtractions.add(scope.total_condition) + remove_file_from_operation(scope, "SOURCES", file_without_minus, RemoveOperation) + if subtractions: + modified_sources[file_without_minus]["subtractions"] = subtractions + + # In case if the source is also listed in a + # NO_PCH_SOURCES operation, remove it from there as + # well, and add it back later. + no_pch_source_removed = remove_file_from_operation( + scope, "NO_PCH_SOURCES", file_without_minus, AddOperation + ) + if no_pch_source_removed: + modified_sources[file_without_minus]["add_to_no_pch_sources"] = True + + for modified_source in modified_sources: + additions = modified_sources[modified_source].get("additions", set()) + subtractions = modified_sources[modified_source].get("subtractions", set()) + add_to_no_pch_sources = modified_sources[modified_source].get( + "add_to_no_pch_sources", False + ) + + for scope in scopes: + sources = scope.get_files("SOURCES") + if modified_source in sources: + # Remove the source file from any addition operations + # that mention it. + remove_file_from_operation(scope, "SOURCES", modified_source, AddOperation) + if scope.total_condition: + additions.add(scope.total_condition) + + # Construct a condition that takes into account all addition + # and subtraction conditions. + addition_str = join_all_conditions(additions) + if addition_str: + addition_str = f"({addition_str})" + subtraction_str = join_all_conditions(subtractions) + if subtraction_str: + subtraction_str = f"NOT ({subtraction_str})" + + condition_str = addition_str + if condition_str and subtraction_str: + condition_str += " AND " + condition_str += subtraction_str + condition_simplified = simplify_condition(condition_str) + + # Create a new scope with that condition and add the source + # operations. + new_scope = Scope( + parent_scope=top_most_scope, + file=top_most_scope.file, + condition=condition_simplified, + base_dir=top_most_scope.basedir, + ) + new_scope.total_condition = condition_simplified + new_scope._append_operation("SOURCES", AddOperation([modified_source])) + if add_to_no_pch_sources: + new_scope._append_operation("NO_PCH_SOURCES", AddOperation([modified_source])) + + new_scopes.append(new_scope) + + # Add all the newly created scopes. + scopes += new_scopes + + def write_main_part( cm_fh: IO[str], name: str, @@ -2424,6 +2569,12 @@ def write_main_part( # Merge scopes based on their conditions: scopes = merge_scopes(scopes) + # Handle SOURCES -= foo calls, and merge scopes one more time + # because there might have been several files removed with the same + # scope condition. + handle_source_subtractions(scopes) + scopes = merge_scopes(scopes) + assert len(scopes) assert scopes[0].total_condition == "ON" -- cgit v1.2.3 From c718213548019d0c48c80b538ca878e6bfe7aaa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Thu, 3 Oct 2019 10:56:32 +0200 Subject: Make SDL2 library-mapping use a wrapper Since at least one system provides config files without an included target. Change-Id: If1f336aab4cec9704412349d7951849ee8c3dabb Reviewed-by: Liang Qi Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 4ababe3117..4c6b811133 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -506,7 +506,7 @@ _library_map = [ LibraryMapping("tiff", "TIFF", "TIFF::TIFF"), LibraryMapping("webp", "WrapWebP", "WrapWebP::WrapWebP"), LibraryMapping("jasper", "WrapJasper", "WrapJasper::WrapJasper"), - LibraryMapping("sdl2", "SDL2", "SDL2::SDL2"), + LibraryMapping("sdl2", "WrapSDL2", "WrapSDL2::WrapSDL2"), ] -- cgit v1.2.3 From a98f64af91f8e96d2efe2b57d64f9fc41e2006b4 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 3 Oct 2019 13:24:51 +0200 Subject: cmake: add more tests in corelib/thread This fixes the qatomicinteger magic by making $$basename work for one particular case. qthreadstorage still needs investigation. Task-number: QTBUG-78221 Change-Id: I7bb38f6ca24273bcf0443ab25685c8e815814c3c Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b10df35ffb..5ef5d85f85 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -626,6 +626,14 @@ def handle_function_value(group: pp.ParseResults): if function_name == "files": return str(function_args[0]) + if function_name == "basename": + if len(function_args) != 1: + print(f"XXXX basename with more than one argument") + if function_args[0] == '_PRO_FILE_PWD_': + return os.path.basename(os.getcwd()) + print(f"XXXX basename with value other than _PRO_FILE_PWD_") + return os.path.basename(str(function_args[0])) + if isinstance(function_args, pp.ParseResults): function_args = list(flatten_list(function_args.asList())) -- cgit v1.2.3 From 9e5e98047e55a66e97a9b90997e1bfacb7e835ac Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 20 Sep 2019 13:37:36 +0200 Subject: Add expansion for QQC2_SOURCE_TREE Test in QtQuickControls2 rely on this variable, which basically points to the root of the project. We can't use PROJECT_SOURCE_DIR since it will vary depending on whether we build tests as stanadlone or part of the build. Change-Id: Ic59a79af2b4b62611d6c32391c936e98e469dea0 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5ef5d85f85..1ddbc96675 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1068,6 +1068,12 @@ class Scope(object): if not is_same_path: relative_path = os.path.relpath(self.currentdir, self.basedir) + if key == "QQC2_SOURCE_TREE": + qmake_conf_path = find_qmake_conf(os.path.abspath(self.currentdir)) + qmake_conf_dir_path = os.path.dirname(qmake_conf_path) + project_relative_path = os.path.relpath(qmake_conf_dir_path, self.currentdir) + return ['${CMAKE_CURRENT_SOURCE_DIR}/'+ project_relative_path] + if key == "_PRO_FILE_PWD_": return ["${CMAKE_CURRENT_SOURCE_DIR}"] if key == "PWD": -- cgit v1.2.3 From 7bca6edc1679813e353e386235c8f88e5af622fd Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 4 Oct 2019 10:18:22 +0200 Subject: cmake: add tests in corelib/serialization The DEFINES -= QT_NO_LINKED_LIST is no longer needed, so just remove it from .pro and CMakeLists.txt. Handle $$[QT_INSTALL_TESTS] and $$TARGET in target.path. When we have DESTDIR set, do not use target.path for OUTPUT_DIRECTORY. Fixes: QTBUG-78219 Change-Id: I5161a955b25ff2f3b35428dc4b935a7c9d2d425b Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor Reviewed-by: Leander Beernaert --- util/cmake/pro2cmake.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1ddbc96675..3fce84b34d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2796,7 +2796,10 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int target_path = scope.get_string("target.path") if target_path: target_path = target_path.replace("$$[QT_INSTALL_EXAMPLES]", "${INSTALL_EXAMPLESDIR}") - extra.append(f'OUTPUT_DIRECTORY "{target_path}"') + target_path = target_path.replace("$$[QT_INSTALL_TESTS]", "${INSTALL_TESTSDIR}") + target_path = target_path.replace("$$TARGET", scope.TARGET) + if not scope.get("DESTDIR"): + extra.append(f'OUTPUT_DIRECTORY "{target_path}"') if "target" in scope.get("INSTALLS"): extra.append(f'INSTALL_DIRECTORY "{target_path}"') -- cgit v1.2.3 From 123ef6c391209b84a245abb536117b1f6a04a860 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 4 Oct 2019 16:47:56 +0200 Subject: CMake: allow $$[QT_INSTALL_PREFIX] This is used in QtPurchasing. Change-Id: I29c3a87d844eeaf3fd1b64057e33d64054f14a89 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3fce84b34d..d32014a8c5 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2627,6 +2627,7 @@ def write_main_part( if destdir: if destdir.startswith("./") or destdir.startswith("../"): destdir = f"${{CMAKE_CURRENT_BINARY_DIR}}/{destdir}" + destdir = destdir.replace("$$[QT_INSTALL_PREFIX]", "${INSTALL_DIRECTORY}") extra_lines.append(f'OUTPUT_DIRECTORY "{destdir}"') cm_fh.write(f"{spaces(indent)}{cmake_function}({name}\n") @@ -2795,6 +2796,7 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int target_path = scope.get_string("target.path") if target_path: + target_path = target_path.replace("$$[QT_INSTALL_PREFIX]", "${INSTALL_DIRECTORY}") target_path = target_path.replace("$$[QT_INSTALL_EXAMPLES]", "${INSTALL_EXAMPLESDIR}") target_path = target_path.replace("$$[QT_INSTALL_TESTS]", "${INSTALL_TESTSDIR}") target_path = target_path.replace("$$TARGET", scope.TARGET) -- cgit v1.2.3 From ed35e1ac845cfa1e4f3e46983f0d4c39d9ea0a4f Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 4 Oct 2019 17:02:53 +0200 Subject: CMake: clean up target.path and DESTDIR replacements Change-Id: I0891008cff939aa98b39a28a23710add0781901e Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index d32014a8c5..91b60f710d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1592,6 +1592,24 @@ def map_condition(condition: str) -> str: return cmake_condition.strip() +_path_replacements = { + "$$[QT_INSTALL_PREFIX]": "${INSTALL_DIRECTORY}", + "$$[QT_INSTALL_EXAMPLES]": "${INSTALL_EXAMPLESDIR}", + "$$[QT_INSTALL_TESTS]": "${INSTALL_TESTSDIR}", + } + +def replace_path_constants(path: str, scope: Scope) -> str: + """ Clean up DESTDIR and target.path """ + if path.startswith("./"): + path = f"${{CMAKE_CURRENT_BINARY_DIR}}/{path[2:]}" + elif path.startswith("../"): + path = f"${{CMAKE_CURRENT_BINARY_DIR}}/{path}" + for original, replacement in _path_replacements: + path = path.replace(original, replacement) + path = path.replace("$$TARGET", scope.TARGET) + return path + + def handle_subdir( scope: Scope, cm_fh: IO[str], *, indent: int = 0, is_example: bool = False ) -> None: @@ -2625,9 +2643,7 @@ def write_main_part( # Check for DESTDIR override destdir = scope.get_string("DESTDIR") if destdir: - if destdir.startswith("./") or destdir.startswith("../"): - destdir = f"${{CMAKE_CURRENT_BINARY_DIR}}/{destdir}" - destdir = destdir.replace("$$[QT_INSTALL_PREFIX]", "${INSTALL_DIRECTORY}") + destdir = replace_path_constants(destdir, scope) extra_lines.append(f'OUTPUT_DIRECTORY "{destdir}"') cm_fh.write(f"{spaces(indent)}{cmake_function}({name}\n") @@ -2796,10 +2812,7 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int target_path = scope.get_string("target.path") if target_path: - target_path = target_path.replace("$$[QT_INSTALL_PREFIX]", "${INSTALL_DIRECTORY}") - target_path = target_path.replace("$$[QT_INSTALL_EXAMPLES]", "${INSTALL_EXAMPLESDIR}") - target_path = target_path.replace("$$[QT_INSTALL_TESTS]", "${INSTALL_TESTSDIR}") - target_path = target_path.replace("$$TARGET", scope.TARGET) + target_path = replace_path_constants(target_path, scope) if not scope.get("DESTDIR"): extra.append(f'OUTPUT_DIRECTORY "{target_path}"') if "target" in scope.get("INSTALLS"): -- cgit v1.2.3 From f55565b77aeec6b166f1656d20094a624f48ea9d Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 7 Oct 2019 11:04:13 +0200 Subject: Fix pro2cmake.py path replacement I forgot to add .items() in ed35e1ac845cfa1e4f3e46983f0d4c39d9ea0a4f. Change-Id: Ic46134daffa8f1a1b5b59040c8bcca76d32dbb35 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 91b60f710d..7698876553 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1604,7 +1604,7 @@ def replace_path_constants(path: str, scope: Scope) -> str: path = f"${{CMAKE_CURRENT_BINARY_DIR}}/{path[2:]}" elif path.startswith("../"): path = f"${{CMAKE_CURRENT_BINARY_DIR}}/{path}" - for original, replacement in _path_replacements: + for original, replacement in _path_replacements.items(): path = path.replace(original, replacement) path = path.replace("$$TARGET", scope.TARGET) return path -- cgit v1.2.3 From b86630b4b0c2fc76c49baed205b3bbfc0fa612cb Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 7 Oct 2019 15:28:05 +0200 Subject: pro2cmake: Allow disabling condition cache New option --skip-condition-cache allows forcing recomputation of condition simplification. Useful when debugging certain condition mappings. Change-Id: I68a85c2e4085ad7a3043d7334db71a334a6469e9 Reviewed-by: Simon Hausmann --- util/cmake/condition_simplifier_cache.py | 9 ++++++++- util/cmake/pro2cmake.py | 12 +++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/condition_simplifier_cache.py b/util/cmake/condition_simplifier_cache.py index b405ef23f8..d1efff1d87 100644 --- a/util/cmake/condition_simplifier_cache.py +++ b/util/cmake/condition_simplifier_cache.py @@ -37,6 +37,13 @@ import time from typing import Callable +condition_simplifier_cache_enabled = True + + +def set_condition_simplified_cache_enabled(value: bool): + global condition_simplifier_cache_enabled + condition_simplifier_cache_enabled = value + def get_current_file_path() -> str: try: @@ -106,7 +113,7 @@ def simplify_condition_memoize(f: Callable[[str], str]): atexit.register(update_cache_file) def helper(condition: str) -> str: - if condition not in cache_file_content["cache"]["conditions"]: + if condition not in cache_file_content["cache"]["conditions"] or not condition_simplifier_cache_enabled: cache_file_content["cache"]["conditions"][condition] = f(condition) return cache_file_content["cache"]["conditions"][condition] diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7698876553..042d8022f1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -40,6 +40,7 @@ import glob import collections from condition_simplifier import simplify_condition +from condition_simplifier_cache import set_condition_simplified_cache_enabled try: collectionsAbc = collections.abc @@ -155,6 +156,14 @@ def _parse_commandline(): help="Don't automatically remove CMakeLists.gen.txt and other " "intermediate files.", ) + parser.add_argument( + "-e", + "--skip-condition-cache", + dest="skip_condition_cache", + action="store_true", + help="Don't use condition simplifier cache (conversion speed may decrease).", + ) + parser.add_argument( "files", metavar="<.pro/.pri file>", @@ -162,7 +171,6 @@ def _parse_commandline(): nargs="+", help="The .pro/.pri file to process", ) - return parser.parse_args() @@ -3460,6 +3468,8 @@ def main() -> None: args = _parse_commandline() debug_parsing = args.debug_parser or args.debug + if args.skip_condition_cache: + set_condition_simplified_cache_enabled(False) backup_current_dir = os.getcwd() -- cgit v1.2.3 From dca364ed1999ff1d4d9aa08154802963445f6508 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 7 Oct 2019 16:18:04 +0200 Subject: pro2cmake: Fix QT_BUILD_TREE variable mapping CMake doesn't have a PROJECT_BUILD_DIR variable, but it does have a PROJECT_BINARY_DIR variable. Note that this might still be the wrong thing to do according to 35a30c7ebbd50e8b5b1fad7c00bd6c36b0dca8b9 , but it's still somwehat better. Change-Id: I493f82a791d933a16011d91774aaac4bcaffc5e5 Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 042d8022f1..e55f703930 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -797,7 +797,7 @@ class Scope(object): if operations is None: operations = { "QT_SOURCE_TREE": [SetOperation(["${QT_SOURCE_TREE}"])], - "QT_BUILD_TREE": [SetOperation(["${PROJECT_BUILD_DIR}"])], + "QT_BUILD_TREE": [SetOperation(["${PROJECT_BINARY_DIR}"])], } self._operations = copy.deepcopy(operations) -- cgit v1.2.3 From 887a56f8cc05902f89ea2c02526004cc17e9031b Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 7 Oct 2019 16:56:29 +0200 Subject: pro2cmake: Handle parentheses in if() scopes better The unwrap_if handler just removed the if keyword from a condition expression, and return the rest of the condition as-is. This proves not be enough, because map_condition splits on spaces and tries to map platform keywords, which would fail for values like "unix)". Change unwrap_if to add additional space between the values in the parentheses. Change-Id: I769ab430363d008a9fd91eaba014c88bd5ee43bd Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e55f703930..fec4eed19d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1491,7 +1491,20 @@ def parseProFile(file: str, *, debug=False): def unwrap_if(input_string): # Compute the grammar only once. if not hasattr(unwrap_if, "if_grammar"): + def handle_expr_with_parentheses(s, l, t): + # The following expression unwraps the condition via the + # additional info set by originalTextFor, thus returning the + # condition without parentheses. + condition_without_parentheses = s[t._original_start + 1 : t._original_end - 1] + + # Re-add the parentheses, but with spaces in-between. This + # fixes map_condition -> map_platform to apply properly. + condition_with_parentheses = "( " + condition_without_parentheses + " )" + return condition_with_parentheses + expr_with_parentheses = pp.originalTextFor(pp.nestedExpr()) + expr_with_parentheses.setParseAction(handle_expr_with_parentheses) + if_keyword = pp.Suppress(pp.Keyword("if")) unwrap_if.if_grammar = if_keyword + expr_with_parentheses -- cgit v1.2.3 From d56e627427b9707caa64e10d1214b0adacbc970b Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 7 Oct 2019 15:58:44 +0200 Subject: pro2cmake: Don't add default Qt libraries to bootstrapped tools Change-Id: I3ccc20ed5fcd2eaceb5af09df4a8bf7ccb2d525f Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index fec4eed19d..64ca5d78d4 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2765,7 +2765,13 @@ def write_module(cm_fh: 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 [] + if "force_bootstrap" in scope.get("CONFIG"): + extra = ["BOOTSTRAP"] + + # Remove default QT libs. + scope._append_operation("QT", RemoveOperation(["core", "gui"])) + else: + extra = [] write_main_part( cm_fh, -- cgit v1.2.3 From 361a24ae1c3676f971a66d9e91715c07bc7bf5ed Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 8 Oct 2019 11:19:20 +0200 Subject: run_pro2cmake: Fix the file preference sorter When invoking the script with "." as the only argument, and there are multiple .pro files in the current directory, the script would not choose the correct preferred .pro file (the one that has the same name as the current directory). Fix the sorter to handle such a case. Change-Id: I6df70d7c577649f8854bca9b8879f13bdb1b4d26 Reviewed-by: Simon Hausmann --- util/cmake/run_pro2cmake.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index cb7181449a..0db9f2a607 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -77,7 +77,9 @@ def find_all_pro_files(base_path: str, args: argparse.Namespace): """ Sorter that tries to prioritize main pro files in a directory. """ 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 == ".": + dir_name = os.path.basename(os.getcwd()) + if dir_name.endswith(pro_file_without_suffix): return dir_name return dir_name + "/__" + pro_file -- cgit v1.2.3 From a54649248107825da45f32edfdc3e9e48418e7ac Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 8 Oct 2019 11:57:27 +0200 Subject: pro2cmake: Allow skipping regeneration of a project via inline marker If a CMakeLists.txt file has the special marker # special case skip regeneration then pro2cmake will skip the convertion of the .pro file that is present in the same folder as the CMakeLists.txt file. The --ignore-skip-marker can be used to force conversion of such a project file. Change-Id: I73aae742d1843148b5c22217cb3fc6b0f8e87b82 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 64ca5d78d4..dee9da5b56 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -164,6 +164,14 @@ def _parse_commandline(): help="Don't use condition simplifier cache (conversion speed may decrease).", ) + parser.add_argument( + "-i", + "--ignore-skip-marker", + dest="ignore_skip_marker", + action="store_true", + help="If set, pro file will be converted even if skip marker is found in CMakeLists.txt.", + ) + parser.add_argument( "files", metavar="<.pro/.pri file>", @@ -3446,7 +3454,22 @@ def copy_generated_file_to_final_location(scope: Scope, keep_temporary_files=Fal os.remove(scope.generated_cmake_lists_path) -def should_convert_project(project_file_path: str = "") -> bool: +def cmake_project_has_skip_marker(project_file_path: str = "") -> bool: + dir_path = os.path.dirname(project_file_path) + cmake_project_path = os.path.join(dir_path, "CMakeLists.txt") + if not os.path.exists(cmake_project_path): + return False + + with open(cmake_project_path, "r") as file_fd: + contents = file_fd.read() + + if "# special case skip regeneration" in contents: + return True + + return False + + +def should_convert_project(project_file_path: str = "", ignore_skip_marker: bool = False) -> bool: qmake_conf_path = find_qmake_conf(project_file_path) qmake_conf_dir_path = os.path.dirname(qmake_conf_path) @@ -3477,6 +3500,11 @@ def should_convert_project(project_file_path: str = "") -> bool: if skip_certain_tests: return False + # Skip if CMakeLists.txt in the same path as project_file_path has a + # special skip marker. + if not ignore_skip_marker and cmake_project_has_skip_marker(project_file_path): + return False + return True @@ -3499,7 +3527,7 @@ def main() -> None: os.chdir(new_current_dir) project_file_absolute_path = os.path.abspath(file_relative_path) - if not should_convert_project(project_file_absolute_path): + if not should_convert_project(project_file_absolute_path, args.ignore_skip_marker): print(f'Skipping conversion of project: "{project_file_absolute_path}"') continue -- cgit v1.2.3 From 5260c1d62a43538f6d1552e0cc409216c89786bd Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 8 Oct 2019 15:35:19 +0200 Subject: Update add_qml_module() to use INSTALL_QML_FILES Update add_qml_module() to use the new INSTALL_QML_FILES argument from qt6_add_qml_module(). This patch also updates pro2cmake.py to remove the QT_QML_SOURCE_INSTALL property from qml files. Change-Id: I6623b2de76bb55bd6750e48f7d45c53ca536b391 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 1 - 1 file changed, 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index dee9da5b56..0db814d982 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3139,7 +3139,6 @@ def write_qml_plugin_epilogue( != qmldir_file_info.type_name ): cm_fh.write(f"{indent_1}QT_QML_SOURCE_TYPENAME {qmldir_file_info.type_name}\n") - cm_fh.write(f"{indent_1}QT_QML_SOURCE_INSTALL TRUE\n") if qmldir_file_info.singleton: cm_fh.write(f"{indent_1}QT_QML_SINGLETON_TYPE TRUE\n") if qmldir_file_info.internal: -- cgit v1.2.3 From 82c86856d1fefac5badf91d77a4f81a068fd5060 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Fri, 4 Oct 2019 14:04:10 +0200 Subject: pro2cmake: Use get_files for STATECHARTS ...to get proper variable replacements. Change-Id: I533a6abfbc7721313840fd75125eeaa7b26f2eba Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0db814d982..4c3c474811 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2283,7 +2283,7 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, is_example=False): - sources = scope.get("STATECHARTS") + sources = scope.get_files("STATECHARTS", use_vpath=True) if not sources: return cm_fh.write("\n# Statecharts:\n") -- cgit v1.2.3 From 37c330fe0717a6b031f82815ac33fb1f0abdc08b Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 1 Oct 2019 08:47:00 +0200 Subject: pro2cmake: Handle QLALRSOURCES Change-Id: I8e6b2d542c96947d487b934c27544a8a4ae8745c Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4c3c474811..5ac824f63c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2296,6 +2296,17 @@ def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0 cm_fh.write(f"{spaces(indent)}{f}\n") cm_fh.write(")\n") +def write_qlalrsources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): + sources = scope.get_files("QLALRSOURCES", use_vpath=True) + if not sources: + return + cm_fh.write("\n# QLALR Grammars:\n") + cm_fh.write(f"qt_process_qlalr(\n") + indent += 1 + cm_fh.write(f"{spaces(indent)}{';'.join(sources)}\n") + cm_fh.write(f"{spaces(indent)}{target}\n") + cm_fh.write(f"{spaces(indent)}\"\"\n") + cm_fh.write(")\n") def expand_project_requirements(scope: Scope, skip_message: bool = False) -> str: requirements = "" @@ -2690,6 +2701,8 @@ def write_main_part( write_statecharts(cm_fh, name, scope, indent) + write_qlalrsources(cm_fh, name, scope, indent) + write_simd_part(cm_fh, name, scope, indent) write_android_part(cm_fh, name, scopes[0], indent) -- cgit v1.2.3 From 1aaa53b6dbbc01c27742bd032d500123bcb11a48 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 1 Oct 2019 14:40:00 +0200 Subject: pro2cmake: Handle REPC_{SOURCE|REPLICA|MERGED} Change-Id: I7c058bdc5a93038e7b67a727125c315062560d8f Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5ac824f63c..3579e8d35e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2308,6 +2308,17 @@ def write_qlalrsources(cm_fh: IO[str], target: str, scope: Scope, indent: int = cm_fh.write(f"{spaces(indent)}\"\"\n") cm_fh.write(")\n") +def write_repc_files(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): + for t in ["SOURCE", "REPLICA", "MERGED"]: + sources = scope.get_files("REPC_" + t, use_vpath=True) + if not sources: + continue + cm_fh.write(f"qt6_add_repc_{t.lower()}({target}\n") + indent += 1 + for f in sources: + cm_fh.write(f"{spaces(indent)}{f}\n") + cm_fh.write(")\n") + def expand_project_requirements(scope: Scope, skip_message: bool = False) -> str: requirements = "" for requirement in scope.get("_REQUIREMENTS"): @@ -2703,6 +2714,8 @@ def write_main_part( write_qlalrsources(cm_fh, name, scope, indent) + write_repc_files(cm_fh, name, scope, indent) + write_simd_part(cm_fh, name, scope, indent) write_android_part(cm_fh, name, scopes[0], indent) @@ -3007,6 +3020,7 @@ def write_example( write_resources(cm_fh, binary_name, scope, indent=indent, is_example=True) write_statecharts(cm_fh, binary_name, scope, indent=indent, is_example=True) + write_repc_files(cm_fh, binary_name, scope, indent=indent) if qmldir: write_qml_plugin_epilogue(cm_fh, binary_name, scope, qmldir, indent) -- cgit v1.2.3 From 152c593423ee42c9197bf2b6f7e9e44e9fd6111b Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Fri, 4 Oct 2019 14:03:42 +0200 Subject: pro2cmake: Handle QTRO_SOURCE_TREE ...which is the source root of qtremoteobjects. Set to CMAKE_SOURCE_DIR for now. Change-Id: I6797e170749ed01bbc1bac4daa7709d3aae1f84b Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3579e8d35e..5e1af0d373 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -806,6 +806,7 @@ class Scope(object): operations = { "QT_SOURCE_TREE": [SetOperation(["${QT_SOURCE_TREE}"])], "QT_BUILD_TREE": [SetOperation(["${PROJECT_BINARY_DIR}"])], + "QTRO_SOURCE_TREE": [SetOperation(["${CMAKE_SOURCE_DIR}"])], } self._operations = copy.deepcopy(operations) -- cgit v1.2.3 From f7bb15a11b82ce54d551e131a99aa5e75889bd46 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 8 Oct 2019 18:36:30 +0200 Subject: pro2cmake: Ignore QTRO_SOURCE_TREE if unused Gets rid of new flood of QTRO_SOURCE_TREE unused commented lines when regenerating projects. Amends 152c593423ee42c9197bf2b6f7e9e44e9fd6111b Change-Id: I5485643459078cd53face3080aa600df7d082595 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5e1af0d373..d287856b49 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2105,6 +2105,7 @@ def write_ignored_keys(scope: Scope, indent: str) -> str: "QMAKE_DOCS", "QT_SOURCE_TREE", "QT_BUILD_TREE", + "QTRO_SOURCE_TREE", "TRACEPOINT_PROVIDER", "PLUGIN_TYPE", "PLUGIN_CLASS_NAME", -- cgit v1.2.3 From b7adc85642584be26a1870617a9aa83c16e40cfb Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 8 Oct 2019 15:07:41 +0200 Subject: cmake scripts: do not redefine built-ins Try not to override built-ins, the code becomes confusing to editors and people. Change-Id: I9e9421e1506a206551ccfc550f882a075e208181 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/configurejson2cmake.py | 18 +++++++++--------- util/cmake/helper.py | 6 +++--- util/cmake/pro2cmake.py | 14 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index ae7c0328a8..0545d034d5 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -147,8 +147,8 @@ def cm(ctx, *output): return ctx -def readJsonFromDir(dir): - path = posixpath.join(dir, "configure.json") +def readJsonFromDir(path: str) -> str: + path = posixpath.join(path, "configure.json") print(f"Reading {path}...") assert posixpath.exists(path) @@ -942,8 +942,8 @@ def processInputs(ctx, data, cm_fh): if "options" not in commandLine: return - for input in commandLine["options"]: - parseInput(ctx, input, commandLine["options"][input], cm_fh) + for input_option in commandLine["options"]: + parseInput(ctx, input_option, commandLine["options"][input_option], cm_fh) def processTests(ctx, data, cm_fh): @@ -974,23 +974,23 @@ def processLibraries(ctx, data, cm_fh): parseLib(ctx, lib, data, cm_fh, cmake_find_packages_set) -def processSubconfigs(dir, ctx, data): +def processSubconfigs(path, ctx, data): assert ctx is not None if "subconfigs" in data: for subconf in data["subconfigs"]: - subconfDir = posixpath.join(dir, subconf) + subconfDir = posixpath.join(path, subconf) subconfData = readJsonFromDir(subconfDir) subconfCtx = ctx processJson(subconfDir, subconfCtx, subconfData) -def processJson(dir, ctx, data): +def processJson(path, ctx, data): ctx["module"] = data.get("module", "global") ctx["test_dir"] = data.get("testDir", "") ctx = processFiles(ctx, data) - with open(posixpath.join(dir, "configure.cmake"), "w") as cm_fh: + with open(posixpath.join(path, "configure.cmake"), "w") as cm_fh: cm_fh.write("\n\n#### Inputs\n\n") processInputs(ctx, data, cm_fh) @@ -1016,7 +1016,7 @@ def processJson(dir, ctx, data): cm_fh.write('qt_extra_definition("QT_VERSION_PATCH" ${PROJECT_VERSION_PATCH} PUBLIC)\n') # do this late: - processSubconfigs(dir, ctx, data) + processSubconfigs(path, ctx, data) def main(): diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 4c6b811133..aa73168718 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -554,11 +554,11 @@ def find_library_info_for_target(targetName: str) -> typing.Optional[LibraryMapp return None -def featureName(input: str) -> str: +def featureName(name: str) -> str: replacement_char = "_" - if input.startswith("c++"): + if name.startswith("c++"): replacement_char = "x" - return re.sub(r"[^a-zA-Z0-9_]", replacement_char, input) + return re.sub(r"[^a-zA-Z0-9_]", replacement_char, name) def map_qt_library(lib: str) -> str: diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index d287856b49..89c9273f28 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -665,7 +665,7 @@ class Operation: self._value = [str(value)] def process( - self, key: str, input: List[str], transformer: Callable[[List[str]], List[str]] + self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] ) -> List[str]: assert False @@ -1032,12 +1032,12 @@ class Scope(object): for c in self._included_children: c.dump(indent=indent + 1) - def dump_structure(self, *, type: str = "ROOT", indent: int = 0) -> None: - print(f"{spaces(indent)}{type}: {self}") + def dump_structure(self, *, structure_type: str = "ROOT", indent: int = 0) -> None: + print(f"{spaces(indent)}{structure_type}: {self}") for i in self._included_children: - i.dump_structure(type="INCL", indent=indent + 1) + i.dump_structure(structure_type="INCL", indent=indent + 1) for i in self._children: - i.dump_structure(type="CHLD", indent=indent + 1) + i.dump_structure(structure_type="CHLD", indent=indent + 1) @property def keys(self): @@ -1812,11 +1812,11 @@ def sort_sources(sources: List[str]) -> List[str]: if s is None: continue - dir = os.path.dirname(s) + path = os.path.dirname(s) base = os.path.splitext(os.path.basename(s))[0] if base.endswith("_p"): base = base[:-2] - sort_name = posixpath.join(dir, base) + sort_name = posixpath.join(path, base) array = to_sort.get(sort_name, []) array.append(s) -- cgit v1.2.3 From 3a103b360802b81318a20063f2b4884864e79b15 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 7 Oct 2019 12:22:04 +0200 Subject: cmake scripts: fix type issues Some of the changes are just cosmetic, but a bunch of them must be bugs, such as mixing lists and str randomly. Change-Id: Idd8340e17bcea7af3b87e595e251e13f5df1aa3f Reviewed-by: Alexandru Croitor --- util/cmake/condition_simplifier.py | 2 +- util/cmake/condition_simplifier_cache.py | 6 +- util/cmake/pro2cmake.py | 133 +++++++++++++++---------------- 3 files changed, 70 insertions(+), 71 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/condition_simplifier.py b/util/cmake/condition_simplifier.py index de329be1d4..d02e70e489 100644 --- a/util/cmake/condition_simplifier.py +++ b/util/cmake/condition_simplifier.py @@ -29,7 +29,7 @@ import re -from sympy import simplify_logic, And, Or, Not, SympifyError +from sympy import simplify_logic, And, Or, Not, SympifyError # type: ignore from condition_simplifier_cache import simplify_condition_memoize diff --git a/util/cmake/condition_simplifier_cache.py b/util/cmake/condition_simplifier_cache.py index d1efff1d87..947b18af81 100644 --- a/util/cmake/condition_simplifier_cache.py +++ b/util/cmake/condition_simplifier_cache.py @@ -35,7 +35,7 @@ import os import sys import time -from typing import Callable +from typing import Any, Callable, Dict, Union condition_simplifier_cache_enabled = True @@ -66,7 +66,7 @@ def get_file_checksum(file_path: str) -> str: with open(file_path, "r") as content_file: content = content_file.read() except IOError: - content = time.time() + content = str(time.time()) checksum = hashlib.md5(content.encode("utf-8")).hexdigest() return checksum @@ -88,7 +88,7 @@ def init_cache_dict(): def simplify_condition_memoize(f: Callable[[str], str]): cache_path = get_cache_location() - cache_file_content = None + cache_file_content: Dict[str, Any] = {} if os.path.exists(cache_path): try: diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 89c9273f28..46c70cca91 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -42,12 +42,7 @@ import collections from condition_simplifier import simplify_condition from condition_simplifier_cache import set_condition_simplified_cache_enabled -try: - collectionsAbc = collections.abc -except AttributeError: - collectionsAbc = collections - -import pyparsing as pp +import pyparsing as pp # type: ignore import xml.etree.ElementTree as ET from argparse import ArgumentParser @@ -63,7 +58,6 @@ from typing import ( Set, IO, Union, - Mapping, Any, Callable, FrozenSet, @@ -243,13 +237,13 @@ def is_config_test_project(project_file_path: str = "") -> bool: @lru_cache(maxsize=None) -def find_qmake_conf(project_file_path: str = "") -> Optional[str]: +def find_qmake_conf(project_file_path: str = "") -> str: if not os.path.isabs(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 + return "" cwd = os.path.dirname(project_file_path) file_name = ".qmake.conf" @@ -261,7 +255,8 @@ def find_qmake_conf(project_file_path: str = "") -> Optional[str]: else: cwd = os.path.dirname(cwd) - return None + print(f"Warning: could not find .qmake.conf file") + return "" def process_qrc_file( @@ -350,7 +345,7 @@ def write_add_qt_resource_call( target: str, resource_name: str, prefix: Optional[str], - base_dir: Optional[str], + base_dir: str, lang: Optional[str], files: Dict[str, str], skip_qtquick_compiler: bool, @@ -425,27 +420,28 @@ def write_add_qt_resource_call( class QmlDirFileInfo: - def __init__(self, file_path: str, type_name: str): + def __init__(self, file_path: str, type_name: str) -> None: self.file_path = file_path self.version = "" self.type_name = type_name self.internal = False self.singleton = False + self.path = "" class QmlDir: - def __init__(self): + def __init__(self) -> None: self.module = "" self.plugin_name = "" self.plugin_path = "" self.classname = "" - self.imports = [] # typing.List[str] - self.type_names = {} # typing.Dict[str, QmlDirFileInfo] - self.type_infos = [] # typing.List[str] - self.depends = [] # typing.List[[str,str]] + self.imports: List[str] = [] + self.type_names: Dict[str, QmlDirFileInfo] = {} + self.type_infos: List[str] = [] + self.depends: List[str] = [] self.designer_supported = False - def __str__(self): + def __str__(self) -> str: type_infos_line = " \n".join(self.type_infos) imports_line = " \n".join(self.imports) string = f"""\ @@ -522,8 +518,6 @@ class QmlDir: self.classname = entries[1] elif entries[0] == "typeinfo": self.type_infos.append(entries[1]) - elif entries[0] == "depends": - self.depends.append((entries[1], entries[2])) elif entries[0] == "designersupported": self.designer_supported = True elif entries[0] == "import": @@ -618,7 +612,7 @@ def handle_vpath(source: str, base_dir: str, vpath: List[str]) -> str: def flatten_list(l): """ Flattens an irregular nested list into a simple list.""" for el in l: - if isinstance(el, collectionsAbc.Iterable) and not isinstance(el, (str, bytes)): + if isinstance(el, collections.abc.Iterable) and not isinstance(el, (str, bytes)): yield from flatten_list(el) else: yield el @@ -658,7 +652,7 @@ def handle_function_value(group: pp.ParseResults): class Operation: - def __init__(self, value: Union[List[str], str]): + def __init__(self, value: Union[List[str], str]) -> None: if isinstance(value, list): self._value = value else: @@ -797,19 +791,19 @@ class Scope(object): self, *, parent_scope: Optional[Scope], - file: Optional[str] = None, + qmake_file: str, condition: str = "", base_dir: str = "", - operations: Union[Mapping[str, List[Operation]], None] = None, + operations: Union[Dict[str, List[Operation]], None] = None, ) -> None: - if operations is None: + if not operations: operations = { "QT_SOURCE_TREE": [SetOperation(["${QT_SOURCE_TREE}"])], "QT_BUILD_TREE": [SetOperation(["${PROJECT_BINARY_DIR}"])], "QTRO_SOURCE_TREE": [SetOperation(["${CMAKE_SOURCE_DIR}"])], } - self._operations = copy.deepcopy(operations) + self._operations: Dict[str, List[Operation]] = copy.deepcopy(operations) if parent_scope: parent_scope._add_child(self) else: @@ -820,17 +814,15 @@ class Scope(object): self._operations["QT"] = [SetOperation(["core", "gui"])] self._basedir = base_dir - if file: - self._currentdir = os.path.dirname(file) - if not self._currentdir: - self._currentdir = "." + if qmake_file: + self._currentdir = os.path.dirname(qmake_file) or "." if not self._basedir: self._basedir = self._currentdir self._scope_id = Scope.SCOPE_ID Scope.SCOPE_ID += 1 - self._file = file - self._file_absolute_path = os.path.abspath(file) + self._file = qmake_file + self._file_absolute_path = os.path.abspath(qmake_file) self._condition = map_condition(condition) self._children = [] # type: List[Scope] self._included_children = [] # type: List[Scope] @@ -896,7 +888,7 @@ class Scope(object): 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) + scope = Scope(parent_scope=parent_scope, qmake_file=file, condition=cond, base_dir=base_dir) for statement in statements: if isinstance(statement, list): # Handle skipped parts... assert not statement @@ -1181,7 +1173,7 @@ class Scope(object): else: # Recursively expand each value from the result list # returned from self.get(). - result_list = [] + result_list: List[str] = [] for entry_value in get_result: result_list += self._expand_value(self._replace_env_var_value(entry_value)) return result_list @@ -1716,14 +1708,14 @@ def handle_subdir( c, cm_fh, indent=indent + 1, - is_example=is_example, current_conditions=frozenset((*current_conditions, child_condition)), + is_example=is_example, ) - def group_and_print_sub_dirs(scope: Scope, indent: int = 0): + def group_and_print_sub_dirs(scope: Scope, indent: int = 0) -> None: # Simplify conditions, and group # subdirectories with the same conditions. - grouped_sub_dirs = {} + grouped_sub_dirs: Dict[str, List[str]] = {} # Wraps each element in the given interable with parentheses, # to make sure boolean simplification happens correctly. @@ -1770,7 +1762,7 @@ def handle_subdir( condition_simplified = simplify_condition(condition_str) condition_key = condition_simplified - sub_dir_list_by_key = grouped_sub_dirs.get(condition_key, []) + sub_dir_list_by_key: List[str] = grouped_sub_dirs.get(condition_key, []) sub_dir_list_by_key.append(subdir_name) grouped_sub_dirs[condition_key] = sub_dir_list_by_key @@ -1793,7 +1785,7 @@ def handle_subdir( # A set of conditions which will be ANDed together. The set is recreated with more conditions # as the scope deepens. - current_conditions = frozenset() + current_conditions: FrozenSet[str] = frozenset() # Compute the total condition for scopes. Needed for scopes that # have 'else' as a condition. @@ -2221,24 +2213,26 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, else: immediate_files_filtered.append(f) immediate_files = {f: "" for f in immediate_files_filtered} - immediate_prefix = scope.get(f"{r}.prefix") - if immediate_prefix: - immediate_prefix = immediate_prefix[0] + scope_prefix = scope.get(f"{r}.prefix") + if scope_prefix: + immediate_prefix = scope_prefix[0] else: immediate_prefix = "/" - immediate_base = scope.get(f"{r}.base") + immediate_base_list = scope.get(f"{r}.base") + assert len(immediate_base_list) < 2, f"immediate base directory must be at most one entry" + immediate_base = "".join(immediate_base_list) immediate_lang = None 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, + target=target, + resource_name=immediate_name, + prefix=immediate_prefix, + base_dir=immediate_base, + lang=immediate_lang, + files=immediate_files, + skip_qtquick_compiler=skip_qtquick_compiler, + retain_qtquick_compiler=retain_qtquick_compiler, + is_example=is_example, ) else: if "*" in r: @@ -2262,17 +2256,17 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, if standalone_files: name = "qmake_immediate" prefix = "/" - base = None + base = "" lang = None files = {f: "" for f in standalone_files} skip_qtquick_compiler = False qrc_output += write_add_qt_resource_call( - target, - name, - prefix, - base, - lang, - files, + target=target, + resource_name=name, + prefix=prefix, + base_dir=base, + lang=lang, + files=files, skip_qtquick_compiler=False, retain_qtquick_compiler=False, is_example=is_example, @@ -2347,6 +2341,7 @@ def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, indent: int = write_sources_section(extend_qt_io_string, scope) extend_qt_string = extend_qt_io_string.getvalue() + assert scope.total_condition, "Cannot write CONDITION when scope.condition is None" extend_scope = ( f"\n{ind}extend_target({target} CONDITION" f" {map_to_cmake_condition(scope.total_condition)}\n" @@ -2372,7 +2367,7 @@ def merge_scopes(scopes: List[Scope]) -> List[Scope]: result = [] # type: List[Scope] # Merge scopes with their parents: - known_scopes = {} # type: Mapping[str, Scope] + known_scopes = {} # type: Dict[str, Scope] for scope in scopes: total_condition = scope.total_condition assert total_condition @@ -2461,7 +2456,7 @@ def write_android_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = cm_fh.write(f"{spaces(indent)}endif()\n") -def write_wayland_part(cm_fh: typing.IO[str], target: str, scope:Scope, indent: int = 0): +def write_wayland_part(cm_fh: IO[str], target: str, scope:Scope, indent: int = 0): client_sources = scope.get_files('WAYLANDCLIENTSOURCES', use_vpath=True) server_sources = scope.get_files('WAYLANDSERVERSOURCES', use_vpath=True) if len(client_sources) == 0 and len(server_sources) == 0: @@ -2559,11 +2554,13 @@ def handle_source_subtractions(scopes: List[Scope]): modified_sources[file_without_minus] = {} subtractions = modified_sources[file_without_minus].get("subtractions", set()) + assert isinstance(subtractions, set) # Add the condition to the set of conditions and remove # the file subtraction from the processed scope, which # will be later re-added in a new scope. if scope.condition: + assert scope.total_condition subtractions.add(scope.total_condition) remove_file_from_operation(scope, "SOURCES", file_without_minus, RemoveOperation) if subtractions: @@ -2580,7 +2577,9 @@ def handle_source_subtractions(scopes: List[Scope]): for modified_source in modified_sources: additions = modified_sources[modified_source].get("additions", set()) + assert isinstance(additions, set), f"Additions must be a set, got {additions} instead." subtractions = modified_sources[modified_source].get("subtractions", set()) + assert isinstance(subtractions, set), f"Subtractions must be a set, got {additions} instead." add_to_no_pch_sources = modified_sources[modified_source].get( "add_to_no_pch_sources", False ) @@ -2613,7 +2612,7 @@ def handle_source_subtractions(scopes: List[Scope]): # operations. new_scope = Scope( parent_scope=top_most_scope, - file=top_most_scope.file, + qmake_file=top_most_scope.file, condition=condition_simplified, base_dir=top_most_scope.basedir, ) @@ -2957,11 +2956,10 @@ def write_example( """ ) - qmldir_file_path = scope.get_files("qmldir.files") - if qmldir_file_path: - qmldir_file_path = os.path.join(os.getcwd(), qmldir_file_path[0]) - else: - qmldir_file_path = os.path.join(os.getcwd(), "qmldir") + qmldir_file_path_list = scope.get_files("qmldir.files") + assert len(qmldir_file_path_list) < 2, "File path must only contain one path" + qmldir_file_path = qmldir_file_path_list[0] if qmldir_file_path_list else "qmldir" + qmldir_file_path = os.path.join(os.getcwd(), qmldir_file_path[0]) if os.path.exists(qmldir_file_path): qml_dir = QmlDir() @@ -3402,6 +3400,7 @@ def handle_config_test_project(scope: Scope, cm_fh: IO[str]): extend_string = extend_scope_io_string.getvalue() if extend_string: + assert c.total_condition, "Cannot write if with empty condition" extend_scope = ( f"\nif({map_to_cmake_condition(c.total_condition)})\n" f"{extend_string}" -- cgit v1.2.3 From f95988261631a47fcc5cacf57a1226cb8e391c64 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 8 Oct 2019 15:36:05 +0200 Subject: cmake scripts: more type cleanup Change-Id: Ic32394548bb997af96756f260b453e830d8b9e9b Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 4 ++-- util/cmake/json_parser.py | 2 +- util/cmake/pro_conversion_rate.py | 4 +--- util/cmake/run_pro2cmake.py | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 0545d034d5..32394f31c4 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -31,7 +31,7 @@ import json_parser import posixpath import re import sys -from typing import Set +from typing import Optional, Set from textwrap import dedent from helper import ( @@ -52,7 +52,7 @@ class LibraryMapping: self.appendFoundSuffix = appendFoundSuffix -def map_tests(test: str) -> str: +def map_tests(test: str) -> Optional[str]: testmap = { "c++11": "$", "c++14": "$", diff --git a/util/cmake/json_parser.py b/util/cmake/json_parser.py index 36e8cee37f..a0aaecab9d 100644 --- a/util/cmake/json_parser.py +++ b/util/cmake/json_parser.py @@ -27,7 +27,7 @@ ## ############################################################################# -import pyparsing as pp +import pyparsing as pp # type: ignore import json import re from helper import _set_up_py_parsing_nicer_debug_output diff --git a/util/cmake/pro_conversion_rate.py b/util/cmake/pro_conversion_rate.py index a3ba8455c8..1224f999a8 100755 --- a/util/cmake/pro_conversion_rate.py +++ b/util/cmake/pro_conversion_rate.py @@ -68,7 +68,7 @@ class Blacklist: try: # If package is available, use Aho-Corasick algorithm, - from ahocorapy.keywordtree import KeywordTree + from ahocorapy.keywordtree import KeywordTree # type: ignore self.tree = KeywordTree(case_insensitive=True) @@ -101,8 +101,6 @@ def recursive_scan(path: str, extension: str, result_paths: typing.List[str], bl """ Find files ending with a certain extension, filtering out blacklisted entries """ try: for entry in os.scandir(path): - entry: os.DirEntry = entry - if entry.is_file() and entry.path.endswith(extension): result_paths.append(entry.path) elif entry.is_dir(): diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index 0db9f2a607..7c38a8aef9 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -84,7 +84,7 @@ def find_all_pro_files(base_path: str, args: argparse.Namespace): return dir_name + "/__" + pro_file all_files = [] - previous_dir_name: str = None + previous_dir_name: typing.Optional[str] = None print("Finding .pro files.") glob_result = glob.glob(os.path.join(base_path, "**/*.pro"), recursive=True) -- cgit v1.2.3 From af7b0534c6d6c42c949a802952dab981d64d1f6f Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 9 Oct 2019 11:23:03 +0200 Subject: Correct Examples install directory Set the example install directory based on the path specified in target.path qmake property. Change-Id: Ia791995a3241fbf2f5ea1e48f2b9f93a90f1c3d7 Reviewed-by: Alexandru Croitor Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 46c70cca91..2ced79af85 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2921,6 +2921,11 @@ def write_example( binary_name = scope.TARGET assert binary_name + example_install_dir = scope.get_string('target.path') + if not example_install_dir: + example_install = 'examples' + example_install_dir = example_install_dir.replace('$$[QT_INSTALL_EXAMPLES]', 'examples') + cm_fh.write( "cmake_minimum_required(VERSION 3.14)\n" f"project({binary_name} LANGUAGES CXX)\n\n" @@ -2928,7 +2933,7 @@ def write_example( "set(CMAKE_AUTOMOC ON)\n" "set(CMAKE_AUTORCC ON)\n" "set(CMAKE_AUTOUIC ON)\n\n" - 'set(INSTALL_EXAMPLEDIR "examples")\n\n' + f'set(INSTALL_EXAMPLEDIR "{example_install_dir}")\n\n' ) (public_libs, private_libs) = extract_cmake_libraries(scope) -- cgit v1.2.3 From 03f365f93e8dab6bcd8473b5f5ba172bab200e1f Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 9 Oct 2019 13:11:58 +0200 Subject: cmake scripts: fix conversion of qmldir files I broke the depends in 3a103b360802b81318a20063f2b4884864e79b15 wrongly assuming it was dead code. Bring it back and actually fix the type to be a list of tuples. Change-Id: I96f04843ff2e2293969b5ba0efe02fb51dc88404 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 2ced79af85..4f8c158cc1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -438,7 +438,7 @@ class QmlDir: self.imports: List[str] = [] self.type_names: Dict[str, QmlDirFileInfo] = {} self.type_infos: List[str] = [] - self.depends: List[str] = [] + self.depends: List[Tuple[str, str]] = [] self.designer_supported = False def __str__(self) -> str: @@ -518,6 +518,8 @@ class QmlDir: self.classname = entries[1] elif entries[0] == "typeinfo": self.type_infos.append(entries[1]) + elif entries[0] == "depends": + self.depends.append((entries[1], entries[2])) elif entries[0] == "designersupported": self.designer_supported = True elif entries[0] == "import": -- cgit v1.2.3 From c0618eb5835547250a6b01c7c4d6b8d921e45ddc Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 9 Oct 2019 10:54:00 +0200 Subject: cmake scripts: Do not add empty child conditions into condition list c.condition can be None, there is no point in adding it into the set of conditions. An example is tests/auto/corelib/codecs/qtextcodec/test.pro Initializing the set to an empty set instead of None makes mypy happy, since we could otherwise end up passing None where frozenset was expected. Change-Id: If88a86d810b4c55aae7f1ee97a62db559abfc86d Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4f8c158cc1..9308118729 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1671,7 +1671,7 @@ def handle_subdir( cm_fh: IO[str], *, indent: int = 0, - current_conditions: FrozenSet[str] = None, + current_conditions: FrozenSet[str] = frozenset(), is_example: bool = False, ): for sd in scope.get_files("SUBDIRS"): @@ -1705,12 +1705,16 @@ def handle_subdir( for c in scope.children: # Use total_condition for 'else' conditions, otherwise just use the regular value to # simplify the logic. + child_conditions = current_conditions child_condition = c.total_condition if c.condition == "else" else c.condition + if child_condition: + child_conditions = frozenset((*child_conditions, child_condition)) + handle_subdir_helper( c, cm_fh, indent=indent + 1, - current_conditions=frozenset((*current_conditions, child_condition)), + current_conditions=child_conditions, is_example=is_example, ) -- cgit v1.2.3 From 5484629f2b7e94abdc5f0ba69129a545fca7ea0e Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 9 Oct 2019 13:39:38 +0200 Subject: Add scope conditions for examples Update example conversion to process scopes and conditions. Change-Id: If1bbbd25092a8d5371b2d1fa2830d91e5aa591c5 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 86 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 29 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9308118729..c24aab59ba 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2942,6 +2942,18 @@ def write_example( f'set(INSTALL_EXAMPLEDIR "{example_install_dir}")\n\n' ) + recursive_evaluate_scope(scope) + + # Get a flat list of all scopes but the main one: + scopes = flatten_scopes(scope) + # Merge scopes based on their conditions: + scopes = merge_scopes(scopes) + # Handle SOURCES -= foo calls, and merge scopes one more time + # because there might have been several files removed with the same + # scope condition. + handle_source_subtractions(scopes) + scopes = merge_scopes(scopes) + (public_libs, private_libs) = extract_cmake_libraries(scope) write_find_package_section(cm_fh, public_libs, private_libs, indent=indent) @@ -2997,41 +3009,57 @@ def write_example( else: add_target = f'add_{"qt_gui_" if gui else ""}executable({binary_name}' + write_all_source_file_lists(cm_fh, scope, add_target, indent=0) cm_fh.write(")\n") - write_wayland_part(cm_fh, binary_name, scope, indent=0) + for scope in scopes : + # write wayland already has condition scope handling + write_wayland_part(cm_fh, binary_name, scope, indent=0) - 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=")" - ) + # The following options do not + condition = "ON" + if scope.total_condition: + condition = map_to_cmake_condition(scope.total_condition) + + if condition != "ON": + cm_fh.write(f"\n{spaces(indent)}if({condition})\n") + indent += 1 + + write_include_paths( + cm_fh, scope, f"target_include_directories({binary_name} PUBLIC", indent=indent, footer=")" + ) + write_defines( + cm_fh, scope, f"target_compile_definitions({binary_name} PUBLIC", indent=indent, 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=indent, footer=")" + ) + + write_resources(cm_fh, binary_name, scope, indent=indent, is_example=True) + write_statecharts(cm_fh, binary_name, scope, indent=indent, is_example=True) + write_repc_files(cm_fh, binary_name, scope, indent=indent) - write_resources(cm_fh, binary_name, scope, indent=indent, is_example=True) - write_statecharts(cm_fh, binary_name, scope, indent=indent, is_example=True) - write_repc_files(cm_fh, binary_name, scope, indent=indent) + if condition != "ON": + indent -= 1 + cm_fh.write(f"\n{spaces(indent)}endif()\n") if qmldir: write_qml_plugin_epilogue(cm_fh, binary_name, scope, qmldir, indent) -- cgit v1.2.3 From decce2cd76cefeaf86df796b16ebf1fecd6947bb Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 9 Oct 2019 14:10:12 +0200 Subject: Extract public/private libraries from scope for Examples Public/public libraries were always processed from the global scope. Change-Id: I1223f04f100f91790ced1da41fda2fb9b52c5987 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index c24aab59ba..cf823c9f94 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3033,9 +3033,12 @@ def write_example( write_defines( cm_fh, scope, f"target_compile_definitions({binary_name} PUBLIC", indent=indent, footer=")" ) + + (scope_public_libs, scope_private_libs) = extract_cmake_libraries(scope) + write_list( cm_fh, - private_libs, + scope_private_libs, "", indent=indent, header=f"target_link_libraries({binary_name} PRIVATE\n", @@ -3043,7 +3046,7 @@ def write_example( ) write_list( cm_fh, - public_libs, + scope_public_libs, "", indent=indent, header=f"target_link_libraries({binary_name} PUBLIC\n", -- cgit v1.2.3 From 4691b4fbae9927992f8153c3517ae8c4d0f74b46 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 9 Oct 2019 14:58:37 +0200 Subject: Use regular -framework for examples Do not replace framework libraries in examples as they tend to use only system level frameworks. Change-Id: Ide604b3ecc90f1f4c81b9ddaaa8e0a5acb486080 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index cf823c9f94..9252ceb236 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1828,7 +1828,8 @@ def sort_sources(sources: List[str]) -> List[str]: return lines -def _map_libraries_to_cmake(libraries: List[str], known_libraries: Set[str]) -> List[str]: +def _map_libraries_to_cmake(libraries: List[str], known_libraries: Set[str], + is_example : bool = False) -> List[str]: result = [] # type: List[str] is_framework = False @@ -1837,7 +1838,10 @@ def _map_libraries_to_cmake(libraries: List[str], known_libraries: Set[str]) -> is_framework = True continue if is_framework: - lib = f"${{FW{lib}}}" + if is_example: + lib = f'"-framework {lib}"' + else: + lib = f"${{FW{lib}}}" if lib.startswith("-l"): lib = lib[2:] @@ -1856,7 +1860,8 @@ def _map_libraries_to_cmake(libraries: List[str], known_libraries: Set[str]) -> def extract_cmake_libraries( - scope: Scope, *, known_libraries: Optional[Set[str]] = None + scope: Scope, *, known_libraries: Optional[Set[str]] = None, + is_example: bool = False ) -> Tuple[List[str], List[str]]: if known_libraries is None: known_libraries = set() @@ -1884,8 +1889,8 @@ def extract_cmake_libraries( public_dependencies.append(mapped_lib) return ( - _map_libraries_to_cmake(public_dependencies, known_libraries), - _map_libraries_to_cmake(private_dependencies, known_libraries), + _map_libraries_to_cmake(public_dependencies, known_libraries, is_example=is_example), + _map_libraries_to_cmake(private_dependencies, known_libraries, is_example=is_example), ) @@ -2954,7 +2959,7 @@ def write_example( handle_source_subtractions(scopes) scopes = merge_scopes(scopes) - (public_libs, private_libs) = extract_cmake_libraries(scope) + (public_libs, private_libs) = extract_cmake_libraries(scope, is_example = True) write_find_package_section(cm_fh, public_libs, private_libs, indent=indent) add_target = "" @@ -3034,7 +3039,7 @@ def write_example( cm_fh, scope, f"target_compile_definitions({binary_name} PUBLIC", indent=indent, footer=")" ) - (scope_public_libs, scope_private_libs) = extract_cmake_libraries(scope) + (scope_public_libs, scope_private_libs) = extract_cmake_libraries(scope, is_example = True) write_list( cm_fh, -- cgit v1.2.3 From 3a9bca6574012c01d37180a0f4851686c483c240 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 9 Oct 2019 13:54:03 +0200 Subject: cmake scripts: when writing resources, replace OUT_PWD Needed in tests/auto/corelib/plugin/qfactoryloader/test/ for example. Change-Id: I5a2904e25d2895355fe11d4fc3e2e7c742346e42 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 9252ceb236..52b2c93d3e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1620,6 +1620,7 @@ _path_replacements = { "$$[QT_INSTALL_PREFIX]": "${INSTALL_DIRECTORY}", "$$[QT_INSTALL_EXAMPLES]": "${INSTALL_EXAMPLESDIR}", "$$[QT_INSTALL_TESTS]": "${INSTALL_TESTSDIR}", + "$$OUT_PWD": "${CMAKE_CURRENT_BINARY_DIR}", } def replace_path_constants(path: str, scope: Scope) -> str: @@ -2231,7 +2232,7 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, immediate_prefix = "/" immediate_base_list = scope.get(f"{r}.base") assert len(immediate_base_list) < 2, f"immediate base directory must be at most one entry" - immediate_base = "".join(immediate_base_list) + immediate_base = replace_path_constants("".join(immediate_base_list), scope) immediate_lang = None immediate_name = f"qmake_{r}" qrc_output += write_add_qt_resource_call( -- cgit v1.2.3 From fbace1f4e059dee498332146da0d90c627886952 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 9 Oct 2019 17:27:17 +0200 Subject: pro2cmake: Make condition cache work well with run_pro2cmake When using run_pro2cmake, multiple pro2cmake processes try to access and override the condition cache. Make sure that reads / writes of the cache file are protected by a file lock, and the content is merged rather than overridden. This requires use of a new pip package, portalocker. The script will tell the user to install it if it's missing. Change-Id: I44798c46ff0912981b186bec40e3e918f249fb4d Reviewed-by: Simon Hausmann --- util/cmake/condition_simplifier_cache.py | 75 +++++++++++++++++++++++++++++--- util/cmake/requirements.txt | 1 + 2 files changed, 70 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/condition_simplifier_cache.py b/util/cmake/condition_simplifier_cache.py index 947b18af81..6303ff828e 100644 --- a/util/cmake/condition_simplifier_cache.py +++ b/util/cmake/condition_simplifier_cache.py @@ -35,7 +35,7 @@ import os import sys import time -from typing import Any, Callable, Dict, Union +from typing import Any, Callable, Dict condition_simplifier_cache_enabled = True @@ -86,16 +86,49 @@ def init_cache_dict(): } +def merge_dicts_recursive(a: Dict[str, Any], other: Dict[str, Any]) -> Dict[str, Any]: + """Merges values of "other" into "a", mutates a.""" + for key in other: + if key in a: + if isinstance(a[key], dict) and isinstance(other[key], dict): + merge_dicts_recursive(a[key], other[key]) + elif a[key] == other[key]: + pass + else: + a[key] = other[key] + return a + + +def open_file_safe(file_path: str, mode: str = "r+"): + # Use portalocker package for file locking if available, + # otherwise print a message to install the package. + try: + import portalocker # type: ignore + + file_open_func = portalocker.Lock + file_open_args = [file_path] + file_open_kwargs = {"mode": mode, "flags": portalocker.LOCK_EX} + file_handle = file_open_func(*file_open_args, **file_open_kwargs) + return file_handle + except ImportError: + print( + "The conversion script is missing a required package: portalocker. Please run " + "python -m pip install requirements.txt to install the missing dependency." + ) + exit(1) + + def simplify_condition_memoize(f: Callable[[str], str]): cache_path = get_cache_location() cache_file_content: Dict[str, Any] = {} if os.path.exists(cache_path): try: - with open(cache_path, "r") as cache_file: + with open_file_safe(cache_path, mode="r") as cache_file: cache_file_content = json.load(cache_file) except (IOError, ValueError): - pass + print(f"Invalid pro2cmake cache file found at: {cache_path}. Removing it.") + os.remove(cache_path) if not cache_file_content: cache_file_content = init_cache_dict() @@ -107,13 +140,43 @@ def simplify_condition_memoize(f: Callable[[str], str]): def update_cache_file(): if not os.path.exists(cache_path): os.makedirs(os.path.dirname(cache_path), exist_ok=True) - with open(cache_path, "w") as cache_file_write_handle: - json.dump(cache_file_content, cache_file_write_handle, indent=4) + # Create the file if it doesn't exist, but don't override + # it. + with open(cache_path, "a") as temp_file_handle: + pass + + updated_cache = cache_file_content + + with open_file_safe(cache_path, "r+") as cache_file_write_handle: + # Read any existing cache content, and truncate the file. + cache_file_existing_content = cache_file_write_handle.read() + cache_file_write_handle.seek(0) + cache_file_write_handle.truncate() + + # Merge the new cache into the old cache if it exists. + if cache_file_existing_content: + possible_cache = json.loads(cache_file_existing_content) + if ( + "checksum" in possible_cache + and "schema_version" in possible_cache + and possible_cache["checksum"] == cache_file_content["checksum"] + and possible_cache["schema_version"] == cache_file_content["schema_version"] + ): + updated_cache = merge_dicts_recursive(dict(possible_cache), updated_cache) + + json.dump(updated_cache, cache_file_write_handle, indent=4) + + # Flush any buffered writes. + cache_file_write_handle.flush() + os.fsync(cache_file_write_handle.fileno()) atexit.register(update_cache_file) def helper(condition: str) -> str: - if condition not in cache_file_content["cache"]["conditions"] or not condition_simplifier_cache_enabled: + if ( + condition not in cache_file_content["cache"]["conditions"] + or not condition_simplifier_cache_enabled + ): cache_file_content["cache"]["conditions"][condition] = f(condition) return cache_file_content["cache"]["conditions"][condition] diff --git a/util/cmake/requirements.txt b/util/cmake/requirements.txt index 3a5d19a044..4d42912956 100644 --- a/util/cmake/requirements.txt +++ b/util/cmake/requirements.txt @@ -2,3 +2,4 @@ pytest; python_version >= '3.7' mypy; python_version >= '3.7' pyparsing; python_version >= '3.7' sympy; python_version >= '3.7' +portalocker; python_version >= '3.7' -- cgit v1.2.3 From fbf27595984f79787588bf4c59217ae6707313a3 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 9 Oct 2019 15:18:56 +0200 Subject: cmake scripts: add mapping for mkspec conditions Improves for example tests/auto/tools/moc/moc.pro conversion. Change-Id: I80f82be75299a0407be510824df35f0ea101c0c5 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 52b2c93d3e..6e9f53aa3f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1571,6 +1571,13 @@ def map_condition(condition: str) -> str: condition = re.sub(r"\s*==\s*", "___STREQUAL___", condition) condition = re.sub(r"\bexists\s*\((.*?)\)", r"EXISTS \1", condition) + # checking mkspec, predating gcc scope in qmake, will then be replaced by platform_mapping in helper.py + condition = condition.replace("*-g++*", "GCC") + condition = condition.replace("*-icc*", "ICC") + condition = condition.replace("*-clang*", "CLANG") + condition = condition.replace("*-llvm", "CLANG") + condition = condition.replace("win32-*", "WIN32") + pattern = r"CONFIG\((debug|release),debug\|release\)" match_result = re.match(pattern, condition) if match_result: -- cgit v1.2.3 From b42feb02ceebfcf86caf8c8aa8be3e84b8e431ca Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 10 Oct 2019 14:21:02 +0200 Subject: Add install directory for plugins without type When we run into a plugin that does not have a type and is not a qml plugin, we try to see if we can find the target installation path and provide INSTALL_DIRECTORY AND ARCHIVE_INSTALL_DIRECTORY to the add_qt_plugin call. We run into this frequently with the unit tests. This patch also changes add_qt_plugin() to use the value provided in INSTALL_DIRECTORY for ARCHIVE_INSTALL_DIRECTORY if no value is provided for the latter. Change-Id: I61278904a4d2d72308079cd362bd085b4e2f540c Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6e9f53aa3f..211678a886 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3106,6 +3106,11 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: elif is_qml_plugin: plugin_function_name = "add_qml_module" qmldir = write_qml_plugin(cm_fh, plugin_name, scope, indent=indent, extra_lines=extra) + else: + target_path = scope.expandString('target.path') + target_path = replace_path_constants(target_path, scope) + if target_path: + extra.append(f'INSTALL_DIRECTORY "{target_path}"') plugin_class_name = scope.get_string("PLUGIN_CLASS_NAME") if plugin_class_name: -- cgit v1.2.3 From 2659d31d936bac6383e3e87fecd5179d2b4bc9ad Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 10 Oct 2019 10:02:22 +0200 Subject: cmake scripts: fix test_scope_handling The file parameter for a new Scope got renamed to qmake_file. Change-Id: I6cb9d010892f3e3132fac09eead1dbf45d6ba86d Reviewed-by: Alexandru Croitor --- util/cmake/tests/test_scope_handling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/tests/test_scope_handling.py b/util/cmake/tests/test_scope_handling.py index 14fd266c9c..1db8b2a079 100755 --- a/util/cmake/tests/test_scope_handling.py +++ b/util/cmake/tests/test_scope_handling.py @@ -43,7 +43,7 @@ def _map_to_operation(**kwargs): def _new_scope(*, parent_scope=None, condition='', **kwargs) -> Scope: return Scope(parent_scope=parent_scope, - file='file1', condition=condition, operations=_map_to_operation(**kwargs)) + qmake_file='file1', condition=condition, operations=_map_to_operation(**kwargs)) def _evaluate_scopes(scopes: ScopeList) -> ScopeList: -- cgit v1.2.3 From a5060e9f995de9d5a8f755a1837f0200e464e4af Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 10 Oct 2019 09:58:38 +0200 Subject: cmake scripts: move parser into separate file The code is nicely separated between parsing and processing. Splitting that into two files makes it easier to follow which function belongs to which part. Change-Id: I576b8613b0d05b2dae3f9c6fa65d9ed5b582a0f7 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 345 +--------------------------------- util/cmake/qmake_parser.py | 377 ++++++++++++++++++++++++++++++++++++++ util/cmake/tests/test_lc_fixup.py | 4 +- util/cmake/tests/test_parsing.py | 2 +- 4 files changed, 381 insertions(+), 347 deletions(-) create mode 100644 util/cmake/qmake_parser.py (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 211678a886..5b7c02a739 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -48,7 +48,6 @@ import xml.etree.ElementTree as ET from argparse import ArgumentParser from textwrap import dedent from textwrap import indent as textwrap_indent -from itertools import chain from functools import lru_cache from shutil import copyfile from typing import ( @@ -65,6 +64,8 @@ from typing import ( Match, Type, ) + +from qmake_parser import parseProFile from special_case_helper import SpecialCaseHandler from helper import ( map_qt_library, @@ -76,9 +77,6 @@ from helper import ( 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" @@ -530,37 +528,6 @@ class QmlDir: raise RuntimeError(f"Uhandled qmldir entry {line}") -def fixup_linecontinuation(contents: str) -> str: - # Remove all line continuations, aka a backslash followed by - # 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) - return contents - - -def fixup_comments(contents: str) -> str: - # Get rid of completely commented out lines. - # So any line which starts with a '#' char and ends with a new line - # will be replaced by a single new line. - # - # This is needed because qmake syntax is weird. In a multi line - # assignment (separated by backslashes and newlines aka - # # \\\n ), if any of the lines are completely commented out, in - # principle the assignment should fail. - # - # It should fail because you would have a new line separating - # the previous value from the next value, and the next value would - # not be interpreted as a value, but as a new token / operation. - # qmake is lenient though, and accepts that, so we need to take - # 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) - return contents - - def spaces(indent: int) -> str: return " " * indent @@ -611,48 +578,6 @@ def handle_vpath(source: str, base_dir: str, vpath: List[str]) -> str: return f"{source}-NOTFOUND" -def flatten_list(l): - """ Flattens an irregular nested list into a simple list.""" - for el in l: - if isinstance(el, collections.abc.Iterable) and not isinstance(el, (str, bytes)): - yield from flatten_list(el) - else: - yield el - - -def handle_function_value(group: pp.ParseResults): - function_name = group[0] - function_args = group[1] - if function_name == "qtLibraryTarget": - if len(function_args) > 1: - raise RuntimeError( - "Don't know what to with more than one function argument " - "for $$qtLibraryTarget()." - ) - return str(function_args[0]) - - if function_name == "quote": - # Do nothing, just return a string result - return str(group) - - if function_name == "files": - return str(function_args[0]) - - if function_name == "basename": - if len(function_args) != 1: - print(f"XXXX basename with more than one argument") - if function_args[0] == '_PRO_FILE_PWD_': - return os.path.basename(os.getcwd()) - print(f"XXXX basename with value other than _PRO_FILE_PWD_") - return os.path.basename(str(function_args[0])) - - if isinstance(function_args, pp.ParseResults): - function_args = list(flatten_list(function_args.asList())) - - # For other functions, return the whole expression as a string. - return f"$${function_name}({' '.join(function_args)})" - - class Operation: def __init__(self, value: Union[List[str], str]) -> None: if isinstance(value, list): @@ -1223,272 +1148,6 @@ class Scope(object): return self.get("_INCLUDED") -class QmakeParser: - def __init__(self, *, debug: bool = False) -> None: - self.debug = debug - self._Grammar = self._generate_grammar() - - def _generate_grammar(self): - # Define grammar: - pp.ParserElement.setDefaultWhitespaceChars(" \t") - - def add_element(name: str, value: pp.ParserElement): - nonlocal self - if self.debug: - value.setName(name) - 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(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("$")) - + 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")) - - Op = add_element( - "OP", pp.Literal("=") | pp.Literal("-=") | pp.Literal("+=") | pp.Literal("*=") | pp.Literal("~=") - ) - - Key = add_element("Key", Identifier) - - Operation = add_element("Operation", Key("key") + Op("operation") + Values("value")) - CallArgs = add_element("CallArgs", pp.nestedExpr()) - - def parse_call_args(results): - out = "" - for item in chain(*results): - if isinstance(item, str): - out += item - else: - out += "(" + parse_call_args(item) + ")" - return out - - 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")) - RequiresCondition = add_element("RequiresCondition", pp.originalTextFor(pp.nestedExpr())) - - def parse_requires_condition(s, l, t): - # The following expression unwraps the condition via the additional info - # set by originalTextFor. - condition_without_parentheses = s[t._original_start + 1 : t._original_end - 1] - - # And this replaces the colons with '&&' similar how it's done for 'Condition'. - condition_without_parentheses = ( - condition_without_parentheses.strip().replace(":", " && ").strip(" && ") - ) - return condition_without_parentheses - - RequiresCondition.setParseAction(parse_requires_condition) - Requires = add_element( - "Requires", pp.Keyword("requires") + RequiresCondition("project_required_condition") - ) - - # ignore the whole thing... - DefineTestDefinition = add_element( - "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()) - ), - ) - - # ignore the whole thing... - ForLoopSingleLine = add_element( - "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())) - - Scope = add_element("Scope", pp.Forward()) - - Statement = add_element( - "Statement", - pp.Group( - Load - | Include - | Option - | Requires - | 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), - ) - - 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")) - ConditionPart = add_element( - "ConditionPart", (ConditionPart1 ^ ConditionPart2) + ConditionEnd - ) - - 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) - ) - - 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"), - ) - - 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)) - - # Scope is already add_element'ed in the forward declaration above. - Scope <<= pp.Group( - Condition("condition") - + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) - + pp.Optional(ElseBranch)("else_statements") - ) - - Grammar = StatementGroup("statements") - Grammar.ignore(pp.pythonStyleComment()) - - return Grammar - - def parseFile(self, file: str): - print(f'Parsing "{file}"...') - try: - with open(file, "r") as file_fd: - contents = file_fd.read() - - # 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(f"{' ' * (pe.col-1)}^") - print(pe) - raise pe - return result - - -def parseProFile(file: str, *, debug=False): - parser = QmakeParser(debug=debug) - return parser.parseFile(file) - - # Given "if(a|b):c" returns "(a|b):c". Uses pyparsing to keep the parentheses # balanced. def unwrap_if(input_string): diff --git a/util/cmake/qmake_parser.py b/util/cmake/qmake_parser.py new file mode 100644 index 0000000000..7aba0784e2 --- /dev/null +++ b/util/cmake/qmake_parser.py @@ -0,0 +1,377 @@ +#!/usr/bin/env python3 +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the plugins of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import collections +import os +import re +from itertools import chain + +import pyparsing as pp # type: ignore + +from helper import _set_up_py_parsing_nicer_debug_output +_set_up_py_parsing_nicer_debug_output(pp) + + +def fixup_linecontinuation(contents: str) -> str: + # Remove all line continuations, aka a backslash followed by + # 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) + return contents + + +def fixup_comments(contents: str) -> str: + # Get rid of completely commented out lines. + # So any line which starts with a '#' char and ends with a new line + # will be replaced by a single new line. + # + # This is needed because qmake syntax is weird. In a multi line + # assignment (separated by backslashes and newlines aka + # # \\\n ), if any of the lines are completely commented out, in + # principle the assignment should fail. + # + # It should fail because you would have a new line separating + # the previous value from the next value, and the next value would + # not be interpreted as a value, but as a new token / operation. + # qmake is lenient though, and accepts that, so we need to take + # 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) + return contents + + +def flatten_list(l): + """ Flattens an irregular nested list into a simple list.""" + for el in l: + if isinstance(el, collections.abc.Iterable) and not isinstance(el, (str, bytes)): + yield from flatten_list(el) + else: + yield el + + +def handle_function_value(group: pp.ParseResults): + function_name = group[0] + function_args = group[1] + if function_name == "qtLibraryTarget": + if len(function_args) > 1: + raise RuntimeError( + "Don't know what to with more than one function argument " + "for $$qtLibraryTarget()." + ) + return str(function_args[0]) + + if function_name == "quote": + # Do nothing, just return a string result + return str(group) + + if function_name == "files": + return str(function_args[0]) + + if function_name == "basename": + if len(function_args) != 1: + print(f"XXXX basename with more than one argument") + if function_args[0] == '_PRO_FILE_PWD_': + return os.path.basename(os.getcwd()) + print(f"XXXX basename with value other than _PRO_FILE_PWD_") + return os.path.basename(str(function_args[0])) + + if isinstance(function_args, pp.ParseResults): + function_args = list(flatten_list(function_args.asList())) + + # For other functions, return the whole expression as a string. + return f"$${function_name}({' '.join(function_args)})" + + +class QmakeParser: + def __init__(self, *, debug: bool = False) -> None: + self.debug = debug + self._Grammar = self._generate_grammar() + + def _generate_grammar(self): + # Define grammar: + pp.ParserElement.setDefaultWhitespaceChars(" \t") + + def add_element(name: str, value: pp.ParserElement): + nonlocal self + if self.debug: + value.setName(name) + 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(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("$")) + + 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")) + + Op = add_element( + "OP", pp.Literal("=") | pp.Literal("-=") | pp.Literal("+=") | pp.Literal("*=") | pp.Literal("~=") + ) + + Key = add_element("Key", Identifier) + + Operation = add_element("Operation", Key("key") + Op("operation") + Values("value")) + CallArgs = add_element("CallArgs", pp.nestedExpr()) + + def parse_call_args(results): + out = "" + for item in chain(*results): + if isinstance(item, str): + out += item + else: + out += "(" + parse_call_args(item) + ")" + return out + + 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")) + RequiresCondition = add_element("RequiresCondition", pp.originalTextFor(pp.nestedExpr())) + + def parse_requires_condition(s, l, t): + # The following expression unwraps the condition via the additional info + # set by originalTextFor. + condition_without_parentheses = s[t._original_start + 1 : t._original_end - 1] + + # And this replaces the colons with '&&' similar how it's done for 'Condition'. + condition_without_parentheses = ( + condition_without_parentheses.strip().replace(":", " && ").strip(" && ") + ) + return condition_without_parentheses + + RequiresCondition.setParseAction(parse_requires_condition) + Requires = add_element( + "Requires", pp.Keyword("requires") + RequiresCondition("project_required_condition") + ) + + # ignore the whole thing... + DefineTestDefinition = add_element( + "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()) + ), + ) + + # ignore the whole thing... + ForLoopSingleLine = add_element( + "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())) + + Scope = add_element("Scope", pp.Forward()) + + Statement = add_element( + "Statement", + pp.Group( + Load + | Include + | Option + | Requires + | 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), + ) + + 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")) + ConditionPart = add_element( + "ConditionPart", (ConditionPart1 ^ ConditionPart2) + ConditionEnd + ) + + 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) + ) + + 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"), + ) + + 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)) + + # Scope is already add_element'ed in the forward declaration above. + Scope <<= pp.Group( + Condition("condition") + + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) + + pp.Optional(ElseBranch)("else_statements") + ) + + Grammar = StatementGroup("statements") + Grammar.ignore(pp.pythonStyleComment()) + + return Grammar + + def parseFile(self, file: str): + print(f'Parsing "{file}"...') + try: + with open(file, "r") as file_fd: + contents = file_fd.read() + + # 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(f"{' ' * (pe.col-1)}^") + print(pe) + raise pe + return result + + +def parseProFile(file: str, *, debug=False): + parser = QmakeParser(debug=debug) + return parser.parseFile(file) diff --git a/util/cmake/tests/test_lc_fixup.py b/util/cmake/tests/test_lc_fixup.py index 841e11615e..42094a5288 100755 --- a/util/cmake/tests/test_lc_fixup.py +++ b/util/cmake/tests/test_lc_fixup.py @@ -27,7 +27,7 @@ ## ############################################################################# -from pro2cmake import fixup_linecontinuation +from qmake_parser import fixup_linecontinuation def test_no_change(): @@ -42,5 +42,3 @@ def test_fix(): output = "test line2 line3 line4 line5 \n\n" result = fixup_linecontinuation(input) assert output == result - - diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index 4019836ae1..95653dc39d 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -28,7 +28,7 @@ ############################################################################# import os -from pro2cmake import QmakeParser +from qmake_parser import QmakeParser _tests_path = os.path.dirname(os.path.abspath(__file__)) -- cgit v1.2.3 From ec706c05e1766e266b3c655c4ecac68d3be0bda7 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 10 Oct 2019 15:28:12 +0200 Subject: Set STATIC argument for plugins Set the STATIC argument when we have 'static' in the qmake configuration. Change-Id: Ia8369bbd5ec4cfce0be51f36c61a811d53522729 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5b7c02a739..475ff6affa 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2775,6 +2775,9 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: if plugin_class_name: extra.append(f"CLASS_NAME {plugin_class_name}") + if 'static' in scope.get('CONFIG'): + extra.append('STATIC') + write_main_part( cm_fh, plugin_name, -- cgit v1.2.3 From cac594683dc84b2d202d2c6dbb75798ae7684f40 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 10 Oct 2019 14:55:35 +0200 Subject: Add a makefile to conveniently run python tests Change-Id: I9b1bbc9b21d9292eac60715116f02533563fa606 Reviewed-by: Alexandru Croitor --- util/cmake/Makefile | 14 ++++++++++++++ util/cmake/Pipfile | 6 ++++-- util/cmake/requirements.txt | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 util/cmake/Makefile (limited to 'util/cmake') diff --git a/util/cmake/Makefile b/util/cmake/Makefile new file mode 100644 index 0000000000..402e45b707 --- /dev/null +++ b/util/cmake/Makefile @@ -0,0 +1,14 @@ + +test: flake8 mypy pytest + +coverage: + pytest --cov . + +flake8: + flake8 *.py --ignore=E501,E266,W503 + +pytest: + pytest + +mypy: + mypy --pretty *.py diff --git a/util/cmake/Pipfile b/util/cmake/Pipfile index 7fbf716eb8..941e848c34 100644 --- a/util/cmake/Pipfile +++ b/util/cmake/Pipfile @@ -4,10 +4,12 @@ verify_ssl = true name = "pypi" [packages] -pytest = "*" -mypy = "*" pyparsing = "*" sympy = "*" +mypy = "*" +pytest = "*" +pytest-cov = "*" +flake8 = "*" [dev-packages] diff --git a/util/cmake/requirements.txt b/util/cmake/requirements.txt index 4d42912956..16febdd4a3 100644 --- a/util/cmake/requirements.txt +++ b/util/cmake/requirements.txt @@ -1,4 +1,5 @@ pytest; python_version >= '3.7' +pytest-cov; python_version >= '3.7' mypy; python_version >= '3.7' pyparsing; python_version >= '3.7' sympy; python_version >= '3.7' -- cgit v1.2.3 From 441a9f562e8f82e492ad6eb94bb12069f28e3b15 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 10 Oct 2019 15:42:49 +0200 Subject: cmake scripts: add portalocker as dependency for Pipenv Change-Id: I0b377e042a7abe69664776a6676df53e601c07af Reviewed-by: Alexandru Croitor --- util/cmake/Pipfile | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/Pipfile b/util/cmake/Pipfile index 941e848c34..21c18f4743 100644 --- a/util/cmake/Pipfile +++ b/util/cmake/Pipfile @@ -10,6 +10,7 @@ mypy = "*" pytest = "*" pytest-cov = "*" flake8 = "*" +portalocker = "*" [dev-packages] -- cgit v1.2.3 From 6167031ecc3c5bac924ca8deb469cf3dc6886704 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 10 Oct 2019 15:40:38 +0200 Subject: Add 'add_cmake_library' to QtBuild.cmake Add add_cmake_library to allow us to create normal cmake targets using all the information we have collected via the conversion script. This function is only meant for tests. For an example, see tests/auto/corelib/plugin/qpluginloader/lib/lib.pro. Change-Id: I738cb8ac241b8da1a1da3ef957c24dc7a754d43f Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 475ff6affa..d7e521ff30 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2421,6 +2421,42 @@ def write_main_part( cm_fh.write(ignored_keys_report) +def write_generic_library(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: + + target_name = scope.TARGET + + library_type = "" + + if 'dll' in scope.get('CONFIG'): + library_type = "SHARED" + + if 'static' in scope.get('CONFIG'): + library_type = "STATIC" + + extra_lines = [] + + if library_type: + extra_lines.append(library_type) + + target_path = scope.expandString('target.path') + target_path = replace_path_constants(target_path, scope) + if target_path: + extra_lines.append(f'INSTALL_DIRECTORY "{target_path}"') + + write_main_part( + cm_fh, + target_name, + "Generic Library", + "add_cmake_library", + scope, + extra_lines=extra_lines, + indent=indent, + known_libraries={}, + extra_keys=[], + ) + + return target_name + def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: module_name = scope.TARGET if not module_name.startswith("Qt"): @@ -2921,9 +2957,12 @@ def handle_app_or_lib( elif 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 and "qt_module" in scope.get("_LOADED"): assert not is_example target = write_module(cm_fh, scope, indent=indent) + elif is_lib: + assert not is_example + target = write_generic_library(cm_fh, scope, indent=indent) elif "qt_tool" in scope.get("_LOADED"): assert not is_example target = write_tool(cm_fh, scope, indent=indent) -- cgit v1.2.3 From 174b17d65d2a181502c81cdd490f25da40d1c22d Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 10 Oct 2019 15:10:37 +0200 Subject: cmake scripts: flake8 cleanup At least setting the example install dir looks like a bug. Change-Id: Ibcced739d05de5445fa455e509cc6f42b9ed935b Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 2 +- util/cmake/helper.py | 2 +- util/cmake/pro2cmake.py | 30 +++++++++++++++++------------- util/cmake/qmake_parser.py | 2 +- 4 files changed, 20 insertions(+), 16 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 32394f31c4..38b80e055f 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -307,7 +307,7 @@ def map_condition(condition): print(f' XXXX Unknown condition "{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:] diff --git a/util/cmake/helper.py b/util/cmake/helper.py index aa73168718..b6cfb33860 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -417,7 +417,7 @@ _library_map = [ LibraryMapping("wayland-client", "Wayland", "Wayland::Client"), LibraryMapping("wayland-cursor", "Wayland", "Wayland::Cursor"), LibraryMapping("wayland-egl", "Wayland", "Wayland::Egl"), - LibraryMapping('wayland-kms', 'Waylandkms', 'PkgConfig::Waylandkms'), #TODO: check if this actually works + LibraryMapping('wayland-kms', 'Waylandkms', 'PkgConfig::Waylandkms'), # TODO: check if this actually works LibraryMapping("x11", "X11", "X11::X11"), LibraryMapping("x11sm", "X11", "${X11_SM_LIB} ${X11_ICE_LIB}", resultVariable="X11_SM"), LibraryMapping( diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index d7e521ff30..3bad2f0fc9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -37,7 +37,6 @@ import sys import re import io import glob -import collections from condition_simplifier import simplify_condition from condition_simplifier_cache import set_condition_simplified_cache_enabled @@ -618,6 +617,7 @@ class AddOperation(Operation): def __repr__(self): return f"+({self._dump()})" + class UniqueAddOperation(Operation): def process( self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] @@ -631,6 +631,7 @@ class UniqueAddOperation(Operation): def __repr__(self): return f"*({self._dump()})" + class ReplaceOperation(Operation): def process( self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] @@ -663,6 +664,7 @@ class ReplaceOperation(Operation): def __repr__(self): return f"*({self._dump()})" + class SetOperation(Operation): def process( self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] @@ -1008,7 +1010,7 @@ class Scope(object): qmake_conf_path = find_qmake_conf(os.path.abspath(self.currentdir)) qmake_conf_dir_path = os.path.dirname(qmake_conf_path) project_relative_path = os.path.relpath(qmake_conf_dir_path, self.currentdir) - return ['${CMAKE_CURRENT_SOURCE_DIR}/'+ project_relative_path] + return ['${CMAKE_CURRENT_SOURCE_DIR}/' + project_relative_path] if key == "_PRO_FILE_PWD_": return ["${CMAKE_CURRENT_SOURCE_DIR}"] @@ -1107,7 +1109,7 @@ class Scope(object): 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() :] + result = result[: match.start()] + replacement_str + result[match.end():] result = self._replace_env_var_value(result) if result == old_result: @@ -1157,7 +1159,7 @@ def unwrap_if(input_string): # The following expression unwraps the condition via the # additional info set by originalTextFor, thus returning the # condition without parentheses. - condition_without_parentheses = s[t._original_start + 1 : t._original_end - 1] + condition_without_parentheses = s[t._original_start + 1: t._original_end - 1] # Re-add the parentheses, but with spaces in-between. This # fixes map_condition -> map_platform to apply properly. @@ -1287,7 +1289,8 @@ _path_replacements = { "$$[QT_INSTALL_EXAMPLES]": "${INSTALL_EXAMPLESDIR}", "$$[QT_INSTALL_TESTS]": "${INSTALL_TESTSDIR}", "$$OUT_PWD": "${CMAKE_CURRENT_BINARY_DIR}", - } +} + def replace_path_constants(path: str, scope: Scope) -> str: """ Clean up DESTDIR and target.path """ @@ -1496,7 +1499,7 @@ def sort_sources(sources: List[str]) -> List[str]: def _map_libraries_to_cmake(libraries: List[str], known_libraries: Set[str], - is_example : bool = False) -> List[str]: + is_example: bool = False) -> List[str]: result = [] # type: List[str] is_framework = False @@ -1970,6 +1973,7 @@ def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0 cm_fh.write(f"{spaces(indent)}{f}\n") cm_fh.write(")\n") + def write_qlalrsources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): sources = scope.get_files("QLALRSOURCES", use_vpath=True) if not sources: @@ -1982,6 +1986,7 @@ def write_qlalrsources(cm_fh: IO[str], target: str, scope: Scope, indent: int = cm_fh.write(f"{spaces(indent)}\"\"\n") cm_fh.write(")\n") + def write_repc_files(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): for t in ["SOURCE", "REPLICA", "MERGED"]: sources = scope.get_files("REPC_" + t, use_vpath=True) @@ -1993,6 +1998,7 @@ def write_repc_files(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0) cm_fh.write(f"{spaces(indent)}{f}\n") cm_fh.write(")\n") + def expand_project_requirements(scope: Scope, skip_message: bool = False) -> str: requirements = "" for requirement in scope.get("_REQUIREMENTS"): @@ -2134,7 +2140,7 @@ def write_android_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = cm_fh.write(f"{spaces(indent)}endif()\n") -def write_wayland_part(cm_fh: IO[str], target: str, scope:Scope, indent: int = 0): +def write_wayland_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): client_sources = scope.get_files('WAYLANDCLIENTSOURCES', use_vpath=True) server_sources = scope.get_files('WAYLANDSERVERSOURCES', use_vpath=True) if len(client_sources) == 0 and len(server_sources) == 0: @@ -2637,7 +2643,7 @@ def write_example( example_install_dir = scope.get_string('target.path') if not example_install_dir: - example_install = 'examples' + example_install_dir = 'examples' example_install_dir = example_install_dir.replace('$$[QT_INSTALL_EXAMPLES]', 'examples') cm_fh.write( @@ -2662,7 +2668,7 @@ def write_example( handle_source_subtractions(scopes) scopes = merge_scopes(scopes) - (public_libs, private_libs) = extract_cmake_libraries(scope, is_example = True) + (public_libs, private_libs) = extract_cmake_libraries(scope, is_example=True) write_find_package_section(cm_fh, public_libs, private_libs, indent=indent) add_target = "" @@ -2717,12 +2723,10 @@ def write_example( else: add_target = f'add_{"qt_gui_" if gui else ""}executable({binary_name}' - write_all_source_file_lists(cm_fh, scope, add_target, indent=0) - cm_fh.write(")\n") - for scope in scopes : + for scope in scopes: # write wayland already has condition scope handling write_wayland_part(cm_fh, binary_name, scope, indent=0) @@ -2742,7 +2746,7 @@ def write_example( cm_fh, scope, f"target_compile_definitions({binary_name} PUBLIC", indent=indent, footer=")" ) - (scope_public_libs, scope_private_libs) = extract_cmake_libraries(scope, is_example = True) + (scope_public_libs, scope_private_libs) = extract_cmake_libraries(scope, is_example=True) write_list( cm_fh, diff --git a/util/cmake/qmake_parser.py b/util/cmake/qmake_parser.py index 7aba0784e2..e75e884959 100644 --- a/util/cmake/qmake_parser.py +++ b/util/cmake/qmake_parser.py @@ -219,7 +219,7 @@ class QmakeParser: def parse_requires_condition(s, l, t): # The following expression unwraps the condition via the additional info # set by originalTextFor. - condition_without_parentheses = s[t._original_start + 1 : t._original_end - 1] + condition_without_parentheses = s[t._original_start + 1: t._original_end - 1] # And this replaces the colons with '&&' similar how it's done for 'Condition'. condition_without_parentheses = ( -- cgit v1.2.3 From f1be97e01ebb22c4a8c6d2c22f685cd38a097148 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 10 Oct 2019 16:32:19 +0200 Subject: Reformat conversion scripts with black Change-Id: Ida7d42dc86c81315bc0727839a620fb68b7f0268 Reviewed-by: Leander Beernaert Reviewed-by: Simon Hausmann Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 2 +- util/cmake/pro2cmake.py | 74 ++++++++++++++++++++++++--------------- util/cmake/qmake_parser.py | 12 +++++-- 3 files changed, 56 insertions(+), 32 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 38b80e055f..32394f31c4 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -307,7 +307,7 @@ def map_condition(condition): print(f' XXXX Unknown condition "{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:] diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3bad2f0fc9..06c11f2d8d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -654,11 +654,11 @@ class ReplaceOperation(Operation): m = rex.search(s) if not m: return pattern, replacement - pattern = s[:m.start() + 1] - replacement = s[m.end():] + pattern = s[: m.start() + 1] + replacement = s[m.end() :] m = rex.search(replacement) if m: - replacement = replacement[:m.start() + 1] + replacement = replacement[: m.start() + 1] return pattern, replacement def __repr__(self): @@ -1010,7 +1010,7 @@ class Scope(object): qmake_conf_path = find_qmake_conf(os.path.abspath(self.currentdir)) qmake_conf_dir_path = os.path.dirname(qmake_conf_path) project_relative_path = os.path.relpath(qmake_conf_dir_path, self.currentdir) - return ['${CMAKE_CURRENT_SOURCE_DIR}/' + project_relative_path] + return ["${CMAKE_CURRENT_SOURCE_DIR}/" + project_relative_path] if key == "_PRO_FILE_PWD_": return ["${CMAKE_CURRENT_SOURCE_DIR}"] @@ -1109,7 +1109,7 @@ class Scope(object): 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():] + result = result[: match.start()] + replacement_str + result[match.end() :] result = self._replace_env_var_value(result) if result == old_result: @@ -1155,11 +1155,12 @@ class Scope(object): def unwrap_if(input_string): # Compute the grammar only once. if not hasattr(unwrap_if, "if_grammar"): + def handle_expr_with_parentheses(s, l, t): # The following expression unwraps the condition via the # additional info set by originalTextFor, thus returning the # condition without parentheses. - condition_without_parentheses = s[t._original_start + 1: t._original_end - 1] + condition_without_parentheses = s[t._original_start + 1 : t._original_end - 1] # Re-add the parentheses, but with spaces in-between. This # fixes map_condition -> map_platform to apply properly. @@ -1498,8 +1499,9 @@ def sort_sources(sources: List[str]) -> List[str]: return lines -def _map_libraries_to_cmake(libraries: List[str], known_libraries: Set[str], - is_example: bool = False) -> List[str]: +def _map_libraries_to_cmake( + libraries: List[str], known_libraries: Set[str], is_example: bool = False +) -> List[str]: result = [] # type: List[str] is_framework = False @@ -1530,8 +1532,7 @@ def _map_libraries_to_cmake(libraries: List[str], known_libraries: Set[str], def extract_cmake_libraries( - scope: Scope, *, known_libraries: Optional[Set[str]] = None, - is_example: bool = False + scope: Scope, *, known_libraries: Optional[Set[str]] = None, is_example: bool = False ) -> Tuple[List[str], List[str]]: if known_libraries is None: known_libraries = set() @@ -1900,7 +1901,9 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, else: immediate_prefix = "/" immediate_base_list = scope.get(f"{r}.base") - assert len(immediate_base_list) < 2, f"immediate base directory must be at most one entry" + assert ( + len(immediate_base_list) < 2 + ), f"immediate base directory must be at most one entry" immediate_base = replace_path_constants("".join(immediate_base_list), scope) immediate_lang = None immediate_name = f"qmake_{r}" @@ -1983,7 +1986,7 @@ def write_qlalrsources(cm_fh: IO[str], target: str, scope: Scope, indent: int = indent += 1 cm_fh.write(f"{spaces(indent)}{';'.join(sources)}\n") cm_fh.write(f"{spaces(indent)}{target}\n") - cm_fh.write(f"{spaces(indent)}\"\"\n") + cm_fh.write(f'{spaces(indent)}""\n') cm_fh.write(")\n") @@ -2141,8 +2144,8 @@ def write_android_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = def write_wayland_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): - client_sources = scope.get_files('WAYLANDCLIENTSOURCES', use_vpath=True) - server_sources = scope.get_files('WAYLANDSERVERSOURCES', use_vpath=True) + client_sources = scope.get_files("WAYLANDCLIENTSOURCES", use_vpath=True) + server_sources = scope.get_files("WAYLANDSERVERSOURCES", use_vpath=True) if len(client_sources) == 0 and len(server_sources) == 0: return @@ -2156,12 +2159,16 @@ def write_wayland_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = if len(client_sources) != 0: cm_fh.write(f"\n{spaces(indent)}qt6_generate_wayland_protocol_client_sources({target}\n") - write_list(cm_fh, client_sources, 'FILES', indent + 1, prefix='${CMAKE_CURRENT_SOURCE_DIR}/') + write_list( + cm_fh, client_sources, "FILES", indent + 1, prefix="${CMAKE_CURRENT_SOURCE_DIR}/" + ) cm_fh.write(f"{spaces(indent)})\n") if len(server_sources) != 0: cm_fh.write(f"\n{spaces(indent)}qt6_generate_wayland_protocol_server_sources({target}\n") - write_list(cm_fh, server_sources, 'FILES', indent + 1, prefix='${CMAKE_CURRENT_SOURCE_DIR}/') + write_list( + cm_fh, server_sources, "FILES", indent + 1, prefix="${CMAKE_CURRENT_SOURCE_DIR}/" + ) cm_fh.write(f"{spaces(indent)})\n") if condition != "ON": @@ -2263,7 +2270,9 @@ def handle_source_subtractions(scopes: List[Scope]): additions = modified_sources[modified_source].get("additions", set()) assert isinstance(additions, set), f"Additions must be a set, got {additions} instead." subtractions = modified_sources[modified_source].get("subtractions", set()) - assert isinstance(subtractions, set), f"Subtractions must be a set, got {additions} instead." + assert isinstance( + subtractions, set + ), f"Subtractions must be a set, got {additions} instead." add_to_no_pch_sources = modified_sources[modified_source].get( "add_to_no_pch_sources", False ) @@ -2433,10 +2442,10 @@ def write_generic_library(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> s library_type = "" - if 'dll' in scope.get('CONFIG'): + if "dll" in scope.get("CONFIG"): library_type = "SHARED" - if 'static' in scope.get('CONFIG'): + if "static" in scope.get("CONFIG"): library_type = "STATIC" extra_lines = [] @@ -2444,7 +2453,7 @@ def write_generic_library(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> s if library_type: extra_lines.append(library_type) - target_path = scope.expandString('target.path') + target_path = scope.expandString("target.path") target_path = replace_path_constants(target_path, scope) if target_path: extra_lines.append(f'INSTALL_DIRECTORY "{target_path}"') @@ -2463,6 +2472,7 @@ def write_generic_library(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> s return target_name + def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: module_name = scope.TARGET if not module_name.startswith("Qt"): @@ -2641,10 +2651,10 @@ def write_example( binary_name = scope.TARGET assert binary_name - example_install_dir = scope.get_string('target.path') + example_install_dir = scope.get_string("target.path") if not example_install_dir: - example_install_dir = 'examples' - example_install_dir = example_install_dir.replace('$$[QT_INSTALL_EXAMPLES]', 'examples') + example_install_dir = "examples" + example_install_dir = example_install_dir.replace("$$[QT_INSTALL_EXAMPLES]", "examples") cm_fh.write( "cmake_minimum_required(VERSION 3.14)\n" @@ -2740,10 +2750,18 @@ def write_example( indent += 1 write_include_paths( - cm_fh, scope, f"target_include_directories({binary_name} PUBLIC", indent=indent, footer=")" + cm_fh, + scope, + f"target_include_directories({binary_name} PUBLIC", + indent=indent, + footer=")", ) write_defines( - cm_fh, scope, f"target_compile_definitions({binary_name} PUBLIC", indent=indent, footer=")" + cm_fh, + scope, + f"target_compile_definitions({binary_name} PUBLIC", + indent=indent, + footer=")", ) (scope_public_libs, scope_private_libs) = extract_cmake_libraries(scope, is_example=True) @@ -2806,7 +2824,7 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: plugin_function_name = "add_qml_module" qmldir = write_qml_plugin(cm_fh, plugin_name, scope, indent=indent, extra_lines=extra) else: - target_path = scope.expandString('target.path') + target_path = scope.expandString("target.path") target_path = replace_path_constants(target_path, scope) if target_path: extra.append(f'INSTALL_DIRECTORY "{target_path}"') @@ -2815,8 +2833,8 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: if plugin_class_name: extra.append(f"CLASS_NAME {plugin_class_name}") - if 'static' in scope.get('CONFIG'): - extra.append('STATIC') + if "static" in scope.get("CONFIG"): + extra.append("STATIC") write_main_part( cm_fh, diff --git a/util/cmake/qmake_parser.py b/util/cmake/qmake_parser.py index e75e884959..95cbe8aa5b 100644 --- a/util/cmake/qmake_parser.py +++ b/util/cmake/qmake_parser.py @@ -35,6 +35,7 @@ from itertools import chain import pyparsing as pp # type: ignore from helper import _set_up_py_parsing_nicer_debug_output + _set_up_py_parsing_nicer_debug_output(pp) @@ -99,7 +100,7 @@ def handle_function_value(group: pp.ParseResults): if function_name == "basename": if len(function_args) != 1: print(f"XXXX basename with more than one argument") - if function_args[0] == '_PRO_FILE_PWD_': + if function_args[0] == "_PRO_FILE_PWD_": return os.path.basename(os.getcwd()) print(f"XXXX basename with value other than _PRO_FILE_PWD_") return os.path.basename(str(function_args[0])) @@ -192,7 +193,12 @@ class QmakeParser: Values = add_element("Values", pp.ZeroOrMore(Value)("value")) Op = add_element( - "OP", pp.Literal("=") | pp.Literal("-=") | pp.Literal("+=") | pp.Literal("*=") | pp.Literal("~=") + "OP", + pp.Literal("=") + | pp.Literal("-=") + | pp.Literal("+=") + | pp.Literal("*=") + | pp.Literal("~="), ) Key = add_element("Key", Identifier) @@ -219,7 +225,7 @@ class QmakeParser: def parse_requires_condition(s, l, t): # The following expression unwraps the condition via the additional info # set by originalTextFor. - condition_without_parentheses = s[t._original_start + 1: t._original_end - 1] + condition_without_parentheses = s[t._original_start + 1 : t._original_end - 1] # And this replaces the colons with '&&' similar how it's done for 'Condition'. condition_without_parentheses = ( -- cgit v1.2.3 From 20f4f50a3a84cd0405990d83da008cb7aff2ceaa Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 10 Oct 2019 16:47:17 +0200 Subject: Fix invalid condition in module generation Fix invalid condition which would cause valid qt modules not to be written out as modules. Change-Id: Id4b408f2502a34c011595c4602145b6980ee9d58 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 06c11f2d8d..0b814f432f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2979,12 +2979,12 @@ def handle_app_or_lib( elif is_plugin: assert not is_example target = write_plugin(cm_fh, scope, indent=indent) - elif is_lib and "qt_module" in scope.get("_LOADED"): - assert not is_example - target = write_module(cm_fh, scope, indent=indent) - elif is_lib: + elif is_lib and not "qt_module" in scope.get("_LOADED"): assert not is_example target = write_generic_library(cm_fh, scope, indent=indent) + 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"): assert not is_example target = write_tool(cm_fh, scope, indent=indent) -- cgit v1.2.3 From c31f2683cc84b29b0044ec258b0055f6e5be4944 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 10 Oct 2019 15:56:57 +0200 Subject: cmake scripts: make pro_conversion_rate.py mypy clean Change-Id: Id5b210361b6df61bb54324507fcff259cc4091e4 Reviewed-by: Alexandru Croitor Reviewed-by: Tobias Hunger --- util/cmake/pro_conversion_rate.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro_conversion_rate.py b/util/cmake/pro_conversion_rate.py index 1224f999a8..3c0c7e3070 100755 --- a/util/cmake/pro_conversion_rate.py +++ b/util/cmake/pro_conversion_rate.py @@ -43,6 +43,7 @@ from argparse import ArgumentParser import os import typing +from typing import Dict, Union from timeit import default_timer @@ -94,7 +95,7 @@ class Blacklist: return False def is_blacklisted_part_aho(self, dir_path: str) -> bool: - return self.tree.search(dir_path) is not None + return self.tree.search(dir_path) is not None # type: ignore def recursive_scan(path: str, extension: str, result_paths: typing.List[str], blacklist: Blacklist): @@ -124,7 +125,7 @@ def compute_stats( existing_pros: int, missing_pros: int, ) -> dict: - stats = {} + stats: Dict[str, Dict[str, Union[str, int, float]]] = {} stats["total projects"] = {"label": "Total pro files found", "value": total_pros} stats["existing projects"] = { "label": "Existing CMakeLists.txt files found", @@ -142,17 +143,21 @@ def compute_stats( for p in pros_with_missing_project: rel_path = os.path.relpath(p, src_path) if rel_path.startswith("examples"): + assert isinstance(stats["missing examples"]["value"], int) stats["missing examples"]["value"] += 1 elif rel_path.startswith("tests"): + assert isinstance(stats["missing tests"]["value"], int) stats["missing tests"]["value"] += 1 elif rel_path.startswith(os.path.join("src", "plugins")): + assert isinstance(stats["missing plugins"]["value"], int) stats["missing plugins"]["value"] += 1 elif rel_path.startswith("src"): + assert isinstance(stats["missing src"]["value"], int) 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 int(stats[stat]["value"]) > 0: + stats[stat]["percentage"] = round(float(stats[stat]["value"]) * 100 / total_pros, 2) return stats -- cgit v1.2.3 From 2d1aee85958c7a3ea7e322c06f882185e40aa1ac Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 10 Oct 2019 17:05:59 +0200 Subject: cmake scripts: minor cleanup not in is considered easier to read and thus recommended (flake8) and avoid one temporary name that is never used. Adjust flake8 to be compatible with black formatting. Change-Id: Ia049adb2344f11b53c78574972f6d9d670f4e66d Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/Makefile | 2 +- util/cmake/condition_simplifier_cache.py | 2 +- util/cmake/pro2cmake.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/Makefile b/util/cmake/Makefile index 402e45b707..67affa0e95 100644 --- a/util/cmake/Makefile +++ b/util/cmake/Makefile @@ -5,7 +5,7 @@ coverage: pytest --cov . flake8: - flake8 *.py --ignore=E501,E266,W503 + flake8 *.py --ignore=E501,E266,E203,W503 pytest: pytest diff --git a/util/cmake/condition_simplifier_cache.py b/util/cmake/condition_simplifier_cache.py index 6303ff828e..4afd91597b 100644 --- a/util/cmake/condition_simplifier_cache.py +++ b/util/cmake/condition_simplifier_cache.py @@ -142,7 +142,7 @@ def simplify_condition_memoize(f: Callable[[str], str]): os.makedirs(os.path.dirname(cache_path), exist_ok=True) # Create the file if it doesn't exist, but don't override # it. - with open(cache_path, "a") as temp_file_handle: + with open(cache_path, "a"): pass updated_cache = cache_file_content diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0b814f432f..bcb145cb59 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2979,7 +2979,7 @@ def handle_app_or_lib( elif is_plugin: assert not is_example target = write_plugin(cm_fh, scope, indent=indent) - elif is_lib and not "qt_module" in scope.get("_LOADED"): + elif is_lib and "qt_module" not in scope.get("_LOADED"): assert not is_example target = write_generic_library(cm_fh, scope, indent=indent) elif is_lib or "qt_module" in scope.get("_LOADED"): -- cgit v1.2.3 From ffb28b99303a7ba68d249c5da0e74587058dcde5 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 10 Oct 2019 17:15:58 +0200 Subject: cmake scripts: format with black There is agreement to use black for formatting, run it and make flake8 happy with its output. Change-Id: I800d764a50cacba0eac1d8efb5c1dee0cf10a4ad Reviewed-by: Simon Hausmann --- util/cmake/Makefile | 8 +++++++- util/cmake/helper.py | 15 +++++++++++---- util/cmake/requirements.txt | 2 ++ 3 files changed, 20 insertions(+), 5 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/Makefile b/util/cmake/Makefile index 67affa0e95..2243ad111e 100644 --- a/util/cmake/Makefile +++ b/util/cmake/Makefile @@ -1,9 +1,15 @@ -test: flake8 mypy pytest +test: flake8 mypy pytest black_format_check coverage: pytest --cov . +format: + black *.py --line-length 100 + +black_format_check: + black *.py --line-length 100 --check + flake8: flake8 *.py --ignore=E501,E266,E203,W503 diff --git a/util/cmake/helper.py b/util/cmake/helper.py index b6cfb33860..ed044755b6 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -281,7 +281,9 @@ _qt_library_map = [ ), LibraryMapping("quickwidgets", "Qt6", "Qt::QuickWidgets", extra=["COMPONENTS", "QuickWidgets"]), LibraryMapping("render", "Qt6", "Qt::3DRender", extra=["COMPONENTS", "3DRender"]), - LibraryMapping("remoteobjects", "Qt6", "Qt::RemoteObjects", extra=["COMPONENTS", "RemoteObjects"]), + LibraryMapping( + "remoteobjects", "Qt6", "Qt::RemoteObjects", extra=["COMPONENTS", "RemoteObjects"] + ), LibraryMapping("script", "Qt6", "Qt::Script", extra=["COMPONENTS", "Script"]), LibraryMapping("scripttools", "Qt6", "Qt::ScriptTools", extra=["COMPONENTS", "ScriptTools"]), LibraryMapping("scxml", "Qt6", "Qt::Scxml", extra=["COMPONENTS", "Scxml"]), @@ -312,7 +314,10 @@ _qt_library_map = [ "waylandclient", "Qt6", "Qt::WaylandClient", extra=["COMPONENTS", "WaylandClient"] ), LibraryMapping( - "waylandcompositor", "Qt6", "Qt::WaylandCompositor", extra=["COMPONENTS", "WaylandCompositor"] + "waylandcompositor", + "Qt6", + "Qt::WaylandCompositor", + extra=["COMPONENTS", "WaylandCompositor"], ), LibraryMapping("webchannel", "Qt6", "Qt::WebChannel", extra=["COMPONENTS", "WebChannel"]), LibraryMapping("webengine", "Qt6", "Qt::WebEngine", extra=["COMPONENTS", "WebEngine"]), @@ -417,7 +422,9 @@ _library_map = [ LibraryMapping("wayland-client", "Wayland", "Wayland::Client"), LibraryMapping("wayland-cursor", "Wayland", "Wayland::Cursor"), LibraryMapping("wayland-egl", "Wayland", "Wayland::Egl"), - LibraryMapping('wayland-kms', 'Waylandkms', 'PkgConfig::Waylandkms'), # TODO: check if this actually works + LibraryMapping( + "wayland-kms", "Waylandkms", "PkgConfig::Waylandkms" + ), # TODO: check if this actually works LibraryMapping("x11", "X11", "X11::X11"), LibraryMapping("x11sm", "X11", "${X11_SM_LIB} ${X11_ICE_LIB}", resultVariable="X11_SM"), LibraryMapping( @@ -495,7 +502,7 @@ _library_map = [ "xcb_xkb", "XCB", "XCB::XKB", extra=["COMPONENTS", "XKB"], resultVariable="XCB_XKB" ), LibraryMapping("xcb_xlib", "X11_XCB", "X11::XCB"), - LibraryMapping('xcomposite', 'XComposite', 'PkgConfig::XComposite'), + LibraryMapping("xcomposite", "XComposite", "PkgConfig::XComposite"), 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"]), diff --git a/util/cmake/requirements.txt b/util/cmake/requirements.txt index 16febdd4a3..16fb99a08c 100644 --- a/util/cmake/requirements.txt +++ b/util/cmake/requirements.txt @@ -4,3 +4,5 @@ mypy; python_version >= '3.7' pyparsing; python_version >= '3.7' sympy; python_version >= '3.7' portalocker; python_version >= '3.7' +black; python_version >= '3.7' + -- cgit v1.2.3 From a986455d108d99ebc8234b9bfab2407c33422e41 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 11 Oct 2019 11:12:43 +0200 Subject: configurejson2cmake: Re-add wayland_server library for qtbase Now there are two of them, one used in qtbase and one in qtwayland. In the future we should merge them. Amends 08aba5ea0ab4196779c79a4e8d8ba6d510b14e12 Change-Id: Ife98df28c762836277a02a34dd07e82ce7a1e871 Reviewed-by: Johan Helsing --- util/cmake/helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index ed044755b6..570dc14868 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -418,7 +418,8 @@ _library_map = [ 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_server', 'Wayland', 'Wayland::Server'), # used in qtbase/src/gui + LibraryMapping("wayland-server", "Wayland", "Wayland::Server"), # used in qtwayland LibraryMapping("wayland-client", "Wayland", "Wayland::Client"), LibraryMapping("wayland-cursor", "Wayland", "Wayland::Cursor"), LibraryMapping("wayland-egl", "Wayland", "Wayland::Egl"), -- cgit v1.2.3 From fd431fadc1a5af1bd3922f5148f39b574cf272f5 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 11 Oct 2019 11:17:55 +0200 Subject: Fix styling of configurejson2cmake script to make flake8 happy Amends a986455d108d99ebc8234b9bfab2407c33422e41 Change-Id: I5a6327f5bfd9d73e518f0ec1d9bb418282c45077 Reviewed-by: Simon Hausmann --- util/cmake/helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 570dc14868..9927be28bd 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -418,8 +418,8 @@ _library_map = [ LibraryMapping("udev", "Libudev", "PkgConfig::Libudev"), LibraryMapping("udev", "Libudev", "PkgConfig::Libudev"), # see also libudev! LibraryMapping("vulkan", "Vulkan", "Vulkan::Vulkan"), - LibraryMapping('wayland_server', 'Wayland', 'Wayland::Server'), # used in qtbase/src/gui - LibraryMapping("wayland-server", "Wayland", "Wayland::Server"), # used in qtwayland + LibraryMapping("wayland_server", "Wayland", "Wayland::Server"), # used in qtbase/src/gui + LibraryMapping("wayland-server", "Wayland", "Wayland::Server"), # used in qtwayland LibraryMapping("wayland-client", "Wayland", "Wayland::Client"), LibraryMapping("wayland-cursor", "Wayland", "Wayland::Cursor"), LibraryMapping("wayland-egl", "Wayland", "Wayland::Egl"), -- cgit v1.2.3 From 1dd82a88438a8e76bc25c5ef122a3b6afa0bcbdf Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 10 Oct 2019 17:02:59 +0200 Subject: Add SKIP_INSTALL to add_qt_plugin() Provide a SKIP_INSTALL argument to add_qt_plugin for test cases with plugins lacking install information. Change-Id: Iddb3843fab1790d69d64686530a46057a2ff0477 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index bcb145cb59..bf73c87e44 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2828,6 +2828,8 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: target_path = replace_path_constants(target_path, scope) if target_path: extra.append(f'INSTALL_DIRECTORY "{target_path}"') + else: + extra.append('SKIP_INSTALL') plugin_class_name = scope.get_string("PLUGIN_CLASS_NAME") if plugin_class_name: -- cgit v1.2.3 From f2d15b9683c58755cf53250ca30fa60b9c4eb215 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 11 Oct 2019 13:42:06 +0200 Subject: Minor fix to qt_process_qlalr API It's probably best to make it follow the usual calling convention that the associated (consuming) target is the first parameter of the function. So first this change accepts both formats. Change-Id: I1f20706b23d5e819e0eb689eecedb3afb49df3b7 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index bf73c87e44..a9504d17c9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1984,8 +1984,8 @@ def write_qlalrsources(cm_fh: IO[str], target: str, scope: Scope, indent: int = cm_fh.write("\n# QLALR Grammars:\n") cm_fh.write(f"qt_process_qlalr(\n") indent += 1 - cm_fh.write(f"{spaces(indent)}{';'.join(sources)}\n") cm_fh.write(f"{spaces(indent)}{target}\n") + cm_fh.write(f"{spaces(indent)}{';'.join(sources)}\n") cm_fh.write(f'{spaces(indent)}""\n') cm_fh.write(")\n") -- cgit v1.2.3 From dbb29e8235941c646dc4b455db6e89fc6e0b590f Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 11 Oct 2019 14:06:25 +0200 Subject: Expand test install path Expand target.path value for tests. Change-Id: Ic35122b0ef1440950c0ef2ba9a04e8697ca2cdfe Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index a9504d17c9..d61a5d27d2 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2651,7 +2651,7 @@ def write_example( binary_name = scope.TARGET assert binary_name - example_install_dir = scope.get_string("target.path") + example_install_dir = scope.expandString("target.path") if not example_install_dir: example_install_dir = "examples" example_install_dir = example_install_dir.replace("$$[QT_INSTALL_EXAMPLES]", "examples") -- cgit v1.2.3 From 0a85f4f834d499ee4b4560c92db7516db898b76b Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 10 Oct 2019 15:56:16 +0200 Subject: pro2cmake: Handle simd assignments in scopes Make sure to process simd assignments in scopes by enclosing the add_qt_simd calls into if blocks. Change-Id: I3c7f2466cfa5bb0136d40deffb190ea8aabfb572 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index d61a5d27d2..eaef854f32 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2100,18 +2100,40 @@ def write_simd_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): "avx512common", "avx512core", ] + + simd_io_string = io.StringIO() + + condition = "ON" + if scope.total_condition: + condition = map_to_cmake_condition(scope.total_condition) + + if condition != "ON": + indent += 1 + for simd in simd_options: SIMD = simd.upper() write_source_file_list( - cm_fh, + simd_io_string, scope, "SOURCES", [f"{SIMD}_HEADERS", f"{SIMD}_SOURCES", f"{SIMD}_C_SOURCES", f"{SIMD}_ASM"], - indent, + indent=indent, header=f"add_qt_simd_part({target} SIMD {simd}\n", - footer=")\n\n", + footer=")\n", ) + simd_string = simd_io_string.getvalue() + if simd_string: + simd_string = simd_string.rstrip("\n") + cond_start = "" + cond_end = "" + if condition != "ON": + cond_start = f"{spaces(indent - 1)}if({condition})" + cond_end = f"{spaces(indent - 1)}endif()" + + extend_scope = f"\n{cond_start}\n" f"{simd_string}" f"\n{cond_end}\n" + cm_fh.write(extend_scope) + def write_android_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): keys = [ @@ -2431,6 +2453,8 @@ def write_main_part( write_android_part(cm_fh, name, c, indent=indent) write_wayland_part(cm_fh, name, c, indent=indent) write_extend_target(cm_fh, name, c, indent=indent) + write_simd_part(cm_fh, name, c, indent=indent) + ignored_keys_report = write_ignored_keys(c, spaces(indent)) if ignored_keys_report: cm_fh.write(ignored_keys_report) -- cgit v1.2.3 From dd57f9900478cdec615e25660a9336f8c25ab043 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 11 Oct 2019 14:32:43 +0200 Subject: pro2cmake: Handle source files in example scopes Change-Id: I653801d6b13eb144719a4e6eac4779b1cd15e75c Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index eaef854f32..18855e66b4 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2760,6 +2760,8 @@ def write_example( write_all_source_file_lists(cm_fh, scope, add_target, indent=0) cm_fh.write(")\n") + handling_first_scope = True + for scope in scopes: # write wayland already has condition scope handling write_wayland_part(cm_fh, binary_name, scope, indent=0) @@ -2773,6 +2775,10 @@ def write_example( cm_fh.write(f"\n{spaces(indent)}if({condition})\n") indent += 1 + if not handling_first_scope: + target_sources = f"target_sources({binary_name} PUBLIC" + write_all_source_file_lists(cm_fh, scope, target_sources, indent=indent, footer=")\n") + write_include_paths( cm_fh, scope, @@ -2817,6 +2823,7 @@ def write_example( if condition != "ON": indent -= 1 cm_fh.write(f"\n{spaces(indent)}endif()\n") + handling_first_scope = False if qmldir: write_qml_plugin_epilogue(cm_fh, binary_name, scope, qmldir, indent) -- cgit v1.2.3 From 9bd6cec74dbbc5aece55dc0c8808494db29b9963 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Fri, 11 Oct 2019 14:35:35 +0200 Subject: pro2cmake: Translate C++ standard version checks Translate checks like contains(QT_CONFIG, c++11) to cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES Also allows contains(CONFIG, c++11) and different versions of course. Change-Id: I0f51a1ee7c92df6b87c31d0fb64c22fdba2002ec Reviewed-by: Simon Hausmann Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 18855e66b4..8d3514a3ba 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1227,6 +1227,7 @@ def map_condition(condition: str) -> str: condition = unwrap_if(condition) condition = re.sub(r"\bisEmpty\s*\((.*?)\)", r"\1_ISEMPTY", condition) + condition = re.sub(r"\bcontains\s*\(\s*(?:QT_)?CONFIG\s*,\s*c\+\+(\d+)\)", r"cxx_std_\1 IN_LIST CMAKE_CXX_COMPILE_FEATURES", 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) -- cgit v1.2.3 From d62a2797ae45d5a13ff3852cb322495a0e6d94a3 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 11 Oct 2019 16:43:29 +0200 Subject: Handle empty contents for condition scopes in examples Only print condition scopes when there is something to print. Change-Id: I24151ca4834317940712b6173046abe91aac5628 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8d3514a3ba..6c53a719e2 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2768,62 +2768,70 @@ def write_example( write_wayland_part(cm_fh, binary_name, scope, indent=0) # The following options do not + io_string = io.StringIO() + condition_str = "" condition = "ON" if scope.total_condition: condition = map_to_cmake_condition(scope.total_condition) if condition != "ON": - cm_fh.write(f"\n{spaces(indent)}if({condition})\n") + condition_str = f"\n{spaces(indent)}if({condition})\n" indent += 1 if not handling_first_scope: target_sources = f"target_sources({binary_name} PUBLIC" - write_all_source_file_lists(cm_fh, scope, target_sources, indent=indent, footer=")\n") + write_all_source_file_lists(io_string, scope, target_sources, indent=indent, footer=")\n") write_include_paths( - cm_fh, + io_string, scope, f"target_include_directories({binary_name} PUBLIC", indent=indent, - footer=")", + footer=")\n", ) write_defines( - cm_fh, + io_string, scope, f"target_compile_definitions({binary_name} PUBLIC", indent=indent, - footer=")", + footer=")\n", ) (scope_public_libs, scope_private_libs) = extract_cmake_libraries(scope, is_example=True) write_list( - cm_fh, + io_string, scope_private_libs, "", indent=indent, header=f"target_link_libraries({binary_name} PRIVATE\n", - footer=")", + footer=")\n", ) write_list( - cm_fh, + io_string, scope_public_libs, "", indent=indent, header=f"target_link_libraries({binary_name} PUBLIC\n", - footer=")", + footer=")\n", ) write_compile_options( - cm_fh, scope, f"target_compile_options({binary_name}", indent=indent, footer=")" + io_string, scope, f"target_compile_options({binary_name}", indent=indent, footer=")\n" ) - write_resources(cm_fh, binary_name, scope, indent=indent, is_example=True) - write_statecharts(cm_fh, binary_name, scope, indent=indent, is_example=True) - write_repc_files(cm_fh, binary_name, scope, indent=indent) + write_resources(io_string, binary_name, scope, indent=indent, is_example=True) + write_statecharts(io_string, binary_name, scope, indent=indent, is_example=True) + write_repc_files(io_string, binary_name, scope, indent=indent) if condition != "ON": indent -= 1 - cm_fh.write(f"\n{spaces(indent)}endif()\n") + string = io_string.getvalue() + if len(string) != 0: + string = string.rstrip("\n") + cm_fh.write(f"{condition_str}{string}\n") + if condition != "ON": + cm_fh.write(f"{spaces(indent)}endif()\n") + handling_first_scope = False if qmldir: -- cgit v1.2.3 From 665f75f1f112f948f8acfadc1d54b0df8c4211a4 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 14 Oct 2019 11:12:47 +0200 Subject: Run blake on pro2cmake.py Change-Id: I16cc7058f817d22e2c72fb6d42c472250a7e6cfb Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6c53a719e2..1c3648f718 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1227,7 +1227,11 @@ def map_condition(condition: str) -> str: condition = unwrap_if(condition) condition = re.sub(r"\bisEmpty\s*\((.*?)\)", r"\1_ISEMPTY", condition) - condition = re.sub(r"\bcontains\s*\(\s*(?:QT_)?CONFIG\s*,\s*c\+\+(\d+)\)", r"cxx_std_\1 IN_LIST CMAKE_CXX_COMPILE_FEATURES", condition) + condition = re.sub( + r"\bcontains\s*\(\s*(?:QT_)?CONFIG\s*,\s*c\+\+(\d+)\)", + r"cxx_std_\1 IN_LIST CMAKE_CXX_COMPILE_FEATURES", + 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) @@ -2780,7 +2784,9 @@ def write_example( if not handling_first_scope: target_sources = f"target_sources({binary_name} PUBLIC" - write_all_source_file_lists(io_string, scope, target_sources, indent=indent, footer=")\n") + write_all_source_file_lists( + io_string, scope, target_sources, indent=indent, footer=")\n" + ) write_include_paths( io_string, @@ -2869,7 +2875,7 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: if target_path: extra.append(f'INSTALL_DIRECTORY "{target_path}"') else: - extra.append('SKIP_INSTALL') + extra.append("SKIP_INSTALL") plugin_class_name = scope.get_string("PLUGIN_CLASS_NAME") if plugin_class_name: -- cgit v1.2.3 From 6694689a26c97d76f2fb7a518f0788b1e0eaf4e6 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 14 Oct 2019 15:18:50 +0200 Subject: Fix Android build from multi-arch qmake changes Add condition replacements for the android ABIs. Add a replacement for QT_ARCH to ANDROID_ABI, since QT_ARCH is only used with the android build for now. Change-Id: I553d7910546de32236f723ec2e9a05a18da76130 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1c3648f718..2722fda906 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1012,6 +1012,9 @@ class Scope(object): project_relative_path = os.path.relpath(qmake_conf_dir_path, self.currentdir) return ["${CMAKE_CURRENT_SOURCE_DIR}/" + project_relative_path] + if key == "QT_ARCH": + return ["${ANDROID_ABI}"] + if key == "_PRO_FILE_PWD_": return ["${CMAKE_CURRENT_SOURCE_DIR}"] if key == "PWD": @@ -1245,6 +1248,12 @@ def map_condition(condition: str) -> str: condition = condition.replace("*-llvm", "CLANG") condition = condition.replace("win32-*", "WIN32") + # new conditions added by the android multi arch qmake build + condition = re.sub(r'x86[^\_]', "TEST_architecture_arch STREQUAL x86", condition) + condition = condition.replace('x86_64', "TEST_architecture_arch STREQUAL x86_64") + condition = condition.replace('arm64-v8a', "TEST_architecture_arch STREQUAL arm64") + condition = condition.replace('armeabi-v7a', "TEST_architecture_arch STREQUAL arm") + pattern = r"CONFIG\((debug|release),debug\|release\)" match_result = re.match(pattern, condition) if match_result: -- cgit v1.2.3 From dcb1253d931942ebb4ec16bbe843941e25df5aff Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 14 Oct 2019 15:54:02 +0200 Subject: Fix up android architecture suffixing Don't globally map any variable use of QT_ARCH to ANDROID_ABI, as that'll break if somebody uses it in a different context. Instead the CMAKE_SYSTEM_PROCESSOR variable provides a reasonable value and it is also set by the Android toolchain files. Change-Id: Ibf203c39db586bbec5b800a365d83b3a509dbb62 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 2722fda906..21946883fd 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1013,7 +1013,7 @@ class Scope(object): return ["${CMAKE_CURRENT_SOURCE_DIR}/" + project_relative_path] if key == "QT_ARCH": - return ["${ANDROID_ABI}"] + return ["${CMAKE_SYSTEM_PROCESSOR}"] if key == "_PRO_FILE_PWD_": return ["${CMAKE_CURRENT_SOURCE_DIR}"] -- cgit v1.2.3 From 190e9dcdcf1a2debfbf624af4cb2154172908a64 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 11 Oct 2019 16:55:55 +0200 Subject: Regenerate files after dev -> wip/cmake merge Note the following bigger things that had to be done: Handle GSS library / feature with a new custom find module. Implement rudimentary support for relocatability (does not currently handle extprefix). Change-Id: Ic6cd27dda7ebca9829f51cb42ea76fff6d1767ef Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 4 ++++ util/cmake/helper.py | 1 + 2 files changed, 5 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 32394f31c4..11918f1a32 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -57,6 +57,8 @@ def map_tests(test: str) -> Optional[str]: "c++11": "$", "c++14": "$", "c++1z": "$", + "c++17": "$", + "c++20": "$", "c99": "$", "c11": "$", "x86SimdAlways": "ON", # FIXME: Make this actually do a compile test. @@ -489,6 +491,8 @@ def parseTest(ctx, test, data, cm_fh): skip_tests = { "c++11", "c++14", + "c++17", + "c++20", "c++1y", "c++1z", "c11", diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 9927be28bd..df6b8773f0 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -374,6 +374,7 @@ _library_map = [ LibraryMapping("glib", "GLIB2", "GLIB2::GLIB2"), LibraryMapping("gnu_iconv", None, None), LibraryMapping("gtk3", "GTK3", "PkgConfig::GTK3"), + LibraryMapping("gssapi", "GSSAPI", "GSSAPI::GSSAPI"), LibraryMapping("harfbuzz", "harfbuzz", "harfbuzz::harfbuzz"), LibraryMapping("host_dbus", None, None), LibraryMapping( -- cgit v1.2.3 From 1f9d2412163282b46135a08244268e84c430d7c6 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 15 Oct 2019 10:00:42 +0200 Subject: Update android arch condition conversion Only convert the architectures if they are in a stand alone form. This means conditions such as 'if(x86 OR x86_64)', 'if(x86)', among others. This also correctly converts statements such as 'equals(QT_ARCH,x86)'. Change-Id: I1c3b8580ff9e4077c03a229d894d2bd3d95dba3d Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 21946883fd..420df3011d 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1248,12 +1248,6 @@ def map_condition(condition: str) -> str: condition = condition.replace("*-llvm", "CLANG") condition = condition.replace("win32-*", "WIN32") - # new conditions added by the android multi arch qmake build - condition = re.sub(r'x86[^\_]', "TEST_architecture_arch STREQUAL x86", condition) - condition = condition.replace('x86_64', "TEST_architecture_arch STREQUAL x86_64") - condition = condition.replace('arm64-v8a', "TEST_architecture_arch STREQUAL arm64") - condition = condition.replace('armeabi-v7a', "TEST_architecture_arch STREQUAL arm") - pattern = r"CONFIG\((debug|release),debug\|release\)" match_result = re.match(pattern, condition) if match_result: @@ -1272,6 +1266,12 @@ def map_condition(condition: str) -> str: condition = condition.replace("&&", " AND ") condition = condition.replace("|", " OR ") + # new conditions added by the android multi arch qmake build + condition = re.sub(r'(^| )x86([^\_]|$)', "TEST_architecture_arch STREQUAL x86", condition) + condition = re.sub(r'(^| )x86_64', " TEST_architecture_arch STREQUAL x86_64", condition) + condition = re.sub(r'(^| )arm64-v8a', "TEST_architecture_arch STREQUAL arm64", condition) + condition = re.sub(r'(^| )armeabi-v7a', "TEST_architecture_arch STREQUAL arm", condition) + cmake_condition = "" for part in condition.split(): # some features contain e.g. linux, that should not be -- cgit v1.2.3 From 355a00270437d76fb98c14ac672ee662be0c06f5 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 15 Oct 2019 10:48:53 +0200 Subject: Fix add_qt_resource condition scopes Calls to add_qt_resource from extend_target were not being wrapped in a condition scope. Change-Id: I78cf889fcf4663660fd870bfb93eec90a4ca1c47 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 420df3011d..5041519348 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1971,9 +1971,10 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, ) if qrc_output: - cm_fh.write("\n# Resources:\n") + str_indent = spaces(indent) + cm_fh.write(f"\n{str_indent}# Resources:\n") for line in qrc_output.split("\n"): - cm_fh.write(f"{' ' * indent}{line}\n") + cm_fh.write(f"{str_indent}{line}\n") def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, is_example=False): @@ -2043,9 +2044,12 @@ def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, indent: int = extend_qt_string = extend_qt_io_string.getvalue() assert scope.total_condition, "Cannot write CONDITION when scope.condition is None" + + condition = map_to_cmake_condition(scope.total_condition) + extend_scope = ( f"\n{ind}extend_target({target} CONDITION" - f" {map_to_cmake_condition(scope.total_condition)}\n" + f" {condition}\n" f"{extend_qt_string}{ind})\n" ) @@ -2054,8 +2058,13 @@ def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, indent: int = cm_fh.write(extend_scope) - write_resources(cm_fh, target, scope, indent) - + io_string = io.StringIO() + write_resources(io_string, target, scope, indent + 1) + resource_string = io_string.getvalue() + if len(resource_string) != 0: + resource_string = resource_string.strip('\n').rstrip(f'\n{spaces(indent + 1)}') + cm_fh.write(f"\n{spaces(indent)}if({condition})\n{resource_string}") + cm_fh.write(f"\n{spaces(indent)}endif()\n") def flatten_scopes(scope: Scope) -> List[Scope]: result = [scope] # type: List[Scope] -- cgit v1.2.3 From ca33f45d70d451dd370df7ef551e6bba1975d43b Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Tue, 15 Oct 2019 13:34:46 +0200 Subject: Fix message about missing portalocker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ia82c0aab7c9085eee8a07c4a15485c0804e65e5a Reviewed-by: MĂĄrten Nordheim --- util/cmake/condition_simplifier_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/condition_simplifier_cache.py b/util/cmake/condition_simplifier_cache.py index 4afd91597b..58cd5b88c5 100644 --- a/util/cmake/condition_simplifier_cache.py +++ b/util/cmake/condition_simplifier_cache.py @@ -113,7 +113,7 @@ def open_file_safe(file_path: str, mode: str = "r+"): except ImportError: print( "The conversion script is missing a required package: portalocker. Please run " - "python -m pip install requirements.txt to install the missing dependency." + "python -m pip install -r requirements.txt to install the missing dependency." ) exit(1) -- cgit v1.2.3 From 2fa23e46c0b79a065d92a95033bfc3ae10b707cf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 Oct 2019 10:49:44 +0200 Subject: Fix C++ standard detection We cannot use a generator expression in an if statement, it does not work. Instead, we could inspect the CMAKE_C/CXX_COMPILE_FEATURES list, but unfortunately that's not reliable. For example it detects that ICPC supports C++17 when in fact that depends on the installed libstdc++. Therefore this patch revives our own configure tests. Change-Id: Ic3bc5762fbe81837722523e3881ac16e84628519 Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 11918f1a32..ba6c85af28 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -54,13 +54,8 @@ class LibraryMapping: def map_tests(test: str) -> Optional[str]: testmap = { - "c++11": "$", - "c++14": "$", - "c++1z": "$", - "c++17": "$", - "c++20": "$", - "c99": "$", - "c11": "$", + "c99": "c_std_99 IN_LIST CMAKE_C_COMPILE_FEATURES", + "c11": "c_std_11 IN_LIST CMAKE_C_COMPILE_FEATURES", "x86SimdAlways": "ON", # FIXME: Make this actually do a compile test. "aesni": "TEST_subarch_aes", "avx": "TEST_subarch_avx", @@ -489,12 +484,6 @@ def parseInput(ctx, sinput, data, cm_fh): # }, def parseTest(ctx, test, data, cm_fh): skip_tests = { - "c++11", - "c++14", - "c++17", - "c++20", - "c++1y", - "c++1z", "c11", "c99", "gc_binaries", @@ -572,6 +561,7 @@ endif() sourceCode = sourceCode.replace('"', '\\"') librariesCmakeName = "" + languageStandard = "" qmakeFixme = "" cm_fh.write(f"# {test}\n") @@ -594,6 +584,12 @@ endif() elif details["qmake"] == "CONFIG += c++11": # do nothing we're always in c++11 mode pass + elif details["qmake"] == "CONFIG += c++11 c++14": + languageStandard = "CXX_STANDARD 14" + elif details["qmake"] == "CONFIG += c++11 c++14 c++17": + languageStandard = "CXX_STANDARD 17" + elif details["qmake"] == "CONFIG += c++11 c++14 c++17 c++2a": + languageStandard = "CXX_STANDARD 20" else: qmakeFixme = f"# FIXME: qmake: {details['qmake']}\n" @@ -614,6 +610,8 @@ endif() cm_fh.write('"' + sourceCode + '"') if qmakeFixme != "": cm_fh.write(qmakeFixme) + if languageStandard != "": + cm_fh.write(f"\n {languageStandard}\n") cm_fh.write(")\n\n") elif data["type"] == "libclang": -- cgit v1.2.3 From 373be4200b162ae9b2244d7019770911bc73317a Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 18 Oct 2019 14:28:08 +0200 Subject: Add conversion code for Java code Add support to pro2cmake to convert java code for android. Add support to override API_LEVEL for the Android sdk jar file. If the sdk is not found, we'll default to the one located by QT_ANDROID_JAR. Change-Id: If0b746dc7f9148ac43e6592a4a4dd23d46bbd4cd Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 5041519348..24c77cb2b1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2692,6 +2692,44 @@ def write_find_package_section( cm_fh.write("\n") +def write_jar(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: + + target = scope.TARGET + + install_dir = scope.expandString("target.path") + if not install_dir: + raise RuntimeError("Could not locate jar install path") + install_dir = install_dir.replace("$$[QT_INSTALL_PREFIX]/", "") + + android_sdk_jar = "${QT_ANDROID_JAR}" + android_api_level = scope.get_string("API_VERSION") + if android_api_level: + cm_fh.write(f'{spaces(indent)}qt_get_android_sdk_jar_for_api("{android_api_level}" android_sdk)\n\n') + android_sdk_jar ="${android_sdk}" + + write_source_file_list( + cm_fh, + scope, + "", + ["JAVASOURCES"], + indent=indent, + header=f"set(java_sources\n", + footer=")\n", + ) + + cm_fh.write(f"{spaces(indent)}add_jar({target}\n") + cm_fh.write(f"{spaces(indent+1)}INCLUDE_JARS {android_sdk_jar}\n") + cm_fh.write(f"{spaces(indent+1)}SOURCES ${{java_sources}}\n") + cm_fh.write(f"{spaces(indent)})\n\n") + + cm_fh.write(f"{spaces(indent)}install_jar({target}\n") + cm_fh.write(f"{spaces(indent+1)}DESTINATION {install_dir}\n") + cm_fh.write(f"{spaces(indent+1)}COMPONENT Devel\n") + cm_fh.write(f"{spaces(indent)})\n\n") + + return target + + def write_example( cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = 0, is_plugin: bool = False ) -> str: @@ -3030,6 +3068,7 @@ def handle_app_or_lib( assert scope.TEMPLATE in ("app", "lib") config = scope.get("CONFIG") + is_jar = "java" in config is_lib = scope.TEMPLATE == "lib" is_qml_plugin = any("qml_plugin" == s for s in scope.get("_LOADED")) is_plugin = ( @@ -3040,7 +3079,9 @@ def handle_app_or_lib( val not in config for val in ["console", "cmdline"] ) and "testlib" not in scope.expand("QT") - if is_example: + if is_jar: + tar = write_jar(cm_fh, scope, indent=indent) + elif is_example: target = write_example(cm_fh, scope, gui, indent=indent, is_plugin=is_plugin) elif is_plugin: assert not is_example -- cgit v1.2.3 From 58964af094e836af94dde6673512d4dbe61d6f34 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 21 Oct 2019 14:32:07 +0200 Subject: Handle Library dependencies in compile tests Update configurejson2cmake.py to also write out the libraries compile tests depend on. Change-Id: I22dbc227a33b9b5d39a2198c6ee6062e7e1bf3de Reviewed-by: Simon Hausmann --- util/cmake/configurejson2cmake.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index ba6c85af28..0b91a8befd 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -593,20 +593,31 @@ endif() else: qmakeFixme = f"# FIXME: qmake: {details['qmake']}\n" + library_list = [] if "use" in data: - if data["use"] == "egl xcb_xlib": - librariesCmakeName = format(featureName(test)) + "_TEST_LIBRARIES" - cm_fh.write("if (HAVE_EGL AND X11_XCB_FOUND AND X11_FOUND)\n") - cm_fh.write(" set(" + librariesCmakeName + " EGL::EGL X11::X11 X11::XCB)\n") - cm_fh.write("endif()\n") - else: - qmakeFixme += f"# FIXME: use: {data['use']}\n" + for library in data["use"].split(" "): + if len(library) == 0: + continue + + mapped_library = find_3rd_party_library_mapping(library) + if not mapped_library: + qmakeFixme += f"# FIXME: use: unmapped library: {library}\n" + continue + library_list.append(mapped_library.targetName) + + cm_fh.write(f"qt_config_compile_test({featureName(test)}\n") cm_fh.write(lineify("LABEL", data.get("label", ""))) - if librariesCmakeName != "": - cm_fh.write(lineify("LIBRARIES", "${" + librariesCmakeName + "}")) - cm_fh.write(" CODE\n") + if librariesCmakeName != "" or len(library_list) != 0: + cm_fh.write(" LIBRARIES\n") + if librariesCmakeName != "": + cm_fh.write(lineify("", "${" + librariesCmakeName + "}")) + if len(library_list) != 0: + cm_fh.write(" ") + cm_fh.write("\n ".join(library_list)) + cm_fh.write("\n") + cm_fh.write(" CODE\n") cm_fh.write('"' + sourceCode + '"') if qmakeFixme != "": cm_fh.write(qmakeFixme) -- cgit v1.2.3 From ccdbd32416541454cb4a6c3a417a0c6cc19d5bae Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 23 Oct 2019 09:50:10 +0200 Subject: cmake scripts: handle aix-g++ Change-Id: I62b9e9fa9aae3350dc8c668a98453c2373c1a709 Reviewed-by: Leander Beernaert Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 24c77cb2b1..ace4f44da6 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1243,6 +1243,7 @@ def map_condition(condition: str) -> str: # checking mkspec, predating gcc scope in qmake, will then be replaced by platform_mapping in helper.py condition = condition.replace("*-g++*", "GCC") + condition = condition.replace("aix-g++*", "AIX") condition = condition.replace("*-icc*", "ICC") condition = condition.replace("*-clang*", "CLANG") condition = condition.replace("*-llvm", "CLANG") -- cgit v1.2.3 From 61fa61ec164a57265c4d65d9292428f0050454a1 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 23 Oct 2019 09:50:58 +0200 Subject: cmake scripts: do not add spaces in empty lines When adding resources inside a condition, we'd end up with lines containing just spaces. Change-Id: I623203d042e2492bdd72f97d8b9d90517040b0df Reviewed-by: Leander Beernaert Reviewed-by: Simon Hausmann Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ace4f44da6..833a3e58bd 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1975,7 +1975,11 @@ def write_resources(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, str_indent = spaces(indent) cm_fh.write(f"\n{str_indent}# Resources:\n") for line in qrc_output.split("\n"): - cm_fh.write(f"{str_indent}{line}\n") + if line: + cm_fh.write(f"{str_indent}{line}\n") + else: + # do not add spaces to empty lines + cm_fh.write("\n") def write_statecharts(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0, is_example=False): -- cgit v1.2.3 From f745ef0f678b42c7a350d0b427ddafeab4d38451 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 23 Oct 2019 09:38:19 +0200 Subject: cmake scripts: do not override local target through includes This is needed for tests/auto/gui/kernel/qguiapplication which includes the qcoreapplication test. Without this change, the target name is taken from the included .pro file, instead of from the current file. Change-Id: I2d8a207fd70a49ad956fb9a83a59811d89682082 Reviewed-by: Leander Beernaert Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 833a3e58bd..e9492f7484 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -992,16 +992,16 @@ class Scope(object): def op_transformer(files): return files + for ic in self._included_children: + result = list(ic._evalOps(key, transformer, result)) + for op in self._operations.get(key, []): result = op.process(key, result, op_transformer) - for ic in self._included_children: - result = list(ic._evalOps(key, transformer, result)) return result 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) -- cgit v1.2.3 From d5dc755c653b56d8c09d8331e3a56e0382cf5ac5 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 22 Oct 2019 14:14:30 +0200 Subject: Convert ANDROID_PACKAGE_SOURCE_DIR for android (pro2cmake) Change-Id: I76730852e4433d095bc42ff617254a77f2d1bf51 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e9492f7484..36e899d781 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2171,11 +2171,12 @@ def write_android_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = "ANDROID_LIB_DEPENDENCY_REPLACEMENTS", "ANDROID_BUNDLED_FILES", "ANDROID_PERMISSIONS", + "ANDROID_PACKAGE_SOURCE_DIR" ] has_no_values = True for key in keys: - value = scope.get(key) + value = scope.expand(key) if len(value) != 0: if has_no_values: if scope.condition: -- cgit v1.2.3 From 69fb4ae343777af9078e5128ec41cfb3cea2cd76 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 23 Oct 2019 14:45:20 +0200 Subject: Distinguish between qt_plugin and regular plugins If we do not encounter the load(qt_plugin) statement in the .pro file but we do see the entry CONFIG+=plugin, treat the target as a regular CMake library instead of treating it as a qt_plugin by default. Change-Id: I67ad5c865a1a5ab691a6b0d86c2db4b686aa04dd Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 36e899d781..100e81e2c5 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2498,6 +2498,12 @@ def write_generic_library(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> s if "dll" in scope.get("CONFIG"): library_type = "SHARED" + is_plugin = False + if "plugin" in scope.get("CONFIG"): + library_type = "MODULE" + is_plugin = True + + # static after plugin in order to handle static library plugins if "static" in scope.get("CONFIG"): library_type = "STATIC" @@ -2523,6 +2529,13 @@ def write_generic_library(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> s extra_keys=[], ) + if is_plugin: + # Plugins need to be able to run auto moc + cm_fh.write(f"\nqt_autogen_tools_initial_setup({target_name})\n") + + if library_type == "STATIC": + cm_fh.write(f"\ntarget_compile_definitions({target_name} PRIVATE QT_STATICPLUGIN)\n") + return target_name @@ -3077,8 +3090,9 @@ def handle_app_or_lib( is_jar = "java" in 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 + is_plugin = "plugin" in config + is_qt_plugin = ( + any("qt_plugin" == s for s in scope.get("_LOADED")) or is_qml_plugin ) target = "" gui = all( @@ -3089,10 +3103,10 @@ def handle_app_or_lib( tar = write_jar(cm_fh, scope, indent=indent) elif is_example: target = write_example(cm_fh, scope, gui, indent=indent, is_plugin=is_plugin) - elif is_plugin: + elif is_qt_plugin: assert not is_example target = write_plugin(cm_fh, scope, indent=indent) - elif is_lib and "qt_module" not in scope.get("_LOADED"): + elif (is_lib and "qt_module" not in scope.get("_LOADED")) or is_plugin: assert not is_example target = write_generic_library(cm_fh, scope, indent=indent) elif is_lib or "qt_module" in scope.get("_LOADED"): -- cgit v1.2.3 From 4046b970e73a08be30e0c44dbb99fc06603ceec2 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 28 Oct 2019 11:33:44 +0100 Subject: Add replacement for '*g++*' to pro2cmake.py Change-Id: I3dc04a6169a98b7ff22392b979e743c41d6e0475 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 100e81e2c5..6058c15106 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1243,6 +1243,7 @@ def map_condition(condition: str) -> str: # checking mkspec, predating gcc scope in qmake, will then be replaced by platform_mapping in helper.py condition = condition.replace("*-g++*", "GCC") + condition = condition.replace("*g++*", "GCC") condition = condition.replace("aix-g++*", "AIX") condition = condition.replace("*-icc*", "ICC") condition = condition.replace("*-clang*", "CLANG") -- cgit v1.2.3 From 3e5e7b82012b99231492f48a202044369cb76c0d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 29 Oct 2019 09:57:19 +0100 Subject: Fix formatting to conform to black rules Ran make format. Change-Id: Ib4fc021c30834a69a9a5df653435dd92dc6a9c05 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 2 -- util/cmake/pro2cmake.py | 36 ++++++++++++++---------------------- 2 files changed, 14 insertions(+), 24 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 0b91a8befd..70d28c276b 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -605,8 +605,6 @@ endif() continue library_list.append(mapped_library.targetName) - - cm_fh.write(f"qt_config_compile_test({featureName(test)}\n") cm_fh.write(lineify("LABEL", data.get("label", ""))) if librariesCmakeName != "" or len(library_list) != 0: diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 6058c15106..ec13f26fbd 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -998,7 +998,6 @@ class Scope(object): for op in self._operations.get(key, []): result = op.process(key, result, op_transformer) - return result def get(self, key: str, *, ignore_includes: bool = False, inherit: bool = False) -> List[str]: @@ -1269,10 +1268,10 @@ def map_condition(condition: str) -> str: condition = condition.replace("|", " OR ") # new conditions added by the android multi arch qmake build - condition = re.sub(r'(^| )x86([^\_]|$)', "TEST_architecture_arch STREQUAL x86", condition) - condition = re.sub(r'(^| )x86_64', " TEST_architecture_arch STREQUAL x86_64", condition) - condition = re.sub(r'(^| )arm64-v8a', "TEST_architecture_arch STREQUAL arm64", condition) - condition = re.sub(r'(^| )armeabi-v7a', "TEST_architecture_arch STREQUAL arm", condition) + condition = re.sub(r"(^| )x86([^\_]|$)", "TEST_architecture_arch STREQUAL x86", condition) + condition = re.sub(r"(^| )x86_64", " TEST_architecture_arch STREQUAL x86_64", condition) + condition = re.sub(r"(^| )arm64-v8a", "TEST_architecture_arch STREQUAL arm64", condition) + condition = re.sub(r"(^| )armeabi-v7a", "TEST_architecture_arch STREQUAL arm", condition) cmake_condition = "" for part in condition.split(): @@ -2054,9 +2053,7 @@ def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, indent: int = condition = map_to_cmake_condition(scope.total_condition) extend_scope = ( - f"\n{ind}extend_target({target} CONDITION" - f" {condition}\n" - f"{extend_qt_string}{ind})\n" + f"\n{ind}extend_target({target} CONDITION" f" {condition}\n" f"{extend_qt_string}{ind})\n" ) if not extend_qt_string: @@ -2068,10 +2065,11 @@ def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, indent: int = write_resources(io_string, target, scope, indent + 1) resource_string = io_string.getvalue() if len(resource_string) != 0: - resource_string = resource_string.strip('\n').rstrip(f'\n{spaces(indent + 1)}') + resource_string = resource_string.strip("\n").rstrip(f"\n{spaces(indent + 1)}") cm_fh.write(f"\n{spaces(indent)}if({condition})\n{resource_string}") cm_fh.write(f"\n{spaces(indent)}endif()\n") + def flatten_scopes(scope: Scope) -> List[Scope]: result = [scope] # type: List[Scope] for c in scope.children: @@ -2172,7 +2170,7 @@ def write_android_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = "ANDROID_LIB_DEPENDENCY_REPLACEMENTS", "ANDROID_BUNDLED_FILES", "ANDROID_PERMISSIONS", - "ANDROID_PACKAGE_SOURCE_DIR" + "ANDROID_PACKAGE_SOURCE_DIR", ] has_no_values = True @@ -2724,17 +2722,13 @@ def write_jar(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: android_sdk_jar = "${QT_ANDROID_JAR}" android_api_level = scope.get_string("API_VERSION") if android_api_level: - cm_fh.write(f'{spaces(indent)}qt_get_android_sdk_jar_for_api("{android_api_level}" android_sdk)\n\n') - android_sdk_jar ="${android_sdk}" + cm_fh.write( + f'{spaces(indent)}qt_get_android_sdk_jar_for_api("{android_api_level}" android_sdk)\n\n' + ) + android_sdk_jar = "${android_sdk}" write_source_file_list( - cm_fh, - scope, - "", - ["JAVASOURCES"], - indent=indent, - header=f"set(java_sources\n", - footer=")\n", + cm_fh, scope, "", ["JAVASOURCES"], indent=indent, header=f"set(java_sources\n", footer=")\n" ) cm_fh.write(f"{spaces(indent)}add_jar({target}\n") @@ -3092,9 +3086,7 @@ def handle_app_or_lib( is_lib = scope.TEMPLATE == "lib" is_qml_plugin = any("qml_plugin" == s for s in scope.get("_LOADED")) is_plugin = "plugin" in config - is_qt_plugin = ( - any("qt_plugin" == s for s in scope.get("_LOADED")) or is_qml_plugin - ) + is_qt_plugin = any("qt_plugin" == s for s in scope.get("_LOADED")) or is_qml_plugin target = "" gui = all( val not in config for val in ["console", "cmdline"] -- cgit v1.2.3 From 5c6cf6692e23a1603deee5ba4aa4566d245d7fbb Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 31 Oct 2019 16:52:23 +0100 Subject: Prevent infinite recursion In qtvirtualkeyboard/src/plugins/lipi-toolkit/3rdparty/lipi-toolkit/src/lipicommon.pri we have TARGET = $$TARGET$$qtPlatformTargetSuffix which keeps on recursing. Change-Id: Ia0e020c7258cd87bba9b9681ed7b4568e8f0c9c2 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ec13f26fbd..c16864d474 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1111,6 +1111,9 @@ class Scope(object): else: replacement = self.get(match.group(1), inherit=True) replacement_str = replacement[0] if replacement else "" + if replacement_str == value: + # we have recursed + replacement_str = "" result = result[: match.start()] + replacement_str + result[match.end() :] result = self._replace_env_var_value(result) -- cgit v1.2.3 From cd9813d2763adb715d1cf1a37b86439fea255172 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 28 Oct 2019 15:35:47 +0100 Subject: Add support for benchmark conversion Convert benchmark executables to add_qt_benchmark(). Currently add_qt_benchmark just calls add_qt_executable() and ensures that it they build under CMAKE_CURRENT_BUILD_DIR and do not install. Add QT_BUILD_BENCHMARKS option to enable/disable building of benchmarks. Change-Id: Id0bc676698d21d50048d97d9abef51d92ccb6638 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index c16864d474..89e3e6441a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -233,6 +233,16 @@ def is_config_test_project(project_file_path: str = "") -> bool: ) +def is_benchmark_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 'tests/benchmarks' + # relative to the repo source dir, then it must be benchmark + return project_relative_path.startswith("tests/benchmarks") + + @lru_cache(maxsize=None) def find_qmake_conf(project_file_path: str = "") -> str: if not os.path.isabs(project_file_path): @@ -2661,22 +2671,29 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int binary_name = scope.TARGET assert binary_name + is_benchmark = is_benchmark_project(scope.file_absolute_path) + 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_keys = [] if is_qt_test_helper: binary_name += "_helper" cmake_function_call = "add_qt_test_helper" - target_path = scope.get_string("target.path") - if target_path: - target_path = replace_path_constants(target_path, scope) - if not scope.get("DESTDIR"): - extra.append(f'OUTPUT_DIRECTORY "{target_path}"') - if "target" in scope.get("INSTALLS"): - extra.append(f'INSTALL_DIRECTORY "{target_path}"') + if is_benchmark: + cmake_function_call = "add_qt_benchmark" + else: + extra_keys = ["target.path", "INSTALLS"] + target_path = scope.get_string("target.path") + if target_path: + target_path = replace_path_constants(target_path, scope) + if not scope.get("DESTDIR"): + 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, @@ -2687,7 +2704,7 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int extra_lines=extra, indent=indent, known_libraries={"Qt::Core"}, - extra_keys=["target.path", "INSTALLS"], + extra_keys=extra_keys, ) return binary_name -- cgit v1.2.3 From 512aae3abea65dcdc1e3d667eab73b8f41cf5601 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 7 Nov 2019 15:03:33 +0100 Subject: Fix Android x86 builds Replace condition x86 to i386 to match other platforms. Regenerate src/gui/CMakeLists.txt Disable SSE4 on android x86 to match qmake. Change-Id: Ic0d330206f2d70a79d72553aa3ff0f91ff58119c Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 89e3e6441a..07f1fe26df 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1281,7 +1281,7 @@ def map_condition(condition: str) -> str: condition = condition.replace("|", " OR ") # new conditions added by the android multi arch qmake build - condition = re.sub(r"(^| )x86([^\_]|$)", "TEST_architecture_arch STREQUAL x86", condition) + condition = re.sub(r"(^| )x86([^\_]|$)", "TEST_architecture_arch STREQUAL i386", condition) condition = re.sub(r"(^| )x86_64", " TEST_architecture_arch STREQUAL x86_64", condition) condition = re.sub(r"(^| )arm64-v8a", "TEST_architecture_arch STREQUAL arm64", condition) condition = re.sub(r"(^| )armeabi-v7a", "TEST_architecture_arch STREQUAL arm", condition) -- cgit v1.2.3 From 706e7fdb0ca5ab2beabecf39809c032bd98ac84a Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 8 Nov 2019 10:37:38 +0100 Subject: Add generic lessThan|greaterThan|equal condition map to pro2cmake.py Change-Id: Ib611aa9a808a05b38a83bd3fd7d95081bdf6e256 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 07f1fe26df..87e593a423 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1238,6 +1238,24 @@ def map_condition(condition: str) -> str: pattern = r"(equals|greaterThan|lessThan)\(WINDOWS_SDK_VERSION,[ ]*([0-9]+)\)" condition = re.sub(pattern, windows_sdk_version_handler, condition) + # Generic lessThan|equals|lessThan() + + def generic_version_handler(match_obj: Match): + operator = match_obj.group(1) + if operator == "equals": + operator = "EQUAL" + elif operator == "greaterThan": + operator = "GREATER" + elif operator == "lessThan": + operator = "LESS" + + variable = match_obj.group(2) + version = match_obj.group(3) + return f"({variable} {operator} {version})" + + pattern = r"(equals|greaterThan|lessThan)\((.+),[ ]*([0-9]+)\)" + condition = re.sub(pattern, generic_version_handler, condition) + # Handle if(...) conditions. condition = unwrap_if(condition) -- cgit v1.2.3 From 942d391b53a87ed8b313b48dbf57b2cedcd3e896 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 8 Nov 2019 11:22:02 +0100 Subject: Add condition maps for special cases in tests/manual Change-Id: I426766c68f6f118d0706455689d968534e406d3d Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 87e593a423..b8588203bd 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1879,6 +1879,9 @@ def map_to_cmake_condition(condition: str = "") -> str: r'(TEST_architecture_arch STREQUAL "\1")', condition or "", ) + condition = condition.replace('QT___contains___opengl', 'QT_FEATURE_opengl') + condition = condition.replace('QT___contains___widgets', 'QT_FEATURE_widgets') + condition = condition.replace('DEFINES___contains___QT_NO_PRINTER', '(QT_FEATURE_printer EQUAL FALSE)') return condition -- cgit v1.2.3 From de3a806def4b9a754825a2233c9d4952a9b2d0eb Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 1 Nov 2019 11:48:23 +0100 Subject: Make standalone tests build via top level repo project Previously repo/tests/CMakeLists.txt was a standalone project on which CMake could be called. This was useful for Coin to be able to build and package only tests, but was a bit troublesome because that means having to specify the usual boilerplate like minimum CMake version, which packages to find in every tests.pro project. Instead of having a separate standalone project, modify the top level project and associated CMake code to allow passing a special QT_BUILD_STANDALONE_TESTS variable, which causes the top level project to build only tests, and find Qt in the previously installed qt location. This also means that when building a repo, we generate a ${repo_name}TestsConfig.cmake file which does find_package on all the modules that have been built as part of that repo. So that when standalone tests bare built for that repo, the modules are automatically found. qt_set_up_standalone_tests_build() is modified to be a no-op because it is not needed anymore. Its usage should be removed from all the other repos, and then removed from qtbase. Non-adjusted tests/CMakeLists.txt projects in other repositories should still be buildable with the current code, until they are updated to the new format. Adjust the Coin build instructions to build the standalone tests in a separate directory. Adjust pro2cmake to generate new structure for the tests/tests.pro projects. Adjust the qtbase tests project. Fixes: QTBUG-79239 Change-Id: Ib4b66bc772d8876cdcbae1e90ce5a5a5234fa675 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b8588203bd..ea5f2b91e1 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3248,12 +3248,9 @@ def handle_top_level_repo_tests_project(scope: Scope, cm_fh: IO[str]): 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){requires_content} - qt_set_up_standalone_tests_build() + if(QT_BUILD_STANDALONE_TESTS) + # Add qt_find_package calls for extra dependencies that need to be found when building + # the standalone tests here. endif() qt_build_tests() """ -- cgit v1.2.3 From 37ab7eae9de557ad58cf371a6ce049fdf7545e6e Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 11 Nov 2019 18:47:28 +0100 Subject: pro2cmake: Clean up debug messages Don't needlessly show a bunch of special case debug messages unless debugging is enabled. There used to be a barrage of scary message when using run_pro2cmake. Change-Id: I49ab3522e11844a99653034b5f15a634a368d227 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 14 +++++++++----- util/cmake/special_case_helper.py | 29 ++++++++++++++++------------- 2 files changed, 25 insertions(+), 18 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ea5f2b91e1..d8168aa489 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3383,8 +3383,9 @@ def cmakeify_scope( cm_fh.write(buffer_value) -def generate_new_cmakelists(scope: Scope, *, is_example: bool = False) -> None: - print("Generating CMakeLists.gen.txt") +def generate_new_cmakelists(scope: Scope, *, is_example: bool = False, debug: bool = False) -> None: + if debug: + print("Generating CMakeLists.gen.txt") with open(scope.generated_cmake_lists_path, "w") as cm_fh: assert scope.file cm_fh.write(f"# Generated from {os.path.basename(scope.file)}.\n\n") @@ -3415,8 +3416,11 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: scope.merge(include_scope) -def copy_generated_file_to_final_location(scope: Scope, keep_temporary_files=False) -> None: - print(f"Copying {scope.generated_cmake_lists_path} to {scope.original_cmake_lists_path}") +def copy_generated_file_to_final_location( + scope: Scope, keep_temporary_files=False, debug: bool = False +) -> None: + if debug: + 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) @@ -3526,7 +3530,7 @@ def main() -> None: file_scope.dump() print("\n#### End of full .pro/.pri file structure.\n") - generate_new_cmakelists(file_scope, is_example=args.is_example) + generate_new_cmakelists(file_scope, is_example=args.is_example, debug=args.debug) copy_generated_file = True if not args.skip_special_case_preservation: diff --git a/util/cmake/special_case_helper.py b/util/cmake/special_case_helper.py index 60443aeb61..48c7181a04 100644 --- a/util/cmake/special_case_helper.py +++ b/util/cmake/special_case_helper.py @@ -161,13 +161,14 @@ def run_process_quiet(args_string: str, debug=False) -> bool: # 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( - dedent( - f"""\ - Error while running: "{args_string}" - {e.stdout}""" + if debug: + print( + dedent( + f"""\ + Error while running: "{args_string}" + {e.stdout}""" + ) ) - ) return False return True @@ -333,9 +334,11 @@ class SpecialCaseHandler(object): time.sleep(0.1) if failed_once and not success: - print("Retrying git add, the index.lock was probably acquired.") + if self.debug: + print("Retrying git add, the index.lock was probably acquired.") if failed_once and success: - print("git add succeeded.") + if self.debug: + print("git add succeeded.") elif failed_once and not success: print(f"git add failed. Make sure to git add {self.prev_file_path} yourself.") @@ -375,11 +378,11 @@ class SpecialCaseHandler(object): copyfile_log(self.post_merge_file_path, self.generated_file_path) 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." - ) + if self.debug: + print( + "Special case reapplication using git is complete. " + "Make sure to fix remaining conflict markers." + ) except Exception as e: print(f"Error occurred while trying to reapply special case modifications: {e}") -- cgit v1.2.3 From 2c66bdde5a79f1597ca9bb610d4d51a7a4ec61c1 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 11 Nov 2019 18:56:47 +0100 Subject: pro2cmake: Skip errors regarding not found generated .pri files A lot of project files include files like qtsqldrivers-config.pri which do not exist when running pro2cmake. It should be safe to hide this error messages when converting projects. Change-Id: I4205010e537f67f9b276b4cb94ec30f847c43c68 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index d8168aa489..f3c9873e62 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3403,7 +3403,10 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: if not include_file: continue if not os.path.isfile(include_file): - print(f" XXXX: Failed to include {include_file}.") + generated_config_pri_pattern = re.compile(r"qt.+?-config\.pri$") + match_result = re.search(generated_config_pri_pattern, include_file) + if not match_result: + print(f" XXXX: Failed to include {include_file}.") continue include_result = parseProFile(include_file, debug=debug) -- cgit v1.2.3 From 4fe8330b5b71c546c891a6ec3e706e48842ea67a Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 11 Nov 2019 19:03:36 +0100 Subject: pro2cmake: Fix formatting and mypy errors Change-Id: Ic41998c39f5f5b890b3aa8ec43c6f3f03fee54da Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f3c9873e62..b40c35e355 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1879,9 +1879,11 @@ def map_to_cmake_condition(condition: str = "") -> str: r'(TEST_architecture_arch STREQUAL "\1")', condition or "", ) - condition = condition.replace('QT___contains___opengl', 'QT_FEATURE_opengl') - condition = condition.replace('QT___contains___widgets', 'QT_FEATURE_widgets') - condition = condition.replace('DEFINES___contains___QT_NO_PRINTER', '(QT_FEATURE_printer EQUAL FALSE)') + condition = condition.replace("QT___contains___opengl", "QT_FEATURE_opengl") + condition = condition.replace("QT___contains___widgets", "QT_FEATURE_widgets") + condition = condition.replace( + "DEFINES___contains___QT_NO_PRINTER", "(QT_FEATURE_printer EQUAL FALSE)" + ) return condition @@ -2698,7 +2700,7 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int extra = ["GUI"] if gui and not is_qt_test_helper else [] cmake_function_call = "add_qt_executable" - extra_keys = [] + extra_keys: List[str] = [] if is_qt_test_helper: binary_name += "_helper" @@ -3134,7 +3136,7 @@ def handle_app_or_lib( ) and "testlib" not in scope.expand("QT") if is_jar: - tar = write_jar(cm_fh, scope, indent=indent) + write_jar(cm_fh, scope, indent=indent) elif is_example: target = write_example(cm_fh, scope, gui, indent=indent, is_plugin=is_plugin) elif is_qt_plugin: -- cgit v1.2.3 From c1e0e0adb2fb5c62f4d1677a85c677c57e2737f3 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 11 Nov 2019 19:09:59 +0100 Subject: pro2cmake: Fix incorrect replacement of x86 conditions The regular expression used to eat the character that comes after the x86 token, which was incorrect when matching x86SimdAlways. Fix the regular expression to use a lookahead with a negative character class of word characters. Change-Id: I9ed8f9a490369bf5704b752f8bba3b7118a2cd5b Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b40c35e355..b95b469e37 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1299,7 +1299,7 @@ def map_condition(condition: str) -> str: condition = condition.replace("|", " OR ") # new conditions added by the android multi arch qmake build - condition = re.sub(r"(^| )x86([^\_]|$)", "TEST_architecture_arch STREQUAL i386", condition) + condition = re.sub(r"(^| )x86((?=[^\w])|$)", "TEST_architecture_arch STREQUAL i386", condition) condition = re.sub(r"(^| )x86_64", " TEST_architecture_arch STREQUAL x86_64", condition) condition = re.sub(r"(^| )arm64-v8a", "TEST_architecture_arch STREQUAL arm64", condition) condition = re.sub(r"(^| )armeabi-v7a", "TEST_architecture_arch STREQUAL arm", condition) -- cgit v1.2.3 From d7c4fa46ac3565a11787e5d12b3d190f54108ff9 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 11 Nov 2019 19:31:31 +0100 Subject: pro2cmake: Allow skipping subdirs projects via command line Pass either --skip-subdirs-project to pro2cmake or --skip-subdirs-projects to run_pro2cmake. Change-Id: Ic444858f6fc6deb3c893782c0770993aa39d5579 Reviewed-by: Qt CMake Build Bot Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 20 ++++++++++++++++++++ util/cmake/run_pro2cmake.py | 8 ++++++++ 2 files changed, 28 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b95b469e37..536bbdc656 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -155,6 +155,13 @@ def _parse_commandline(): help="Don't use condition simplifier cache (conversion speed may decrease).", ) + parser.add_argument( + "--skip-subdirs-project", + dest="skip_subdirs_project", + action="store_true", + help="Skip converting project if it ends up being a TEMPLATE=subdirs project.", + ) + parser.add_argument( "-i", "--ignore-skip-marker", @@ -3485,6 +3492,15 @@ def should_convert_project(project_file_path: str = "", ignore_skip_marker: bool return True +def should_convert_project_after_parsing( + file_scope: Scope, skip_subdirs_project: bool = False +) -> bool: + template = file_scope.TEMPLATE + if template == "subdirs" and skip_subdirs_project: + return False + return True + + def main() -> None: # Be sure of proper Python version assert sys.version_info >= (3, 7) @@ -3535,6 +3551,10 @@ def main() -> None: file_scope.dump() print("\n#### End of full .pro/.pri file structure.\n") + if not should_convert_project_after_parsing(file_scope, args.skip_subdirs_project): + print(f'Skipping conversion of project: "{project_file_absolute_path}"') + continue + generate_new_cmakelists(file_scope, is_example=args.is_example, debug=args.debug) copy_generated_file = True diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index 7c38a8aef9..eaece147d2 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -59,6 +59,12 @@ def parse_command_line(): action="store_true", help="Run pro2cmake only on the main modules in qtbase.", ) + parser.add_argument( + "--skip-subdirs-projects", + dest="skip_subdirs_projects", + action="store_true", + help="Don't run pro2cmake on TEMPLATE=subdirs projects.", + ) parser.add_argument( "--is-example", dest="is_example", @@ -162,6 +168,8 @@ def run(all_files: typing.List[str], pro2cmake: str, args: argparse.Namespace) - pro2cmake_args.append(pro2cmake) if args.is_example: pro2cmake_args.append("--is-example") + if args.skip_subdirs_projects: + pro2cmake_args.append("--skip-subdirs-project") pro2cmake_args.append(os.path.basename(filename)) result = subprocess.run( pro2cmake_args, -- cgit v1.2.3 From 026471378d80f32e236e675b01ab0e7ff4a4c383 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 12 Nov 2019 11:19:58 +0100 Subject: Add mapping entry for LinuxOfonoSupport Change-Id: I4c88b58cad6b4f4a88f02a38dd84d3a667bcc063 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index df6b8773f0..28f5efa87a 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -347,6 +347,9 @@ _qt_library_map = [ ), LibraryMapping( "quickparticles", "Qt6", "Qt::QuickParticles", extra=["COMPONENTS", "QuickParticles"] + ), + LibraryMapping( + "linuxofono_support", "Qt6", "Qt::LinuxOfonoSupport", extra=["COMPONENTS", "LinuxOfonoSupport"] ) # qtzlib: No longer supported. ] -- cgit v1.2.3 From a0967c2a4f171e9edf6edef0c2ea966500343563 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 8 Nov 2019 15:42:35 +0100 Subject: pro2cmake: Handle operation evaluation order when including children Instead of processing included_children operations either before or after the parent scope, collect all operations within that scope and its included children scopes, and order them based on line number information. This requires propagating line numbers for each operation as well as line numbers for each include() statement, all the way from the parser grammar to the operator evaluation routines. This should improve operation handling for included_children (via include()), but not for regular children (introduced by ifs or elses), aka this doesn't solve the whole imperative vs declarative dilemma. Sample projects where the improvement should be seen: tests/auto/gui/kernel/qguiapplication and src/plugins/sqldrivers/sqlite. This amends f745ef0f678b42c7a350d0b427ddafeab4d38451 Change-Id: I40b8302ba6aa09b6b9986ea60eac87de8676b469 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 185 ++++++++++++++++++++++++++++++++------- util/cmake/qmake_parser.py | 15 ++-- util/cmake/tests/test_parsing.py | 10 ++- 3 files changed, 167 insertions(+), 43 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 536bbdc656..e2cd148b40 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -595,11 +595,12 @@ def handle_vpath(source: str, base_dir: str, vpath: List[str]) -> str: class Operation: - def __init__(self, value: Union[List[str], str]) -> None: + def __init__(self, value: Union[List[str], str], line_no: int = -1) -> None: if isinstance(value, list): self._value = value else: self._value = [str(value)] + self._line_no = line_no def process( self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] @@ -703,9 +704,6 @@ class SetOperation(Operation): class RemoveOperation(Operation): - def __init__(self, value): - super().__init__(value) - def process( self, key: str, sinput: List[str], transformer: Callable[[List[str]], List[str]] ) -> List[str]: @@ -729,6 +727,34 @@ class RemoveOperation(Operation): return f"-({self._dump()})" +# Helper class that stores a list of tuples, representing a scope id and +# a line number within that scope's project file. The whole list +# represents the full path location for a certain operation while +# traversing include()'d scopes. Used for sorting when determining +# operation order when evaluating operations. +class OperationLocation(object): + def __init__(self): + self.list_of_scope_ids_and_line_numbers = [] + + def clone_and_append(self, scope_id: int, line_number: int) -> OperationLocation: + new_location = OperationLocation() + new_location.list_of_scope_ids_and_line_numbers = list( + self.list_of_scope_ids_and_line_numbers + ) + new_location.list_of_scope_ids_and_line_numbers.append((scope_id, line_number)) + return new_location + + def __lt__(self, other: OperationLocation) -> Any: + return self.list_of_scope_ids_and_line_numbers < other.list_of_scope_ids_and_line_numbers + + def __repr__(self) -> str: + s = "" + for t in self.list_of_scope_ids_and_line_numbers: + s += f"s{t[0]}:{t[1]} " + s = s.strip(" ") + return s + + class Scope(object): SCOPE_ID: int = 1 @@ -741,6 +767,7 @@ class Scope(object): condition: str = "", base_dir: str = "", operations: Union[Dict[str, List[Operation]], None] = None, + parent_include_line_no: int = -1, ) -> None: if not operations: operations = { @@ -774,6 +801,7 @@ class Scope(object): self._included_children = [] # type: List[Scope] self._visited_keys = set() # type: Set[str] self._total_condition = None # type: Optional[str] + self._parent_include_line_no = parent_include_line_no def __repr__(self): return ( @@ -832,9 +860,21 @@ class Scope(object): @staticmethod def FromDict( - parent_scope: Optional["Scope"], file: str, statements, cond: str = "", base_dir: str = "" + parent_scope: Optional["Scope"], + file: str, + statements, + cond: str = "", + base_dir: str = "", + project_file_content: str = "", + parent_include_line_no: int = -1, ) -> Scope: - scope = Scope(parent_scope=parent_scope, qmake_file=file, condition=cond, base_dir=base_dir) + scope = Scope( + parent_scope=parent_scope, + qmake_file=file, + condition=cond, + base_dir=base_dir, + parent_include_line_no=parent_include_line_no, + ) for statement in statements: if isinstance(statement, list): # Handle skipped parts... assert not statement @@ -846,16 +886,20 @@ class Scope(object): value = statement.get("value", []) assert key != "" + op_location_start = operation["locn_start"] + operation = operation["value"] + op_line_no = pp.lineno(op_location_start, project_file_content) + if operation == "=": - scope._append_operation(key, SetOperation(value)) + scope._append_operation(key, SetOperation(value, line_no=op_line_no)) elif operation == "-=": - scope._append_operation(key, RemoveOperation(value)) + scope._append_operation(key, RemoveOperation(value, line_no=op_line_no)) elif operation == "+=": - scope._append_operation(key, AddOperation(value)) + scope._append_operation(key, AddOperation(value, line_no=op_line_no)) elif operation == "*=": - scope._append_operation(key, UniqueAddOperation(value)) + scope._append_operation(key, UniqueAddOperation(value, line_no=op_line_no)) elif operation == "~=": - scope._append_operation(key, ReplaceOperation(value)) + scope._append_operation(key, ReplaceOperation(value, line_no=op_line_no)) else: print(f'Unexpected operation "{operation}" in scope "{scope}".') assert False @@ -883,7 +927,12 @@ class Scope(object): included = statement.get("included", None) if included: - scope._append_operation("_INCLUDED", UniqueAddOperation(included)) + included_location_start = included["locn_start"] + included = included["value"] + included_line_no = pp.lineno(included_location_start, project_file_content) + scope._append_operation( + "_INCLUDED", UniqueAddOperation(included, line_no=included_line_no) + ) continue project_required_condition = statement.get("project_required_condition") @@ -985,6 +1034,51 @@ class Scope(object): def visited_keys(self): return self._visited_keys + # Traverses a scope and its children, and collects operations + # that need to be processed for a certain key. + def _gather_operations_from_scope( + self, + operations_result: List[Dict[str, Any]], + current_scope: Scope, + op_key: str, + current_location: OperationLocation, + ): + for op in current_scope._operations.get(op_key, []): + new_op_location = current_location.clone_and_append( + current_scope._scope_id, op._line_no + ) + op_info: Dict[str, Any] = {} + op_info["op"] = op + op_info["scope"] = current_scope + op_info["location"] = new_op_location + operations_result.append(op_info) + + for included_child in current_scope._included_children: + new_scope_location = current_location.clone_and_append( + current_scope._scope_id, included_child._parent_include_line_no + ) + self._gather_operations_from_scope( + operations_result, included_child, op_key, new_scope_location + ) + + # Partially applies a scope argument to a given transformer. + @staticmethod + def _create_transformer_for_operation( + given_transformer: Optional[Callable[[Scope, List[str]], List[str]]], + transformer_scope: Scope, + ) -> Callable[[List[str]], List[str]]: + if given_transformer: + + def wrapped_transformer(values): + return given_transformer(transformer_scope, values) + + else: + + def wrapped_transformer(values): + return values + + return wrapped_transformer + def _evalOps( self, key: str, @@ -995,26 +1089,29 @@ class Scope(object): ) -> List[str]: self._visited_keys.add(key) - # Inherrit values from above: + # Inherit values from parent scope. + # This is a strange edge case which is wrong in principle, because + # .pro files are imperative and not declarative. Nevertheless + # this fixes certain mappings (e.g. for handling + # VERSIONTAGGING_SOURCES in src/corelib/global/global.pri). if self._parent and inherit: result = self._parent._evalOps(key, transformer, result) - if transformer: - - def op_transformer(files): - return transformer(self, files) - - else: - - def op_transformer(files): - return files - - for ic in self._included_children: - result = list(ic._evalOps(key, transformer, result)) + operations_to_run: List[Dict[str, Any]] = [] + starting_location = OperationLocation() + starting_scope = self + self._gather_operations_from_scope( + operations_to_run, starting_scope, key, starting_location + ) - for op in self._operations.get(key, []): - result = op.process(key, result, op_transformer) + # Sorts the operations based on the location of each operation. Technically compares two + # lists of tuples. + operations_to_run = sorted(operations_to_run, key=lambda o: o["location"]) + # Process the operations. + for op_info in operations_to_run: + op_transformer = self._create_transformer_for_operation(transformer, op_info["scope"]) + result = op_info["op"].process(key, result, op_transformer) return result def get(self, key: str, *, ignore_includes: bool = False, inherit: bool = False) -> List[str]: @@ -1155,6 +1252,9 @@ class Scope(object): assert len(result) == 1 return result[0] + def _get_operation_at_index(self, key, index): + return self._operations[key][index] + @property def TEMPLATE(self) -> str: return self.get_string("TEMPLATE", "app") @@ -1413,9 +1513,14 @@ def handle_subdir( if dirname: collect_subdir_info(dirname, current_conditions=current_conditions) else: - subdir_result = parseProFile(sd, debug=False) + subdir_result, project_file_content = parseProFile(sd, debug=False) subdir_scope = Scope.FromDict( - scope, sd, subdir_result.asDict().get("statements"), "", scope.basedir + scope, + sd, + subdir_result.asDict().get("statements"), + "", + scope.basedir, + project_file_content=project_file_content, ) do_include(subdir_scope) @@ -3408,7 +3513,7 @@ 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_index, include_file in enumerate(scope.get_files("_INCLUDED", is_include=True)): if not include_file: continue if not os.path.isfile(include_file): @@ -3418,9 +3523,18 @@ def do_include(scope: Scope, *, debug: bool = False) -> None: print(f" XXXX: Failed to include {include_file}.") continue - include_result = parseProFile(include_file, debug=debug) + include_op = scope._get_operation_at_index("_INCLUDED", include_index) + include_line_no = include_op._line_no + + include_result, project_file_content = parseProFile(include_file, debug=debug) include_scope = Scope.FromDict( - None, include_file, include_result.asDict().get("statements"), "", scope.basedir + None, + include_file, + include_result.asDict().get("statements"), + "", + scope.basedir, + project_file_content=project_file_content, + parent_include_line_no=include_line_no, ) # This scope will be merged into scope! do_include(include_scope) @@ -3524,7 +3638,7 @@ def main() -> None: print(f'Skipping conversion of project: "{project_file_absolute_path}"') continue - parseresult = parseProFile(file_relative_path, debug=debug_parsing) + parseresult, project_file_content = parseProFile(file_relative_path, debug=debug_parsing) if args.debug_parse_result or args.debug: print("\n\n#### Parser result:") @@ -3536,7 +3650,10 @@ def main() -> None: print("\n#### End of parser result dictionary.\n") file_scope = Scope.FromDict( - None, file_relative_path, parseresult.asDict().get("statements") + None, + file_relative_path, + parseresult.asDict().get("statements"), + project_file_content=project_file_content, ) if args.debug_pro_structure or args.debug: diff --git a/util/cmake/qmake_parser.py b/util/cmake/qmake_parser.py index 95cbe8aa5b..5cb629a495 100644 --- a/util/cmake/qmake_parser.py +++ b/util/cmake/qmake_parser.py @@ -31,6 +31,7 @@ import collections import os import re from itertools import chain +from typing import Tuple import pyparsing as pp # type: ignore @@ -203,7 +204,9 @@ class QmakeParser: Key = add_element("Key", Identifier) - Operation = add_element("Operation", Key("key") + Op("operation") + Values("value")) + Operation = add_element( + "Operation", Key("key") + pp.locatedExpr(Op)("operation") + Values("value") + ) CallArgs = add_element("CallArgs", pp.nestedExpr()) def parse_call_args(results): @@ -218,7 +221,9 @@ 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")) + Include = add_element( + "Include", pp.Keyword("include") + pp.locatedExpr(CallArgs)("included") + ) Option = add_element("Option", pp.Keyword("option") + CallArgs("option")) RequiresCondition = add_element("RequiresCondition", pp.originalTextFor(pp.nestedExpr())) @@ -360,7 +365,7 @@ class QmakeParser: return Grammar - def parseFile(self, file: str): + def parseFile(self, file: str) -> Tuple[pp.ParseResults, str]: print(f'Parsing "{file}"...') try: with open(file, "r") as file_fd: @@ -375,9 +380,9 @@ class QmakeParser: print(f"{' ' * (pe.col-1)}^") print(pe) raise pe - return result + return result, contents -def parseProFile(file: str, *, debug=False): +def parseProFile(file: str, *, debug=False) -> Tuple[pp.ParseResults, str]: parser = QmakeParser(debug=debug) return parser.parseFile(file) diff --git a/util/cmake/tests/test_parsing.py b/util/cmake/tests/test_parsing.py index 95653dc39d..9acee46007 100755 --- a/util/cmake/tests/test_parsing.py +++ b/util/cmake/tests/test_parsing.py @@ -36,7 +36,7 @@ _tests_path = os.path.dirname(os.path.abspath(__file__)) def validate_op(key, op, value, to_validate): assert key == to_validate['key'] - assert op == to_validate['operation'] + assert op == to_validate['operation']['value'] assert value == to_validate.get('value', None) @@ -71,7 +71,7 @@ def validate_default_else_test(file_name): def parse_file(file): p = QmakeParser(debug=True) - result = p.parseFile(file) + result, _ = p.parseFile(file) print('\n\n#### Parser result:') print(result) @@ -153,7 +153,8 @@ def test_include(): validate_op('A', '=', ['42'], result[0]) include = result[1] assert len(include) == 1 - assert include.get('included', '') == 'foo' + assert 'included' in include + assert include['included'].get('value', '') == 'foo' validate_op('B', '=', ['23'], result[2]) @@ -260,7 +261,8 @@ def test_realworld_comment_scope(): assert len(if_branch) == 1 validate_op('QMAKE_LFLAGS_NOUNDEF', '=', None, if_branch[0]) - assert result[1].get('included', '') == 'animation/animation.pri' + assert 'included' in result[1] + assert result[1]['included'].get('value', '') == 'animation/animation.pri' def test_realworld_contains_scope(): -- cgit v1.2.3 From f33069be243b08fef71e2681e1ed53a9bc6c89b6 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 12 Nov 2019 14:05:35 +0100 Subject: pro2cmake: Consider _LOADED operations as always processed To remove wrong unused keys comments from the project file. Change-Id: Ib52b1738b6136a54f6accb86156b9b2609f331c3 Reviewed-by: Tobias Hunger --- util/cmake/pro2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e2cd148b40..63865aafd7 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1933,6 +1933,7 @@ def write_ignored_keys(scope: Scope, indent: str) -> str: for k in sorted(ignored_keys): if k in { "_INCLUDED", + "_LOADED", "TARGET", "QMAKE_DOCS", "QT_SOURCE_TREE", -- cgit v1.2.3 From 681e8b4ead432b2658b0be9f90454bbdbd80d1b1 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 12 Nov 2019 15:31:34 +0100 Subject: Add add_qt_manual_test() This patch adds add_qt_manual_test() which is a simple wrapper around add_qt_executable() which does not build under ${CMAKE_BUILD_DIR}/bin or install the targets. This could potentially be used later to tag manual tests. Change-Id: Ic4e0a1d133009f5a858b9394347a0996cf42683f Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 63865aafd7..18ec08ad0a 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -246,9 +246,18 @@ def is_benchmark_project(project_file_path: str = "") -> bool: project_relative_path = os.path.relpath(project_file_path, qmake_conf_dir_path) # If the project file is found in a subdir called 'tests/benchmarks' - # relative to the repo source dir, then it must be benchmark + # relative to the repo source dir, then it must be a benchmark return project_relative_path.startswith("tests/benchmarks") +def is_manual_test(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 'tests/manual' + # relative to the repo source dir, then it must be a manual test + return project_relative_path.startswith("tests/manual") + @lru_cache(maxsize=None) def find_qmake_conf(project_file_path: str = "") -> str: @@ -2808,6 +2817,7 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int assert binary_name is_benchmark = is_benchmark_project(scope.file_absolute_path) + is_manual_test = is_manual_test(scope.file_absolute_path) is_qt_test_helper = "qt_test_helper" in scope.get("_LOADED") @@ -2821,6 +2831,8 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int if is_benchmark: cmake_function_call = "add_qt_benchmark" + elif is_manual_test: + cmake_function_call = "add_qt_manual_test" else: extra_keys = ["target.path", "INSTALLS"] target_path = scope.get_string("target.path") -- cgit v1.2.3 From 800cd1d8b966fcedf696618e669848bd9350a6df Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 13 Nov 2019 10:53:13 +0100 Subject: pro2cmake: Fix broken is_manual_test usage Amends 681e8b4ead432b2658b0be9f90454bbdbd80d1b1 Change-Id: I5ac8761de10d945b6ac78ba95a179209c80cb893 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 18ec08ad0a..2c12c7a4ae 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -249,7 +249,7 @@ def is_benchmark_project(project_file_path: str = "") -> bool: # relative to the repo source dir, then it must be a benchmark return project_relative_path.startswith("tests/benchmarks") -def is_manual_test(project_file_path: str = "") -> bool: +def is_manual_test_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) @@ -2817,7 +2817,7 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int assert binary_name is_benchmark = is_benchmark_project(scope.file_absolute_path) - is_manual_test = is_manual_test(scope.file_absolute_path) + is_manual_test = is_manual_test_project(scope.file_absolute_path) is_qt_test_helper = "qt_test_helper" in scope.get("_LOADED") -- cgit v1.2.3 From 2679ae3719c9cb2fe3a9735b84c5bfe07f5d35d5 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Tue, 12 Nov 2019 18:10:58 +0100 Subject: pro2cmake: If CONFIG-=app_bundle consider this a non gui app Change-Id: I45804af3c43cf1af8b00ac3542e045fe697bf1b6 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 2c12c7a4ae..0d1025988e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3257,7 +3257,7 @@ def handle_app_or_lib( is_qt_plugin = any("qt_plugin" == s for s in scope.get("_LOADED")) or is_qml_plugin target = "" gui = all( - val not in config for val in ["console", "cmdline"] + val not in config for val in ["console", "cmdline", "-app_bundle"] ) and "testlib" not in scope.expand("QT") if is_jar: -- cgit v1.2.3 From c219aaf4d119d7114b9f366d43d3ff2700aaf4ad Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 13 Nov 2019 10:53:59 +0100 Subject: pro2cmake: Reformat code with black Change-Id: I699031d4a05dad84a0807039dcdba17fd5acf2e3 Reviewed-by: Qt CMake Build Bot Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 5 ++++- util/cmake/pro2cmake.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 28f5efa87a..3a48f63de2 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -349,7 +349,10 @@ _qt_library_map = [ "quickparticles", "Qt6", "Qt::QuickParticles", extra=["COMPONENTS", "QuickParticles"] ), LibraryMapping( - "linuxofono_support", "Qt6", "Qt::LinuxOfonoSupport", extra=["COMPONENTS", "LinuxOfonoSupport"] + "linuxofono_support", + "Qt6", + "Qt::LinuxOfonoSupport", + extra=["COMPONENTS", "LinuxOfonoSupport"], ) # qtzlib: No longer supported. ] diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 0d1025988e..1b4df942f9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -249,6 +249,7 @@ def is_benchmark_project(project_file_path: str = "") -> bool: # relative to the repo source dir, then it must be a benchmark return project_relative_path.startswith("tests/benchmarks") + def is_manual_test_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) -- cgit v1.2.3 From 3919ed58abdd80ac7f964c91bead7bd8d4543079 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 13 Nov 2019 12:53:01 +0100 Subject: Allow passing args from run_pro2cmake to pro2cmake Change-Id: Ic874d4cd3488903ffec438f5c127b589e6371f7a Reviewed-by: Leander Beernaert Reviewed-by: Simon Hausmann --- util/cmake/run_pro2cmake.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index eaece147d2..961e2931db 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -37,9 +37,11 @@ import argparse from argparse import ArgumentParser -def parse_command_line(): +def parse_command_line() -> argparse.Namespace: parser = ArgumentParser( - description="Run pro2cmake on all .pro files recursively in given path." + description="Run pro2cmake on all .pro files recursively in given path. " + "You can pass additional arguments to the pro2cmake calls by appending " + "-- --foo --bar" ) parser.add_argument( "--only-existing", @@ -75,7 +77,16 @@ def parse_command_line(): "path", metavar="", type=str, help="The path where to look for .pro files." ) - return parser.parse_args() + args, unknown = parser.parse_known_args() + + # Error out when the unknown arguments do not start with a "--", + # which implies passing through arguments to pro2cmake. + if len(unknown) > 0 and unknown[0] != "--": + parser.error("unrecognized arguments: {}".format(" ".join(unknown))) + else: + args.pro2cmake_args = unknown[1:] + + return args def find_all_pro_files(base_path: str, args: argparse.Namespace): @@ -171,6 +182,10 @@ def run(all_files: typing.List[str], pro2cmake: str, args: argparse.Namespace) - if args.skip_subdirs_projects: pro2cmake_args.append("--skip-subdirs-project") pro2cmake_args.append(os.path.basename(filename)) + + if args.pro2cmake_args: + pro2cmake_args += args.pro2cmake_args + result = subprocess.run( pro2cmake_args, cwd=os.path.dirname(filename), -- cgit v1.2.3 From ab3bbc9d744527fe3d44aa2b23db942b0fad153f Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 13 Nov 2019 14:52:54 +0100 Subject: pro2cmake: Fix generic_version_handler not to be greedy Otherwise in a condition with multiple comparisons, the regexp would match too many characters, and not extract the correct variable name. And to be extra safe, match until a comma is encountered. Change-Id: I29d8344287efca5c5b232006f6bbdf39e6e4ee67 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1b4df942f9..dcddfe65d8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1370,7 +1370,7 @@ def map_condition(condition: str) -> str: version = match_obj.group(3) return f"({variable} {operator} {version})" - pattern = r"(equals|greaterThan|lessThan)\((.+),[ ]*([0-9]+)\)" + pattern = r"(equals|greaterThan|lessThan)\(([^,]+?),[ ]*([0-9]+)\)" condition = re.sub(pattern, generic_version_handler, condition) # Handle if(...) conditions. -- cgit v1.2.3 From 0956e2d9907c50169117b8df7aa8998c0c185879 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 13 Nov 2019 19:29:50 +0100 Subject: pro2cmake: Allow limiting the number of projects to convert Change-Id: I7c06d530cb98cc07e4a618427a7cf7b5c70aa171 Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/run_pro2cmake.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index 961e2931db..ce4a952f94 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -73,6 +73,18 @@ def parse_command_line() -> argparse.Namespace: action="store_true", help="Run pro2cmake with --is-example flag.", ) + parser.add_argument( + "--count", + dest="count", + help="How many projects should be converted.", + type=int, + ) + parser.add_argument( + "--offset", + dest="offset", + help="From the list of found projects, from which project should conversion begin.", + type=int, + ) parser.add_argument( "path", metavar="", type=str, help="The path where to look for .pro files." ) @@ -214,7 +226,12 @@ def main() -> None: base_path = args.path all_files = find_all_pro_files(base_path, args) + if args.offset: + all_files = all_files[args.offset:] + if args.count: + all_files = all_files[:args.count] files_count = len(all_files) + failed_files = run(all_files, pro2cmake, args) if len(all_files) == 0: print("No files found.") -- cgit v1.2.3 From ad98624f7493c66560da3e2af8ed0d3759a4d2f1 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 13 Nov 2019 14:03:20 +0100 Subject: pro2cmake: Adjust script to generate new CMake API calls The script can now generate both old style CMake API calls (add_qt_module), as well as new style (qt_add_module). The first case is considered api version 1, and the second case is version 2. You can force which api version to use for generation using the --api-version command line argument. This is useful when you want to regenerate an old project (one that hasn't switched to new style), to keep the diffs easier to read. When no parameter is specified, the script will auto-detect which api version is used in the existing CMakeLists.txt file (if it exists), and keep using that, until someone force regenerates the project with a new api version. Change-Id: I41e4e6ed5db94ef7220c09b46fc5f12e6613e5d2 Reviewed-by: Leander Beernaert Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 139 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 124 insertions(+), 15 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index dcddfe65d8..d8753a7e2c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -49,6 +49,7 @@ from textwrap import dedent from textwrap import indent as textwrap_indent from functools import lru_cache from shutil import copyfile +from collections import defaultdict from typing import ( List, Optional, @@ -79,6 +80,7 @@ from helper import ( cmake_version_string = "3.15.0" +cmake_api_version = 2 def _parse_commandline(): @@ -170,6 +172,13 @@ def _parse_commandline(): help="If set, pro file will be converted even if skip marker is found in CMakeLists.txt.", ) + parser.add_argument( + "--api-version", + dest="api_version", + type=int, + help="Specify which cmake api version should be generated. 1 or 2, 2 is latest.", + ) + parser.add_argument( "files", metavar="<.pro/.pri file>", @@ -283,6 +292,82 @@ def find_qmake_conf(project_file_path: str = "") -> str: return "" +def set_up_cmake_api_calls(): + def nested_dict(): + return defaultdict(nested_dict) + + api = nested_dict() + + api[1]["qt_extend_target"] = "extend_target" + api[1]["qt_add_module"] = "add_qt_module" + api[1]["qt_add_plugin"] = "add_qt_plugin" + api[1]["qt_add_tool"] = "add_qt_tool" + api[1]["qt_add_test"] = "add_qt_test" + api[1]["qt_add_test_helper"] = "add_qt_test_helper" + api[1]["qt_add_manual_test"] = "add_qt_manual_test" + api[1]["qt_add_benchmark"] = "add_qt_benchmark" + api[1]["qt_add_executable"] = "add_qt_executable" + api[1]["qt_add_simd_part"] = "add_qt_simd_part" + api[1]["qt_add_docs"] = "add_qt_docs" + api[1]["qt_add_resource"] = "add_qt_resource" + api[1]["qt_add_qml_module"] = "add_qml_module" + api[1]["qt_add_cmake_library"] = "add_cmake_library" + + api[2]["qt_extend_target"] = "qt_extend_target" + api[2]["qt_add_module"] = "qt_add_module" + api[2]["qt_add_plugin"] = "qt_add_plugin" + api[2]["qt_add_tool"] = "qt_add_tool" + api[2]["qt_add_test"] = "qt_add_test" + api[2]["qt_add_test_helper"] = "qt_add_test_helper" + api[2]["qt_add_manual_test"] = "qt_add_manual_test" + api[2]["qt_add_benchmark"] = "qt_add_benchmark" + api[2]["qt_add_executable"] = "qt_add_executable" + api[2]["qt_add_simd_part"] = "qt_add_simd_part" + api[2]["qt_add_docs"] = "qt_add_docs" + api[2]["qt_add_resource"] = "qt_add_resource" + api[2]["qt_add_qml_module"] = "qt_add_qml_module" + api[2]["qt_add_cmake_library"] = "qt_add_cmake_library" + + return api + + +cmake_api_calls = set_up_cmake_api_calls() + + +def detect_cmake_api_version_used_in_file_content(project_file_path: str) -> Optional[int]: + dir_path = os.path.dirname(project_file_path) + cmake_project_path = os.path.join(dir_path, "CMakeLists.txt") + + # If file doesn't exist, None implies default version selected by + # script. + if not os.path.exists(cmake_project_path): + return None + + with open(cmake_project_path, "r") as file_fd: + contents = file_fd.read() + + new_api_calls = [api_call for api_call in cmake_api_calls[2]] + new_api_calls_alternatives = "|".join(new_api_calls) + match = re.search(new_api_calls_alternatives, contents) + + # If new style found, return latest api version. Otherwise + # the old version. + if match: + return 2 + else: + return 1 + + +def get_cmake_api_call(api_name: str, api_version: Optional[int] = None) -> str: + if not api_version: + global cmake_api_version + api_version = cmake_api_version + if not cmake_api_calls[api_version][api_name]: + raise RuntimeError(f"No CMake API call {api_name} of version {api_version} found.") + + return cmake_api_calls[api_version][api_name] + + def process_qrc_file( target: str, filepath: str, @@ -434,7 +519,7 @@ def write_add_qt_resource_call( if is_example: add_resource_command = "qt6_add_resources" else: - add_resource_command = "add_qt_resource" + add_resource_command = get_cmake_api_call("qt_add_resource") output += ( f'{add_resource_command}({target} "{resource_name}"\n{params}{spaces(1)}FILES\n' f"{spaces(2)}{file_list}\n)\n" @@ -2036,7 +2121,7 @@ def expand_resource_glob(cm_fh: IO[str], expression: str) -> str: 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: + # Handle QRC files by turning them into qt_add_resource: resources = scope.get_files("RESOURCES") qtquickcompiler_skipped = scope.get_files("QTQUICK_COMPILER_SKIPPED_RESOURCES") qtquickcompiler_retained = scope.get_files("QTQUICK_COMPILER_RETAINED_RESOURCES") @@ -2211,8 +2296,11 @@ def write_extend_target(cm_fh: IO[str], target: str, scope: Scope, indent: int = condition = map_to_cmake_condition(scope.total_condition) + cmake_api_call = get_cmake_api_call("qt_extend_target") extend_scope = ( - f"\n{ind}extend_target({target} CONDITION" f" {condition}\n" f"{extend_qt_string}{ind})\n" + f"\n{ind}{cmake_api_call}({target} CONDITION" + f" {condition}\n" + f"{extend_qt_string}{ind})\n" ) if not extend_qt_string: @@ -2304,7 +2392,7 @@ def write_simd_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0): "SOURCES", [f"{SIMD}_HEADERS", f"{SIMD}_SOURCES", f"{SIMD}_C_SOURCES", f"{SIMD}_ASM"], indent=indent, - header=f"add_qt_simd_part({target} SIMD {simd}\n", + header=f"{get_cmake_api_call('qt_add_simd_part')}({target} SIMD {simd}\n", footer=")\n", ) @@ -2679,7 +2767,7 @@ def write_generic_library(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> s cm_fh, target_name, "Generic Library", - "add_cmake_library", + get_cmake_api_call("qt_add_cmake_library"), scope, extra_lines=extra_lines, indent=indent, @@ -2734,7 +2822,7 @@ def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: cm_fh, target_name, "Module", - "add_qt_module", + f"{get_cmake_api_call('qt_add_module')}", scope, extra_lines=extra, indent=indent, @@ -2766,7 +2854,7 @@ def write_tool(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: cm_fh, tool_name, "Tool", - "add_qt_tool", + get_cmake_api_call("qt_add_tool"), scope, indent=indent, known_libraries={"Qt::Core"}, @@ -2802,7 +2890,7 @@ def write_test(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = cm_fh, test_name, "Test", - "add_qt_test", + get_cmake_api_call("qt_add_test"), scope, indent=indent, known_libraries=libraries, @@ -2823,17 +2911,17 @@ def write_binary(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int 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" + cmake_function_call = get_cmake_api_call("qt_add_executable") extra_keys: List[str] = [] if is_qt_test_helper: binary_name += "_helper" - cmake_function_call = "add_qt_test_helper" + cmake_function_call = get_cmake_api_call("qt_add_test_helper") if is_benchmark: - cmake_function_call = "add_qt_benchmark" + cmake_function_call = get_cmake_api_call("qt_add_benchmark") elif is_manual_test: - cmake_function_call = "add_qt_manual_test" + cmake_function_call = get_cmake_api_call("qt_add_manual_test") else: extra_keys = ["target.path", "INSTALLS"] target_path = scope.get_string("target.path") @@ -3102,11 +3190,11 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: qmldir = None 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_function_name = get_cmake_api_call("qt_add_plugin") if plugin_type: extra.append(f"TYPE {plugin_type}") elif is_qml_plugin: - plugin_function_name = "add_qml_module" + plugin_function_name = get_cmake_api_call("qt_add_qml_module") qmldir = write_qml_plugin(cm_fh, plugin_name, scope, indent=indent, extra_lines=extra) else: target_path = scope.expandString("target.path") @@ -3285,8 +3373,15 @@ def handle_app_or_lib( target = write_binary(cm_fh, scope, gui, indent=indent) # ind = spaces(indent) + cmake_api_call = get_cmake_api_call("qt_add_docs") write_source_file_list( - cm_fh, scope, "", ["QMAKE_DOCS"], indent, header=f"add_qt_docs({target}\n", footer=")\n" + cm_fh, + scope, + "", + ["QMAKE_DOCS"], + indent, + header=f"{cmake_api_call}({target}\n", + footer=")\n", ) @@ -3654,6 +3749,20 @@ def main() -> None: parseresult, project_file_content = parseProFile(file_relative_path, debug=debug_parsing) + # If CMake api version is given on command line, that means the + # user wants to force use that api version. + global cmake_api_version + if args.api_version: + cmake_api_version = args.api_version + else: + # Otherwise detect the api version in the old CMakeLists.txt + # if it exsists. + detected_cmake_api_version = detect_cmake_api_version_used_in_file_content( + file_relative_path + ) + if detected_cmake_api_version: + cmake_api_version = detected_cmake_api_version + if args.debug_parse_result or args.debug: print("\n\n#### Parser result:") print(parseresult) -- cgit v1.2.3 From 7a6c6da2ebbfef33f7284ce5c96aa0c3acabc6bc Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 14 Nov 2019 11:23:18 +0100 Subject: Add more special condition conversions Handle conversions for QT_NO_CURSOR, QT_NO_TRANSLATION and qtConfig(opengles.). Change-Id: Idd930f77e78f235b7997a5083ac7faf630ed9801 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index d8753a7e2c..1d241efc8e 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1396,6 +1396,9 @@ def unwrap_if(input_string): def map_condition(condition: str) -> str: # Some hardcoded cases that are too bothersome to generalize. + condition = re.sub(r"qtConfig\(opengles\.\)", + r"(QT_FEATURE_opengles2 OR QT_FEATURE_opengles3 OR QT_FEATURE_opengles31 OR QT_FEATURE_opengles32)", + condition) condition = re.sub( r"qtConfig\(opengl\(es1\|es2\)\?\)", r"QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3", @@ -1506,6 +1509,14 @@ def map_condition(condition: str) -> str: condition = re.sub(r"(^| )arm64-v8a", "TEST_architecture_arch STREQUAL arm64", condition) condition = re.sub(r"(^| )armeabi-v7a", "TEST_architecture_arch STREQUAL arm", condition) + # some defines replacements + condition = re.sub(r"DEFINES___contains___QT_NO_CURSOR", + r"(NOT QT_FEATURE_cursor)", + condition) + condition = re.sub(r"DEFINES___contains___QT_NO_TRANSLATION", + r"(NOT QT_FEATURE_translation)", + condition) + cmake_condition = "" for part in condition.split(): # some features contain e.g. linux, that should not be -- cgit v1.2.3 From a640db0180f5217f8dc45687c27ea615ea013ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Fri, 22 Nov 2019 17:05:29 +0100 Subject: Add parts needed for QtQuick3D There is a patch in progress to add mappings for Qt3D, so reuse the old broken Qt3D mappings, delete the duplicates. QtOpenGLExtensions is needed, so this patch includes it. Change-Id: I27896ee88b9e6873c8cd52d86afc330e309e1e14 Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 3a48f63de2..f0fa9d5287 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -258,18 +258,17 @@ _qt_library_map = [ extra=["COMPONENTS", "MultimediaQuick"], ), LibraryMapping( - "quick3danimation", "Qt6", "Qt::3DQuickAnimation", extra=["COMPONENTS", "3DQuickAnimation"] + "quick3dassetimport", "Qt6", "Qt::Quick3DAssetImport", extra=["COMPONENTS", "Quick3DAssetImport"] ), + LibraryMapping("quick3d", "Qt6", "Qt::Quick3D", extra=["COMPONENTS", "Quick3D"]), LibraryMapping( - "quick3dextras", "Qt6", "Qt::3DQuickExtras", extra=["COMPONENTS", "3DQuickExtras"] + "quick3drender", "Qt6", "Qt::Quick3DRender", extra=["COMPONENTS", "Quick3DRender"] ), - LibraryMapping("quick3dinput", "Qt6", "Qt::3DQuickInput", extra=["COMPONENTS", "3DQuickInput"]), - LibraryMapping("quick3d", "Qt6", "Qt::3DQuick", extra=["COMPONENTS", "3DQuick"]), LibraryMapping( - "quick3drender", "Qt6", "Qt::3DQuickRender", extra=["COMPONENTS", "3DQuickRender"] + "quick3druntimerender", "Qt6", "Qt::Quick3DRuntimeRender", extra=["COMPONENTS", "Quick3DRuntimeRender"] ), LibraryMapping( - "quick3dscene2d", "Qt6", "Qt::3DQuickScene2D", extra=["COMPONENTS", "3DQuickScene2D"] + "quick3dutils", "Qt6", "Qt::Quick3DUtils", extra=["COMPONENTS", "Quick3DUtils"] ), LibraryMapping( "quickcontrols2", "Qt6", "Qt::QuickControls2", extra=["COMPONENTS", "QuickControls2"] -- cgit v1.2.3 From 4ff9d6d52384f7db4ae1713839928f434025e4ca Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 22 Nov 2019 18:26:41 +0100 Subject: Fix pro2cmake formatting ... by running make format. Change-Id: I523a48abd2c483107f5c16c24daa695fcc0a55e5 Reviewed-by: Alexandru Croitor Reviewed-by: Cristian Maureira-Fredes Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 18 +++++++++--------- util/cmake/run_pro2cmake.py | 9 +++------ 2 files changed, 12 insertions(+), 15 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 1d241efc8e..b0ff3a8171 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1396,9 +1396,11 @@ def unwrap_if(input_string): def map_condition(condition: str) -> str: # Some hardcoded cases that are too bothersome to generalize. - condition = re.sub(r"qtConfig\(opengles\.\)", - r"(QT_FEATURE_opengles2 OR QT_FEATURE_opengles3 OR QT_FEATURE_opengles31 OR QT_FEATURE_opengles32)", - condition) + condition = re.sub( + r"qtConfig\(opengles\.\)", + r"(QT_FEATURE_opengles2 OR QT_FEATURE_opengles3 OR QT_FEATURE_opengles31 OR QT_FEATURE_opengles32)", + condition, + ) condition = re.sub( r"qtConfig\(opengl\(es1\|es2\)\?\)", r"QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3", @@ -1510,12 +1512,10 @@ def map_condition(condition: str) -> str: condition = re.sub(r"(^| )armeabi-v7a", "TEST_architecture_arch STREQUAL arm", condition) # some defines replacements - condition = re.sub(r"DEFINES___contains___QT_NO_CURSOR", - r"(NOT QT_FEATURE_cursor)", - condition) - condition = re.sub(r"DEFINES___contains___QT_NO_TRANSLATION", - r"(NOT QT_FEATURE_translation)", - condition) + condition = re.sub(r"DEFINES___contains___QT_NO_CURSOR", r"(NOT QT_FEATURE_cursor)", condition) + condition = re.sub( + r"DEFINES___contains___QT_NO_TRANSLATION", r"(NOT QT_FEATURE_translation)", condition + ) cmake_condition = "" for part in condition.split(): diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py index ce4a952f94..4a12c57b83 100755 --- a/util/cmake/run_pro2cmake.py +++ b/util/cmake/run_pro2cmake.py @@ -74,10 +74,7 @@ def parse_command_line() -> argparse.Namespace: help="Run pro2cmake with --is-example flag.", ) parser.add_argument( - "--count", - dest="count", - help="How many projects should be converted.", - type=int, + "--count", dest="count", help="How many projects should be converted.", type=int ) parser.add_argument( "--offset", @@ -227,9 +224,9 @@ def main() -> None: all_files = find_all_pro_files(base_path, args) if args.offset: - all_files = all_files[args.offset:] + all_files = all_files[args.offset :] if args.count: - all_files = all_files[:args.count] + all_files = all_files[: args.count] files_count = len(all_files) failed_files = run(all_files, pro2cmake, args) -- cgit v1.2.3 From 2dcab2b9e07bc72ce32af6030e3833b5741a187a Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 25 Nov 2019 09:37:45 +0100 Subject: Fix formatting in helper.py Change-Id: I8ee2abd27064bfc5bf3f291c2743570d11227fcf Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index f0fa9d5287..38ce134051 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -258,18 +258,22 @@ _qt_library_map = [ extra=["COMPONENTS", "MultimediaQuick"], ), LibraryMapping( - "quick3dassetimport", "Qt6", "Qt::Quick3DAssetImport", extra=["COMPONENTS", "Quick3DAssetImport"] + "quick3dassetimport", + "Qt6", + "Qt::Quick3DAssetImport", + extra=["COMPONENTS", "Quick3DAssetImport"], ), LibraryMapping("quick3d", "Qt6", "Qt::Quick3D", extra=["COMPONENTS", "Quick3D"]), LibraryMapping( "quick3drender", "Qt6", "Qt::Quick3DRender", extra=["COMPONENTS", "Quick3DRender"] ), LibraryMapping( - "quick3druntimerender", "Qt6", "Qt::Quick3DRuntimeRender", extra=["COMPONENTS", "Quick3DRuntimeRender"] - ), - LibraryMapping( - "quick3dutils", "Qt6", "Qt::Quick3DUtils", extra=["COMPONENTS", "Quick3DUtils"] + "quick3druntimerender", + "Qt6", + "Qt::Quick3DRuntimeRender", + extra=["COMPONENTS", "Quick3DRuntimeRender"], ), + LibraryMapping("quick3dutils", "Qt6", "Qt::Quick3DUtils", extra=["COMPONENTS", "Quick3DUtils"]), LibraryMapping( "quickcontrols2", "Qt6", "Qt::QuickControls2", extra=["COMPONENTS", "QuickControls2"] ), -- cgit v1.2.3 From 96d6e2276d5d820622c0cfd22b84157f88c1a261 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 22 Nov 2019 13:01:45 +0100 Subject: Add support for DYNAMIC_QMLDIR to pro2cmake Change-Id: I95575dc352343ec86a6f7cacd7b49521262a9a7a Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 184 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 134 insertions(+), 50 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index b0ff3a8171..3f5cc50fa5 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -599,44 +599,51 @@ class QmlDir: qmldir_file.path = path return qmldir_file + def from_lines(self, lines: [str]): + for line in lines: + self.handle_line(line) + def from_file(self, path: str): f = open(path, "r") if not f: raise RuntimeError(f"Failed to open qmldir file at: {path}") for line in f: - if line.startswith("#"): - continue - line = line.strip().replace("\n", "") - if len(line) == 0: - continue - - entries = line.split(" ") - if len(entries) == 0: - raise RuntimeError("Unexpected QmlDir file line entry") - if entries[0] == "module": - self.module = entries[1] - elif entries[0] == "[singleton]": - self.handle_file_singleton(entries[1], entries[2], entries[3]) - elif entries[0] == "internal": - self.handle_file_internal(entries[1], entries[2]) - elif entries[0] == "plugin": - self.plugin_name = entries[1] - if len(entries) > 2: - self.plugin_path = entries[2] - elif entries[0] == "classname": - self.classname = entries[1] - elif entries[0] == "typeinfo": - self.type_infos.append(entries[1]) - elif entries[0] == "depends": - self.depends.append((entries[1], entries[2])) - elif entries[0] == "designersupported": - self.designer_supported = True - elif entries[0] == "import": - self.imports.append(entries[1]) - elif len(entries) == 3: - self.handle_file(entries[0], entries[1], entries[2]) - else: - raise RuntimeError(f"Uhandled qmldir entry {line}") + self.handle_line(line) + + def handle_line(self, line: str): + if line.startswith("#"): + return + line = line.strip().replace("\n", "") + if len(line) == 0: + return + + entries = line.split(" ") + if len(entries) == 0: + raise RuntimeError("Unexpected QmlDir file line entry") + if entries[0] == "module": + self.module = entries[1] + elif entries[0] == "[singleton]": + self.handle_file_singleton(entries[1], entries[2], entries[3]) + elif entries[0] == "internal": + self.handle_file_internal(entries[1], entries[2]) + elif entries[0] == "plugin": + self.plugin_name = entries[1] + if len(entries) > 2: + self.plugin_path = entries[2] + elif entries[0] == "classname": + self.classname = entries[1] + elif entries[0] == "typeinfo": + self.type_infos.append(entries[1]) + elif entries[0] == "depends": + self.depends.append((entries[1], entries[2])) + elif entries[0] == "designersupported": + self.designer_supported = True + elif entries[0] == "import": + self.imports.append(entries[1]) + elif len(entries) == 3: + self.handle_file(entries[0], entries[1], entries[2]) + else: + raise RuntimeError(f"Uhandled qmldir entry {line}") def spaces(indent: int) -> str: @@ -3061,14 +3068,10 @@ def write_example( uri = os.path.basename(dest_dir) dest_dir = f"${{CMAKE_CURRENT_BINARY_DIR}}/{dest_dir}" - add_target = dedent( - f"""\ - qt6_add_qml_module({binary_name} - OUTPUT_DIRECTORY "{dest_dir}" - VERSION 1.0 - URI "{uri}" - """ - ) + add_target = "" + + qml_dir = None + qml_dir_dynamic_imports = False qmldir_file_path_list = scope.get_files("qmldir.files") assert len(qmldir_file_path_list) < 2, "File path must only contain one path" @@ -3078,19 +3081,63 @@ def write_example( if os.path.exists(qmldir_file_path): qml_dir = QmlDir() qml_dir.from_file(qmldir_file_path) + else: + dynamic_qmldir = scope.get("DYNAMIC_QMLDIR") + if not dynamic_qmldir: + return None + qml_dir = QmlDir() + qml_dir.from_lines(dynamic_qmldir) + qml_dir_dynamic_imports = True + + add_target += "set(module_dynamic_qml_imports\n " + if len(qml_dir.imports) != 0: + add_target += "\n ".join(qml_dir.imports) + add_target += "\n)\n\n" + + for sc in scopes[1:]: + import_list = [] + qml_imports = sc.get("DYNAMIC_QMLDIR") + for qml_import in qml_imports: + if not qml_import.startswith("import "): + raise RuntimeError( + "Only qmldir import statements expected in conditional scope!" + ) + import_list.append(qml_import[len("import ") :]) + if len(import_list) == 0: + continue + + assert sc.condition + + add_target += f"if ({sc.condition})\n" + add_target += f" list(APPEND module_dynamic_qml_imports\n " + add_target += "\n ".join(import_list) + add_target += f"\n )\nendif()\n\n" + + add_target += dedent( + f"""\ + qt6_add_qml_module({binary_name} + OUTPUT_DIRECTORY "{dest_dir}" + VERSION 1.0 + URI "{uri}" + """ + ) + + if qml_dir != None: if qml_dir.designer_supported: add_target += " DESIGNER_SUPPORTED\n" if len(qml_dir.classname) != 0: add_target += f" CLASSNAME {qml_dir.classname}\n" - if len(qml_dir.imports) != 0: - qml_dir_imports_line = " \n".join(qml_dir.imports) - add_target += f" IMPORTS\n{qml_dir_imports_line}" if len(qml_dir.depends) != 0: add_target += " DEPENDENCIES\n" for dep in qml_dir.depends: add_target += f" {dep[0]}/{dep[1]}\n" if len(qml_dir.type_names) == 0: add_target += " SKIP_TYPE_REGISTRATION\n" + if len(qml_dir.imports) != 0 and not qml_dir_dynamic_imports: + qml_dir_imports_line = " \n".join(qml_dir.imports) + add_target += f" IMPORTS\n{qml_dir_imports_line}" + if qml_dir_dynamic_imports: + add_target += " IMPORTS ${module_dynamic_qml_imports}\n" add_target += " INSTALL_LOCATION ${INSTALL_EXAMPLEDIR}\n)\n\n" add_target += f"target_sources({binary_name} PRIVATE" @@ -3282,27 +3329,64 @@ def write_qml_plugin( if plugindump_dep: extra_lines.append(f'QML_PLUGINDUMP_DEPENDENCIES "{plugindump_dep}"') + qml_dir = None qmldir_file_path = os.path.join(os.getcwd(), "qmldir") + qml_dir_dynamic_imports = False if os.path.exists(qmldir_file_path): qml_dir = QmlDir() qml_dir.from_file(qmldir_file_path) + else: + dynamic_qmldir = scope.get("DYNAMIC_QMLDIR") + if not dynamic_qmldir: + return None + qml_dir = QmlDir() + qml_dir.from_lines(dynamic_qmldir) + qml_dir_dynamic_imports = True + + # Check scopes for conditional entries + scopes = flatten_scopes(scope) + cm_fh.write("set(module_dynamic_qml_imports\n ") + if len(qml_dir.imports) != 0: + cm_fh.write("\n ".join(qml_dir.imports)) + cm_fh.write("\n)\n\n") + + for sc in scopes[1:]: + import_list = [] + qml_imports = sc.get("DYNAMIC_QMLDIR") + for qml_import in qml_imports: + if not qml_import.startswith("import "): + raise RuntimeError( + "Only qmldir import statements expected in conditional scope!" + ) + import_list.append(qml_import[len("import ") :]) + if len(import_list) == 0: + continue + + assert sc.condition + + cm_fh.write(f"if ({sc.condition})\n") + cm_fh.write(f" list(APPEND module_dynamic_qml_imports\n ") + cm_fh.write("\n ".join(import_list)) + cm_fh.write(f"\n )\nendif()\n\n") + + if qml_dir != None: if qml_dir.designer_supported: extra_lines.append("DESIGNER_SUPPORTED") if len(qml_dir.classname) != 0: extra_lines.append(f"CLASSNAME {qml_dir.classname}") - if len(qml_dir.imports) != 0: - qml_dir_imports_line = "\n ".join(qml_dir.imports) - extra_lines.append("IMPORTS\n " f"{qml_dir_imports_line}") if len(qml_dir.depends) != 0: extra_lines.append("DEPENDENCIES") for dep in qml_dir.depends: extra_lines.append(f" {dep[0]}/{dep[1]}") if len(qml_dir.type_names) == 0: extra_lines.append("SKIP_TYPE_REGISTRATION") + if len(qml_dir.imports) != 0 and not qml_dir_dynamic_imports: + qml_dir_imports_line = "\n ".join(qml_dir.imports) + extra_lines.append("IMPORTS\n " f"{qml_dir_imports_line}") + if qml_dir_dynamic_imports: + extra_lines.append("IMPORTS ${module_dynamic_qml_imports}") - return qml_dir - - return None + return qml_dir def write_qml_plugin_epilogue( -- cgit v1.2.3 From 8d35ad8726f44c4e853b5a192203ffcbbd2476e1 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 20 Nov 2019 17:28:15 +0100 Subject: Add FindWrapHarfbuzz Apparently we didn't have a wrap find module for it before. Add one, and add special support to handle a Gentoo broken Config file which is exported by an autotools build of harbuzz. Change-Id: I83cbeb817caf2610104c16713d4eac6ab6f8c63b Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 38ce134051..20ee7956ab 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -384,7 +384,7 @@ _library_map = [ LibraryMapping("gnu_iconv", None, None), LibraryMapping("gtk3", "GTK3", "PkgConfig::GTK3"), LibraryMapping("gssapi", "GSSAPI", "GSSAPI::GSSAPI"), - LibraryMapping("harfbuzz", "harfbuzz", "harfbuzz::harfbuzz"), + LibraryMapping("harfbuzz", "WrapHarfbuzz", "WrapHarfbuzz::WrapHarfbuzz"), LibraryMapping("host_dbus", None, None), LibraryMapping( "icu", "ICU", "ICU::i18n ICU::uc ICU::data", extra=["COMPONENTS", "i18n", "uc", "data"] -- cgit v1.2.3 From 1c655fb0fc08e89ab5efb23889266b710334f1a5 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 20 Nov 2019 09:25:19 +0100 Subject: Post merge fixes Change-Id: I78d3c9687f99c0a32da04257e297e88ef0b02581 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 6 ++++++ util/cmake/pro2cmake.py | 13 +++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 20ee7956ab..37135be7fa 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -356,6 +356,12 @@ _qt_library_map = [ "Qt6", "Qt::LinuxOfonoSupport", extra=["COMPONENTS", "LinuxOfonoSupport"], + ), + LibraryMapping( + "linuxofono_support_private", + "Qt6", + "Qt::LinuxOfonoSupportPrivate", + extra=["COMPONENTS", "LinuxOfonoSupportPrivate"], ) # qtzlib: No longer supported. ] diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3f5cc50fa5..02a845c713 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1519,10 +1519,15 @@ def map_condition(condition: str) -> str: condition = re.sub(r"(^| )armeabi-v7a", "TEST_architecture_arch STREQUAL arm", condition) # some defines replacements - condition = re.sub(r"DEFINES___contains___QT_NO_CURSOR", r"(NOT QT_FEATURE_cursor)", condition) - condition = re.sub( - r"DEFINES___contains___QT_NO_TRANSLATION", r"(NOT QT_FEATURE_translation)", condition - ) + condition = re.sub(r"DEFINES___contains___QT_NO_CURSOR", + r"(NOT QT_FEATURE_cursor)", + condition) + condition = re.sub(r"DEFINES___contains___QT_NO_TRANSLATION", + r"(NOT QT_FEATURE_translation)", + condition) + condition = re.sub(r"styles___contains___fusion", + r"QT_FEATURE_style_fusion", + condition) cmake_condition = "" for part in condition.split(): -- cgit v1.2.3 From 2637a1aee2d6630dd0d0e313d045f107bde1cb92 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 25 Nov 2019 16:00:32 +0100 Subject: pro2cmake: Format again Change-Id: I3c82e6c13d01203bda8ab28dc5cb2f9fe6a0c4c2 Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 02a845c713..89d30a469b 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1519,15 +1519,11 @@ def map_condition(condition: str) -> str: condition = re.sub(r"(^| )armeabi-v7a", "TEST_architecture_arch STREQUAL arm", condition) # some defines replacements - condition = re.sub(r"DEFINES___contains___QT_NO_CURSOR", - r"(NOT QT_FEATURE_cursor)", - condition) - condition = re.sub(r"DEFINES___contains___QT_NO_TRANSLATION", - r"(NOT QT_FEATURE_translation)", - condition) - condition = re.sub(r"styles___contains___fusion", - r"QT_FEATURE_style_fusion", - condition) + condition = re.sub(r"DEFINES___contains___QT_NO_CURSOR", r"(NOT QT_FEATURE_cursor)", condition) + condition = re.sub( + r"DEFINES___contains___QT_NO_TRANSLATION", r"(NOT QT_FEATURE_translation)", condition + ) + condition = re.sub(r"styles___contains___fusion", r"QT_FEATURE_style_fusion", condition) cmake_condition = "" for part in condition.split(): -- cgit v1.2.3 From 55a15a1c1b93d36d705fc69e44b5c806b807dd55 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 8 Apr 2019 17:23:57 +0200 Subject: Add initial support for cross-building to iOS Tested locally with the following configurations: - iOS device builds (arm64) - iOS simulator builds (x86_64) - iOS simulator_and_device builds (fat arm64 and x86_64 archives) All iOS builds currently require a custom vcpkg fork which contains fixes for building the required 3rd party libraries. qtsvg, qtdeclarative, qtgraphicaleffects and qtquickcontrols2 have also been tested to build successfully. simulator_and_device builds are also supported, but require an umerged patch in upstream CMake as well as further patches to vcpkg. Task-number: QTBUG-75576 Change-Id: Icd29913fbbd52a60e07ea5253fd9c7af7f8ce44c Reviewed-by: Cristian Adam Reviewed-by: Qt CMake Build Bot Reviewed-by: Leander Beernaert --- util/cmake/configurejson2cmake.py | 1 + 1 file changed, 1 insertion(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 70d28c276b..481701eaed 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -708,6 +708,7 @@ def parseFeature(ctx, feature, data, cm_fh): "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 )" }, + "simulator_and_device": {"condition": "APPLE_UIKIT AND NOT QT_UIKIT_SDK"}, "pkg-config": None, "posix_fallocate": None, # Only needed for sqlite, which we do not want to build "posix-libiconv": { -- cgit v1.2.3 From 461020a86aa5822325edba9ec565db39e5df8092 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 28 Aug 2019 15:15:50 +0200 Subject: Export non-private and non-public features and CONFIG values Before we only exported features that had outputType PUBLIC or PRIVATE on the various "QT_ENABLED_PUBLIC_FEATURES" target properties. Now we also export features that have output type privateConfig, publicConfig and publicQtConfig. The new properties names are: - QT_QMAKE_PUBLIC_CONFIG for outputType == publicConfig - QT_QMAKE_PRIVATE_CONFIG for outputType == privateConfig - QT_QMAKE_PUBLIC_QT_CONFIG for outputType == publicQtConfig These need to be exported for 2 reasons: - other modules that need to check the config values - in preparation for generating proper qmake .prl and .pri information for each module Note that the config values are now considered actual features when doing condition evaluation. So if there exists a feature "ssse3" with outputType privateConfig, its enabled state can be checked via QT_FEATURE_ssse3 in consuming modules (but not in the declaring module). These config values are also placed in the respective QT_ENABLED_PUBLIC_FEATURES, QT_ENABLED_PRIVATE_FEATURES properties when exporting a target, so the properties will now contain both features and config values. In order to make this work, feature name normalization has to happen at CMake time, rather than done by the python script. This means that features like "developer-build" need to retain the dash in the qt_feature(), qt_feature_definition() and qt_feature_config() calls, rather than generating "developer_build" as the script did before. The normalization is done at CMake time. Feature conditions, CMake code, and -DFEATURE_foo=bar options passed on the command line should still use the underscore version, but the original name is used for the QT_QMAKE_PUBLIC_CONFIG properties. Note that "c++11" like features are normalized to "cxx11". Implementation wise, the configurejson2cmake script is adjusted to parse these new output types. Also QtBuild and QtFeature are adjusted to save the config values in properties, and re-export them from GlobalConfig to Core. Task-number: QTBUG-75666 Task-number: QTBUG-78178 Change-Id: Ibd4b152e372bdf2d09ed117644f2f2ac53ec5e75 Reviewed-by: Qt CMake Build Bot Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 90 +++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 18 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 481701eaed..f986b65acb 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -803,22 +803,16 @@ def parseFeature(ctx, feature, data, cm_fh): negativeFeature = False # #define QT_NO_featurename in public header internalFeature = False # No custom or QT_FEATURE_ defines publicDefine = False # #define MY_CUSTOM_DEFINE in public header + publicConfig = False # add to CONFIG in public pri file + privateConfig = False # add to CONFIG in private pri file + publicQtConfig = False # add to QT_CONFIG in public pri file for o in output: outputType = o - outputArgs = {} if isinstance(o, dict): outputType = o["type"] - outputArgs = o - if outputType in [ - "varAssign", - "varAppend", - "varRemove", - "publicQtConfig", - "privateConfig", - "publicConfig", - ]: + if outputType in ["varAssign", "varAppend", "varRemove"]: continue elif outputType == "define": publicDefine = True @@ -830,15 +824,32 @@ def parseFeature(ctx, feature, data, cm_fh): privateFeature = True elif outputType == "internalFeature": internalFeature = True + elif outputType == "publicConfig": + publicConfig = True + elif outputType == "privateConfig": + privateConfig = True + elif outputType == "publicQtConfig": + publicQtConfig = True else: print(f" XXXX UNHANDLED OUTPUT TYPE {outputType} in feature {feature}.") continue - if not any([publicFeature, privateFeature, internalFeature, publicDefine, negativeFeature]): + if not any( + [ + publicFeature, + privateFeature, + internalFeature, + publicDefine, + negativeFeature, + publicConfig, + privateConfig, + publicQtConfig, + ] + ): print(f" **** Skipping feature {feature}: Not relevant for C++.") return - cxxFeature = featureName(feature) + normalized_feature_name = featureName(feature) def writeFeature( name, @@ -877,12 +888,12 @@ def parseFeature(ctx, feature, data, cm_fh): # Default internal feature case. featureCalls = {} - featureCalls[cxxFeature] = {"name": cxxFeature, "labelAppend": "", "autoDetect": autoDetect} + featureCalls[feature] = {"name": feature, "labelAppend": "", "autoDetect": autoDetect} # Go over all outputs to compute the number of features that have to be declared for o in output: outputType = o - name = cxxFeature + name = feature # The label append is to provide a unique label for features that have more than one output # with different names. @@ -899,13 +910,19 @@ def parseFeature(ctx, feature, data, cm_fh): if name not in featureCalls: featureCalls[name] = {"name": name, "labelAppend": labelAppend} - if name != cxxFeature: - featureCalls[name]["superFeature"] = cxxFeature + if name != feature: + featureCalls[name]["superFeature"] = normalized_feature_name if outputType in ["feature", "publicFeature"]: featureCalls[name]["publicFeature"] = True elif outputType == "privateFeature": featureCalls[name]["privateFeature"] = True + elif outputType == "publicConfig": + featureCalls[name]["publicConfig"] = True + elif outputType == "privateConfig": + featureCalls[name]["privateConfig"] = True + elif outputType == "publicQtConfig": + featureCalls[name]["publicQtConfig"] = True # Write the qt_feature() calls from the computed feature map for _, args in featureCalls.items(): @@ -923,7 +940,7 @@ def parseFeature(ctx, feature, data, cm_fh): if outputType == "feature": outputType = "define" outputArgs = { - "name": f"QT_NO_{cxxFeature.upper()}", + "name": f"QT_NO_{normalized_feature_name.upper()}", "negative": True, "value": 1, "type": "define", @@ -937,13 +954,50 @@ def parseFeature(ctx, feature, data, cm_fh): continue out_name = outputArgs.get("name") - cm_fh.write(f'qt_feature_definition("{cxxFeature}" "{out_name}"') + cm_fh.write(f'qt_feature_definition("{feature}" "{out_name}"') if outputArgs.get("negative", False): cm_fh.write(" NEGATE") if outputArgs.get("value") is not None: cm_fh.write(f' VALUE "{outputArgs.get("value")}"') cm_fh.write(")\n") + # Write qt_feature_config() calls + for o in output: + outputType = o + name = feature + modified_name = name + + outputArgs = {} + if isinstance(o, dict): + outputType = o["type"] + outputArgs = o + if "name" in o: + modified_name = o["name"] + + if outputType not in ["publicConfig", "privateConfig", "publicQtConfig"]: + continue + + config_type = "" + if outputType == "publicConfig": + config_type = "QMAKE_PUBLIC_CONFIG" + elif outputType == "privateConfig": + config_type = "QMAKE_PRIVATE_CONFIG" + elif outputType == "publicQtConfig": + config_type = "QMAKE_PUBLIC_QT_CONFIG" + + if not config_type: + print(" XXXX config output without type in feature {}.".format(feature)) + continue + + cm_fh.write('qt_feature_config("{}" {}'.format(name, config_type)) + if outputArgs.get("negative", False): + cm_fh.write("\n NEGATE") + if modified_name != name: + cm_fh.write("\n") + cm_fh.write(lineify("NAME", modified_name, quote=True)) + + cm_fh.write(")\n") + def processInputs(ctx, data, cm_fh): print(" inputs:") -- cgit v1.2.3 From 0f220d473a6048c0f1267a96ed5cc18fa39ee61c Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 26 Nov 2019 10:10:55 +0100 Subject: Collect Json Metatypes from CMake's Automoc This patch adds a new bootstrap tool which will read CMake's AutoGenInfo.json and ParseCache.txt to determine what the current list of json files is that needs to be passed to moc --collect-json option. Right now this is enabled for qt_add_module() with the option GENERATE_METATYPES. pro2cmake has also been updated to detect qmake's CONFIG += metatypes and to generate the above option for modules. The implementation lives in Qt6CoreMacros so it can eventually be used in the public facing apis. The generated meta types file is saved under the target property QT_MODULE_META_TYPES_FILE. Change-Id: I03709c662be81dd0912d0068c23ee2507bfe4383 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 89d30a469b..7c54e2be2c 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2827,6 +2827,8 @@ def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: extra.append("NO_PRIVATE_MODULE") if "header_module" in scope.get("CONFIG"): extra.append("HEADER_MODULE") + if "metatypes" in scope.get("CONFIG"): + extra.append("GENERATE_METATYPES") module_config = scope.get("MODULE_CONFIG") if len(module_config): -- cgit v1.2.3 From ea9c2e558c202069b1358f5801808d0adb29dbc7 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 9 Dec 2019 13:12:20 +0100 Subject: Add library mapping for QtTools Change-Id: I59fdad5b56875b965a4831b07b5d8d13f9d7b10f Reviewed-by: Alexandru Croitor Reviewed-by: Qt CMake Build Bot --- util/cmake/helper.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 37135be7fa..359a224053 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -362,6 +362,9 @@ _qt_library_map = [ "Qt6", "Qt::LinuxOfonoSupportPrivate", extra=["COMPONENTS", "LinuxOfonoSupportPrivate"], + ), + LibraryMapping( + "tools", "Qt6", "Qt::Tools", extra=["COMPONENTS", "Tools"] ) # qtzlib: No longer supported. ] -- cgit v1.2.3 From e0205f1793315cfece4fa61200048ea2eda4aa13 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 10 Dec 2019 13:16:02 +0100 Subject: Add special condition replacement for libclang in qttools Change-Id: Icf35a2e39307e6239d8704824105bdc6b9081ffd Reviewed-by: Alexandru Croitor --- util/cmake/configurejson2cmake.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index f986b65acb..9f93ecafa6 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -317,6 +317,9 @@ def map_condition(condition): condition = re.sub("\\s+", " ", mapped_condition) condition = condition.strip() + # Special case for WrapLibClang in qttools + condition = condition.replace("TEST_libclang.has_clangcpp", "TEST_libclang") + if has_failed: condition += " OR FIXME" -- cgit v1.2.3 From 19e79ca0a4655abf580b0bc023b3f1664f7b156b Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 11 Dec 2019 11:22:05 +0100 Subject: Add condition mapping for cross_compile to CMAKE_CROSS_COMPILING Change-Id: I90220eda846299c14aca67ba59a2a9518bea2dee Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 7c54e2be2c..f4c9aef9ac 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1525,6 +1525,8 @@ def map_condition(condition: str) -> str: ) condition = re.sub(r"styles___contains___fusion", r"QT_FEATURE_style_fusion", condition) + condition = condition.replace("cross_compile", "CMAKE_CROSS_COMPILING") + cmake_condition = "" for part in condition.split(): # some features contain e.g. linux, that should not be -- cgit v1.2.3 From 6d73b02ace298daabbb8f692fcbbbe3728c8b25a Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 11 Dec 2019 10:42:47 +0100 Subject: Add library mappings for QtAxContainer and QtWekKitWidgets Change-Id: I5d5b77e86437f752465bf8ae7c6dd6730b3bf471 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 359a224053..9ee3714cdc 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -365,6 +365,12 @@ _qt_library_map = [ ), LibraryMapping( "tools", "Qt6", "Qt::Tools", extra=["COMPONENTS", "Tools"] + ), + LibraryMapping( + "axcontainer", "Qt6", "Qt::AxContainer", extra=["COMPONENTS", "AxContainer"] + ), + LibraryMapping( + "webkitwidgets", "Qt6", "Qt::WebKitWidgets", extra=["COMPONENTS", "WebKitWidgets"] ) # qtzlib: No longer supported. ] -- cgit v1.2.3 From 30af71d7eb6651e25355441261553c24fbd06927 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 13 Jan 2020 10:22:30 +0100 Subject: Fix CMAKE_CROSSCOMPILING property name conversion Change-Id: I481043166ce3b0b70834bf63f2a70e84203304e9 Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f4c9aef9ac..8378cd6228 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1525,7 +1525,7 @@ def map_condition(condition: str) -> str: ) condition = re.sub(r"styles___contains___fusion", r"QT_FEATURE_style_fusion", condition) - condition = condition.replace("cross_compile", "CMAKE_CROSS_COMPILING") + condition = condition.replace("cross_compile", "CMAKE_CROSSCOMPILING") cmake_condition = "" for part in condition.split(): -- cgit v1.2.3 From ee5d7facc14a4d91475ac5e95dba5f346ee3f880 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 14 Jan 2020 13:30:12 +0100 Subject: Adjust OUTPUT_DIRECTORY for tests when targets starts with ../ Automatically insert OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../ for each tests that has a target name which starts with ../. To do so we added the TARGET_ORIGINAL property which does not remove the ../ from the target name. Change-Id: I55ba1385aa160610a0ff80ed56ff37a80c3590fa Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8378cd6228..97f92d7614 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1369,6 +1369,10 @@ class Scope(object): target = self.expandString("TARGET") or os.path.splitext(os.path.basename(self.file))[0] return re.sub(r"\.\./", "", target) + @property + def TARGET_ORIGINAL(self) -> str: + return self.expandString("TARGET") or os.path.splitext(os.path.basename(self.file))[0] + @property def _INCLUDED(self) -> List[str]: return self.get("_INCLUDED") @@ -2904,6 +2908,10 @@ def write_test(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = for path in importpath: extra.append(f' "{path}"') + target_original = scope.TARGET_ORIGINAL + if target_original and target_original.startswith("../"): + extra.append("OUTPUT_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/../\"") + requires_content = expand_project_requirements(scope, skip_message=True) if requires_content: requires_content += "\n" -- cgit v1.2.3 From 99a824bbb74aa88a16a8c7b483e1ca599dd66d4f Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 16 Jan 2020 09:29:29 +0100 Subject: Do not set OUTPUT_DIRECTORY if it is already set This can happen in unit tests where a test has TARGET set to "../name", which requires the target to placed in the parent binary directory. It is possible to run into a second assignment for OUTPUT_DIRECTORY via the DESTDIR property (e.g: qdbushmarshall test) which can then result in two OUTPUT_DIRECTORY values. However, the first one needs to take precedence or the tests won't execute properly. Change-Id: Ib263843fa86c3dd68d92a0989b95f2890335c92d Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 97f92d7614..ffb36324cd 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2710,8 +2710,14 @@ def write_main_part( # Check for DESTDIR override destdir = scope.get_string("DESTDIR") if destdir: - destdir = replace_path_constants(destdir, scope) - extra_lines.append(f'OUTPUT_DIRECTORY "{destdir}"') + already_added = False + for line in extra_lines: + if line.startswith("OUTPUT_DIRECTORY"): + already_added = True + break + if not already_added: + destdir = replace_path_constants(destdir, scope) + extra_lines.append(f'OUTPUT_DIRECTORY "{destdir}"') cm_fh.write(f"{spaces(indent)}{cmake_function}({name}\n") for extra_line in extra_lines: -- cgit v1.2.3 From b29cb7889ac5b3a7b4b515971f462f65b7db7ec4 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 16 Jan 2020 09:39:26 +0100 Subject: Run make format Change-Id: If1107aefbfd374d6b664a94b21b9bbb5ed4cc178 Reviewed-by: Alexandru Croitor --- util/cmake/helper.py | 8 ++------ util/cmake/pro2cmake.py | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 9ee3714cdc..091b619201 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -363,12 +363,8 @@ _qt_library_map = [ "Qt::LinuxOfonoSupportPrivate", extra=["COMPONENTS", "LinuxOfonoSupportPrivate"], ), - LibraryMapping( - "tools", "Qt6", "Qt::Tools", extra=["COMPONENTS", "Tools"] - ), - LibraryMapping( - "axcontainer", "Qt6", "Qt::AxContainer", extra=["COMPONENTS", "AxContainer"] - ), + LibraryMapping("tools", "Qt6", "Qt::Tools", extra=["COMPONENTS", "Tools"]), + LibraryMapping("axcontainer", "Qt6", "Qt::AxContainer", extra=["COMPONENTS", "AxContainer"]), LibraryMapping( "webkitwidgets", "Qt6", "Qt::WebKitWidgets", extra=["COMPONENTS", "WebKitWidgets"] ) diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ffb36324cd..3ad427f609 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2916,7 +2916,7 @@ def write_test(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = target_original = scope.TARGET_ORIGINAL if target_original and target_original.startswith("../"): - extra.append("OUTPUT_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/../\"") + extra.append('OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../"') requires_content = expand_project_requirements(scope, skip_message=True) if requires_content: -- cgit v1.2.3 From cce8ada8141d786c1deda78fdba485b4c67f9687 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 4 Dec 2019 11:09:33 +0100 Subject: Support for QML Type Registrar Change-Id: Ifc1f44cf40b22c20ab768333ba9d5ce58a5f7250 Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 3ad427f609..fe2a08b71f 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -2839,7 +2839,7 @@ def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: extra.append("NO_PRIVATE_MODULE") if "header_module" in scope.get("CONFIG"): extra.append("HEADER_MODULE") - if "metatypes" in scope.get("CONFIG"): + if "metatypes" in scope.get("CONFIG") or "qmltypes" in scope.get("CONFIG"): extra.append("GENERATE_METATYPES") module_config = scope.get("MODULE_CONFIG") @@ -3280,6 +3280,8 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: extra.append(f'INSTALL_DIRECTORY "{target_path}"') else: extra.append("SKIP_INSTALL") + if "qmltypes" in scope.get("CONFIG"): + extra.append("GENERATE_QMLTYPES") plugin_class_name = scope.get_string("PLUGIN_CLASS_NAME") if plugin_class_name: @@ -3498,6 +3500,39 @@ def handle_app_or_lib( footer=")\n", ) + # Generate qmltypes instruction for anything that may have CONFIG += qmltypes + # that is not a qml plugin + if "qmltypes" in scope.get("CONFIG") and not "qml_plugin" in scope.get("_LOADED"): + cm_fh.write(f"\n{spaces(indent)}set_target_properties({target} PROPERTIES\n") + cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_INSTALL_QMLTYPES TRUE\n") + + import_version = scope.get_string("IMPORT_VERSION") + if not import_version: + import_version = scope.get_string("QML_IMPORT_VERSION") + if import_version: + import_version = import_version.replace( + "$$QT_MINOR_VERSION", "${CMAKE_PROJECT_VERSION_MINOR}" + ) + cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_VERSION {import_version}\n") + + import_name = scope.expandString("QML_IMPORT_NAME") + if import_name: + cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_URI {import_name}\n") + + target_path = scope.get("TARGETPATH") + if target_path: + cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_TARGET_PATH {target_path}\n") + + install_dir = scope.expandString("QMLTYPES_INSTALL_DIR") + if install_dir: + install_dir = install_dir.replace( + "$$[QT_INSTALL_QML]","${Qt6_DIR}/../../../qml" + ) + cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_INSTALL_DIR \"{install_dir}\"\n") + + cm_fh.write(f"{spaces(indent)})\n\n") + cm_fh.write(f"qt6_qml_type_registration({target})\n") + def handle_top_level_repo_project(scope: Scope, cm_fh: IO[str]): # qtdeclarative -- cgit v1.2.3 From f67d8ae2d4339c50cf0a4ca26f25c3afebc128ea Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 22 Jan 2020 14:22:19 +0100 Subject: Fix plugin target names to be compatible with Qt 5 In Qt 5, qmake generates CMake Config files which expose the plugins as imported libraries. The name of these libraries are derived from the plugin class name. So QCocoaIntegrationPlugin, and not qcocoa. To keep compatibility between Qt5 and Qt6 CMake target names, the pro2cmake script should generate plugin target names based on the plugin class names. To avoid passing the same name in qt_add_plugin (target and CLASS_NAME), derive the class name from the target if the class name is not explicitly specified. Also add a new OUTPUT_NAME parameter which is used to change the final file name of the plugin, so that it's compatible with Qt5. For example to generate a qcocoa.dylib file, instead of QCocoaIntegrationPlugin.dylib file. The same OUTPUT_NAME value will be used for generation of plugin .prl files for qmake consumption. Change-Id: I4d53e680d7beb62befecd359cdf6bba60a34ff0d Reviewed-by: Qt CMake Build Bot Reviewed-by: Simon Hausmann --- util/cmake/pro2cmake.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index fe2a08b71f..24dfd0ca8b 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3259,10 +3259,30 @@ def write_example( def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: - plugin_name = scope.TARGET + extra = [] + qmake_target_name = scope.TARGET + + # Forward the original Qt5 plugin target name, to correctly name the + # final library file name, and also for .prl generation. + if qmake_target_name: + extra.append(f"OUTPUT_NAME {qmake_target_name}") + + # In Qt 6 CMake, the CMake target name for a plugin should be the + # same as it is in Qt5. qmake in Qt 5 derived the CMake target name + # from the "plugin class name", so use that. + # If the class name isn't empty, use that as the target name. + # Otherwise use the of value qmake TARGET + plugin_class_name = scope.get_string("PLUGIN_CLASS_NAME") + if plugin_class_name: + plugin_name = plugin_class_name + else: + plugin_name = qmake_target_name assert plugin_name - extra = [] + # If the target name is derived from the class name, no need to + # forward the class name. + if plugin_class_name and plugin_class_name != plugin_name: + extra.append(f"CLASS_NAME {plugin_class_name}") qmldir = None plugin_type = scope.get_string("PLUGIN_TYPE") @@ -3283,10 +3303,6 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: if "qmltypes" in scope.get("CONFIG"): extra.append("GENERATE_QMLTYPES") - plugin_class_name = scope.get_string("PLUGIN_CLASS_NAME") - if plugin_class_name: - extra.append(f"CLASS_NAME {plugin_class_name}") - if "static" in scope.get("CONFIG"): extra.append("STATIC") -- cgit v1.2.3 From 604aa83ad910c3a17af33850312d3c61a59f558f Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 24 Jan 2020 14:04:15 +0100 Subject: Reformat pro2cmake Change-Id: I97175e8edbd4b911397f5b03a646006eff32a5bc Reviewed-by: Simon Hausmann Reviewed-by: Qt CMake Build Bot --- util/cmake/pro2cmake.py | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 24dfd0ca8b..ed74a50b66 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3519,35 +3519,33 @@ def handle_app_or_lib( # Generate qmltypes instruction for anything that may have CONFIG += qmltypes # that is not a qml plugin if "qmltypes" in scope.get("CONFIG") and not "qml_plugin" in scope.get("_LOADED"): - cm_fh.write(f"\n{spaces(indent)}set_target_properties({target} PROPERTIES\n") - cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_INSTALL_QMLTYPES TRUE\n") - - import_version = scope.get_string("IMPORT_VERSION") - if not import_version: - import_version = scope.get_string("QML_IMPORT_VERSION") - if import_version: - import_version = import_version.replace( - "$$QT_MINOR_VERSION", "${CMAKE_PROJECT_VERSION_MINOR}" - ) - cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_VERSION {import_version}\n") + cm_fh.write(f"\n{spaces(indent)}set_target_properties({target} PROPERTIES\n") + cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_INSTALL_QMLTYPES TRUE\n") + + import_version = scope.get_string("IMPORT_VERSION") + if not import_version: + import_version = scope.get_string("QML_IMPORT_VERSION") + if import_version: + import_version = import_version.replace( + "$$QT_MINOR_VERSION", "${CMAKE_PROJECT_VERSION_MINOR}" + ) + cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_VERSION {import_version}\n") - import_name = scope.expandString("QML_IMPORT_NAME") - if import_name: - cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_URI {import_name}\n") + import_name = scope.expandString("QML_IMPORT_NAME") + if import_name: + cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_URI {import_name}\n") - target_path = scope.get("TARGETPATH") - if target_path: - cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_TARGET_PATH {target_path}\n") + target_path = scope.get("TARGETPATH") + if target_path: + cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_TARGET_PATH {target_path}\n") - install_dir = scope.expandString("QMLTYPES_INSTALL_DIR") - if install_dir: - install_dir = install_dir.replace( - "$$[QT_INSTALL_QML]","${Qt6_DIR}/../../../qml" - ) - cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_INSTALL_DIR \"{install_dir}\"\n") + install_dir = scope.expandString("QMLTYPES_INSTALL_DIR") + if install_dir: + install_dir = install_dir.replace("$$[QT_INSTALL_QML]", "${Qt6_DIR}/../../../qml") + cm_fh.write(f'{spaces(indent+1)}QT_QML_MODULE_INSTALL_DIR "{install_dir}"\n') - cm_fh.write(f"{spaces(indent)})\n\n") - cm_fh.write(f"qt6_qml_type_registration({target})\n") + cm_fh.write(f"{spaces(indent)})\n\n") + cm_fh.write(f"qt6_qml_type_registration({target})\n") def handle_top_level_repo_project(scope: Scope, cm_fh: IO[str]): -- cgit v1.2.3 From 3c6b0a07da2bc2f39d7355e90c37387fc29d5fd8 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 27 Jan 2020 15:15:00 +0100 Subject: pro2cmake: Don't set OUTPUT_NAME for qml plugins Amends f67d8ae2d4339c50cf0a4ca26f25c3afebc128ea Change-Id: I288a9388d4ebd354199bfa0913eff34d93a58d75 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index ed74a50b66..8c82a796b9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3260,11 +3260,12 @@ def write_example( def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: extra = [] + is_qml_plugin = any("qml_plugin" == s for s in scope.get("_LOADED")) qmake_target_name = scope.TARGET # Forward the original Qt5 plugin target name, to correctly name the # final library file name, and also for .prl generation. - if qmake_target_name: + if qmake_target_name and not is_qml_plugin: extra.append(f"OUTPUT_NAME {qmake_target_name}") # In Qt 6 CMake, the CMake target name for a plugin should be the @@ -3286,7 +3287,6 @@ def write_plugin(cm_fh, scope, *, indent: int = 0) -> str: qmldir = None plugin_type = scope.get_string("PLUGIN_TYPE") - is_qml_plugin = any("qml_plugin" == s for s in scope.get("_LOADED")) plugin_function_name = get_cmake_api_call("qt_add_plugin") if plugin_type: extra.append(f"TYPE {plugin_type}") -- cgit v1.2.3 From 88b7f64e7385e51831b3a852c88d6bbcdc665d87 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 28 Jan 2020 16:35:25 +0100 Subject: Fix example generation without qmldir file Do not return when no qmldir file is present and no dynamic qmldir information is set. Change-Id: I04e458f69e4e4a6ec9b1e7ca7ba0b0f7520996f7 Reviewed-by: Qt CMake Build Bot Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 8c82a796b9..627a0402d8 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3097,13 +3097,11 @@ def write_example( qmldir_file_path = qmldir_file_path_list[0] if qmldir_file_path_list else "qmldir" qmldir_file_path = os.path.join(os.getcwd(), qmldir_file_path[0]) + dynamic_qmldir = scope.get("DYNAMIC_QMLDIR") if os.path.exists(qmldir_file_path): qml_dir = QmlDir() qml_dir.from_file(qmldir_file_path) - else: - dynamic_qmldir = scope.get("DYNAMIC_QMLDIR") - if not dynamic_qmldir: - return None + elif dynamic_qmldir: qml_dir = QmlDir() qml_dir.from_lines(dynamic_qmldir) qml_dir_dynamic_imports = True -- cgit v1.2.3 From 957007a9493a41064dcb83155e31ce52021db077 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 29 Jan 2020 15:34:03 +0100 Subject: pro2cmake: Handle QML_IMPORT_MAJOR_VERSION and minor variants This is needed for qtdeclarative examples, otherwise CMake configuration fails due to missing QML import version info. Change-Id: Iedde7b6a9e2d5ac7f6d81969ac7d6d874361c02e Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 627a0402d8..f6017880c2 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3523,6 +3523,17 @@ def handle_app_or_lib( import_version = scope.get_string("IMPORT_VERSION") if not import_version: import_version = scope.get_string("QML_IMPORT_VERSION") + if not import_version: + import_major_version = scope.get_string("QML_IMPORT_MAJOR_VERSION") + import_minor_version = scope.get_string("QML_IMPORT_MINOR_VERSION") + + if not import_major_version and not import_minor_version: + raise RuntimeError(f"No QML_IMPORT_VERSION info found for target {target}.") + + if not import_minor_version: + import_minor_version = 0 + import_version = f"{import_major_version}.{import_minor_version}" + if import_version: import_version = import_version.replace( "$$QT_MINOR_VERSION", "${CMAKE_PROJECT_VERSION_MINOR}" -- cgit v1.2.3 From ccdc6c2dd3d4b7e7b6a06488c278e4c9f06e8925 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 29 Jan 2020 15:36:21 +0100 Subject: pro2cmake: Fix flake and mypy issues Change-Id: I33ab818c53f751ede7a7840b1086a3ae8263e109 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- util/cmake/pro2cmake.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'util/cmake') diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index f6017880c2..04443b00b7 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -599,7 +599,7 @@ class QmlDir: qmldir_file.path = path return qmldir_file - def from_lines(self, lines: [str]): + def from_lines(self, lines: List[str]): for line in lines: self.handle_line(line) @@ -3139,7 +3139,7 @@ def write_example( """ ) - if qml_dir != None: + if qml_dir is not None: if qml_dir.designer_supported: add_target += " DESIGNER_SUPPORTED\n" if len(qml_dir.classname) != 0: @@ -3404,7 +3404,7 @@ def write_qml_plugin( cm_fh.write("\n ".join(import_list)) cm_fh.write(f"\n )\nendif()\n\n") - if qml_dir != None: + if qml_dir is not None: if qml_dir.designer_supported: extra_lines.append("DESIGNER_SUPPORTED") if len(qml_dir.classname) != 0: @@ -3516,7 +3516,7 @@ def handle_app_or_lib( # Generate qmltypes instruction for anything that may have CONFIG += qmltypes # that is not a qml plugin - if "qmltypes" in scope.get("CONFIG") and not "qml_plugin" in scope.get("_LOADED"): + if "qmltypes" in scope.get("CONFIG") and "qml_plugin" not in scope.get("_LOADED"): cm_fh.write(f"\n{spaces(indent)}set_target_properties({target} PROPERTIES\n") cm_fh.write(f"{spaces(indent+1)}QT_QML_MODULE_INSTALL_QMLTYPES TRUE\n") @@ -3531,7 +3531,7 @@ def handle_app_or_lib( raise RuntimeError(f"No QML_IMPORT_VERSION info found for target {target}.") if not import_minor_version: - import_minor_version = 0 + import_minor_version = str(0) import_version = f"{import_major_version}.{import_minor_version}" if import_version: -- cgit v1.2.3