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:
parent
a5d896d70a
commit
d7a739bde1
1
util/cmake/.gitignore
vendored
1
util/cmake/.gitignore
vendored
@ -1 +0,0 @@
|
||||
.pro2cmake_cache/
|
@ -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
|
@ -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"
|
@ -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
|
||||
```
|
@ -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()
|
@ -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"
|
@ -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
@ -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
|
@ -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
|
||||
)
|
@ -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
@ -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()
|
@ -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)
|
@ -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'
|
||||
|
@ -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()
|
@ -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
|
@ -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)
|
@ -1,2 +0,0 @@
|
||||
qmake-clean.commands += (cd qmake && $(MAKE) clean ":-(==)-:" '(Foo)' )
|
||||
|
@ -1,4 +0,0 @@
|
||||
!system("dbus-send --session --type=signal / local.AutotestCheck.Hello >$$QMAKE_SYSTEM_NULL_DEVICE 2>&1") {
|
||||
SOURCES = dbus.cpp
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
a1|a2 {
|
||||
DEFINES += d
|
||||
}
|
||||
|
||||
b1|b2:b3 {
|
||||
DEFINES += d
|
||||
}
|
||||
|
||||
c1|c2:c3|c4 {
|
||||
DEFINES += d
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
write_file("a", contents)|error()
|
||||
|
@ -1,4 +0,0 @@
|
||||
contains(DEFINES,QT_EVAL):include(eval.pri)
|
||||
|
||||
HOST_BINS = $$[QT_HOST_BINS]
|
||||
|
@ -1,4 +0,0 @@
|
||||
TARGET = myapp
|
||||
QT = core network widgets
|
||||
win32: QT += opengl
|
||||
SOURCES = main.cpp
|
@ -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
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
TARGET = myapp
|
||||
QT = core network widgets
|
||||
SOURCES = main.cpp
|
@ -1,6 +0,0 @@
|
||||
defineTest(pathIsAbsolute) {
|
||||
p = $$clean_path($$1)
|
||||
!isEmpty(p):isEqual(p, $$absolute_path($$p)): return(true)
|
||||
return(false)
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
|
||||
linux {
|
||||
SOURCES += a.cpp
|
||||
} else {
|
||||
SOURCES += b.cpp
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
|
||||
osx: A = 1
|
||||
else: win32: B = 2
|
||||
else: C = 3
|
@ -1,7 +0,0 @@
|
||||
qtConfig(timezone) {
|
||||
A = 1
|
||||
} else:win32 {
|
||||
B = 2
|
||||
} else {
|
||||
C = 3
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
qtConfig(timezone) {
|
||||
A = 1
|
||||
} else:win32: B = 2
|
||||
else {
|
||||
C = 3
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# comments
|
||||
qtConfig(timezone) { # bar
|
||||
A = 1
|
||||
} else:win32 {
|
||||
B = 2 # foo
|
||||
} else { C = 3
|
||||
# baz
|
||||
# foobar
|
||||
}
|
||||
# endcomment
|
@ -1,11 +0,0 @@
|
||||
qtConfig(timezone) \
|
||||
{
|
||||
A = \
|
||||
1
|
||||
} \
|
||||
else:win32: \
|
||||
B = 2
|
||||
else: \
|
||||
C \
|
||||
= 3
|
||||
|
@ -1,2 +0,0 @@
|
||||
msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x65000000
|
||||
|
@ -1,5 +0,0 @@
|
||||
qtConfig(timezone) { A = 1 } else:win32: {\
|
||||
B = 2 \
|
||||
} else: \
|
||||
C \
|
||||
= 3 \
|
@ -1,2 +0,0 @@
|
||||
MODULE_AUX_INCLUDES = \
|
||||
\$\$QT_MODULE_INCLUDE_BASE/QtANGLE
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
pathIsAbsolute($$CMAKE_HOST_DATA_DIR) {
|
||||
CMAKE_HOST_DATA_DIR = $$[QT_HOST_DATA/src]/
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
A = 42
|
||||
include(foo) # load foo
|
||||
B=23
|
@ -1,10 +0,0 @@
|
||||
TEMPLATE=subdirs
|
||||
SUBDIRS=\
|
||||
qmacstyle \
|
||||
qstyle \
|
||||
qstyleoption \
|
||||
qstylesheetstyle \
|
||||
|
||||
!qtConfig(private_tests): SUBDIRS -= \
|
||||
qstylesheetstyle \
|
||||
|
@ -1,22 +0,0 @@
|
||||
SUBDIRS = \
|
||||
# dds \
|
||||
tga \
|
||||
wbmp
|
||||
|
||||
MYVAR = foo # comment
|
||||
MYVAR = foo2# comment
|
||||
MYVAR = foo3# comment #
|
||||
|
||||
MYVAR = foo4# comment #
|
||||
|
||||
##
|
||||
#
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
# #
|
||||
|
||||
MYVAR = foo5# comment # #
|
@ -1,3 +0,0 @@
|
||||
A = 42
|
||||
load(foo)# load foo
|
||||
B=23
|
@ -1,3 +0,0 @@
|
||||
equals(a): \
|
||||
greaterThan(a):flags += 1
|
||||
|
@ -1,4 +0,0 @@
|
||||
A = 42 \
|
||||
43 \
|
||||
44
|
||||
B=23
|
@ -1,2 +0,0 @@
|
||||
requires(qtConfig(dlopen))
|
||||
|
@ -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\')\\\"
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
for(d, sd): \
|
||||
exists($$d/$${d}.pro): \
|
||||
SUBDIRS += $$d
|
||||
|
@ -1,3 +0,0 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = \
|
||||
kernel \
|
@ -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
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
unset(f16c_cxx)
|
||||
|
@ -1,2 +0,0 @@
|
||||
TARGET = Dummy
|
||||
TARGET = $$qtLibraryTarget($$TARGET)
|
@ -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)
|
@ -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
|
@ -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')
|
@ -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
|
@ -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)")
|
@ -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']
|
Loading…
x
Reference in New Issue
Block a user