Remove extensions

This commit is contained in:
Dave Dalcino
2022-08-25 04:24:13 -07:00
parent a07c032c86
commit caee04bcb3
7 changed files with 576 additions and 222 deletions

View File

@@ -35,7 +35,7 @@ from logging import getLogger
from logging.handlers import QueueHandler from logging.handlers import QueueHandler
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import List, Optional, Tuple from typing import List, Optional, Set, Tuple
import aqt import aqt
from aqt.archives import QtArchives, QtPackage, SrcDocExamplesArchives, TargetConfig, ToolArchives from aqt.archives import QtArchives, QtPackage, SrcDocExamplesArchives, TargetConfig, ToolArchives
@@ -207,7 +207,9 @@ class Cli:
def opt_version_for_spec(ext: str, _spec: SimpleSpec) -> Optional[Version]: def opt_version_for_spec(ext: str, _spec: SimpleSpec) -> Optional[Version]:
try: try:
return MetadataFactory(ArchiveId("qt", host, target, ext), spec=_spec, base_url=base_url).getList().latest() return (
MetadataFactory(ArchiveId("qt", host, target, {ext}), spec=_spec, base_url=base_url).getList().latest()
)
except AqtException: except AqtException:
return None return None
@@ -478,7 +480,7 @@ class Cli:
else: else:
timeout = (Settings.connection_timeout, Settings.response_timeout) timeout = (Settings.connection_timeout, Settings.response_timeout)
if args.tool_variant is None: if args.tool_variant is None:
archive_id = ArchiveId("tools", os_name, target, "") archive_id = ArchiveId("tools", os_name, target, {""})
meta = MetadataFactory(archive_id, base_url=base, is_latest_version=True, tool_name=tool_name) meta = MetadataFactory(archive_id, base_url=base, is_latest_version=True, tool_name=tool_name)
try: try:
archs = meta.getList() archs = meta.getList()
@@ -514,6 +516,18 @@ class Cli:
def run_list_qt(self, args: argparse.ArgumentParser): def run_list_qt(self, args: argparse.ArgumentParser):
"""Print versions of Qt, extensions, modules, architectures""" """Print versions of Qt, extensions, modules, architectures"""
if args.extensions:
self._warn_on_deprecated_parameter("extensions", args.extensions)
self.logger.warning(
"The '--extensions' flag will always return an empty list, "
"because there are no useful arguments for the '--extension' flag."
)
print("")
return
if args.extension:
self._warn_on_deprecated_parameter("extension", args.extension)
self.logger.warning("The '--extension' flag will be ignored.")
if not args.target: if not args.target:
print(" ".join(ArchiveId.TARGETS_FOR_HOST[args.host])) print(" ".join(ArchiveId.TARGETS_FOR_HOST[args.host]))
return return
@@ -526,9 +540,36 @@ class Cli:
else: else:
modules_ver, modules_query, is_long = None, None, False modules_ver, modules_query, is_long = None, None, False
for version_str in (modules_ver, args.extensions, args.arch, args.archives[0] if args.archives else None): for version_str in (modules_ver, args.arch, args.archives[0] if args.archives else None):
Cli._validate_version_str(version_str, allow_latest=True, allow_empty=True) Cli._validate_version_str(version_str, allow_latest=True, allow_empty=True)
# Select extensions:
def extensions_for_version_arch(version: str, arch: str) -> Set[str]:
is_qt_version_6 = version == "latest" or Version(version) >= Version("6.0.0")
if arch.startswith("wasm"):
return {"wasm"}
elif arch.startswith("android_") and is_qt_version_6:
return {arch[len("android_") :]}
else:
return {""}
if modules_query:
extensions = extensions_for_version_arch(modules_ver, modules_query[1])
elif args.archives and len(args.archives) >= 2:
ver, arch, *_ = args.archives
extensions = extensions_for_version_arch(ver, arch)
elif args.arch:
is_qt_version_6 = args.arch == "latest" or Version(args.arch) >= Version("6.0.0")
is_in_wasm_range = args.arch == "latest" or QtRepoProperty.is_in_wasm_range(args.host, Version(args.arch))
if args.target == "android" and is_qt_version_6:
extensions = ArchiveId.EXTENSIONS_REQUIRED_ANDROID_QT6
elif args.target == "desktop" and is_in_wasm_range:
extensions = {"", "wasm"}
else:
extensions = {""}
else:
extensions = {""}
spec = None spec = None
try: try:
if args.spec is not None: if args.spec is not None:
@@ -541,13 +582,12 @@ class Cli:
"qt", "qt",
args.host, args.host,
args.target, args.target,
args.extension if args.extension else "", extensions,
), ),
spec=spec, spec=spec,
is_latest_version=args.latest_version, is_latest_version=args.latest_version,
modules_query=modules_query, modules_query=modules_query,
is_long_listing=is_long, is_long_listing=is_long,
extensions_ver=args.extensions,
architectures_ver=args.arch, architectures_ver=args.arch,
archives_query=args.archives, archives_query=args.archives,
) )
@@ -574,7 +614,7 @@ class Cli:
version = Cli._determine_qt_version(args.qt_version_spec, args.host, target, arch="") version = Cli._determine_qt_version(args.qt_version_spec, args.host, target, arch="")
is_fetch_modules: bool = getattr(args, "modules", False) is_fetch_modules: bool = getattr(args, "modules", False)
meta = MetadataFactory( meta = MetadataFactory(
archive_id=ArchiveId("qt", args.host, target, "src_doc_examples"), archive_id=ArchiveId("qt", args.host, target, {"src_doc_examples"}),
src_doc_examples_query=(cmd_type, version, is_fetch_modules), src_doc_examples_query=(cmd_type, version, is_fetch_modules),
) )
show_list(meta) show_list(meta)
@@ -730,12 +770,10 @@ class Cli:
epilog="Examples:\n" epilog="Examples:\n"
"$ aqt list-qt mac # print all targets for Mac OS\n" "$ aqt list-qt mac # print all targets for Mac OS\n"
"$ aqt list-qt mac desktop # print all versions of Qt 5\n" "$ aqt list-qt mac desktop # print all versions of Qt 5\n"
"$ aqt list-qt mac desktop --extension wasm # print all wasm versions of Qt 5\n"
'$ aqt list-qt mac desktop --spec "5.9" # print all versions of Qt 5.9\n' '$ aqt list-qt mac desktop --spec "5.9" # print all versions of Qt 5.9\n'
'$ aqt list-qt mac desktop --spec "5.9" --latest-version # print latest Qt 5.9\n' '$ aqt list-qt mac desktop --spec "5.9" --latest-version # print latest Qt 5.9\n'
"$ aqt list-qt mac desktop --modules 5.12.0 clang_64 # print modules for 5.12.0\n" "$ aqt list-qt mac desktop --modules 5.12.0 clang_64 # print modules for 5.12.0\n"
"$ aqt list-qt mac desktop --spec 5.9 --modules latest clang_64 # print modules for latest 5.9\n" "$ aqt list-qt mac desktop --spec 5.9 --modules latest clang_64 # print modules for latest 5.9\n"
"$ aqt list-qt mac desktop --extensions 5.9.0 # print choices for --extension flag\n"
"$ aqt list-qt mac desktop --arch 5.9.9 # print architectures for 5.9.9/mac/desktop\n" "$ aqt list-qt mac desktop --arch 5.9.9 # print architectures for 5.9.9/mac/desktop\n"
"$ aqt list-qt mac desktop --arch latest # print architectures for the latest Qt 5\n" "$ aqt list-qt mac desktop --arch latest # print architectures for the latest Qt 5\n"
"$ aqt list-qt mac desktop --archives 5.9.0 clang_64 # list archives in base Qt installation\n" "$ aqt list-qt mac desktop --archives 5.9.0 clang_64 # list archives in base Qt installation\n"
@@ -752,8 +790,7 @@ class Cli:
list_parser.add_argument( list_parser.add_argument(
"--extension", "--extension",
choices=ArchiveId.ALL_EXTENSIONS, choices=ArchiveId.ALL_EXTENSIONS,
help="Extension of packages to list. " help="Deprecated since aqt v3.1.0. Use of this flag will emit a warning, but will otherwise be ignored.",
"Use the `--extensions` flag to list all relevant options for a host/target.",
) )
list_parser.add_argument( list_parser.add_argument(
"--spec", "--spec",
@@ -786,9 +823,8 @@ class Cli:
"--extensions", "--extensions",
type=str, type=str,
metavar="(VERSION | latest)", metavar="(VERSION | latest)",
help='Qt version in the format of "5.X.Y", or the keyword "latest". ' help="Deprecated since v3.1.0. Prints a list of valid arguments for the '--extension' flag. "
"When set, this prints all valid arguments for the `--extension` flag " "Since the '--extension' flag is now deprecated, this will always print an empty list.",
"for either Qt 5.X.Y or the latest version of Qt.",
) )
output_modifier_exclusive_group.add_argument( output_modifier_exclusive_group.add_argument(
"--arch", "--arch",

View File

@@ -28,7 +28,7 @@ from abc import ABC, abstractmethod
from functools import reduce from functools import reduce
from logging import getLogger from logging import getLogger
from pathlib import Path from pathlib import Path
from typing import Callable, Dict, Generator, Iterable, Iterator, List, Optional, Tuple, Union from typing import Callable, Dict, Generator, Iterable, Iterator, List, Optional, Set, Tuple, Union
from urllib.parse import ParseResult, urlparse from urllib.parse import ParseResult, urlparse
from xml.etree.ElementTree import Element from xml.etree.ElementTree import Element
@@ -205,34 +205,27 @@ class ArchiveId:
"mac": ["android", "desktop", "ios"], "mac": ["android", "desktop", "ios"],
"linux": ["android", "desktop"], "linux": ["android", "desktop"],
} }
ALL_EXTENSIONS = ( EXTENSIONS_REQUIRED_ANDROID_QT6 = {"x86_64", "x86", "armv7", "arm64_v8a"}
"wasm", ALL_EXTENSIONS = {"", "wasm", "src_doc_examples", *EXTENSIONS_REQUIRED_ANDROID_QT6}
"src_doc_examples",
"preview",
"wasm_preview",
"x86_64",
"x86",
"armv7",
"arm64_v8a",
)
EXTENSIONS_REQUIRED_ANDROID_QT6 = "x86_64", "x86", "armv7", "arm64_v8a"
def __init__(self, category: str, host: str, target: str, extension: str = ""): def __init__(self, category: str, host: str, target: str, extensions: Set[str] = None):
if extensions is None:
extensions = {""}
if category not in ArchiveId.CATEGORIES: if category not in ArchiveId.CATEGORIES:
raise ValueError("Category '{}' is invalid".format(category)) raise ValueError("Category '{}' is invalid".format(category))
if host not in ArchiveId.HOSTS: if host not in ArchiveId.HOSTS:
raise ValueError("Host '{}' is invalid".format(host)) raise ValueError("Host '{}' is invalid".format(host))
if target not in ArchiveId.TARGETS_FOR_HOST[host]: if target not in ArchiveId.TARGETS_FOR_HOST[host]:
raise ValueError("Target '{}' is invalid".format(target)) raise ValueError("Target '{}' is invalid".format(target))
if extension and extension not in ArchiveId.ALL_EXTENSIONS: if not extensions.issubset(ArchiveId.ALL_EXTENSIONS):
raise ValueError("Extension '{}' is invalid".format(extension)) raise ValueError(f"Extension(s) { extensions - ArchiveId.ALL_EXTENSIONS } are invalid")
self.category: str = category self.category: str = category
self.host: str = host self.host: str = host
self.target: str = target self.target: str = target
self.extension: str = extension self.extensions: Set[str] = extensions
def is_preview(self) -> bool: def is_preview(self) -> bool:
return "preview" in self.extension if self.extension else False return False
def is_qt(self) -> bool: def is_qt(self) -> bool:
return self.category == "qt" return self.category == "qt"
@@ -251,20 +244,30 @@ class ArchiveId:
target=self.target, target=self.target,
) )
def to_folder(self, qt_version_no_dots: str) -> str: def to_folder(self, qt_version_no_dots: str, extension: Optional[str] = None) -> str:
if extension is None:
extension = self.extension
return "{category}{major}_{ver}{ext}".format( return "{category}{major}_{ver}{ext}".format(
category=self.category, category=self.category,
major=qt_version_no_dots[0], major=qt_version_no_dots[0],
ver=qt_version_no_dots, ver=qt_version_no_dots,
ext="_" + self.extension if self.extension else "", ext="_" + extension if extension else "",
) )
@property
def extension(self) -> str:
assert len(self.extensions) == 1, "Call requires a singular extension"
return next(iter(self.extensions))
def with_ext(self, other_extension: str) -> "ArchiveId":
return ArchiveId(self.category, self.host, self.target, {other_extension})
def __str__(self) -> str: def __str__(self) -> str:
return "{cat}/{host}/{target}{ext}".format( return "{cat}/{host}/{target}{ext}".format(
cat=self.category, cat=self.category,
host=self.host, host=self.host,
target=self.target, target=self.target,
ext="" if not self.extension else "/" + self.extension, ext="" if self.extensions == {""} else "/" + format(self.extensions),
) )
@@ -491,6 +494,14 @@ class QtRepoProperty:
selected_dir = QtRepoProperty.select_default_mingw(arch_dirs, is_dir=True) selected_dir = QtRepoProperty.select_default_mingw(arch_dirs, is_dir=True)
return installed_qt_version_dir / selected_dir if selected_dir else None return installed_qt_version_dir / selected_dir if selected_dir else None
@staticmethod
def is_in_wasm_range(host: str, version: Version) -> bool:
return (
version in SimpleSpec(">=6.2.0")
or (host == "linux" and version in SimpleSpec(">=5.13,<6"))
or version in SimpleSpec(">=5.13.1,<6")
)
class MetadataFactory: class MetadataFactory:
"""Retrieve metadata of Qt variations, versions, and descriptions from Qt site.""" """Retrieve metadata of Qt variations, versions, and descriptions from Qt site."""
@@ -503,7 +514,6 @@ class MetadataFactory:
spec: Optional[SimpleSpec] = None, spec: Optional[SimpleSpec] = None,
is_latest_version: bool = False, is_latest_version: bool = False,
modules_query: Optional[Tuple[str, str]] = None, modules_query: Optional[Tuple[str, str]] = None,
extensions_ver: Optional[str] = None,
architectures_ver: Optional[str] = None, architectures_ver: Optional[str] = None,
archives_query: Optional[List[str]] = None, archives_query: Optional[List[str]] = None,
src_doc_examples_query: Optional[Tuple[str, Version, bool]] = None, src_doc_examples_query: Optional[Tuple[str, Version, bool]] = None,
@@ -518,7 +528,6 @@ class MetadataFactory:
:param is_latest_version: When True, the MetadataFactory will find all versions of Qt :param is_latest_version: When True, the MetadataFactory will find all versions of Qt
matching filters, and only print the most recent version matching filters, and only print the most recent version
:param modules_query: [Version of Qt, architecture] for which to list modules :param modules_query: [Version of Qt, architecture] for which to list modules
:param extensions_ver: Version of Qt for which to list extensions
:param architectures_ver: Version of Qt for which to list architectures :param architectures_ver: Version of Qt for which to list architectures
:param archives_query: [Qt_Version, architecture, *module_names]: used to print list of archives :param archives_query: [Qt_Version, architecture, *module_names]: used to print list of archives
:param tool_name: Name of a tool, without architecture, ie "tools_qtcreator" or "tools_ifw" :param tool_name: Name of a tool, without architecture, ie "tools_qtcreator" or "tools_ifw"
@@ -554,9 +563,6 @@ class MetadataFactory:
self.request_type = "modules" self.request_type = "modules"
version, arch = modules_query version, arch = modules_query
self._action = lambda: self.fetch_modules(self._to_version(version), arch) self._action = lambda: self.fetch_modules(self._to_version(version), arch)
elif extensions_ver:
self.request_type = "extensions"
self._action = lambda: self.fetch_extensions(self._to_version(extensions_ver))
elif architectures_ver: elif architectures_ver:
self.request_type = "architectures" self.request_type = "architectures"
self._action = lambda: self.fetch_arches(self._to_version(architectures_ver)) self._action = lambda: self.fetch_arches(self._to_version(architectures_ver))
@@ -582,31 +588,23 @@ class MetadataFactory:
return self._action() return self._action()
def fetch_arches(self, version: Version) -> List[str]: def fetch_arches(self, version: Version) -> List[str]:
self.validate_extension(version)
if self.archive_id.extension == "src_doc_examples":
return []
qt_ver_str = self._get_qt_version_str(version)
modules = self._fetch_module_metadata(self.archive_id.to_folder(qt_ver_str))
arches = [] arches = []
for name in modules.keys(): for extension in self.archive_id.extensions:
ver, arch = name.split(".")[-2:] if extension == "src_doc_examples":
if ver == qt_ver_str: continue
arches.append(arch) qt_ver_str = self._get_qt_version_str(version)
modules = self._fetch_module_metadata(extension, self.archive_id.to_folder(qt_ver_str, extension))
for name in modules.keys():
ver, arch = name.split(".")[-2:]
if ver == qt_ver_str:
arches.append(arch)
return arches return arches
def fetch_extensions(self, version: Version) -> List[str]:
versions_extensions = self.get_versions_extensions(
self.fetch_http(self.archive_id.to_url(), False), self.archive_id.category
)
filtered = filter(
lambda ver_ext: ver_ext[0] == version and ver_ext[1],
versions_extensions,
)
return list(map(lambda ver_ext: ver_ext[1], filtered))
def fetch_versions(self) -> Versions: def fetch_versions(self) -> Versions:
assert len(self.archive_id.extensions) == 1, "Should not be called with multiple extensions"
def filter_by(ver_ext: Tuple[Optional[Version], str]) -> bool: def filter_by(ver_ext: Tuple[Optional[Version], str]) -> bool:
version, extension = ver_ext version, extension = ver_ext
return version and (self.spec is None or version in self.spec) and (self.archive_id.extension == extension) return version and (self.spec is None or version in self.spec) and (self.archive_id.extension == extension)
@@ -622,53 +620,32 @@ class MetadataFactory:
return Versions(iterables) return Versions(iterables)
def fetch_latest_version(self) -> Optional[Version]: def fetch_latest_version(self) -> Optional[Version]:
return self.fetch_versions().latest() def latest_ver_for_ext(ext: str) -> Optional[Version]:
return MetadataFactory(self.archive_id.with_ext(ext)).fetch_latest_version()
if len(self.archive_id.extensions) <= 1:
return self.fetch_versions().latest()
elif "" in self.archive_id.extensions:
return latest_ver_for_ext("")
else:
latest_versions = map(latest_ver_for_ext, self.archive_id.extensions)
return reduce(lambda a, b: a if a is not None and a > b else b, latest_versions)
def fetch_tools(self) -> List[str]: def fetch_tools(self) -> List[str]:
html_doc = self.fetch_http(self.archive_id.to_url(), False) html_doc = self.fetch_http(self.archive_id.to_url(), False)
return list(self.iterate_folders(html_doc, self.base_url, filter_category="tools")) return list(self.iterate_folders(html_doc, self.base_url, filter_category="tools"))
def fetch_tool_modules(self, tool_name: str) -> List[str]: def fetch_tool_modules(self, tool_name: str) -> List[str]:
tool_data = self._fetch_module_metadata(tool_name) tool_data = self._fetch_module_metadata("", tool_name)
return list(tool_data.keys()) return list(tool_data.keys())
def fetch_tool_by_simple_spec(self, tool_name: str, simple_spec: SimpleSpec) -> Optional[Dict[str, str]]: def fetch_tool_by_simple_spec(self, tool_name: str, simple_spec: SimpleSpec) -> Optional[Dict[str, str]]:
# Get data for all the tool modules # Get data for all the tool modules
all_tools_data = self._fetch_module_metadata(tool_name) all_tools_data = self._fetch_module_metadata("", tool_name)
return self.choose_highest_version_in_spec(all_tools_data, simple_spec) return self.choose_highest_version_in_spec(all_tools_data, simple_spec)
def fetch_tool_long_listing(self, tool_name: str) -> ToolData: def fetch_tool_long_listing(self, tool_name: str) -> ToolData:
return ToolData(self._fetch_module_metadata(tool_name)) return ToolData(self._fetch_module_metadata("", tool_name))
def validate_extension(self, qt_ver: Version) -> None:
"""
Checks extension, and raises CliInputError if invalid.
Rules:
1. On Qt6 for Android, an extension for processor architecture is required.
2. On any platform other than Android, or on Qt5, an extension for
processor architecture is forbidden.
3. The "wasm" extension only works on desktop targets for Qt 5.13-5.15, or for 6.2+
"""
if (
self.archive_id.target == "android"
and qt_ver.major == 6
and self.archive_id.extension not in ArchiveId.EXTENSIONS_REQUIRED_ANDROID_QT6
):
raise CliInputError(
"Qt 6 for Android requires one of the following extensions: "
f"{ArchiveId.EXTENSIONS_REQUIRED_ANDROID_QT6}. "
"Please add your extension using the `--extension` flag."
)
if self.archive_id.extension in ArchiveId.EXTENSIONS_REQUIRED_ANDROID_QT6 and (
self.archive_id.target != "android" or qt_ver.major != 6
):
raise CliInputError(f"The extension '{self.archive_id.extension}' is only valid for Qt 6 for Android")
is_in_wasm_range = qt_ver in SimpleSpec(">=5.13,<6") or qt_ver in SimpleSpec(">=6.2.0")
if "wasm" in self.archive_id.extension and (self.archive_id.target != "desktop" or not is_in_wasm_range):
raise CliInputError(
f"The extension '{self.archive_id.extension}' is only available in Qt 5.13-5.15 and 6.2+ on desktop."
)
@staticmethod @staticmethod
def choose_highest_version_in_spec( def choose_highest_version_in_spec(
@@ -797,8 +774,8 @@ class MetadataFactory:
) )
return f"{version.major}{version.minor}{patch}" return f"{version.major}{version.minor}{patch}"
def _fetch_module_metadata(self, folder: str, predicate: Optional[Callable[[Element], bool]] = None): def _fetch_module_metadata(self, extension: str, folder: str, predicate: Optional[Callable[[Element], bool]] = None):
rest_of_url = posixpath.join(self.archive_id.to_url(), folder, "Updates.xml") rest_of_url = posixpath.join(self.archive_id.with_ext(extension).to_url(), folder, "Updates.xml")
xml = self.fetch_http(rest_of_url) xml = self.fetch_http(rest_of_url)
return xml_to_modules( return xml_to_modules(
xml, xml,
@@ -807,11 +784,11 @@ class MetadataFactory:
def fetch_modules(self, version: Version, arch: str) -> List[str]: def fetch_modules(self, version: Version, arch: str) -> List[str]:
"""Returns list of modules""" """Returns list of modules"""
self.validate_extension(version) assert len(self.archive_id.extensions) == 1, "Should not be called with multiple extensions"
qt_ver_str = self._get_qt_version_str(version) qt_ver_str = self._get_qt_version_str(version)
# Example: re.compile(r"^(preview\.)?qt\.(qt5\.)?590\.(.+)$") # Example: re.compile(r"^(preview\.)?qt\.(qt5\.)?590\.(.+)$")
pattern = re.compile(r"^(preview\.)?qt\.(qt" + str(version.major) + r"\.)?" + qt_ver_str + r"\.(.+)$") pattern = re.compile(r"^(preview\.)?qt\.(qt" + str(version.major) + r"\.)?" + qt_ver_str + r"\.(.+)$")
modules_meta = self._fetch_module_metadata(self.archive_id.to_folder(qt_ver_str)) modules_meta = self._fetch_module_metadata(self.archive_id.extension, self.archive_id.to_folder(qt_ver_str))
def to_module_arch(name: str) -> Tuple[Optional[str], Optional[str]]: def to_module_arch(name: str) -> Tuple[Optional[str], Optional[str]]:
_match = pattern.match(name) _match = pattern.match(name)
@@ -834,7 +811,7 @@ class MetadataFactory:
def fetch_long_modules(self, version: Version, arch: str) -> ModuleData: def fetch_long_modules(self, version: Version, arch: str) -> ModuleData:
"""Returns long listing of modules""" """Returns long listing of modules"""
self.validate_extension(version) assert len(self.archive_id.extensions) == 1, "Should not be called with multiple extensions"
qt_ver_str = self._get_qt_version_str(version) qt_ver_str = self._get_qt_version_str(version)
# Example: re.compile(r"^(preview\.)?qt\.(qt5\.)?590(\.addons)?\.(?P<module>[^.]+)\.gcc_64$") # Example: re.compile(r"^(preview\.)?qt\.(qt5\.)?590(\.addons)?\.(?P<module>[^.]+)\.gcc_64$")
pattern = re.compile( pattern = re.compile(
@@ -851,7 +828,9 @@ class MetadataFactory:
name_node = element.find("Name") name_node = element.find("Name")
return bool(name_node is not None) and bool(pattern.match(str(name_node.text))) return bool(name_node is not None) and bool(pattern.match(str(name_node.text)))
modules_meta = self._fetch_module_metadata(self.archive_id.to_folder(qt_ver_str), matches_arch) modules_meta = self._fetch_module_metadata(
self.archive_id.extension, self.archive_id.to_folder(qt_ver_str), matches_arch
)
m = {pattern.match(key).group("module"): value for key, value in modules_meta.items()} m = {pattern.match(key).group("module"): value for key, value in modules_meta.items()}
return ModuleData(m) return ModuleData(m)
@@ -861,9 +840,9 @@ class MetadataFactory:
assert ( assert (
cmd_type in ("doc", "examples") and self.archive_id.target == "desktop" cmd_type in ("doc", "examples") and self.archive_id.target == "desktop"
), "Internal misuse of fetch_modules_sde" ), "Internal misuse of fetch_modules_sde"
self.validate_extension(version) assert len(self.archive_id.extensions) == 1, "Should not be called with multiple extensions"
qt_ver_str = self._get_qt_version_str(version) qt_ver_str = self._get_qt_version_str(version)
modules_meta = self._fetch_module_metadata(self.archive_id.to_folder(qt_ver_str)) modules_meta = self._fetch_module_metadata(self.archive_id.extension, self.archive_id.to_folder(qt_ver_str))
# pattern: Match all names "qt.qt5.12345.doc.(\w+) # pattern: Match all names "qt.qt5.12345.doc.(\w+)
pattern = re.compile(r"^qt\.(qt" + str(version.major) + r"\.)?" + qt_ver_str + r"\." + cmd_type + r"\.(.+)$") pattern = re.compile(r"^qt\.(qt" + str(version.major) + r"\.)?" + qt_ver_str + r"\." + cmd_type + r"\.(.+)$")
@@ -879,9 +858,11 @@ class MetadataFactory:
assert ( assert (
cmd_type in ("src", "doc", "examples") and self.archive_id.target == "desktop" cmd_type in ("src", "doc", "examples") and self.archive_id.target == "desktop"
), "Internal misuse of fetch_archives_sde" ), "Internal misuse of fetch_archives_sde"
assert len(self.archive_id.extensions) == 1, "Should not be called with multiple extensions"
return self.fetch_archives(version, cmd_type, []) return self.fetch_archives(version, cmd_type, [])
def fetch_archives(self, version: Version, arch: str, modules: List[str]) -> List[str]: def fetch_archives(self, version: Version, arch: str, modules: List[str]) -> List[str]:
assert len(self.archive_id.extensions) == 1, "Should not be called with multiple extensions"
qt_version_str = self._get_qt_version_str(version) qt_version_str = self._get_qt_version_str(version)
nonempty = MetadataFactory._has_nonempty_downloads nonempty = MetadataFactory._has_nonempty_downloads
@@ -899,7 +880,9 @@ class MetadataFactory:
predicate = no_modules if not modules else all_modules if "all" in modules else specify_modules predicate = no_modules if not modules else all_modules if "all" in modules else specify_modules
try: try:
mod_metadata = self._fetch_module_metadata(self.archive_id.to_folder(qt_version_str), predicate=predicate) mod_metadata = self._fetch_module_metadata(
self.archive_id.extension, self.archive_id.to_folder(qt_version_str), predicate=predicate
)
except (AttributeError,) as e: except (AttributeError,) as e:
raise ArchiveListError(f"Downloaded metadata is corrupted. {e}") from e raise ArchiveListError(f"Downloaded metadata is corrupted. {e}") from e
@@ -941,8 +924,6 @@ def suggested_follow_up(meta: MetadataFactory) -> List[str]:
base_cmd = "aqt {0} {1.host} {1.target}".format(list_cmd, meta.archive_id) base_cmd = "aqt {0} {1.host} {1.target}".format(list_cmd, meta.archive_id)
versions_msg = f"Please use '{base_cmd}' to show versions of Qt available." versions_msg = f"Please use '{base_cmd}' to show versions of Qt available."
arches_msg = f"Please use '{base_cmd} --arch <QT_VERSION>' to show architectures available." arches_msg = f"Please use '{base_cmd} --arch <QT_VERSION>' to show architectures available."
if meta.archive_id.extension:
msg.append(f"Please use '{base_cmd} --extensions <QT_VERSION>' to list valid extensions.")
if meta.archive_id.is_tools() and meta.request_type == "tool variant names": if meta.archive_id.is_tools() and meta.request_type == "tool variant names":
msg.append(f"Please use '{base_cmd}' to check what tools are available.") msg.append(f"Please use '{base_cmd}' to check what tools are available.")

View File

@@ -46,7 +46,7 @@ def iter_archive_ids(
use_targets = ArchiveId.TARGETS_FOR_HOST[host] use_targets = ArchiveId.TARGETS_FOR_HOST[host]
for target in use_targets: for target in use_targets:
for ext in iter_extensions(): for ext in iter_extensions():
yield ArchiveId(category, host, target, ext) yield ArchiveId(category, host, target, {ext})
def iter_arches() -> Generator[dict, None, None]: def iter_arches() -> Generator[dict, None, None]:

View File

@@ -0,0 +1,18 @@
{
"architectures": [
"wasm_32"
],
"modules_by_arch": {
"wasm_32": [
"qtcharts",
"qtdatavis3d",
"qtlottie",
"qtnetworkauth",
"qtpurchasing",
"qtquicktimeline",
"qtscript",
"qtvirtualkeyboard",
"qtwebplugin"
]
}
}

View File

@@ -0,0 +1,291 @@
<Updates>
<ApplicationName>{AnyApplication}</ApplicationName>
<ApplicationVersion>1.0.0</ApplicationVersion>
<Checksum>true</Checksum>
<PackageUpdate>
<Name>qt.qt5.5140.qtcharts</Name>
<DisplayName>Qt Charts</DisplayName>
<Description>The Qt Charts API lets you easily create interactive and dynamic 2D charts using C++ and/or Qt Quick.&lt;br>&lt;br>This component is available under commercial licenses from The Qt Company, or under GPL v3. For open source use, please note the additional requirements compared to LGPL v3.</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Default>false</Default>
<SortingPriority>89</SortingPriority>
<Dependencies>qt.qt5.5140.doc.qtcharts, qt.qt5.5140.examples.qtcharts</Dependencies>
<AutoDependOn/>
<Script>installscript.qs</Script>
<DownloadableArchives/>
<UpdateFile CompressedSize="0" OS="Any" UncompressedSize="0"/>
<SHA1>6c5eeb7879b8d08041fc9d1b3ec2c4a83b956f75</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtcharts.wasm_32</Name>
<DisplayName>Qt Charts for WebAssembly</DisplayName>
<Description/>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<AutoDependOn>qt.qt5.5140.qtcharts, qt.qt5.5140.wasm_32</AutoDependOn>
<Dependencies>qt.qt5.5140.wasm_32</Dependencies>
<Virtual>true</Virtual>
<Script>installscript.qs</Script>
<SortingPriority/>
<DownloadableArchives>qtcharts-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z</DownloadableArchives>
<UpdateFile CompressedSize="2491642" OS="Any" UncompressedSize="9011562"/>
<SHA1>826c45ce5c2ead4fb2b98fcaf037f41f30e603bc</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtdatavis3d</Name>
<DisplayName>Qt Data Visualization</DisplayName>
<Description>Qt Data Visualization is a module which provides a way to visualize data in 3D. There are C++ classes and QML types for displaying bar graphs, scatter graphs, surface graphs and ways of manipulating the 3D scene. In addition, the graphs are fully customizable with different themes.&lt;br>&lt;br>This component is available under commercial licenses from The Qt Company, or under GPL v3. For open source use, please note the additional requirements compared to LGPL v3.</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Default>false</Default>
<SortingPriority>88</SortingPriority>
<Dependencies>qt.qt5.5140.doc.qtdatavis3d, qt.qt5.5140.examples.qtdatavis3d</Dependencies>
<AutoDependOn/>
<Script>installscript.qs</Script>
<DownloadableArchives/>
<UpdateFile CompressedSize="0" OS="Any" UncompressedSize="0"/>
<SHA1>00897be97807dba6650c13994b6d1288a7f3c7de</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtdatavis3d.wasm_32</Name>
<DisplayName>Qt Data Visualization for WebAssembly</DisplayName>
<Description/>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<AutoDependOn>qt.qt5.5140.qtdatavis3d, qt.qt5.5140.wasm_32</AutoDependOn>
<Dependencies>qt.qt5.5140.wasm_32</Dependencies>
<Virtual>true</Virtual>
<Script>installscript.qs</Script>
<SortingPriority/>
<DownloadableArchives>qtdatavis3d-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z</DownloadableArchives>
<UpdateFile CompressedSize="2050196" OS="Any" UncompressedSize="7106514"/>
<SHA1>496e2fda0f3408271f6ebe6c880f163ead6be7fa</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtlottie</Name>
<DisplayName>Qt Lottie Animation (Technology Preview)</DisplayName>
<Description>Qt Lottie Animation provides a QML API for rendering graphics and animations that are exported in JSON format by the Bodymovin plugin for Adobe After Effects.&lt;br>&lt;br>This component is available under commercial licenses from The Qt Company, or under GPL v3. For open source use, please note the additional requirements compared to LGPL v3.</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Default>false</Default>
<SortingPriority>88</SortingPriority>
<Dependencies>qt.qt5.5140.doc.qtlottie, qt.qt5.5140.examples.qtlottie</Dependencies>
<AutoDependOn/>
<Script>installscript.qs</Script>
<DownloadableArchives/>
<UpdateFile CompressedSize="0" OS="Any" UncompressedSize="0"/>
<SHA1>12e0a4e0427833e88ef6af4c2b416146764ced84</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtlottie.wasm_32</Name>
<DisplayName>Qt Lottie Animation for WebAssembly</DisplayName>
<Description/>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<AutoDependOn>qt.qt5.5140.qtlottie, qt.qt5.5140.wasm_32</AutoDependOn>
<Dependencies>qt.qt5.5140.wasm_32</Dependencies>
<Virtual>true</Virtual>
<Script>installscript.qs</Script>
<SortingPriority/>
<DownloadableArchives>qtlottie-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z</DownloadableArchives>
<UpdateFile CompressedSize="596645" OS="Any" UncompressedSize="1757999"/>
<SHA1>3d3b8b1ed28bbbbafa20bd7c19b8d23bd5a12b5c</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtnetworkauth</Name>
<DisplayName>Qt Network Authorization</DisplayName>
<Description>Qt Network Authorization is an add-on library that enables Qt applications to use different web authentication systems.</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Default>false</Default>
<SortingPriority>10</SortingPriority>
<Dependencies>qt.qt5.5140.doc.qtnetworkauth, qt.qt5.5140.examples.qtnetworkauth</Dependencies>
<AutoDependOn/>
<Script>installscript.qs</Script>
<DownloadableArchives/>
<UpdateFile CompressedSize="0" OS="Any" UncompressedSize="0"/>
<SHA1>7642c9f02e7bee9e300b56876bf205be22591a54</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtnetworkauth.wasm_32</Name>
<DisplayName>Qt Network Authorization for WebAssembly</DisplayName>
<Description/>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<AutoDependOn>qt.qt5.5140.qtnetworkauth, qt.qt5.5140.wasm_32</AutoDependOn>
<Dependencies>qt.qt5.5140.wasm_32</Dependencies>
<Virtual>true</Virtual>
<Script>installscript.qs</Script>
<SortingPriority/>
<DownloadableArchives>qtnetworkauth-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z</DownloadableArchives>
<UpdateFile CompressedSize="158593" OS="Any" UncompressedSize="448180"/>
<SHA1>69f8e088ec3397f59d9c6f86beed710feadf70ce</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtpurchasing</Name>
<DisplayName>Qt Purchasing</DisplayName>
<Description>Qt Purchasing. Cross-platform APIs for handling in-app purchases on Android, iOS and macOS.</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Default>false</Default>
<SortingPriority>87</SortingPriority>
<Dependencies>qt.qt5.5140.doc.qtpurchasing, qt.qt5.5140.examples.qtpurchasing</Dependencies>
<AutoDependOn/>
<Script>installscript.qs</Script>
<DownloadableArchives/>
<UpdateFile CompressedSize="0" OS="Any" UncompressedSize="0"/>
<SHA1>04aac5a70b3864f5ea89dc0aedcd9a6d6489cc90</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtpurchasing.wasm_32</Name>
<DisplayName>Qt Purchasing for WebAssembly</DisplayName>
<Description/>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<AutoDependOn>qt.qt5.5140.qtpurchasing, qt.qt5.5140.wasm_32</AutoDependOn>
<Dependencies>qt.qt5.5140.wasm_32</Dependencies>
<Virtual>true</Virtual>
<Script>installscript.qs</Script>
<SortingPriority/>
<DownloadableArchives>qtpurchasing-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z</DownloadableArchives>
<UpdateFile CompressedSize="94847" OS="Any" UncompressedSize="331745"/>
<SHA1>594234def34d8b9e66152d024c194827314b7427</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtquicktimeline</Name>
<DisplayName>Qt Quick Timeline </DisplayName>
<Description>The Qt Quick Timeline module enables keyframe-based animations and parameterization. It takes a tooling-friendly approach, and is therefore directly supported by Qt Design Studio and Qt Quick Designer that contain a timeline editor for creating keyframe based animations.&lt;br>&lt;br>This component is available under commercial licenses from The Qt Company, or under GPL v3. For open source use, please note the additional requirements compared to LGPL v3.</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Default>false</Default>
<SortingPriority>0</SortingPriority>
<Dependencies>qt.qt5.5140.doc.qtquicktimeline, qt.qt5.5140.examples.qtquicktimeline</Dependencies>
<AutoDependOn/>
<Script>installscript.qs</Script>
<DownloadableArchives/>
<UpdateFile CompressedSize="0" OS="Any" UncompressedSize="0"/>
<SHA1>0ed2a09e664371f045ffba1ea62230de6a5a09bd</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtquicktimeline.wasm_32</Name>
<DisplayName>Qt Quick Timeline for WebAssembly</DisplayName>
<Description/>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<AutoDependOn>qt.qt5.5140.qtquicktimeline, qt.qt5.5140.wasm_32</AutoDependOn>
<Dependencies>qt.qt5.5140.wasm_32</Dependencies>
<Virtual>true</Virtual>
<Script>installscript.qs</Script>
<SortingPriority/>
<DownloadableArchives>qtquicktimeline-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z</DownloadableArchives>
<UpdateFile CompressedSize="91340" OS="Any" UncompressedSize="238917"/>
<SHA1>fa03bab96695ee6657d7c761af33bc68dbbf9fc5</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtscript</Name>
<DisplayName>Qt Script (Deprecated)</DisplayName>
<Description>Qt Script (Deprecated) Prebuilt Components for Qt 5.14.0.&lt;br>&lt;br>This component contains Third-Party Content licensed under LGPLv2.0. Please note the additional requirements of the license.</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Default>false</Default>
<SortingPriority>9</SortingPriority>
<Dependencies>qt.qt5.5140.doc.qtscript, qt.qt5.5140.examples.qtscript</Dependencies>
<AutoDependOn/>
<Script>installscript.qs</Script>
<DownloadableArchives/>
<UpdateFile CompressedSize="0" OS="Any" UncompressedSize="0"/>
<SHA1>55c913d943ac8c893b0c8cf2295ccabff5ae9a41</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtscript.wasm_32</Name>
<DisplayName>Qt Script for WebAssembly</DisplayName>
<Description/>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<AutoDependOn>qt.qt5.5140.qtscript, qt.qt5.5140.wasm_32</AutoDependOn>
<Dependencies>qt.qt5.5140.wasm_32</Dependencies>
<Virtual>true</Virtual>
<Script>installscript.qs</Script>
<SortingPriority/>
<DownloadableArchives>qtscript-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z</DownloadableArchives>
<UpdateFile CompressedSize="4177683" OS="Any" UncompressedSize="12729104"/>
<SHA1>6951cdbfa4474875de4f6de446a1e2b6f78dd40b</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtvirtualkeyboard</Name>
<DisplayName>Qt Virtual Keyboard</DisplayName>
<Description>The Qt Virtual Keyboard is a Qt Quick virtual keyboard that you can plug in to your platform or application. You can extend it with your own layouts and styles.&lt;br>&lt;br>This component is available under commercial licenses from The Qt Company, or under GPL v3. For open source use, please note the additional requirements compared to LGPL v3.</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Default>false</Default>
<SortingPriority>85</SortingPriority>
<Dependencies>qt.qt5.5140.doc.qtvirtualkeyboard, qt.qt5.5140.examples.qtvirtualkeyboard</Dependencies>
<AutoDependOn/>
<Script>installscript.qs</Script>
<DownloadableArchives/>
<UpdateFile CompressedSize="0" OS="Any" UncompressedSize="0"/>
<SHA1>f20beb1a53b150ed1ff6c9b04fb4717f955cff20</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtvirtualkeyboard.wasm_32</Name>
<DisplayName>Qt Virtual Keyboard for WebAssembly</DisplayName>
<Description/>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<AutoDependOn>qt.qt5.5140.qtvirtualkeyboard, qt.qt5.5140.wasm_32</AutoDependOn>
<Dependencies>qt.qt5.5140.wasm_32</Dependencies>
<Virtual>true</Virtual>
<Script>installscript.qs</Script>
<SortingPriority/>
<DownloadableArchives>qtvirtualkeyboard-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z</DownloadableArchives>
<UpdateFile CompressedSize="3162708" OS="Any" UncompressedSize="8341816"/>
<SHA1>b68ed8d9aabf3465e1ab1e4d90b00540d88c0f0b</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtwebglplugin</Name>
<DisplayName>Qt WebGL Streaming Plugin</DisplayName>
<Description>The Qt WebGL Streaming Plugin is a Qt Platform Abstraction plugin which provides streaming of Qt Quick &amp; Qt OpenGL applications over the network to a WebGL capable browser.&lt;br>&lt;br>This component is available under commercial licenses from The Qt Company, or under GPL v3. For open source use, please note the additional requirements compared to LGPL v3.</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Default>false</Default>
<SortingPriority>10</SortingPriority>
<AutoDependOn/>
<Script>installscript.qs</Script>
<DownloadableArchives/>
<UpdateFile CompressedSize="0" OS="Any" UncompressedSize="0"/>
<SHA1>d6731dfbdd646821d6862d010e1fcaf3177dcaac</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.qtwebglplugin.wasm_32</Name>
<DisplayName>Qt WebGL Streaming Plugin for WebAssembly</DisplayName>
<Description>Qt WebGL Streaming Plugin</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<AutoDependOn>qt.qt5.5140.qtwebglplugin, qt.qt5.5140.wasm_32</AutoDependOn>
<Dependencies>qt.qt5.5140.wasm_32</Dependencies>
<Virtual>true</Virtual>
<Script>installscript.qs</Script>
<SortingPriority/>
<DownloadableArchives>qtwebglplugin-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z</DownloadableArchives>
<UpdateFile CompressedSize="750212" OS="Any" UncompressedSize="2088810"/>
<SHA1>7ffe111181b4a1dd3826c3696b16ee94dc267086</SHA1>
</PackageUpdate>
<PackageUpdate>
<Name>qt.qt5.5140.wasm_32</Name>
<DisplayName>WebAssembly</DisplayName>
<Description>Qt 5.14.0 Prebuilt Components for WebAssembly (Desktop)</Description>
<Version>5.14.0-0-201912110813</Version>
<ReleaseDate>2019-12-11</ReleaseDate>
<Dependencies>qt.tools.qtcreator, qt.qt5.5140.doc, qt.qt5.5140.examples</Dependencies>
<AutoDependOn/>
<Default>false</Default>
<Script>installscript.qs</Script>
<SortingPriority>700</SortingPriority>
<DownloadableArchives>qtbase-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtwebchannel-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtmultimedia-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qttranslations-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtgraphicaleffects-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtsvg-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtdeclarative-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtwebsockets-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtimageformats-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qttools-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtxmlpatterns-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtsensors-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtquickcontrols-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtquickcontrols2-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtwebview-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtscxml-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtgamepad-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtspeech-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, qtremoteobjects-Windows-Windows_10-Mingw73-Windows-WebAssembly-X86_64.7z, x86_64-7.3.0-release-posix-seh-rt_v5-rev0-runtime.7z</DownloadableArchives>
<UpdateFile CompressedSize="78067039" OS="Any" UncompressedSize="280467092"/>
<SHA1>3650fe70aa645834b180b63e27689c55cce884b4</SHA1>
</PackageUpdate>
<MetadataName>2020-08-21-1954_meta.7z</MetadataName>
<SHA1>69ef012916d9974bface5a15406e8acdf9158fa5</SHA1>
</Updates>

View File

@@ -304,6 +304,34 @@ def test_cli_legacy_tool_new_syntax(monkeypatch, capsys, cmd):
assert actual == expected assert actual == expected
@pytest.mark.parametrize(
"cmd, expect_err",
(
(
"list-qt mac --extension wasm",
"WARNING : The parameter 'extension' with value 'wasm' is deprecated "
"and marked for removal in a future version of aqt.\n"
"In the future, please omit this parameter.\n"
"WARNING : The '--extension' flag will be ignored.\n",
),
(
"list-qt mac desktop --extensions 6.2.0",
"WARNING : The parameter 'extensions' with value '6.2.0' is deprecated "
"and marked for removal in a future version of aqt.\n"
"In the future, please omit this parameter.\n"
"WARNING : The '--extensions' flag will always return an empty list, "
"because there are no useful arguments for the '--extension' flag.\n",
),
),
)
def test_cli_list_qt_deprecated_flags(capsys, cmd: str, expect_err: str):
cli = Cli()
cli._setup_settings()
assert 0 == cli.run(cmd.split())
out, err = capsys.readouterr()
assert err == expect_err
# These commands come directly from examples in the legacy documentation # These commands come directly from examples in the legacy documentation
@pytest.mark.parametrize( @pytest.mark.parametrize(
"cmd", "cmd",

View File

@@ -145,42 +145,33 @@ def test_list_versions_tools(monkeypatch, spec_regex, os_name, target, in_file,
tools = MetadataFactory(ArchiveId("tools", os_name, target)).getList() tools = MetadataFactory(ArchiveId("tools", os_name, target)).getList()
assert tools == expected["tools"] assert tools == expected["tools"]
for ext, expected_output in expected["qt"].items(): # Test 'aqt list-qt'
# Test 'aqt list-qt' expected_output = expected["qt"]["qt"]
archive_id = ArchiveId("qt", os_name, target, ext if ext != "qt" else "") archive_id = ArchiveId("qt", os_name, target)
all_versions = MetadataFactory(archive_id).getList() all_versions = MetadataFactory(archive_id).getList()
assert f"{all_versions}" == "\n".join(expected_output)
if len(expected_output) == 0: # Filter for the latest version only
assert not all_versions latest_ver = MetadataFactory(archive_id, is_latest_version=True).getList()
else: assert f"{latest_ver}" == expected_output[-1].split(" ")[-1]
assert f"{all_versions}" == "\n".join(expected_output)
# Filter for the latest version only for row in expected_output:
latest_ver = MetadataFactory(archive_id, is_latest_version=True).getList() spec = SimpleSpec(spec_regex.search(row).group(1))
if len(expected_output) == 0: # Find the latest version for a particular spec
assert not latest_ver latest_ver_for_spec = MetadataFactory(
else: archive_id,
assert f"{latest_ver}" == expected_output[-1].split(" ")[-1] spec=spec,
is_latest_version=True,
).getList()
assert f"{latest_ver_for_spec}" == row.split(" ")[-1]
for row in expected_output: # Find all versions for a particular spec
spec_str = spec_regex.search(row).group(1) all_ver_for_spec = MetadataFactory(
spec = SimpleSpec(spec_str) if not ext.endswith("preview") else SimpleSpec(spec_str + ".0-preview") archive_id,
spec=spec,
# Find the latest version for a particular spec ).getList()
latest_ver_for_spec = MetadataFactory( assert f"{all_ver_for_spec}" == row
archive_id,
spec=spec,
is_latest_version=True,
).getList()
assert f"{latest_ver_for_spec}" == row.split(" ")[-1]
# Find all versions for a particular spec
all_ver_for_spec = MetadataFactory(
archive_id,
spec=spec,
).getList()
assert f"{all_ver_for_spec}" == row
@pytest.mark.parametrize( @pytest.mark.parametrize(
@@ -198,7 +189,7 @@ def test_list_versions_tools(monkeypatch, spec_regex, os_name, target, in_file,
], ],
) )
def test_list_architectures_and_modules(monkeypatch, version: str, extension: str, in_file: str, expect_out_file: str): def test_list_architectures_and_modules(monkeypatch, version: str, extension: str, in_file: str, expect_out_file: str):
archive_id = ArchiveId("qt", "windows", "desktop", extension) archive_id = ArchiveId("qt", "windows", "desktop", {extension})
_xml = (Path(__file__).parent / "data" / in_file).read_text("utf-8") _xml = (Path(__file__).parent / "data" / in_file).read_text("utf-8")
expect = json.loads((Path(__file__).parent / "data" / expect_out_file).read_text("utf-8")) expect = json.loads((Path(__file__).parent / "data" / expect_out_file).read_text("utf-8"))
@@ -241,7 +232,7 @@ def test_list_src_doc_examples_archives(
): ):
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: win_5152_sde_xml_file) monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: win_5152_sde_xml_file)
archive_id = ArchiveId("qt", host, "desktop", "src_doc_examples") archive_id = ArchiveId("qt", host, "desktop", {"src_doc_examples"})
archives = set(MetadataFactory(archive_id).fetch_archives_sde(cmd_type, Version(version))) archives = set(MetadataFactory(archive_id).fetch_archives_sde(cmd_type, Version(version)))
assert archives == expected assert archives == expected
@@ -263,7 +254,7 @@ def test_list_src_doc_examples_modules(
): ):
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: win_5152_sde_xml_file) monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: win_5152_sde_xml_file)
archive_id = ArchiveId("qt", host, "desktop", "src_doc_examples") archive_id = ArchiveId("qt", host, "desktop", {"src_doc_examples"})
modules = set(MetadataFactory(archive_id).fetch_modules_sde(cmd_type, Version(version))) modules = set(MetadataFactory(archive_id).fetch_modules_sde(cmd_type, Version(version)))
assert modules == expected assert modules == expected
@@ -410,17 +401,18 @@ def test_long_qt_modules(monkeypatch, host: str, target: str, version: str, arch
@pytest.fixture @pytest.fixture
def expected_windows_desktop_5140() -> Dict: def expected_windows_desktop_plus_wasm_5140() -> Dict:
xmlexpect = "windows-5140-expect.json" input_filenames = "windows-5140-expect.json", "windows-5140-wasm-expect.json"
return json.loads((Path(__file__).parent / "data" / xmlexpect).read_text("utf-8")) to_join = [json.loads((Path(__file__).parent / "data" / f).read_text("utf-8")) for f in input_filenames]
return {
"architectures": [arch for _dict in to_join for arch in _dict["architectures"]],
"modules_by_arch": {**to_join[0]["modules_by_arch"], **to_join[1]["modules_by_arch"]},
}
@pytest.mark.parametrize( @pytest.mark.parametrize(
"args, expect", "args, expect",
( (
("--extensions latest", {"src_doc_examples"}),
("--spec 5.14 --extensions latest", {"wasm", "src_doc_examples"}),
("--extensions 5.14.0", {"wasm", "src_doc_examples"}),
("--modules latest win64_msvc2017_64", ["modules_by_arch", "win64_msvc2017_64"]), ("--modules latest win64_msvc2017_64", ["modules_by_arch", "win64_msvc2017_64"]),
("--spec 5.14 --modules latest win64_msvc2017_64", ["modules_by_arch", "win64_msvc2017_64"]), ("--spec 5.14 --modules latest win64_msvc2017_64", ["modules_by_arch", "win64_msvc2017_64"]),
("--modules 5.14.0 win32_mingw73", ["modules_by_arch", "win32_mingw73"]), ("--modules 5.14.0 win32_mingw73", ["modules_by_arch", "win32_mingw73"]),
@@ -428,29 +420,23 @@ def expected_windows_desktop_5140() -> Dict:
("--modules 5.14.0 win64_mingw73", ["modules_by_arch", "win64_mingw73"]), ("--modules 5.14.0 win64_mingw73", ["modules_by_arch", "win64_mingw73"]),
("--modules 5.14.0 win64_msvc2015_64", ["modules_by_arch", "win64_msvc2015_64"]), ("--modules 5.14.0 win64_msvc2015_64", ["modules_by_arch", "win64_msvc2015_64"]),
("--modules 5.14.0 win64_msvc2017_64", ["modules_by_arch", "win64_msvc2017_64"]), ("--modules 5.14.0 win64_msvc2017_64", ["modules_by_arch", "win64_msvc2017_64"]),
("--extension wasm --modules latest win64_msvc2017_64", ["modules_by_arch", "win64_msvc2017_64"]),
("--extension wasm --spec 5.14 --modules latest win64_msvc2017_64", ["modules_by_arch", "win64_msvc2017_64"]),
("--extension wasm --modules 5.14.0 win64_msvc2017_64", ["modules_by_arch", "win64_msvc2017_64"]),
("--arch latest", ["architectures"]), ("--arch latest", ["architectures"]),
("--spec 5.14 --arch latest", ["architectures"]), ("--spec 5.14 --arch latest", ["architectures"]),
("--arch 5.14.0", ["architectures"]), ("--arch 5.14.0", ["architectures"]),
("--extension wasm --arch latest", ["architectures"]),
("--extension wasm --spec 5.14 --arch latest", ["architectures"]),
("--extension wasm --arch 5.14.0", ["architectures"]),
), ),
) )
def test_list_qt_cli( def test_list_qt_cli(
monkeypatch, monkeypatch,
capsys, capsys,
expected_windows_desktop_5140: Dict[str, Set[str]], expected_windows_desktop_plus_wasm_5140: Dict[str, Set[str]],
args: str, args: str,
expect: Union[Set[str], List[str]], expect: Union[Set[str], List[str]],
): ):
htmlfile, xmlfile = "windows-desktop.html", "windows-5140-update.xml" htmlfile, xmlfile, wasm_xmlfile = "windows-desktop.html", "windows-5140-update.xml", "windows-5140-wasm-update.xml"
version_string_to_replace = "qt5.5140" version_string_to_replace = "qt5.5140"
if isinstance(expect, list): if isinstance(expect, list):
# In this case, 'expect' is a list of keys to follow to the expected values. # In this case, 'expect' is a list of keys to follow to the expected values.
expected_dict = expected_windows_desktop_5140 expected_dict = expected_windows_desktop_plus_wasm_5140
for key in expect: # Follow the chain of keys to the list of values. for key in expect: # Follow the chain of keys to the list of values.
expected_dict = expected_dict[key] expected_dict = expected_dict[key]
assert isinstance(expected_dict, list) assert isinstance(expected_dict, list)
@@ -464,7 +450,9 @@ def test_list_qt_cli(
if not rest_of_url.endswith("Updates.xml"): if not rest_of_url.endswith("Updates.xml"):
return htmltext return htmltext
xmltext = (Path(__file__).parent / "data" / xmlfile).read_text("utf-8") norm_xmltext = (Path(__file__).parent / "data" / xmlfile).read_text("utf-8")
wasm_xmltext = (Path(__file__).parent / "data" / wasm_xmlfile).read_text("utf-8")
xmltext = wasm_xmltext if rest_of_url.endswith("_wasm/Updates.xml") else norm_xmltext
# If we are serving an Updates.xml, `aqt list` will look for a Qt version number. # If we are serving an Updates.xml, `aqt list` will look for a Qt version number.
# We will replace the version numbers in the file with the requested version. # We will replace the version numbers in the file with the requested version.
match = re.search(r"qt(\d)_(\d+)", rest_of_url) match = re.search(r"qt(\d)_(\d+)", rest_of_url)
@@ -482,6 +470,72 @@ def test_list_qt_cli(
assert output_set == expect_set assert output_set == expect_set
@pytest.mark.parametrize(
"qt_ver_str, expect_set", (("6.2.0", {"android_x86", "android_x86_64", "android_armv7", "android_arm64_v8a"}),)
)
def test_list_android_arches(monkeypatch, capsys, qt_ver_str: str, expect_set: Set[str]):
host, target = "windows", "android"
def _mock_fetch_http(_, rest_of_url, *args, **kwargs: str) -> str:
htmltext = (Path(__file__).parent / "data" / "windows-android.html").read_text("utf-8")
if not rest_of_url.endswith("Updates.xml"):
return htmltext
xmltext = (Path(__file__).parent / "data" / "windows-620-android-armv7-update.xml").read_text("utf-8")
# If we are serving an Updates.xml, `aqt list` will look for a Qt version number.
# We will replace the version numbers in the file with the requested version.
match = re.search(r"qt\d_\d+_(?P<arch>\w+)/Updates\.xml$", rest_of_url)
assert match
return xmltext.replace("armv7", match.group("arch"))
monkeypatch.setattr(MetadataFactory, "fetch_http", _mock_fetch_http)
# Unit test:
archive_id = ArchiveId("qt", host, target, {"x86", "x86_64", "armv7", "arm64_v8a"})
actual_arches = MetadataFactory(archive_id, architectures_ver=qt_ver_str).getList()
assert set(actual_arches) == expect_set
# Integration test:
cli = Cli()
cli.run(["list-qt", host, target, "--arch", qt_ver_str])
out, err = capsys.readouterr()
output_set = set(out.strip().split())
assert output_set == expect_set
@pytest.mark.parametrize(
"host, has_wasm",
(("linux", True), ("windows", False), ("mac", False)),
)
def test_list_desktop_arches_qt5130(monkeypatch, capsys, host: str, has_wasm: bool):
"""Tests the edge case of desktop Qt 5.13.0, in which "wasm_32" is a valid arch for Linux but not win/mac."""
# Generate some mock Updates.xml files. The host mismatch is not important; we just want to check for arch==wasm
updates_xmltext, wasm_updates_xmltext = [
(Path(__file__).parent / "data" / filename).read_text("utf-8").replace("qt5.5140", "qt5.5130")
for filename in ("windows-5140-update.xml", "windows-5140-wasm-update.xml")
]
def _mock_fetch_http(_, rest_of_url, *args, **kwargs: str) -> str:
if rest_of_url == "online/qtsdkrepository/linux_x64/desktop/qt5_5130_wasm/Updates.xml":
return wasm_updates_xmltext
elif rest_of_url.endswith("/desktop/qt5_5130/Updates.xml"):
return updates_xmltext
else:
assert False, f"Fetched an unexpected file at '{rest_of_url}'"
monkeypatch.setattr(MetadataFactory, "fetch_http", _mock_fetch_http)
cli = Cli()
cli.run(["list-qt", host, "desktop", "--arch", "5.13.0"])
out, err = capsys.readouterr()
output_set = set(out.strip().split())
if has_wasm:
assert "wasm_32" in output_set
else:
assert "wasm_32" not in output_set
@pytest.mark.parametrize( @pytest.mark.parametrize(
"cmd, host, expect", "cmd, host, expect",
( (
@@ -569,45 +623,10 @@ def test_list_choose_tool_by_version(simple_spec, expected_name):
assert expected_name is None assert expected_name is None
qt6_android_requires_ext_msg = (
"Qt 6 for Android requires one of the following extensions: "
f"{ArchiveId.EXTENSIONS_REQUIRED_ANDROID_QT6}. "
"Please add your extension using the `--extension` flag."
)
no_arm64_v8_msg = "The extension 'arm64_v8a' is only valid for Qt 6 for Android"
no_wasm_msg = "The extension 'wasm' is only available in Qt 5.13-5.15 and 6.2+ on desktop."
@pytest.mark.parametrize(
"target, ext, version, expected_msg",
(
("android", "", "6.2.0", qt6_android_requires_ext_msg),
("android", "arm64_v8a", "5.13.0", no_arm64_v8_msg),
("desktop", "arm64_v8a", "5.13.0", no_arm64_v8_msg),
("desktop", "arm64_v8a", "6.2.0", no_arm64_v8_msg),
("desktop", "wasm", "5.12.11", no_wasm_msg), # out of range
("desktop", "wasm", "6.1.9", no_wasm_msg), # out of range
("android", "wasm", "5.12.11", no_wasm_msg), # in range, wrong target
("android", "wasm", "5.14.0", no_wasm_msg), # in range, wrong target
("android", "wasm", "6.1.9", qt6_android_requires_ext_msg),
),
)
def test_list_invalid_extensions(capsys, monkeypatch, target, ext, version, expected_msg):
host = "windows"
extension_params = ["--extension", ext] if ext else []
cli = Cli()
cli.run(["list-qt", host, target, *extension_params, "--arch", version])
out, err = capsys.readouterr()
sys.stdout.write(out)
sys.stderr.write(err)
assert expected_msg in err
mac_qt = ArchiveId("qt", "mac", "desktop") mac_qt = ArchiveId("qt", "mac", "desktop")
mac_wasm = ArchiveId("qt", "mac", "desktop", "wasm") mac_wasm = ArchiveId("qt", "mac", "desktop", {"wasm"})
wrong_tool_name_msg = "Please use 'aqt list-tool mac desktop' to check what tools are available." wrong_tool_name_msg = "Please use 'aqt list-tool mac desktop' to check what tools are available."
wrong_qt_version_msg = "Please use 'aqt list-qt mac desktop' to show versions of Qt available." wrong_qt_version_msg = "Please use 'aqt list-qt mac desktop' to show versions of Qt available."
wrong_ext_msg = "Please use 'aqt list-qt mac desktop --extensions <QT_VERSION>' to list valid extensions."
wrong_arch_msg = "Please use 'aqt list-qt mac desktop --arch <QT_VERSION>' to list valid architectures." wrong_arch_msg = "Please use 'aqt list-qt mac desktop --arch <QT_VERSION>' to list valid architectures."
@@ -631,10 +650,6 @@ wrong_arch_msg = "Please use 'aqt list-qt mac desktop --arch <QT_VERSION>' to li
MetadataFactory(mac_qt, modules_query=("1.2.3", "clang_64")), MetadataFactory(mac_qt, modules_query=("1.2.3", "clang_64")),
[wrong_qt_version_msg, wrong_arch_msg], [wrong_qt_version_msg, wrong_arch_msg],
), ),
(
MetadataFactory(mac_qt, extensions_ver="1.2.3"),
[wrong_qt_version_msg],
),
( (
MetadataFactory(mac_qt, archives_query=["1.2.3", "clang_64", "a module", "another module"]), MetadataFactory(mac_qt, archives_query=["1.2.3", "clang_64", "a module", "another module"]),
[ [
@@ -650,35 +665,21 @@ wrong_arch_msg = "Please use 'aqt list-qt mac desktop --arch <QT_VERSION>' to li
"Please use 'aqt list-qt mac desktop --arch <QT_VERSION>' to show architectures available.", "Please use 'aqt list-qt mac desktop --arch <QT_VERSION>' to show architectures available.",
], ],
), ),
(
MetadataFactory(mac_wasm),
[wrong_ext_msg],
),
( (
MetadataFactory(mac_wasm, spec=SimpleSpec("<5.9")), MetadataFactory(mac_wasm, spec=SimpleSpec("<5.9")),
[ ["Please use 'aqt list-qt mac desktop' to check that versions of qt exist within the spec '<5.9'."],
wrong_ext_msg,
"Please use 'aqt list-qt mac desktop' to check that versions of qt exist within the spec '<5.9'.",
],
), ),
( (
MetadataFactory(ArchiveId("tools", "mac", "desktop", "wasm"), tool_name="ifw"), MetadataFactory(ArchiveId("tools", "mac", "desktop", {"wasm"}), tool_name="ifw"),
[ [wrong_tool_name_msg],
"Please use 'aqt list-tool mac desktop --extensions <QT_VERSION>' to list valid extensions.",
wrong_tool_name_msg,
],
), ),
( (
MetadataFactory(mac_wasm, architectures_ver="1.2.3"), MetadataFactory(mac_wasm, architectures_ver="1.2.3"),
[wrong_ext_msg, wrong_qt_version_msg], [wrong_qt_version_msg],
), ),
( (
MetadataFactory(mac_wasm, modules_query=("1.2.3", "clang_64")), MetadataFactory(mac_wasm, modules_query=("1.2.3", "clang_64")),
[wrong_ext_msg, wrong_qt_version_msg, wrong_arch_msg], [wrong_qt_version_msg, wrong_arch_msg],
),
(
MetadataFactory(mac_wasm, extensions_ver="1.2.3"),
[wrong_ext_msg, wrong_qt_version_msg],
), ),
), ),
) )
@@ -688,12 +689,12 @@ def test_suggested_follow_up(meta: MetadataFactory, expected_message: str):
def test_format_suggested_follow_up(): def test_format_suggested_follow_up():
suggestions = [ suggestions = [
"Please use 'aqt list-tool mac desktop --extensions <QT_VERSION>' to list valid extensions.", "Please use 'aqt list-tool mac desktop --modules <QT_VERSION>' to list valid modules.",
"Please use 'aqt list-tool mac desktop' to check what tools are available.", "Please use 'aqt list-tool mac desktop' to check what tools are available.",
] ]
expected = ( expected = (
"==============================Suggested follow-up:==============================\n" "==============================Suggested follow-up:==============================\n"
"* Please use 'aqt list-tool mac desktop --extensions <QT_VERSION>' to list valid extensions.\n" "* Please use 'aqt list-tool mac desktop --modules <QT_VERSION>' to list valid modules.\n"
"* Please use 'aqt list-tool mac desktop' to check what tools are available." "* Please use 'aqt list-tool mac desktop' to check what tools are available."
) )
ex = AqtException("msg", suggested_action=suggestions) ex = AqtException("msg", suggested_action=suggestions)
@@ -713,13 +714,13 @@ def test_format_suggested_follow_up_empty():
"qt/mac/desktop with spec 5.42", "qt/mac/desktop with spec 5.42",
), ),
( (
MetadataFactory(ArchiveId("qt", "mac", "desktop", "wasm"), spec=SimpleSpec("5.42")), MetadataFactory(ArchiveId("qt", "mac", "desktop", {"wasm"}), spec=SimpleSpec("5.42")),
"qt/mac/desktop/wasm with spec 5.42", "qt/mac/desktop/{'wasm'} with spec 5.42",
), ),
(MetadataFactory(ArchiveId("qt", "mac", "desktop")), "qt/mac/desktop"), (MetadataFactory(ArchiveId("qt", "mac", "desktop")), "qt/mac/desktop"),
( (
MetadataFactory(ArchiveId("qt", "mac", "desktop", "wasm")), MetadataFactory(ArchiveId("qt", "mac", "desktop", {"wasm"})),
"qt/mac/desktop/wasm", "qt/mac/desktop/{'wasm'}",
), ),
), ),
) )
@@ -978,7 +979,6 @@ def test_show_list_bad_connection(monkeypatch, capsys, exception_class, error_ms
assert format(error.value) == ( assert format(error.value) == (
f"{error_msg}\n" f"{error_msg}\n"
"==============================Suggested follow-up:==============================\n" "==============================Suggested follow-up:==============================\n"
"* Please use 'aqt list-qt mac desktop --extensions <QT_VERSION>' to list valid extensions.\n"
"* Please use 'aqt list-qt mac desktop' to check that versions of qt exist within the spec '<5.9'." "* Please use 'aqt list-qt mac desktop' to check that versions of qt exist within the spec '<5.9'."
) )