mirror of
https://github.com/miurahr/aqtinstall.git
synced 2025-12-17 20:54:38 +03:00
Merge branch 'miurahr:master' into use-official-doc
This commit is contained in:
@@ -60,6 +60,7 @@ class QtPackageManager:
|
|||||||
self.packages: List[QtPackageInfo] = []
|
self.packages: List[QtPackageInfo] = []
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
|
self.logger = getLogger("aqt.commercial")
|
||||||
|
|
||||||
def _get_cache_dir(self) -> Path:
|
def _get_cache_dir(self) -> Path:
|
||||||
"""Create and return cache directory path."""
|
"""Create and return cache directory path."""
|
||||||
@@ -141,6 +142,7 @@ class QtPackageManager:
|
|||||||
cmd.append(base_package)
|
cmd.append(base_package)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
self.logger.info(f"Running: {cmd}")
|
||||||
output = safely_run_save_output(cmd, Settings.qt_installer_timeout)
|
output = safely_run_save_output(cmd, Settings.qt_installer_timeout)
|
||||||
|
|
||||||
# Handle both string and CompletedProcess outputs
|
# Handle both string and CompletedProcess outputs
|
||||||
@@ -156,8 +158,7 @@ class QtPackageManager:
|
|||||||
self._save_to_cache()
|
self._save_to_cache()
|
||||||
else:
|
else:
|
||||||
# Log the actual output for debugging
|
# Log the actual output for debugging
|
||||||
logger = getLogger("aqt.helper")
|
self.logger.debug(f"Installer output: {output_text}")
|
||||||
logger.debug(f"Installer output: {output_text}")
|
|
||||||
raise RuntimeError("Failed to find package information in installer output")
|
raise RuntimeError("Failed to find package information in installer output")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -217,6 +218,7 @@ class CommercialInstaller:
|
|||||||
override: Optional[List[str]] = None,
|
override: Optional[List[str]] = None,
|
||||||
modules: Optional[List[str]] = None,
|
modules: Optional[List[str]] = None,
|
||||||
no_unattended: bool = False,
|
no_unattended: bool = False,
|
||||||
|
dry_run: bool = False,
|
||||||
):
|
):
|
||||||
self.override = override
|
self.override = override
|
||||||
self.target = target
|
self.target = target
|
||||||
@@ -227,6 +229,7 @@ class CommercialInstaller:
|
|||||||
self.base_url = base_url
|
self.base_url = base_url
|
||||||
self.modules = modules
|
self.modules = modules
|
||||||
self.no_unattended = no_unattended
|
self.no_unattended = no_unattended
|
||||||
|
self.dry_run = dry_run
|
||||||
|
|
||||||
# Extract credentials from override if present
|
# Extract credentials from override if present
|
||||||
if override:
|
if override:
|
||||||
@@ -330,13 +333,12 @@ class CommercialInstaller:
|
|||||||
raise DiskAccessNotPermitted(f"Failed to remove existing version directory {version_dir}: {str(e)}")
|
raise DiskAccessNotPermitted(f"Failed to remove existing version directory {version_dir}: {str(e)}")
|
||||||
|
|
||||||
# Setup temp directory
|
# Setup temp directory
|
||||||
import shutil
|
|
||||||
|
|
||||||
temp_dir = Settings.qt_installer_temp_path
|
temp_dir = Settings.qt_installer_temp_path
|
||||||
temp_path = Path(temp_dir)
|
temp_path = Path(temp_dir)
|
||||||
if temp_path.exists():
|
if not temp_path.exists():
|
||||||
shutil.rmtree(temp_dir)
|
temp_path.mkdir(parents=True, exist_ok=True)
|
||||||
temp_path.mkdir(parents=True, exist_ok=True)
|
else:
|
||||||
|
Settings.qt_installer_cleanup()
|
||||||
installer_path = temp_path / self._installer_filename
|
installer_path = temp_path / self._installer_filename
|
||||||
|
|
||||||
self.logger.info(f"Downloading Qt installer to {installer_path}")
|
self.logger.info(f"Downloading Qt installer to {installer_path}")
|
||||||
@@ -371,8 +373,16 @@ class CommercialInstaller:
|
|||||||
install_cmd = self.package_manager.get_install_command(self.modules, temp_dir)
|
install_cmd = self.package_manager.get_install_command(self.modules, temp_dir)
|
||||||
cmd = [*base_cmd, *install_cmd]
|
cmd = [*base_cmd, *install_cmd]
|
||||||
|
|
||||||
self.logger.info(f"Running: {cmd}")
|
log_cmd = cmd.copy()
|
||||||
safely_run(cmd, Settings.qt_installer_timeout)
|
for i in range(len(log_cmd) - 1):
|
||||||
|
if log_cmd[i] == "--email" or log_cmd[i] == "--pw":
|
||||||
|
log_cmd[i + 1] = "***"
|
||||||
|
|
||||||
|
if not self.dry_run:
|
||||||
|
self.logger.info(f"Running: {log_cmd}")
|
||||||
|
safely_run(cmd, Settings.qt_installer_timeout)
|
||||||
|
else:
|
||||||
|
self.logger.info(f"Would run: {log_cmd}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Installation failed: {str(e)}")
|
self.logger.error(f"Installation failed: {str(e)}")
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import binascii
|
|||||||
import hashlib
|
import hashlib
|
||||||
import logging.config
|
import logging.config
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import posixpath
|
import posixpath
|
||||||
import secrets
|
import secrets
|
||||||
import shutil
|
import shutil
|
||||||
@@ -40,6 +41,7 @@ from xml.etree.ElementTree import Element
|
|||||||
import humanize
|
import humanize
|
||||||
import requests
|
import requests
|
||||||
import requests.adapters
|
import requests.adapters
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
from defusedxml import ElementTree
|
from defusedxml import ElementTree
|
||||||
|
|
||||||
from aqt.exceptions import (
|
from aqt.exceptions import (
|
||||||
@@ -62,6 +64,23 @@ def get_os_name() -> str:
|
|||||||
raise ValueError(f"Unsupported operating system: {system}")
|
raise ValueError(f"Unsupported operating system: {system}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_os_arch() -> str:
|
||||||
|
"""
|
||||||
|
Returns a simplified os-arch string for the current system
|
||||||
|
"""
|
||||||
|
os_name = get_os_name()
|
||||||
|
|
||||||
|
machine = platform.machine().lower()
|
||||||
|
if machine in ["x86_64", "amd64"]:
|
||||||
|
arch = "x64"
|
||||||
|
elif machine in ["arm64", "aarch64"]:
|
||||||
|
arch = "arm64"
|
||||||
|
else:
|
||||||
|
arch = "x64" # Default to x64 for unknown architectures
|
||||||
|
|
||||||
|
return f"{os_name}-{arch}"
|
||||||
|
|
||||||
|
|
||||||
def get_qt_local_folder_path() -> Path:
|
def get_qt_local_folder_path() -> Path:
|
||||||
os_name = get_os_name()
|
os_name = get_os_name()
|
||||||
if os_name == "windows":
|
if os_name == "windows":
|
||||||
@@ -76,13 +95,59 @@ def get_qt_account_path() -> Path:
|
|||||||
return get_qt_local_folder_path() / "qtaccount.ini"
|
return get_qt_local_folder_path() / "qtaccount.ini"
|
||||||
|
|
||||||
|
|
||||||
|
def get_qt_installers() -> dict[str, str]:
|
||||||
|
"""
|
||||||
|
Extracts Qt installer information from {Settings.baseurl}/official_releases/online_installers/
|
||||||
|
Maps OS types and architectures to their respective installer filenames
|
||||||
|
Returns:
|
||||||
|
dict: Mapping of OS identifiers to installer filenames with appropriate aliases
|
||||||
|
"""
|
||||||
|
url = f"{Settings.baseurl}/official_releases/online_installers/"
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, timeout=Settings.response_timeout)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
soup = BeautifulSoup(response.text, "html.parser")
|
||||||
|
|
||||||
|
installers = {}
|
||||||
|
|
||||||
|
os_types = ["windows", "linux", "mac"]
|
||||||
|
|
||||||
|
for link in soup.find_all("a"):
|
||||||
|
filename = link.text.strip()
|
||||||
|
|
||||||
|
if "Parent Directory" in filename or not any(ext in filename.lower() for ext in [".exe", ".dmg", ".run"]):
|
||||||
|
continue
|
||||||
|
|
||||||
|
for os_type in os_types:
|
||||||
|
if os_type.lower() in filename.lower():
|
||||||
|
# Found an OS match, now look for architecture
|
||||||
|
if "arm64" in filename.lower():
|
||||||
|
installers[f"{os_type}-arm64"] = filename
|
||||||
|
elif "x64" in filename.lower():
|
||||||
|
installers[f"{os_type}-x64"] = filename
|
||||||
|
# Also add generic OS entry for x64 variants of Windows and Linux
|
||||||
|
if os_type in ["windows", "linux"]:
|
||||||
|
installers[os_type] = filename
|
||||||
|
else:
|
||||||
|
# Handle case with no explicit architecture
|
||||||
|
# Most likely for macOS which might just say "mac" without arch
|
||||||
|
installers[os_type] = filename
|
||||||
|
|
||||||
|
return installers
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Error fetching installer data: {e}")
|
||||||
|
return {}
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Unexpected error processing installer data: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def get_qt_installer_name() -> str:
|
def get_qt_installer_name() -> str:
|
||||||
installer_dict = {
|
installer_dict = get_qt_installers()
|
||||||
"windows": "qt-unified-windows-x64-online.exe",
|
return installer_dict[get_os_arch()]
|
||||||
"mac": "qt-unified-mac-x64-online.dmg",
|
|
||||||
"linux": "qt-unified-linux-x64-online.run",
|
|
||||||
}
|
|
||||||
return installer_dict[get_os_name()]
|
|
||||||
|
|
||||||
|
|
||||||
def get_qt_installer_path() -> Path:
|
def get_qt_installer_path() -> Path:
|
||||||
@@ -427,8 +492,13 @@ class SettingsClass:
|
|||||||
|
|
||||||
logging.info(f"Cache folder: {self.qt_installer_cache_path}")
|
logging.info(f"Cache folder: {self.qt_installer_cache_path}")
|
||||||
logging.info(f"Temp folder: {self.qt_installer_temp_path}")
|
logging.info(f"Temp folder: {self.qt_installer_temp_path}")
|
||||||
if Path(self.qt_installer_temp_path).exists():
|
|
||||||
shutil.rmtree(self.qt_installer_temp_path)
|
temp_dir = self.qt_installer_temp_path
|
||||||
|
temp_path = Path(temp_dir)
|
||||||
|
if not temp_path.exists():
|
||||||
|
temp_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
else:
|
||||||
|
self.qt_installer_cleanup()
|
||||||
|
|
||||||
def _get_config(self) -> ConfigParser:
|
def _get_config(self) -> ConfigParser:
|
||||||
"""Safe getter for config that ensures it's initialized."""
|
"""Safe getter for config that ensures it's initialized."""
|
||||||
@@ -616,10 +686,12 @@ class SettingsClass:
|
|||||||
return self._get_config().getboolean("qtofficial", "unattended", fallback=True)
|
return self._get_config().getboolean("qtofficial", "unattended", fallback=True)
|
||||||
|
|
||||||
def qt_installer_cleanup(self) -> None:
|
def qt_installer_cleanup(self) -> None:
|
||||||
"""Control whether to use unattended installation flags."""
|
"""Clean tmp folder."""
|
||||||
import shutil
|
for item in Path(self.qt_installer_temp_path).iterdir():
|
||||||
|
if item.is_dir():
|
||||||
shutil.rmtree(self.qt_installer_temp_path)
|
shutil.rmtree(item)
|
||||||
|
else:
|
||||||
|
item.unlink()
|
||||||
|
|
||||||
|
|
||||||
Settings = SettingsClass()
|
Settings = SettingsClass()
|
||||||
@@ -647,7 +719,7 @@ def safely_run_save_output(cmd: List[str], timeout: int) -> Any:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def extract_auth(args: List[str]) -> Tuple[str | None, str | None, List[str] | None]:
|
def extract_auth(args: List[str]) -> Tuple[Union[str, None], Union[str, None], Union[List[str], None]]:
|
||||||
username = None
|
username = None
|
||||||
password = None
|
password = None
|
||||||
i = 0
|
i = 0
|
||||||
|
|||||||
@@ -368,6 +368,62 @@ class Cli:
|
|||||||
else:
|
else:
|
||||||
qt_version = args.qt_version
|
qt_version = args.qt_version
|
||||||
Cli._validate_version_str(qt_version)
|
Cli._validate_version_str(qt_version)
|
||||||
|
|
||||||
|
if hasattr(args, "use_official_installer") and args.use_official_installer is not None:
|
||||||
|
|
||||||
|
if len(args.use_official_installer) not in [0, 2]:
|
||||||
|
raise CliInputError(
|
||||||
|
"When providing arguments to --use-official-installer, exactly 2 arguments are required: "
|
||||||
|
"--use-official-installer email password"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info("Using official Qt installer")
|
||||||
|
|
||||||
|
commercial_args = InstallArgParser()
|
||||||
|
|
||||||
|
# Core parameters required by install-qt-official
|
||||||
|
commercial_args.target = args.target
|
||||||
|
commercial_args.arch = self._set_arch(
|
||||||
|
args.arch, args.host, args.target, getattr(args, "qt_version", getattr(args, "qt_version_spec", ""))
|
||||||
|
)
|
||||||
|
|
||||||
|
commercial_args.version = qt_version
|
||||||
|
|
||||||
|
email = None
|
||||||
|
password = None
|
||||||
|
if len(args.use_official_installer) == 2:
|
||||||
|
email, password = args.use_official_installer
|
||||||
|
self.logger.info("Using credentials provided with --use-official-installer")
|
||||||
|
|
||||||
|
# Optional parameters
|
||||||
|
commercial_args.email = email or getattr(args, "email", None)
|
||||||
|
commercial_args.pw = password or getattr(args, "pw", None)
|
||||||
|
commercial_args.outputdir = args.outputdir
|
||||||
|
commercial_args.modules = args.modules
|
||||||
|
commercial_args.base = getattr(args, "base", None)
|
||||||
|
commercial_args.dry_run = getattr(args, "dry_run", False)
|
||||||
|
commercial_args.override = None
|
||||||
|
|
||||||
|
ignored_options = []
|
||||||
|
if getattr(args, "noarchives", False):
|
||||||
|
ignored_options.append("--noarchives")
|
||||||
|
if getattr(args, "autodesktop", False):
|
||||||
|
ignored_options.append("--autodesktop")
|
||||||
|
if getattr(args, "archives", None):
|
||||||
|
ignored_options.append("--archives")
|
||||||
|
if getattr(args, "timeout", False):
|
||||||
|
ignored_options.append("--timeout")
|
||||||
|
if getattr(args, "keep", False):
|
||||||
|
ignored_options.append("--keep")
|
||||||
|
if getattr(args, "archive_dest", False):
|
||||||
|
ignored_options.append("--archive_dest")
|
||||||
|
|
||||||
|
if ignored_options:
|
||||||
|
self.logger.warning("Options ignored because you requested the official installer:")
|
||||||
|
self.logger.warning(", ".join(ignored_options))
|
||||||
|
|
||||||
|
return self.run_install_qt_commercial(commercial_args, print_version=False)
|
||||||
|
|
||||||
archives = args.archives
|
archives = args.archives
|
||||||
if args.noarchives:
|
if args.noarchives:
|
||||||
if modules is None:
|
if modules is None:
|
||||||
@@ -683,9 +739,10 @@ class Cli:
|
|||||||
)
|
)
|
||||||
show_list(meta)
|
show_list(meta)
|
||||||
|
|
||||||
def run_install_qt_commercial(self, args: InstallArgParser) -> None:
|
def run_install_qt_commercial(self, args: InstallArgParser, print_version: Optional[bool] = True) -> None:
|
||||||
"""Execute commercial Qt installation"""
|
"""Execute commercial Qt installation"""
|
||||||
self.show_aqt_version()
|
if print_version:
|
||||||
|
self.show_aqt_version()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if args.override:
|
if args.override:
|
||||||
@@ -700,6 +757,7 @@ class Cli:
|
|||||||
no_unattended=not Settings.qt_installer_unattended,
|
no_unattended=not Settings.qt_installer_unattended,
|
||||||
username=username or args.email,
|
username=username or args.email,
|
||||||
password=password or args.pw,
|
password=password or args.pw,
|
||||||
|
dry_run=args.dry_run,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if not all([args.target, args.arch, args.version]):
|
if not all([args.target, args.arch, args.version]):
|
||||||
@@ -716,12 +774,13 @@ class Cli:
|
|||||||
base_url=args.base if args.base is not None else Settings.baseurl,
|
base_url=args.base if args.base is not None else Settings.baseurl,
|
||||||
no_unattended=not Settings.qt_installer_unattended,
|
no_unattended=not Settings.qt_installer_unattended,
|
||||||
modules=args.modules,
|
modules=args.modules,
|
||||||
|
dry_run=args.dry_run,
|
||||||
)
|
)
|
||||||
|
|
||||||
commercial_installer.install()
|
commercial_installer.install()
|
||||||
Settings.qt_installer_cleanup()
|
Settings.qt_installer_cleanup()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error installing official installer {str(e)}")
|
self.logger.error(f"Error installing official installer: {str(e)}")
|
||||||
finally:
|
finally:
|
||||||
self.logger.info("Done")
|
self.logger.info("Done")
|
||||||
|
|
||||||
@@ -793,6 +852,16 @@ class Cli:
|
|||||||
"required. When enabled, this option installs the required desktop version automatically. "
|
"required. When enabled, this option installs the required desktop version automatically. "
|
||||||
"It has no effect when the desktop installation is not required.",
|
"It has no effect when the desktop installation is not required.",
|
||||||
)
|
)
|
||||||
|
install_qt_parser.add_argument(
|
||||||
|
"--use-official-installer",
|
||||||
|
nargs="*",
|
||||||
|
default=None,
|
||||||
|
metavar=("EMAIL", "PASSWORD"),
|
||||||
|
help="Use the official Qt installer for installation instead of the aqt downloader. "
|
||||||
|
"Can be used without arguments or with email and password: --use-official-installer email password. "
|
||||||
|
"This redirects to install-qt-official. "
|
||||||
|
"Arguments not compatible with the official installer will be ignored.",
|
||||||
|
)
|
||||||
|
|
||||||
def _set_install_tool_parser(self, install_tool_parser):
|
def _set_install_tool_parser(self, install_tool_parser):
|
||||||
install_tool_parser.set_defaults(func=self.run_install_tool)
|
install_tool_parser.set_defaults(func=self.run_install_tool)
|
||||||
@@ -889,14 +958,12 @@ class Cli:
|
|||||||
self.show_aqt_version()
|
self.show_aqt_version()
|
||||||
|
|
||||||
# Create temporary directory for installer
|
# Create temporary directory for installer
|
||||||
import shutil
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
temp_dir = Settings.qt_installer_temp_path
|
temp_dir = Settings.qt_installer_temp_path
|
||||||
temp_path = Path(temp_dir)
|
temp_path = Path(temp_dir)
|
||||||
if temp_path.exists():
|
if not temp_path.exists():
|
||||||
shutil.rmtree(temp_dir)
|
temp_path.mkdir(parents=True, exist_ok=True)
|
||||||
temp_path.mkdir(parents=True, exist_ok=True)
|
else:
|
||||||
|
Settings.qt_installer_cleanup()
|
||||||
|
|
||||||
# Get installer based on OS
|
# Get installer based on OS
|
||||||
installer_filename = get_qt_installer_name()
|
installer_filename = get_qt_installer_name()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ All contributors, listed alphabetically, are:
|
|||||||
|
|
||||||
* Adrian Eddy (fix the case of Android 6.7.0)
|
* Adrian Eddy (fix the case of Android 6.7.0)
|
||||||
* Alberto Mardegan(ignore_hash option)
|
* Alberto Mardegan(ignore_hash option)
|
||||||
|
* Alexandre @Kidev Poumaroux (official installer option, WASM fixes for Qt6.7.x)
|
||||||
* Andrew Wason (support arm64)
|
* Andrew Wason (support arm64)
|
||||||
* Andrei Yankovich (tools ifw installation)
|
* Andrei Yankovich (tools ifw installation)
|
||||||
* Aurélien Gâteau (patching to qmake)
|
* Aurélien Gâteau (patching to qmake)
|
||||||
@@ -51,4 +52,4 @@ All contributors, listed alphabetically, are:
|
|||||||
* @ypnos (Documents)
|
* @ypnos (Documents)
|
||||||
|
|
||||||
and many other participants and contributors.
|
and many other participants and contributors.
|
||||||
If you find a missing name to record, please feel free to tell me.
|
If you find a missing name to record, please feel free to tell me.
|
||||||
|
|||||||
@@ -44,50 +44,6 @@ Options
|
|||||||
* If none are inputed, the package ``qtX.Y.Z-essentials`` is downloaded (default Qt install, includes ``qtcreator``...)
|
* If none are inputed, the package ``qtX.Y.Z-essentials`` is downloaded (default Qt install, includes ``qtcreator``...)
|
||||||
* If ``all`` is inputed, the package ``qtX.Y.Z-full`` is downloaded (includes everything)
|
* If ``all`` is inputed, the package ``qtX.Y.Z-full`` is downloaded (includes everything)
|
||||||
|
|
||||||
.. dropdown:: Click to see all the special packages (for Linux Qt 6.8.1)
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
Name: qt6.8.1-essentials
|
|
||||||
Display name: Qt 6.8.1 Linux x86_64 Essential Components
|
|
||||||
Description: Qt 6.8.1 Linux x86_64 Essential Libraries, Headers, and Tools
|
|
||||||
Version: 6.8.1
|
|
||||||
Components: qt.qt6.681.linux_gcc_64
|
|
||||||
|
|
||||||
Name: qt6.8.1-essentials-dev
|
|
||||||
Display name: Qt 6.8.1 Linux x86_64 Essential Components (dev)
|
|
||||||
Description: Qt 6.8.1 Linux x86_64 Essential Libraries, Headers, and Tools (dev)
|
|
||||||
Version: 6.8.1
|
|
||||||
Required aliases: qt6.8.1-essentials
|
|
||||||
|
|
||||||
Name: qt6.8.1-full
|
|
||||||
Display name: Qt 6.8.1 Linux x86_64 All Components with Sources
|
|
||||||
Description: Qt 6.8.1 Linux x86_64 All Libraries, Headers, Tools, and Sources
|
|
||||||
Version: 6.8.1
|
|
||||||
Components: qt.qt6.681.src,extensions.qtwebengine.681.src
|
|
||||||
Required aliases: qt6.8.1-essentials,qt6.8.1-addons,qt6.8.1-extensions
|
|
||||||
Optional components: extensions.qtinsighttracker.681.src
|
|
||||||
|
|
||||||
Name: qt6.8.1-full-dbg
|
|
||||||
Display name: Qt 6.8.1 Linux x86_64 All Components with Sources and Debug Information Files
|
|
||||||
Description: Qt 6.8.1 Linux x86_64 All Libraries, Headers, Tools, Sources, and Debug Information Files (dev)
|
|
||||||
Version: 6.8.1
|
|
||||||
Components: qt.qt6.681.debug_info.linux_gcc_64,qt.qt6.681.debug_info,extensions.qtwebengine.681.debug_information
|
|
||||||
Required aliases: qt6.8.1-full-dev
|
|
||||||
|
|
||||||
Name: qt6.8.1-full-dev
|
|
||||||
Display name: Qt 6.8.1 Linux x86_64 All Components with Sources (dev)
|
|
||||||
Description: Qt 6.8.1 Linux x86_64 All Libraries, Headers, Tools, and Sources (dev)
|
|
||||||
Version: 6.8.1
|
|
||||||
Required aliases: qt6.8.1-full
|
|
||||||
|
|
||||||
Name: qt6.8.1-sdk
|
|
||||||
Display name: Qt 6.8.1 Linux x86_64 SDK
|
|
||||||
Description: Qt 6.8.1 Linux x86_64 SDK Tools (Qt Creator, Ninja, and CMake)
|
|
||||||
Version: 6.8.1
|
|
||||||
Components: qt.tools.qtcreator_gui,qt.tools.cmake,qt.tools.ninja
|
|
||||||
Required aliases: qt6.8.1-full-dev,qt6.8.1-full-dbg
|
|
||||||
|
|
||||||
- ``--outputdir <path>`` - Installation directory (default: current directory)
|
- ``--outputdir <path>`` - Installation directory (default: current directory)
|
||||||
- ``--override <args...>`` - Pass all remaining arguments directly to the Qt installer CLI
|
- ``--override <args...>`` - Pass all remaining arguments directly to the Qt installer CLI
|
||||||
|
|
||||||
@@ -106,7 +62,7 @@ Options
|
|||||||
- ``search_terms`` - Terms to search for in package names (grabs all that is not other options)
|
- ``search_terms`` - Terms to search for in package names (grabs all that is not other options)
|
||||||
|
|
||||||
Override Mode
|
Override Mode
|
||||||
------------
|
----------------------
|
||||||
``install-qt-official`` supports an override mode that passes all arguments after ``--override`` directly to the Qt installer CLI, and will ignore all the other params except ``--email`` and ``--pw`` if given prior to it
|
``install-qt-official`` supports an override mode that passes all arguments after ``--override`` directly to the Qt installer CLI, and will ignore all the other params except ``--email`` and ``--pw`` if given prior to it
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
@@ -122,7 +78,7 @@ When using override mode:
|
|||||||
* `More info here <https://doc.qt.io/qt-6/get-and-install-qt-cli.html>`_
|
* `More info here <https://doc.qt.io/qt-6/get-and-install-qt-cli.html>`_
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------------
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# Standard installation
|
# Standard installation
|
||||||
@@ -138,7 +94,7 @@ Examples
|
|||||||
aqt install-qt-official --override install qt.qt6.680.gcc_64 --email user@example.com --pw pass
|
aqt install-qt-official --override install qt.qt6.680.gcc_64 --email user@example.com --pw pass
|
||||||
|
|
||||||
Advanced configs
|
Advanced configs
|
||||||
--------------
|
--------------------------
|
||||||
The file located in ``./aqt/settings.ini`` can be edited in the ``[qtofficial]`` part to fine tune the official installer (`more details here <https://doc.qt.io/qt-6/get-and-install-qt-cli.html#message-identifiers-for-auto-answer>`_):
|
The file located in ``./aqt/settings.ini`` can be edited in the ``[qtofficial]`` part to fine tune the official installer (`more details here <https://doc.qt.io/qt-6/get-and-install-qt-cli.html#message-identifiers-for-auto-answer>`_):
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|||||||
@@ -13,33 +13,10 @@ from aqt.metadata import MetadataFactory, SimpleSpec, Version
|
|||||||
|
|
||||||
|
|
||||||
def expected_help(actual, prefix=None):
|
def expected_help(actual, prefix=None):
|
||||||
expected = (
|
expected = "usage: aqt [-h] [-c CONFIG]"
|
||||||
"usage: aqt [-h] [-c CONFIG]\n"
|
|
||||||
" {install-qt,install-tool,install-qt-official,list-qt-official,install-doc,install-example,"
|
|
||||||
"install-src,list-qt,list-tool,list-doc,list-example,list-src,help,version}\n"
|
|
||||||
" ...\n"
|
|
||||||
"\n"
|
|
||||||
"Another unofficial Qt Installer.\n"
|
|
||||||
"aqt helps you install Qt SDK, tools, examples and others\n"
|
|
||||||
"\n"
|
|
||||||
"option",
|
|
||||||
" -h, --help show this help message and exit\n"
|
|
||||||
" -c CONFIG, --config CONFIG\n"
|
|
||||||
" Configuration ini file.\n"
|
|
||||||
"\n"
|
|
||||||
"subcommands:\n"
|
|
||||||
" aqt accepts several subcommands:\n"
|
|
||||||
" install-* subcommands are commands that install components\n"
|
|
||||||
" list-* subcommands are commands that show available components\n"
|
|
||||||
"\n"
|
|
||||||
" {install-qt,install-tool,install-qt-official,list-qt-official,install-doc,install-example,install-src,"
|
|
||||||
"list-qt,list-tool,list-doc,list-example,list-src,help,version}\n"
|
|
||||||
" Please refer to each help message by using '--help' "
|
|
||||||
"with each subcommand\n",
|
|
||||||
)
|
|
||||||
if prefix is not None:
|
if prefix is not None:
|
||||||
return actual.startswith(prefix + expected[0]) and actual.endswith(expected[1])
|
return actual.startswith(prefix + expected)
|
||||||
return actual.startswith(expected[0]) and actual.endswith(expected[1])
|
return actual.startswith(expected)
|
||||||
|
|
||||||
|
|
||||||
def test_cli_help(capsys):
|
def test_cli_help(capsys):
|
||||||
|
|||||||
@@ -3,17 +3,15 @@ import shutil
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from aqt.commercial import CommercialInstaller, QtPackageInfo, QtPackageManager
|
from aqt.commercial import CommercialInstaller, QtPackageInfo, QtPackageManager
|
||||||
from aqt.exceptions import DiskAccessNotPermitted
|
from aqt.exceptions import DiskAccessNotPermitted
|
||||||
from aqt.helper import Settings, download_installer, get_qt_account_path
|
from aqt.helper import Settings, download_installer, get_os_name, get_qt_account_path, get_qt_installer_name
|
||||||
from aqt.installer import Cli
|
from aqt.installer import Cli
|
||||||
from aqt.metadata import Version
|
from aqt.metadata import Version
|
||||||
from tests.test_helper import mocked_requests_get
|
|
||||||
|
|
||||||
|
|
||||||
class CompletedProcess:
|
class CompletedProcess:
|
||||||
@@ -51,15 +49,6 @@ class MockResponse:
|
|||||||
yield self.content
|
yield self.content
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_settings(monkeypatch):
|
|
||||||
"""Setup test settings"""
|
|
||||||
# Instead of trying to set properties directly, we should mock the property getter
|
|
||||||
monkeypatch.setattr(Settings, "qt_installer_timeout", property(lambda self: 60))
|
|
||||||
monkeypatch.setattr(Settings, "qt_installer_cache_path", property(lambda self: str(Path.home() / ".qt" / "cache")))
|
|
||||||
monkeypatch.setattr(Settings, "qt_installer_temp_path", property(lambda self: str(Path.home() / ".qt" / "temp")))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def commercial_installer():
|
def commercial_installer():
|
||||||
return CommercialInstaller(
|
return CommercialInstaller(
|
||||||
@@ -206,23 +195,34 @@ def test_build_command(
|
|||||||
assert cmd == expected_cmd
|
assert cmd == expected_cmd
|
||||||
|
|
||||||
|
|
||||||
def test_commercial_installer_download_sha256(tmp_path, monkeypatch, commercial_installer):
|
@pytest.mark.enable_socket
|
||||||
"""Test downloading of commercial installer"""
|
@pytest.mark.parametrize(
|
||||||
|
"os, osarch, expected_suffix",
|
||||||
|
[
|
||||||
|
pytest.param("windows", "windows", ".exe"),
|
||||||
|
pytest.param("windows", "windows-x64", ".exe"),
|
||||||
|
pytest.param("windows", "windows-arm64", ".exe"),
|
||||||
|
pytest.param("linux", "linux", ".run"),
|
||||||
|
pytest.param("linux", "linux-x64", ".run"),
|
||||||
|
pytest.param("linux", "linux-arm64", ".run"),
|
||||||
|
pytest.param("mac", "mac-x64", ".dmg"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_commercial_installer_names(monkeypatch, os, osarch, expected_suffix):
|
||||||
|
"""Test installer names finder"""
|
||||||
|
|
||||||
def mock_getUrl(url, *args, **kwargs):
|
monkeypatch.setattr("aqt.helper.get_os_arch", lambda: osarch)
|
||||||
hash_filename = str(urlparse(url).path.split("/")[-1])
|
|
||||||
assert hash_filename.endswith(".sha256")
|
|
||||||
filename = hash_filename[: -len(".sha256")]
|
|
||||||
return f"07b3ef4606b712923a14816b1cfe9649687e617d030fc50f948920d784c0b1cd {filename}"
|
|
||||||
|
|
||||||
monkeypatch.setattr("aqt.helper.getUrl", mock_getUrl)
|
installer_name = get_qt_installer_name()
|
||||||
monkeypatch.setattr(requests.Session, "get", mocked_requests_get)
|
|
||||||
|
|
||||||
target_path = tmp_path / "qt-installer"
|
assert installer_name.endswith(expected_suffix)
|
||||||
|
|
||||||
timeout = (Settings.connection_timeout, Settings.response_timeout)
|
if os == get_os_name() and osarch in ["windows", "linux", "mac-x64"]:
|
||||||
download_installer(commercial_installer.base_url, commercial_installer._installer_filename, target_path, timeout)
|
target_path = Settings.qt_installer_temp_path / Path(installer_name)
|
||||||
assert target_path.exists()
|
base_url = Settings.baseurl
|
||||||
|
timeout = (Settings.connection_timeout, Settings.response_timeout)
|
||||||
|
download_installer(base_url, installer_name, target_path, timeout)
|
||||||
|
assert True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -275,6 +275,17 @@ def test_get_install_command(monkeypatch, modules: Optional[List[str]], expected
|
|||||||
"stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore,"
|
"stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore,"
|
||||||
"AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}",
|
"AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"install-qt linux desktop 6.8.1 {} --outputdir ./install-qt-flag --use-official-installer {} {}",
|
||||||
|
{"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"},
|
||||||
|
["./install-qt-official", "qt6", "681"],
|
||||||
|
"{} --email ******** --pw ******** --root {} "
|
||||||
|
"--accept-licenses --accept-obligations "
|
||||||
|
"--confirm-command "
|
||||||
|
"--auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=Yes,"
|
||||||
|
"stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore,"
|
||||||
|
"AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_install_qt_commercial(
|
def test_install_qt_commercial(
|
||||||
@@ -301,7 +312,10 @@ def test_install_qt_commercial(
|
|||||||
password = TEST_PASSWORD
|
password = TEST_PASSWORD
|
||||||
|
|
||||||
formatted_cmd = cmd.format(arch, email, password)
|
formatted_cmd = cmd.format(arch, email, password)
|
||||||
formatted_expected = expected_command.format(current_platform, abs_out, *details[1:], arch)
|
if expected_command.startswith("install-qt-official"):
|
||||||
|
formatted_expected = expected_command.format(current_platform, abs_out, *details[1:], arch)
|
||||||
|
else:
|
||||||
|
formatted_expected = expected_command.format(get_qt_installer_name(), abs_out, *details[1:], arch)
|
||||||
|
|
||||||
cli = Cli()
|
cli = Cli()
|
||||||
cli._setup_settings()
|
cli._setup_settings()
|
||||||
@@ -330,7 +344,7 @@ def test_install_qt_commercial(
|
|||||||
else:
|
else:
|
||||||
cli.run(new_cmd.split())
|
cli.run(new_cmd.split())
|
||||||
|
|
||||||
def modify_qt_config(content):
|
def modify_qt_config(content, endwith):
|
||||||
"""
|
"""
|
||||||
Takes content of INI file as string and returns modified content
|
Takes content of INI file as string and returns modified content
|
||||||
"""
|
"""
|
||||||
@@ -339,12 +353,12 @@ def test_install_qt_commercial(
|
|||||||
modified = []
|
modified = []
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# Check if we're entering qtofficial section
|
|
||||||
if line.strip() == "[qtofficial]":
|
if line.strip() == "[qtofficial]":
|
||||||
in_qt_commercial = True
|
in_qt_commercial = True
|
||||||
|
|
||||||
# If in qtofficial section, look for the target line
|
if endwith is not None and endwith in ["Yes", "No"] and "overwrite_target_directory : " in line:
|
||||||
if in_qt_commercial and "overwrite_target_directory : No" in line:
|
line = f"overwrite_target_directory : {endwith}"
|
||||||
|
elif in_qt_commercial and "overwrite_target_directory : No" in line:
|
||||||
line = "overwrite_target_directory : Yes"
|
line = "overwrite_target_directory : Yes"
|
||||||
elif in_qt_commercial and "overwrite_target_directory : Yes" in line:
|
elif in_qt_commercial and "overwrite_target_directory : Yes" in line:
|
||||||
line = "overwrite_target_directory : No"
|
line = "overwrite_target_directory : No"
|
||||||
@@ -359,7 +373,10 @@ def test_install_qt_commercial(
|
|||||||
with open(config_path, "r") as f:
|
with open(config_path, "r") as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
|
|
||||||
modified_content = modify_qt_config(content)
|
if expected_command.startswith("install-qt-official"):
|
||||||
|
modified_content = modify_qt_config(content, None)
|
||||||
|
else:
|
||||||
|
modified_content = modify_qt_config(content, "No")
|
||||||
|
|
||||||
with open(config_path, "w") as f:
|
with open(config_path, "w") as f:
|
||||||
f.write(modified_content)
|
f.write(modified_content)
|
||||||
|
|||||||
@@ -1694,7 +1694,8 @@ def test_install_qt6_wasm_autodesktop(monkeypatch, capsys, version, str_version,
|
|||||||
r"(?:INFO : Found extension .*?\n)*"
|
r"(?:INFO : Found extension .*?\n)*"
|
||||||
r"(?:INFO : Downloading (?:qt[^\n]*|icu[^\n]*)\n"
|
r"(?:INFO : Downloading (?:qt[^\n]*|icu[^\n]*)\n"
|
||||||
r"Finished installation of .*?\.7z in \d+\.\d+\n)*"
|
r"Finished installation of .*?\.7z in \d+\.\d+\n)*"
|
||||||
r"(?:INFO : Patching (?:/tmp/[^/]+|[A-Za-z]:[\\/].*?)/6\.8\.0/wasm_singlethread/bin/(?:qmake(?:6)?|qtpaths(?:6)?|target_qt\.conf)\n)*"
|
r"(?:INFO : Patching (?:/tmp/[^/]+|[A-Za-z]:[\\/].*?)/6\.8\.0/wasm_singlethread/bin/"
|
||||||
|
r"(?:qmake(?:6)?|qtpaths(?:6)?|target_qt\.conf)\n)*"
|
||||||
r"INFO : \n"
|
r"INFO : \n"
|
||||||
r"INFO : Autodesktop will now install linux desktop 6\.8\.0 linux_gcc_64 as required by Qt6-WASM\n"
|
r"INFO : Autodesktop will now install linux desktop 6\.8\.0 linux_gcc_64 as required by Qt6-WASM\n"
|
||||||
r"INFO : aqtinstall\(aqt\) v.*? on Python 3.*?\n"
|
r"INFO : aqtinstall\(aqt\) v.*? on Python 3.*?\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user