CMake: Remove pro2cmake and configurejson2cmake

Most qt repos and modules are now ported from qmake to CMake. The
CMake API for internal Qt modules has evolved, and these tools have
not been kept up-to-date. It's time to remove them. Developers can
still use qmake2cmake for their own user projects, which is hosted in
a different repo.

If we do end up needing the scripts again, they can be used from one
of the older branches like 6.9.

Fixes: QTBUG-133678
Change-Id: I8d9a765f2575a6c0fcfe9a0346a06a7eec302914
Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
Alexandru Croitor 2025-02-11 13:30:02 +01:00
parent a5d896d70a
commit d7a739bde1
59 changed files with 0 additions and 10603 deletions

View File

@ -1 +0,0 @@
.pro2cmake_cache/

View File

@ -1,20 +0,0 @@
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,F541
pytest:
pytest
mypy:
mypy --pretty *.py

View File

@ -1,18 +0,0 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
pyparsing = "*"
sympy = "*"
mypy = "*"
pytest = "*"
pytest-cov = "*"
flake8 = "*"
portalocker = "*"
[dev-packages]
[requires]
python_version = "3.7"

View File

@ -1,57 +0,0 @@
# CMake Utils
This directory holds scripts to help the porting process from `qmake` to `cmake` for Qt6.
If you're looking to port your own Qt-based project from `qmake` to `cmake`, please use
[qmake2cmake](https://wiki.qt.io/Qmake2cmake).
# 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`
If the `pip install` command above doesn't work, try:
```
python3.7 -m pip install -r requirements.txt
```
# Contributing to the scripts
You can verify if the styling of a script is compliant with PEP8, with a couple of exceptions:
Install [flake8](http://flake8.pycqa.org/en/latest/) (`pip install flake8`) and run it
on all python source files:
```
make flake8
```
You can also modify the file with an automatic formatter,
like [black](https://black.readthedocs.io/en/stable/) (`pip install black`),
and execute it:
```
make format
```

View File

@ -1,116 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from argparse import ArgumentParser
import os
import re
import subprocess
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="<Source Directory>",
type=str,
help="The Qt module source directory",
)
parser.add_argument(
"binary_directory",
metavar="<CMake build directory>",
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(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)
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(f'Running cmake in "{binary_directory}"')
result = subprocess.run(["/usr/bin/cmake", "-GNinja", abs_source], cwd=binary_directory)
if debug:
print(f"CMake return code: {result.returncode}.")
assert result.returncode == 0
if debug:
print(f'Running ninja in "{binary_directory}".')
result = subprocess.run("/usr/bin/ninja", cwd=binary_directory)
if debug:
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(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(f"Test summary: {summary} ({result.returncode}).")
matches = re.fullmatch(r"\d+% tests passed, (\d+) tests failed out of (\d+)", summary)
if matches:
if debug:
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)
def main() -> int:
args = _parse_commandline()
base_line = calculate_baseline(args.source_directory, debug=args.debug)
if base_line <= 0:
print(f"Could not find the qmake baseline in {args.source_directory}.")
return 1
if args.debug:
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)
finally:
if cmake_total == 0:
print("\n\n\nCould not calculate the cmake state.")
return 2
else:
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__":
main()

View File

@ -1,211 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import re
from sympy import simplify_logic, And, Or, Not, SympifyError # type: ignore
from condition_simplifier_cache import simplify_condition_memoize
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 = ("MACOS", "UIKIT", "IOS", "TVOS", "WATCHOS")
bsds = ("FREEBSD", "OPENBSD", "NETBSD")
androids = ("ANDROID",)
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)
# 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
@simplify_condition_memoize
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"

View File

@ -1,154 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import atexit
import hashlib
import json
import os
import sys
import time
from typing import Any, Callable, Dict
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:
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 = str(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 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
return portalocker.Lock(file_path, mode=mode, flags=portalocker.LOCK_EX)
except ImportError:
print(
"The conversion script is missing a required package: portalocker. Please run "
"python -m pip install -r 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_file_safe(cache_path, mode="r") as cache_file:
cache_file_content = json.load(cache_file)
except (IOError, ValueError):
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()
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)
# Create the file if it doesn't exist, but don't override
# it.
with open(cache_path, "a"):
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
):
cache_file_content["cache"]["conditions"][condition] = f(condition)
return cache_file_content["cache"]["conditions"][condition]
return helper

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
#!/usr/bin/bash
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
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

View File

@ -1,868 +0,0 @@
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
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] = [],
components: typing.Optional[typing.List[str]] = None,
appendFoundSuffix: bool = True,
emit_if: str = "",
is_bundled_with_qt: bool = False,
test_library_overwrite: str = "",
run_library_test: bool = False,
no_link_so_name: str = "",
) -> None:
self.soName = soName
self.packageName = packageName
self.resultVariable = resultVariable
self.appendFoundSuffix = appendFoundSuffix
# Allows passing addiitonal arguments to the generated find_package call.
self.extra = extra
self.components = components
self.targetName = targetName
# True if qt bundles the library sources as part of Qt.
self.is_bundled_with_qt = is_bundled_with_qt
# 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
# Allow overwriting library name when used with tests. E.g.: _nolink
# targets do not exist when used during compile tests
self.test_library_overwrite = test_library_overwrite
# Run the library compile test of configure.json
self.run_library_test = run_library_test
# The custom nolink library mapping associated with this one.
self.no_link_so_name = no_link_so_name
def is_qt(self) -> bool:
return self.packageName == "Qt" or self.packageName == "Qt5" or self.packageName == "Qt6"
_qt_library_map = [
# Qt:
LibraryMapping("androidextras", "Qt6", "Qt::AndroidExtras", components=["AndroidExtras"]),
LibraryMapping("3danimation", "Qt6", "Qt::3DAnimation", components=["3DAnimation"]),
LibraryMapping("3dcore", "Qt6", "Qt::3DCore", components=["3DCore"]),
LibraryMapping("3dcoretest", "Qt6", "Qt::3DCoreTest", components=["3DCoreTest"]),
LibraryMapping("3dextras", "Qt6", "Qt::3DExtras", components=["3DExtras"]),
LibraryMapping("3dinput", "Qt6", "Qt::3DInput", components=["3DInput"]),
LibraryMapping("3dlogic", "Qt6", "Qt::3DLogic", components=["3DLogic"]),
LibraryMapping("3dquick", "Qt6", "Qt::3DQuick", components=["3DQuick"]),
LibraryMapping("3dquickextras", "Qt6", "Qt::3DQuickExtras", components=["3DQuickExtras"]),
LibraryMapping("3dquickinput", "Qt6", "Qt::3DQuickInput", components=["3DQuickInput"]),
LibraryMapping("3dquickrender", "Qt6", "Qt::3DQuickRender", components=["3DQuickRender"]),
LibraryMapping("3drender", "Qt6", "Qt::3DRender", components=["3DRender"]),
LibraryMapping(
"application-lib", "Qt6", "Qt::AppManApplication", components=["AppManApplication"]
),
LibraryMapping("axbase", "Qt6", "Qt::AxBasePrivate", components=["AxBasePrivate"]),
LibraryMapping("axcontainer", "Qt6", "Qt::AxContainer", components=["AxContainer"]),
LibraryMapping("axserver", "Qt6", "Qt::AxServer", components=["AxServer"]),
LibraryMapping("bluetooth", "Qt6", "Qt::Bluetooth", components=["Bluetooth"]),
LibraryMapping("bootstrap", "Qt6", "Qt::Bootstrap", components=["Bootstrap"]),
# bootstrap-dbus: Not needed in Qt6!
LibraryMapping("client", "Qt6", "Qt::WaylandClient", components=["WaylandClient"]),
LibraryMapping("coap", "Qt6", "Qt::Coap", components=["Coap"]),
LibraryMapping("common-lib", "Qt6", "Qt::AppManCommon", components=["AppManCommon"]),
LibraryMapping("compositor", "Qt6", "Qt::WaylandCompositor", components=["WaylandCompositor"]),
LibraryMapping("concurrent", "Qt6", "Qt::Concurrent", components=["Concurrent"]),
LibraryMapping("container", "Qt6", "Qt::AxContainer", components=["AxContainer"]),
LibraryMapping("control", "Qt6", "Qt::AxServer", components=["AxServer"]),
LibraryMapping("core_headers", "Qt6", "Qt::WebEngineCore", components=["WebEngineCore"]),
LibraryMapping("core", "Qt6", "Qt::Core", components=["Core"]),
LibraryMapping("crypto-lib", "Qt6", "Qt::AppManCrypto", components=["AppManCrypto"]),
LibraryMapping("dbus", "Qt6", "Qt::DBus", components=["DBus"]),
LibraryMapping("designer", "Qt6", "Qt::Designer", components=["Designer"]),
LibraryMapping(
"designercomponents",
"Qt6",
"Qt::DesignerComponentsPrivate",
components=["DesignerComponentsPrivate"],
),
LibraryMapping(
"devicediscovery",
"Qt6",
"Qt::DeviceDiscoverySupportPrivate",
components=["DeviceDiscoverySupportPrivate"],
),
LibraryMapping(
"devicediscovery_support",
"Qt6",
"Qt::DeviceDiscoverySupportPrivate",
components=["DeviceDiscoverySupportPrivate"],
),
LibraryMapping("edid", "Qt6", "Qt::EdidSupport", components=["EdidSupport"]),
LibraryMapping("edid_support", "Qt6", "Qt::EdidSupport", components=["EdidSupport"]),
LibraryMapping("eglconvenience", "Qt6", "Qt::EglSupport", components=["EglSupport"]),
LibraryMapping(
"eglfsdeviceintegration",
"Qt6",
"Qt::EglFSDeviceIntegrationPrivate",
components=["EglFSDeviceIntegrationPrivate"],
),
LibraryMapping(
"eglfs_kms_support",
"Qt6",
"Qt::EglFsKmsSupportPrivate",
components=["EglFsKmsSupportPrivate"],
),
LibraryMapping(
"eglfs_kms_gbm_support",
"Qt6",
"Qt::EglFsKmsGbmSupportPrivate",
components=["EglFsKmsGbmSupportPrivate"],
),
LibraryMapping("egl_support", "Qt6", "Qt::EglSupport", components=["EglSupport"]),
# enginio: Not needed in Qt6!
LibraryMapping(
"eventdispatchers",
"Qt6",
"Qt::EventDispatcherSupport",
components=["EventDispatcherSupport"],
),
LibraryMapping(
"eventdispatcher_support",
"Qt6",
"Qt::EventDispatcherSupport",
components=["EventDispatcherSupport"],
),
LibraryMapping("fbconvenience", "Qt6", "Qt::FbSupportPrivate", components=["FbSupportPrivate"]),
LibraryMapping("fb_support", "Qt6", "Qt::FbSupportPrivate", components=["FbSupportPrivate"]),
LibraryMapping(
"fontdatabase_support",
"Qt6",
"Qt::FontDatabaseSupport",
components=["FontDatabaseSupport"],
),
LibraryMapping("gamepad", "Qt6", "Qt::Gamepad", components=["Gamepad"]),
LibraryMapping("geniviextras", "Qt6", "Qt::GeniviExtras", components=["GeniviExtras"]),
LibraryMapping("global", "Qt6", "Qt::Core", components=["Core"]), # manually added special case
LibraryMapping("glx_support", "Qt6", "Qt::GlxSupport", components=["GlxSupport"]),
LibraryMapping("gsttools", "Qt6", "Qt::MultimediaGstTools", components=["MultimediaGstTools"]),
LibraryMapping("gui", "Qt6", "Qt::Gui", components=["Gui"]),
LibraryMapping("help", "Qt6", "Qt::Help", components=["Help"]),
LibraryMapping(
"hunspellinputmethod",
"Qt6",
"Qt::HunspellInputMethodPrivate",
components=["HunspellInputMethodPrivate"],
),
LibraryMapping("input", "Qt6", "Qt::InputSupportPrivate", components=["InputSupportPrivate"]),
LibraryMapping(
"input_support",
"Qt6",
"Qt::InputSupportPrivate",
components=["InputSupportPrivate"],
),
LibraryMapping("installer-lib", "Qt6", "Qt::AppManInstaller", components=["AppManInstaller"]),
LibraryMapping("ivi", "Qt6", "Qt::Ivi", components=["Ivi"]),
LibraryMapping("ivicore", "Qt6", "Qt::IviCore", components=["IviCore"]),
LibraryMapping("ivimedia", "Qt6", "Qt::IviMedia", components=["IviMedia"]),
LibraryMapping("knx", "Qt6", "Qt::Knx", components=["Knx"]),
LibraryMapping(
"kmsconvenience", "Qt6", "Qt::KmsSupportPrivate", components=["KmsSupportPrivate"]
),
LibraryMapping("kms_support", "Qt6", "Qt::KmsSupportPrivate", components=["KmsSupportPrivate"]),
LibraryMapping("launcher-lib", "Qt6", "Qt::AppManLauncher", components=["AppManLauncher"]),
LibraryMapping("lib", "Qt6", "Qt::Designer", components=["Designer"]),
LibraryMapping(
"linuxaccessibility_support",
"Qt6",
"Qt::LinuxAccessibilitySupport",
components=["LinuxAccessibilitySupport"],
),
LibraryMapping("location", "Qt6", "Qt::Location", components=["Location"]),
LibraryMapping("macextras", "Qt6", "Qt::MacExtras", components=["MacExtras"]),
LibraryMapping("main-lib", "Qt6", "Qt::AppManMain", components=["AppManMain"]),
LibraryMapping("manager-lib", "Qt6", "Qt::AppManManager", components=["AppManManager"]),
LibraryMapping("monitor-lib", "Qt6", "Qt::AppManMonitor", components=["AppManMonitor"]),
LibraryMapping("mqtt", "Qt6", "Qt::Mqtt", components=["Mqtt"]),
LibraryMapping("multimedia", "Qt6", "Qt::Multimedia", components=["Multimedia"]),
LibraryMapping(
"multimediawidgets",
"Qt6",
"Qt::MultimediaWidgets",
components=["MultimediaWidgets"],
),
LibraryMapping("network", "Qt6", "Qt::Network", components=["Network"]),
LibraryMapping("networkauth", "Qt6", "Qt::NetworkAuth", components=["NetworkAuth"]),
LibraryMapping("nfc", "Qt6", "Qt::Nfc", components=["Nfc"]),
LibraryMapping("oauth", "Qt6", "Qt::NetworkAuth", components=["NetworkAuth"]),
LibraryMapping("opcua", "Qt6", "Qt::OpcUa", components=["OpcUa"]),
LibraryMapping("opcua_private", "Qt6", "Qt::OpcUaPrivate", components=["OpcUaPrivate"]),
LibraryMapping("opengl", "Qt6", "Qt::OpenGL", components=["OpenGL"]),
LibraryMapping("openglwidgets", "Qt6", "Qt::OpenGLWidgets", components=["OpenGLWidgets"]),
LibraryMapping("package-lib", "Qt6", "Qt::AppManPackage", components=["AppManPackage"]),
LibraryMapping(
"packetprotocol",
"Qt6",
"Qt::PacketProtocolPrivate",
components=["PacketProtocolPrivate"],
),
LibraryMapping(
"particles",
"Qt6",
"Qt::QuickParticlesPrivate",
components=["QuickParticlesPrivate"],
),
LibraryMapping(
"plugin-interfaces",
"Qt6",
"Qt::AppManPluginInterfaces",
components=["AppManPluginInterfaces"],
),
LibraryMapping("positioning", "Qt6", "Qt::Positioning", components=["Positioning"]),
LibraryMapping(
"positioningquick", "Qt6", "Qt::PositioningQuick", components=["PositioningQuick"]
),
LibraryMapping("printsupport", "Qt6", "Qt::PrintSupport", components=["PrintSupport"]),
LibraryMapping("purchasing", "Qt6", "Qt::Purchasing", components=["Purchasing"]),
LibraryMapping("qmldebug", "Qt6", "Qt::QmlDebugPrivate", components=["QmlDebugPrivate"]),
LibraryMapping(
"qmldevtools", "Qt6", "Qt::QmlDevToolsPrivate", components=["QmlDevToolsPrivate"]
),
LibraryMapping(
"qmlcompiler", "Qt6", "Qt::QmlCompilerPrivate", components=["QmlCompilerPrivate"]
),
LibraryMapping("qml", "Qt6", "Qt::Qml", components=["Qml"]),
LibraryMapping("qmldom", "Qt6", "Qt::QmlDomPrivate", components=["QmlDomPrivate"]),
LibraryMapping("qmlmodels", "Qt6", "Qt::QmlModels", components=["QmlModels"]),
LibraryMapping("qmltest", "Qt6", "Qt::QuickTest", components=["QuickTest"]),
LibraryMapping(
"qtmultimediaquicktools",
"Qt6",
"Qt::MultimediaQuickPrivate",
components=["MultimediaQuickPrivate"],
),
LibraryMapping(
"quick3dassetimport",
"Qt6",
"Qt::Quick3DAssetImport",
components=["Quick3DAssetImport"],
),
LibraryMapping("core5compat", "Qt6", "Qt::Core5Compat", components=["Core5Compat"]),
LibraryMapping("quick3d", "Qt6", "Qt::Quick3D", components=["Quick3D"]),
LibraryMapping("quick3drender", "Qt6", "Qt::Quick3DRender", components=["Quick3DRender"]),
LibraryMapping(
"quick3druntimerender",
"Qt6",
"Qt::Quick3DRuntimeRender",
components=["Quick3DRuntimeRender"],
),
LibraryMapping("quick3dutils", "Qt6", "Qt::Quick3DUtils", components=["Quick3DUtils"]),
LibraryMapping("quickcontrols2", "Qt6", "Qt::QuickControls2", components=["QuickControls2"]),
LibraryMapping(
"quickcontrols2impl",
"Qt6",
"Qt::QuickControls2Impl",
components=["QuickControls2Impl"],
),
LibraryMapping("quick", "Qt6", "Qt::Quick", components=["Quick"]),
LibraryMapping(
"quickshapes", "Qt6", "Qt::QuickShapesPrivate", components=["QuickShapesPrivate"]
),
LibraryMapping("quicktemplates2", "Qt6", "Qt::QuickTemplates2", components=["QuickTemplates2"]),
LibraryMapping("quickwidgets", "Qt6", "Qt::QuickWidgets", components=["QuickWidgets"]),
LibraryMapping("remoteobjects", "Qt6", "Qt::RemoteObjects", components=["RemoteObjects"]),
LibraryMapping("script", "Qt6", "Qt::Script", components=["Script"]),
LibraryMapping("scripttools", "Qt6", "Qt::ScriptTools", components=["ScriptTools"]),
LibraryMapping("scxml", "Qt6", "Qt::Scxml", components=["Scxml"]),
LibraryMapping("sensors", "Qt6", "Qt::Sensors", components=["Sensors"]),
LibraryMapping("serialport", "Qt6", "Qt::SerialPort", components=["SerialPort"]),
LibraryMapping("serialbus", "Qt6", "Qt::SerialBus", components=["SerialBus"]),
LibraryMapping("services", "Qt6", "Qt::ServiceSupport", components=["ServiceSupport"]),
LibraryMapping("service_support", "Qt6", "Qt::ServiceSupport", components=["ServiceSupport"]),
LibraryMapping("shadertools", "Qt6", "Qt::ShaderTools", components=["ShaderTools"]),
LibraryMapping("statemachine", "Qt6", "Qt::StateMachine", components=["StateMachine"]),
LibraryMapping("sql", "Qt6", "Qt::Sql", components=["Sql"]),
LibraryMapping("svg", "Qt6", "Qt::Svg", components=["Svg"]),
LibraryMapping("svgwidgets", "Qt6", "Qt::SvgWidgets", components=["SvgWidgets"]),
LibraryMapping("charts", "Qt6", "Qt::Charts", components=["Charts"]),
LibraryMapping("testlib", "Qt6", "Qt::Test", components=["Test"]),
LibraryMapping("texttospeech", "Qt6", "Qt::TextToSpeech", components=["TextToSpeech"]),
LibraryMapping("theme_support", "Qt6", "Qt::ThemeSupport", components=["ThemeSupport"]),
LibraryMapping("tts", "Qt6", "Qt::TextToSpeech", components=["TextToSpeech"]),
LibraryMapping("uiplugin", "Qt6", "Qt::UiPlugin", components=["UiPlugin"]),
LibraryMapping("uitools", "Qt6", "Qt::UiTools", components=["UiTools"]),
LibraryMapping("virtualkeyboard", "Qt6", "Qt::VirtualKeyboard", components=["VirtualKeyboard"]),
LibraryMapping("waylandclient", "Qt6", "Qt::WaylandClient", components=["WaylandClient"]),
LibraryMapping(
"waylandcompositor",
"Qt6",
"Qt::WaylandCompositor",
components=["WaylandCompositor"],
),
LibraryMapping("webchannel", "Qt6", "Qt::WebChannel", components=["WebChannel"]),
LibraryMapping("webengine", "Qt6", "Qt::WebEngine", components=["WebEngine"]),
LibraryMapping(
"webenginewidgets", "Qt6", "Qt::WebEngineWidgets", components=["WebEngineWidgets"]
),
LibraryMapping("websockets", "Qt6", "Qt::WebSockets", components=["WebSockets"]),
LibraryMapping("webview", "Qt6", "Qt::WebView", components=["WebView"]),
LibraryMapping("widgets", "Qt6", "Qt::Widgets", components=["Widgets"]),
LibraryMapping("window-lib", "Qt6", "Qt::AppManWindow", components=["AppManWindow"]),
LibraryMapping("winextras", "Qt6", "Qt::WinExtras", components=["WinExtras"]),
LibraryMapping("x11extras", "Qt6", "Qt::X11Extras", components=["X11Extras"]),
LibraryMapping("xcb_qpa_lib", "Qt6", "Qt::XcbQpaPrivate", components=["XcbQpaPrivate"]),
LibraryMapping(
"xkbcommon_support", "Qt6", "Qt::XkbCommonSupport", components=["XkbCommonSupport"]
),
LibraryMapping("xmlpatterns", "Qt6", "Qt::XmlPatterns", components=["XmlPatterns"]),
LibraryMapping("xml", "Qt6", "Qt::Xml", components=["Xml"]),
LibraryMapping("qmlworkerscript", "Qt6", "Qt::QmlWorkerScript", components=["QmlWorkerScript"]),
LibraryMapping(
"quickparticles",
"Qt6",
"Qt::QuickParticlesPrivate",
components=["QuickParticlesPrivate"],
),
LibraryMapping(
"linuxofono_support",
"Qt6",
"Qt::LinuxOfonoSupport",
components=["LinuxOfonoSupport"],
),
LibraryMapping(
"linuxofono_support_private",
"Qt6",
"Qt::LinuxOfonoSupportPrivate",
components=["LinuxOfonoSupportPrivate"],
),
LibraryMapping("tools", "Qt6", "Qt::Tools", components=["Tools"]),
LibraryMapping("axcontainer", "Qt6", "Qt::AxContainer", components=["AxContainer"]),
LibraryMapping("webkitwidgets", "Qt6", "Qt::WebKitWidgets", components=["WebKitWidgets"]),
LibraryMapping("zlib", "Qt6", "Qt::Zlib", components=["Zlib"]),
LibraryMapping("httpserver", "Qt6", "Qt::HttpServer", components=["HttpServer"]),
LibraryMapping("sslserver", "Qt6", "Qt::SslServer", components=["HttpServer"]),
]
# Note that the library map is adjusted dynamically further down.
_library_map = [
# 3rd party:
LibraryMapping("atspi", "ATSPI2", "PkgConfig::ATSPI2"),
LibraryMapping(
"backtrace", "WrapBacktrace", "WrapBacktrace::WrapBacktrace", emit_if="config.unix"
),
LibraryMapping("bluez", "BlueZ", "PkgConfig::BlueZ"),
LibraryMapping("brotli", "WrapBrotli", "WrapBrotli::WrapBrotliDec"),
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", extra=["1.2"]),
LibraryMapping(
"doubleconversion", "WrapSystemDoubleConversion",
"WrapSystemDoubleConversion::WrapSystemDoubleConversion"
),
LibraryMapping("dlt", "DLT", "DLT::DLT"),
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=["2.2.0", "REQUIRED"],
is_bundled_with_qt=True,
),
LibraryMapping("gbm", "gbm", "gbm::gbm"),
LibraryMapping("glib", "GLIB2", "GLIB2::GLIB2"),
LibraryMapping("iconv", "WrapIconv", "WrapIconv::WrapIconv"),
LibraryMapping("gtk3", "GTK3", "PkgConfig::GTK3", extra=["3.6"]),
LibraryMapping("gssapi", "GSSAPI", "GSSAPI::GSSAPI"),
LibraryMapping(
"harfbuzz",
"WrapHarfbuzz",
"WrapHarfbuzz::WrapHarfbuzz",
is_bundled_with_qt=True,
extra=["2.6.0"],
),
LibraryMapping("host_dbus", None, None),
LibraryMapping("icu", "ICU", "ICU::i18n ICU::uc ICU::data", components=["i18n", "uc", "data"]),
LibraryMapping("journald", "Libsystemd", "PkgConfig::Libsystemd"),
LibraryMapping("jpeg", "JPEG", "JPEG::JPEG"), # see also libjpeg
LibraryMapping("libatomic", "WrapAtomic", "WrapAtomic::WrapAtomic"),
LibraryMapping("libb2", "Libb2", "Libb2::Libb2"),
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", "WrapPNG", "WrapPNG::WrapPNG", is_bundled_with_qt=True),
LibraryMapping("libproxy", "Libproxy", "PkgConfig::Libproxy"),
LibraryMapping("librt", "WrapRt", "WrapRt::WrapRt"),
LibraryMapping("libudev", "Libudev", "PkgConfig::Libudev"),
LibraryMapping("lttng-ust", "LTTngUST", "LTTng::UST", resultVariable="LTTNGUST"),
LibraryMapping("libmd4c", "WrapMd4c", "WrapMd4c::WrapMd4c", is_bundled_with_qt=True),
LibraryMapping("mtdev", "Mtdev", "PkgConfig::Mtdev"),
LibraryMapping("mysql", "MySQL", "MySQL::MySQL"),
LibraryMapping("odbc", "ODBC", "ODBC::ODBC"),
LibraryMapping("opengl_es2", "GLESv2", "GLESv2::GLESv2"),
LibraryMapping("opengl", "WrapOpenGL", "WrapOpenGL::WrapOpenGL", resultVariable="WrapOpenGL"),
LibraryMapping(
"openssl_headers",
"WrapOpenSSLHeaders",
"WrapOpenSSLHeaders::WrapOpenSSLHeaders",
resultVariable="TEST_openssl_headers",
appendFoundSuffix=False,
test_library_overwrite="WrapOpenSSLHeaders::WrapOpenSSLHeaders",
run_library_test=True,
),
LibraryMapping(
"openssl",
"WrapOpenSSL",
"WrapOpenSSL::WrapOpenSSL",
resultVariable="TEST_openssl",
appendFoundSuffix=False,
run_library_test=True,
no_link_so_name="openssl_headers",
),
LibraryMapping("oci", "Oracle", "Oracle::OCI"),
LibraryMapping(
"pcre2",
"WrapPCRE2",
"WrapPCRE2::WrapPCRE2",
extra=["10.20", "REQUIRED"],
is_bundled_with_qt=True,
),
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("sqlite", "SQLite3", "SQLite::SQLite3"),
LibraryMapping(
"taglib", "WrapTagLib", "WrapTagLib::WrapTagLib", is_bundled_with_qt=True
), # used in qtivi
LibraryMapping("tslib", "Tslib", "PkgConfig::Tslib"),
LibraryMapping("udev", "Libudev", "PkgConfig::Libudev"),
LibraryMapping("udev", "Libudev", "PkgConfig::Libudev"), # see also libudev!
LibraryMapping("vulkan", "WrapVulkanHeaders", "WrapVulkanHeaders::WrapVulkanHeaders"),
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"),
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",
"XCB",
"XCB::XCB",
extra=["1.11"],
resultVariable="TARGET XCB::XCB",
appendFoundSuffix=False,
),
LibraryMapping("xcb_glx", "XCB", "XCB::GLX", components=["GLX"], resultVariable="XCB_GLX"),
LibraryMapping(
"xcb_cursor",
"XCB",
"XCB::CURSOR",
extra=["0.1.1", "COMPONENTS", "CURSOR"],
resultVariable="XCB_CURSOR",
),
LibraryMapping(
"xcb_icccm",
"XCB",
"XCB::ICCCM",
extra=["0.3.9"],
components=["ICCCM"],
resultVariable="XCB_ICCCM",
),
LibraryMapping(
"xcb_image",
"XCB",
"XCB::IMAGE",
extra=["0.3.9"],
components=["IMAGE"],
resultVariable="XCB_IMAGE",
),
LibraryMapping(
"xcb_keysyms",
"XCB",
"XCB::KEYSYMS",
extra=["0.3.9"],
components=["KEYSYMS"],
resultVariable="XCB_KEYSYMS",
),
LibraryMapping(
"xcb_randr", "XCB", "XCB::RANDR", components=["RANDR"], resultVariable="XCB_RANDR"
),
LibraryMapping(
"xcb_render",
"XCB",
"XCB::RENDER",
components=["RENDER"],
resultVariable="XCB_RENDER",
),
LibraryMapping(
"xcb_renderutil",
"XCB",
"XCB::RENDERUTIL",
extra=["0.3.9"],
components=["RENDERUTIL"],
resultVariable="XCB_RENDERUTIL",
),
LibraryMapping(
"xcb_shape", "XCB", "XCB::SHAPE", components=["SHAPE"], resultVariable="XCB_SHAPE"
),
LibraryMapping("xcb_shm", "XCB", "XCB::SHM", components=["SHM"], resultVariable="XCB_SHM"),
LibraryMapping("xcb_sync", "XCB", "XCB::SYNC", components=["SYNC"], resultVariable="XCB_SYNC"),
LibraryMapping(
"xcb_xfixes",
"XCB",
"XCB::XFIXES",
components=["XFIXES"],
resultVariable="TARGET XCB::XFIXES",
appendFoundSuffix=False,
),
LibraryMapping(
"xcb-xfixes",
"XCB",
"XCB::XFIXES",
components=["XFIXES"],
resultVariable="TARGET XCB::XFIXES",
appendFoundSuffix=False,
),
LibraryMapping(
"xcb_xinput",
"XCB",
"XCB::XINPUT",
extra=["1.12"],
components=["XINPUT"],
resultVariable="XCB_XINPUT",
),
LibraryMapping("xcb_xkb", "XCB", "XCB::XKB", 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.5.0"]), # see also xkbcommon
LibraryMapping("xkbcommon_x11", "XKB_COMMON_X11", "PkgConfig::XKB_COMMON_X11", extra=["0.5.0"]),
LibraryMapping("xkbcommon", "XKB", "XKB::XKB", extra=["0.5.0"]),
LibraryMapping("xlib", "X11", "X11::X11"),
LibraryMapping("xrender", "XRender", "PkgConfig::XRender", extra=["0.6"]),
LibraryMapping("zlib", "WrapZLIB", "WrapZLIB::WrapZLIB", extra=["1.0.8"]),
LibraryMapping("zstd", "WrapZSTD", "WrapZSTD::WrapZSTD", extra=["1.3"]),
LibraryMapping("tiff", "TIFF", "TIFF::TIFF"),
LibraryMapping("webp", "WrapWebP", "WrapWebP::WrapWebP"),
LibraryMapping("jasper", "WrapJasper", "WrapJasper::WrapJasper"),
LibraryMapping("mng", "Libmng", "Libmng::Libmng"),
LibraryMapping("sdl2", "WrapSDL2", "WrapSDL2::WrapSDL2"),
LibraryMapping("hunspell", "Hunspell", "Hunspell::Hunspell"),
LibraryMapping(
"qt3d-assimp",
"WrapQt3DAssimp",
"WrapQt3DAssimp::WrapQt3DAssimp",
extra=["5"],
run_library_test=True,
resultVariable="TEST_assimp",
appendFoundSuffix=False,
),
LibraryMapping(
"quick3d_assimp",
"WrapQuick3DAssimp",
"WrapQuick3DAssimp::WrapQuick3DAssimp",
extra=["5"],
run_library_test=True,
resultVariable="TEST_quick3d_assimp",
appendFoundSuffix=False,
),
]
def _adjust_library_map():
# Assign a Linux condition on all wayland related packages.
# Assign platforms that have X11 condition on all X11 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 = ["wayland"]
x11_package_prefixes = ["xcb", "x11", "xkb", "xrender", "xlib"]
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"
if any([_library_map[i].soName.startswith(p) for p in x11_package_prefixes]):
_library_map[i].emit_if = "X11_SUPPORTED"
_adjust_library_map()
def find_3rd_party_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 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
# For a given qmake library (e.g. 'openssl_headers'), check whether this is a fake library used
# for the /nolink annotation, and return the actual annotated qmake library ('openssl/nolink').
def find_annotated_qmake_lib_name(lib: str) -> str:
for entry in _library_map:
if entry.no_link_so_name == lib:
return entry.soName + "/nolink"
return lib
def featureName(name: str) -> str:
replacement_char = "_"
if name.startswith("c++"):
replacement_char = "x"
return re.sub(r"[^a-zA-Z0-9_]", replacement_char, name)
def map_qt_library(lib: str) -> str:
private = False
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
qt_name = mapped.targetName
if 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",
"android": "ANDROID",
"uikit": "UIKIT",
"tvos": "TVOS",
"watchos": "WATCHOS",
"winrt": "WINRT",
"wasm": "WASM",
"emscripten": "EMSCRIPTEN",
"msvc": "MSVC",
"clang": "CLANG",
"gcc": "GCC",
"icc": "ICC",
"intel_icc": "ICC",
"osx": "MACOS",
"ios": "IOS",
"freebsd": "FREEBSD",
"openbsd": "OPENBSD",
"mingw": "MINGW",
"netbsd": "NETBSD",
"haiku": "HAIKU",
"mac": "APPLE",
"macx": "MACOS",
"macos": "MACOS",
"macx-icc": "(MACOS AND ICC)",
}
def map_platform(platform: str) -> str:
"""Return the qmake platform as cmake platform or the unchanged string."""
return platform_mapping.get(platform, platform)
def is_known_3rd_party_library(lib: str) -> bool:
handling_no_link = False
if lib.endswith("/nolink") or lib.endswith("_nolink"):
lib = lib[:-7]
handling_no_link = True
mapping = find_3rd_party_library_mapping(lib)
if handling_no_link and mapping and mapping.no_link_so_name:
no_link_mapping = find_3rd_party_library_mapping(mapping.no_link_so_name)
if no_link_mapping:
mapping = no_link_mapping
return mapping is not None
def map_3rd_party_library(lib: str) -> str:
handling_no_link = False
libpostfix = ""
if lib.endswith("/nolink"):
lib = lib[:-7]
libpostfix = "_nolink"
handling_no_link = True
mapping = find_3rd_party_library_mapping(lib)
if handling_no_link and mapping and mapping.no_link_so_name:
no_link_mapping = find_3rd_party_library_mapping(mapping.no_link_so_name)
if no_link_mapping:
mapping = no_link_mapping
libpostfix = ""
if not mapping or not mapping.targetName:
return lib
return mapping.targetName + libpostfix
compile_test_dependent_library_mapping = {
"dtls": {"openssl": "openssl_headers"},
"ocsp": {"openssl": "openssl_headers"},
}
def get_compile_test_dependent_library_mapping(compile_test_name: str, dependency_name: str):
if compile_test_name in compile_test_dependent_library_mapping:
mapping = compile_test_dependent_library_mapping[compile_test_name]
if dependency_name in mapping:
return mapping[dependency_name]
return dependency_name
def generate_find_package_info(
lib: LibraryMapping,
use_qt_find_package: bool = True,
*,
indent: int = 0,
emit_if: str = "",
use_system_package_name: bool = False,
remove_REQUIRED_from_extra: bool = True,
components_required: bool = True,
module: str = "",
) -> str:
isRequired = False
extra = lib.extra.copy()
if lib.components:
extra.append("COMPONENTS" if components_required else "OPTIONAL_COMPONENTS")
extra += lib.components
if "REQUIRED" in extra and use_qt_find_package:
isRequired = True
if remove_REQUIRED_from_extra:
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"):
cmake_target_name = cmake_target_name[:-7]
initial_package_name: str = lib.packageName if lib.packageName else ""
package_name: str = initial_package_name
if use_system_package_name:
replace_args = ["Wrap", "WrapSystem"]
package_name = package_name.replace(*replace_args) # type: ignore
cmake_target_name = cmake_target_name.replace(*replace_args) # type: ignore
if use_qt_find_package:
if cmake_target_name:
extra += ["PROVIDED_TARGETS", cmake_target_name]
if module:
extra += ["MODULE_NAME", module]
extra += ["QMAKE_LIB", find_annotated_qmake_lib_name(lib.soName)]
result = ""
one_ind = " "
ind = one_ind * indent
if use_qt_find_package:
if extra:
result = f"{ind}qt_find_package({package_name} {' '.join(extra)})\n"
else:
result = f"{ind}qt_find_package({package_name})\n"
if isRequired:
result += (
f"{ind}set_package_properties({initial_package_name} PROPERTIES TYPE REQUIRED)\n"
)
else:
if extra:
result = f"{ind}find_package({package_name} {' '.join(extra)})\n"
else:
result = f"{ind}find_package({package_name})\n"
# If a package should be found only in certain conditions, wrap
# the find_package call within that condition.
if emit_if:
result = f"if(({emit_if}) OR QT_FIND_ALL_PACKAGES_ALWAYS)\n{one_ind}{result}endif()\n"
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
if hasattr(pp, "_defaultStartDebugAction"):
pp._defaultStartDebugAction = increase_indent(pp._defaultStartDebugAction)
pp._defaultSuccessDebugAction = decrease_indent(pp._defaultSuccessDebugAction)
pp._defaultExceptionDebugAction = decrease_indent(pp._defaultExceptionDebugAction)
elif hasattr(pp.core, "_default_start_debug_action"):
pp.core._default_start_debug_action = increase_indent(pp.core._default_start_debug_action)
pp.core._default_success_debug_action = decrease_indent(
pp.core._default_success_debug_action
)
pp.core._default_exception_debug_action = decrease_indent(
pp.core._default_exception_debug_action
)

View File

@ -1,76 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2019 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import pyparsing as pp # type: ignore
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(f'Pre processing "{file}" using py parsing to remove incorrect newlines.')
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(f'Parsing "{file}" using json.loads().')
json_parsed = json.loads(pre_processed_string)
return json_parsed

File diff suppressed because it is too large Load Diff

View File

@ -1,210 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2019 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from __future__ import annotations
"""
This utility script shows statistics about
converted .pro -> CMakeLists.txt files.
To execute: python3 pro_conversion_rate.py <src dir>
where <src dir> 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 typing import Dict, Union
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="<src dir>", 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 # type: ignore
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 # type: ignore
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):
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: 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",
"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"):
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 int(stats[stat]["value"]) > 0:
stats[stat]["percentage"] = round(float(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(
f"{stats[stat]['label']:<40}: {stats[stat]['value']} ({stats[stat]['percentage']}%)"
)
print(f"\n{'Scan time':<40}: {scan_time:.10f} seconds")
print(f"{'Total script time':<40}: {script_time:.10f} seconds")
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()

View File

@ -1,422 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import collections
import os
import re
from itertools import chain
from typing import Tuple
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.
# The # may be preceded by any number of spaces or tabs.
#
# 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)[ \t]*#[^\n]*?\n", "\n", contents, re.DOTALL)
return contents
def flatten_list(input_list):
"""Flattens an irregular nested list into a simple list."""
for el in input_list:
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") + pp.locatedExpr(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") + pp.locatedExpr(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_unused, 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")
)
FunctionArgumentsAsString = add_element(
"FunctionArgumentsAsString", pp.originalTextFor(pp.nestedExpr())
)
QtNoMakeTools = add_element(
"QtNoMakeTools",
pp.Keyword("qtNomakeTools") + FunctionArgumentsAsString("qt_no_make_tools_arguments"),
)
# 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
| QtNoMakeTools
| 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(" ")))
)
# Unfortunately qmake condition operators have no precedence,
# and are simply evaluated left to right. To emulate that, wrap
# each condition sub-expression in parentheses.
# So c1|c2:c3 is evaluated by qmake as (c1|c2):c3.
# The following variable keeps count on how many parentheses
# should be added to the beginning of the condition. Each
# condition sub-expression always gets an ")", and in the
# end the whole condition gets many "(". Note that instead
# inserting the actual parentheses, we insert special markers
# which get replaced in the end.
condition_parts_count = 0
# Whitespace in the markers is important. Assumes the markers
# never appear in .pro files.
l_paren_marker = "_(_ "
r_paren_marker = " _)_"
def handle_condition_part(condition_part_parse_result: pp.ParseResults) -> str:
condition_part_list = [*condition_part_parse_result]
nonlocal condition_parts_count
condition_parts_count += 1
condition_part_joined = "".join(condition_part_list)
# Add ending parenthesis marker. The counterpart is added
# in handle_condition.
return f"{condition_part_joined}{r_paren_marker}"
ConditionPart.setParseAction(handle_condition_part)
ConditionRepeated = add_element(
"ConditionRepeated", pp.ZeroOrMore(ConditionOp + ConditionWhiteSpace + ConditionPart)
)
def handle_condition(condition_parse_results: pp.ParseResults) -> str:
nonlocal condition_parts_count
prepended_parentheses = l_paren_marker * condition_parts_count
result = prepended_parentheses + " ".join(condition_parse_results).strip().replace(
":", " && "
).strip(" && ")
# If there are only 2 condition sub-expressions, there is no
# need for parentheses.
if condition_parts_count < 3:
result = result.replace(l_paren_marker, "")
result = result.replace(r_paren_marker, "")
result = result.strip(" ")
else:
result = result.replace(l_paren_marker, "( ")
result = result.replace(r_paren_marker, " )")
# Strip parentheses and spaces around the final
# condition.
result = result[1:-1]
result = result.strip(" ")
# Reset the parenthesis count for the next condition.
condition_parts_count = 0
return result
Condition = add_element("Condition", pp.Combine(ConditionPart + ConditionRepeated))
Condition.setParseAction(handle_condition)
# 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) -> Tuple[pp.ParseResults, 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, contents
def parseProFile(file: str, *, debug=False) -> Tuple[pp.ParseResults, str]:
parser = QmakeParser(debug=debug)
return parser.parseFile(file)

View File

@ -1,8 +0,0 @@
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'
portalocker; python_version >= '3.7'
black; python_version >= '3.7'

View File

@ -1,221 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import glob
import os
import subprocess
import concurrent.futures
import sys
import typing
import argparse
from argparse import ArgumentParser
def parse_command_line() -> argparse.Namespace:
parser = ArgumentParser(
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",
dest="only_existing",
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",
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",
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="<path>", type=str, help="The path where to look for .pro files."
)
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):
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 == ".":
dir_name = os.path.basename(os.getcwd())
if dir_name.endswith(pro_file_without_suffix):
return dir_name
return dir_name + "/__" + pro_file
all_files = []
previous_dir_name: typing.Optional[str] = None
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 cmake_lists_missing_filter(path):
return not cmake_lists_exists_filter(path)
def qtbase_main_modules_filter(path):
main_modules = [
"corelib",
"network",
"gui",
"widgets",
"testlib",
"printsupport",
"opengl",
"sql",
"dbus",
"concurrent",
"xml",
]
path_suffixes = [f"src/{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_missing:
filter_func = cmake_lists_missing_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)
else:
all_files.append(pro_file)
previous_dir_name = dir_name
return 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, 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 = []
if sys.platform == "win32":
pro2cmake_args.append(sys.executable)
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))
if args.pro2cmake_args:
pro2cmake_args += args.pro2cmake_args
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)),
):
if return_code:
failed_files.append(filename)
print(stdout)
return failed_files
def main() -> None:
args = parse_command_line()
script_path = os.path.dirname(os.path.abspath(__file__))
pro2cmake = os.path.join(script_path, "pro2cmake.py")
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.")
if failed_files:
print(
f"The following files were not successfully "
f"converted ({len(failed_files)} of {files_count}):"
)
for f in failed_files:
print(f' "{f}"')
if __name__ == "__main__":
main()

View File

@ -1,397 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2019 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
"""
This is a helper script that takes care of reapplying special case
modifications when regenerating a CMakeLists.txt file using
pro2cmake.py or configure.cmake with configurejson2cmake.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/configure.cmake 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/configure.cmake 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"/"configure.cmake" 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
import time
import typing
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,
)
# 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(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")
if is_win:
git_path += ".exe"
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) -> bool:
if debug:
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:
if debug:
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(f"Checking if {file_path} has no leftover conflict markers.")
content_actual = read_content_from_file(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
):
"""
Reads content of original CMakeLists.txt/configure.cmake, 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(f"Removing special case blocks from {original_file_path}.")
content_no_special_cases = remove_special_cases(content_actual)
if debug:
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):
# 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(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:
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:
filename = ".prev_" + os.path.basename(self.original_file_path)
return os.path.join(self.base_dir, filename)
@property
def post_merge_file_path(self) -> str:
original_file_name = os.path.basename(self.original_file_path)
(original_file_basename, original_file_ext) = os.path.splitext(original_file_name)
filename = original_file_basename + "-post-merge" + original_file_ext
return os.path.join(self.base_dir, filename)
@property
def no_special_file_path(self) -> str:
original_file_name = os.path.basename(self.original_file_path)
(original_file_basename, original_file_ext) = os.path.splitext(original_file_name)
filename = original_file_basename + ".no-special" + original_file_ext
return os.path.join(self.base_dir, filename)
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 = os.path.basename(self.original_file_path)
try:
os.mkdir(repo)
current_dir = os.getcwd()
os.chdir(repo)
except Exception as 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)
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/configure.cmake 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(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)
copyfile_log(original_file_path, txt, 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)
# Copy the new "modified" file and make a commit.
copyfile_log(generated_file_path, txt, 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)
# 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(f"Git merge conflict resolution process failed. Exception: {e}")
raise e
finally:
os.chdir(current_dir)
# Remove the temporary repo.
try:
if not self.keep_temporary_files:
rmtree(repo_absolute_path, onerror=rm_tree_on_error_handler)
except Exception as 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)
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)
# 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(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:
if self.debug:
print("Retrying git add, the index.lock was probably acquired.")
if failed_once and success:
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.")
def handle_special_cases_helper(self) -> bool:
"""
Uses git to reapply special case modifications to the "new"
generated CMakeLists.gen.txt/configure.cmake.gen 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(
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()
copyfile_log(self.post_merge_file_path, self.generated_file_path)
if not self.keep_temporary_files:
os.remove(self.post_merge_file_path)
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}")
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

View File

@ -1,6 +0,0 @@
# 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)

View File

@ -1,2 +0,0 @@
qmake-clean.commands += (cd qmake && $(MAKE) clean ":-(==)-:" '(Foo)' )

View File

@ -1,4 +0,0 @@
!system("dbus-send --session --type=signal / local.AutotestCheck.Hello >$$QMAKE_SYSTEM_NULL_DEVICE 2>&1") {
SOURCES = dbus.cpp
}

View File

@ -1,22 +0,0 @@
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
}

View File

@ -1,11 +0,0 @@
a1|a2 {
DEFINES += d
}
b1|b2:b3 {
DEFINES += d
}
c1|c2:c3|c4 {
DEFINES += d
}

View File

@ -1,2 +0,0 @@
write_file("a", contents)|error()

View File

@ -1,4 +0,0 @@
contains(DEFINES,QT_EVAL):include(eval.pri)
HOST_BINS = $$[QT_HOST_BINS]

View File

@ -1,4 +0,0 @@
TARGET = myapp
QT = core network widgets
win32: QT += opengl
SOURCES = main.cpp

View File

@ -1,8 +0,0 @@
QT += core gui
SOURCES += main.cpp
greaterThan(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 1):equals(QT_PATCH_VERSION, 0) {
DEFINES += SUPER_FRESH_MAJOR_QT_RELEASE
}
greaterThan(QT_VERSION, 6.6.5):lessThan(QT_VERSION, 6.6.7):equals(QT_VERSION, 6.6.6): {
DEFINES += QT_VERSION_OF_THE_BEAST
}

View File

@ -1,3 +0,0 @@
TARGET = myapp
QT = core network widgets
SOURCES = main.cpp

View File

@ -1,6 +0,0 @@
defineTest(pathIsAbsolute) {
p = $$clean_path($$1)
!isEmpty(p):isEqual(p, $$absolute_path($$p)): return(true)
return(false)
}

View File

@ -1,6 +0,0 @@
linux {
SOURCES += a.cpp
} else {
SOURCES += b.cpp
}

View File

@ -1,4 +0,0 @@
osx: A = 1
else: win32: B = 2
else: C = 3

View File

@ -1,7 +0,0 @@
qtConfig(timezone) {
A = 1
} else:win32 {
B = 2
} else {
C = 3
}

View File

@ -1,6 +0,0 @@
qtConfig(timezone) {
A = 1
} else:win32: B = 2
else {
C = 3
}

View File

@ -1,10 +0,0 @@
# comments
qtConfig(timezone) { # bar
A = 1
} else:win32 {
B = 2 # foo
} else { C = 3
# baz
# foobar
}
# endcomment

View File

@ -1,11 +0,0 @@
qtConfig(timezone) \
{
A = \
1
} \
else:win32: \
B = 2
else: \
C \
= 3

View File

@ -1,2 +0,0 @@
msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x65000000

View File

@ -1,5 +0,0 @@
qtConfig(timezone) { A = 1 } else:win32: {\
B = 2 \
} else: \
C \
= 3 \

View File

@ -1,2 +0,0 @@
MODULE_AUX_INCLUDES = \
\$\$QT_MODULE_INCLUDE_BASE/QtANGLE

View File

@ -1,11 +0,0 @@
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)
}
}

View File

@ -1,4 +0,0 @@
pathIsAbsolute($$CMAKE_HOST_DATA_DIR) {
CMAKE_HOST_DATA_DIR = $$[QT_HOST_DATA/src]/
}

View File

@ -1,3 +0,0 @@
A = 42
include(foo) # load foo
B=23

View File

@ -1,10 +0,0 @@
TEMPLATE=subdirs
SUBDIRS=\
qmacstyle \
qstyle \
qstyleoption \
qstylesheetstyle \
!qtConfig(private_tests): SUBDIRS -= \
qstylesheetstyle \

View File

@ -1,22 +0,0 @@
SUBDIRS = \
# dds \
tga \
wbmp
MYVAR = foo # comment
MYVAR = foo2# comment
MYVAR = foo3# comment #
MYVAR = foo4# comment #
##
#
#
##
#
#
#
# #
MYVAR = foo5# comment # #

View File

@ -1,3 +0,0 @@
A = 42
load(foo)# load foo
B=23

View File

@ -1,3 +0,0 @@
equals(a): \
greaterThan(a):flags += 1

View File

@ -1,4 +0,0 @@
A = 42 \
43 \
44
B=23

View File

@ -1,2 +0,0 @@
requires(qtConfig(dlopen))

View File

@ -1,5 +0,0 @@
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\')\\\"
}

View File

@ -1,4 +0,0 @@
for(d, sd): \
exists($$d/$${d}.pro): \
SUBDIRS += $$d

View File

@ -1,3 +0,0 @@
TEMPLATE = subdirs
SUBDIRS = \
kernel \

View File

@ -1,17 +0,0 @@
win32 {
!winrt {
SOURCES +=io/qstandardpaths_win.cpp
} else {
SOURCES +=io/qstandardpaths_winrt.cpp
}
} else:unix {
mac {
OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm
} else:android {
SOURCES += io/qstandardpaths_android.cpp
} else:haiku {
SOURCES += io/qstandardpaths_haiku.cpp
} else {
SOURCES += io/qstandardpaths_unix.cpp
}
}

View File

@ -1,2 +0,0 @@
unset(f16c_cxx)

View File

@ -1,2 +0,0 @@
TARGET = Dummy
TARGET = $$qtLibraryTarget($$TARGET)

View File

@ -1,66 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from pro2cmake import Scope, SetOperation, merge_scopes, recursive_evaluate_scope
from tempfile import TemporaryDirectory
import os
import pathlib
import pytest
import re
import shutil
import subprocess
import tempfile
import typing
debug_mode = bool(os.environ.get("DEBUG_PRO2CMAKE_TEST_CONVERSION"))
test_script_dir = pathlib.Path(__file__).parent.resolve()
pro2cmake_dir = test_script_dir.parent.resolve()
pro2cmake_py = pro2cmake_dir.joinpath("pro2cmake.py")
test_data_dir = test_script_dir.joinpath("data", "conversion")
def convert(base_name: str):
pro_file_name = str(base_name) + ".pro"
pro_file_path = test_data_dir.joinpath(pro_file_name)
assert(pro_file_path.exists())
with TemporaryDirectory(prefix="testqmake2cmake") as tmp_dir_str:
tmp_dir = pathlib.Path(tmp_dir_str)
output_file_path = tmp_dir.joinpath("CMakeLists.txt")
exit_code = subprocess.call([pro2cmake_py, "--is-example", "-o", output_file_path, pro_file_path])
assert(exit_code == 0)
if debug_mode:
shutil.copyfile(output_file_path, tempfile.gettempdir() + "/pro2cmake/CMakeLists.txt")
f = open(output_file_path, "r")
assert(f)
content = f.read()
assert(content)
return content
def test_qt_modules():
output = convert("required_qt_modules")
find_package_lines = []
for line in output.split("\n"):
if "find_package(" in line:
find_package_lines.append(line.strip())
assert(["find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)",
"find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Network Widgets)"] == find_package_lines)
output = convert("optional_qt_modules")
find_package_lines = []
for line in output.split("\n"):
if "find_package(" in line:
find_package_lines.append(line.strip())
assert(["find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)",
"find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Network Widgets)",
"find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS OpenGL)"] == find_package_lines)
def test_qt_version_check():
output = convert("qt_version_check")
interesting_lines = []
for line in output.split("\n"):
if line.startswith("if(") and "QT_VERSION" in line:
interesting_lines.append(line.strip())
assert(["if(( ( (QT_VERSION_MAJOR GREATER 5) ) AND (QT_VERSION_MINOR LESS 1) ) AND (QT_VERSION_PATCH EQUAL 0))", "if(( ( (QT_VERSION VERSION_GREATER 6.6.5) ) AND (QT_VERSION VERSION_LESS 6.6.7) ) AND (QT_VERSION VERSION_EQUAL 6.6.6))"] == interesting_lines)

View File

@ -1,19 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from qmake_parser import fixup_linecontinuation
def test_no_change():
input = "test \\\nline2\n line3"
output = "test line2\n line3"
result = fixup_linecontinuation(input)
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 output == result

View File

@ -1,160 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from condition_simplifier 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_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)')
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_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('WATCHOS AND WIN32', 'OFF')
def test_simplify_win32_and_watchos():
validate_simplify('WIN32 AND WATCHOS', 'OFF')
def test_simplify_apple_and_appleosx():
validate_simplify('APPLE AND MACOS', 'MACOS')
def test_simplify_apple_or_appleosx():
validate_simplify('APPLE OR MACOS', 'APPLE')
def test_simplify_apple_or_appleosx_level1():
validate_simplify('foobar AND (APPLE OR MACOS )', 'APPLE AND foobar')
def test_simplify_apple_or_appleosx_level1_double():
validate_simplify('foobar AND (APPLE OR MACOS )', 'APPLE AND foobar')
def test_simplify_apple_or_appleosx_level1_double_with_extra_spaces():
validate_simplify('foobar AND (APPLE OR MACOS ) '
'AND ( MACOS OR APPLE )', 'APPLE AND foobar')
def test_simplify_apple_or_appleosx_level2():
validate_simplify('foobar AND ( ( APPLE OR WATCHOS ) '
'OR MACOS ) AND ( MACOS OR APPLE ) '
'AND ( (WIN32 OR WINRT) OR UNIX) ', 'APPLE AND foobar')
def test_simplify_not_apple_and_appleosx():
validate_simplify('NOT APPLE AND MACOS', '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')
def test_simplify_android_not_apple():
validate_simplify('ANDROID AND NOT MACOS', 'ANDROID')

View File

@ -1,32 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from pro2cmake import AddOperation, SetOperation, UniqueAddOperation, RemoveOperation
def test_add_operation():
op = AddOperation(['bar', 'buz'])
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'], ['foo', 'bar'], lambda x: x)
assert ['foo', 'bar', 'buz'] == result
def test_set_operation():
op = SetOperation(['bar', 'buz'])
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'], ['foo', 'bar'], lambda x: x)
assert ['foo', '-buz'] == result

View File

@ -1,343 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import os
from pro2cmake import map_condition
from qmake_parser import QmakeParser
from condition_simplifier import simplify_condition
_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']['value']
assert value == to_validate.get('value', None)
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)
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_dictionary['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_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
validate_op('A', '=', ['42'], result[0])
include = result[1]
assert len(include) == 1
assert 'included' in include
assert include['included'].get('value', '') == '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_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_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
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
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'
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', '=', [], if_branch[0])
assert 'included' in result[1]
assert result[1]['included'].get('value', '') == 'animation/animation.pri'
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])
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
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])
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])
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')
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
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
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'
def test_condition_operator_precedence():
result = parse_file(_tests_path + '/data/condition_operator_precedence.pro')
def validate_simplify(input_str: str, expected: str) -> None:
output = simplify_condition(map_condition(input_str))
assert output == expected
validate_simplify(result[0]["condition"], "a1 OR a2")
validate_simplify(result[1]["condition"], "b3 AND (b1 OR b2)")
validate_simplify(result[2]["condition"], "c4 OR (c1 AND c3) OR (c2 AND c3)")

View File

@ -1,318 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
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,
qmake_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.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.get_string('test1', 'not found') == 'not found'
assert child.get_string('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.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.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.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():
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.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.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.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():
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.get_string('test') == 'foo'
assert r0.get_string('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.get_string('test') == 'foo'
assert r0.get_string('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.get_string('test1') == 'parent'
assert r0.get_string('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.get_string('test1') == 'parent'
assert r0.get_string('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='MACOS', SOURCES='qsp_mac.mm')
# } else:android {
# SOURCES += io/qstandardpaths_android.cpp
scope8 = _new_scope(parent_scope=scope6, condition='else')
scope9 = _new_scope(parent_scope=scope8, condition='ANDROID AND NOT UNKNOWN_PLATFORM', 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 == 'MACOS'
assert scope8.total_condition == 'UNIX AND NOT MACOS'
assert scope9.total_condition == 'ANDROID AND NOT UNKNOWN_PLATFORM'
assert scope10.total_condition == 'UNIX AND NOT MACOS AND (UNKNOWN_PLATFORM OR NOT ANDROID)'
assert scope11.total_condition == 'HAIKU AND (UNKNOWN_PLATFORM OR NOT ANDROID)'
assert scope12.total_condition == 'UNIX AND NOT HAIKU AND NOT MACOS AND (UNKNOWN_PLATFORM 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']