From 654ec4221a3083c0c8f496d59764519f3d7fb478 Mon Sep 17 00:00:00 2001 From: Alexandre 'Kidev' Poumaroux <1204936+Kidev@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:15:08 +0100 Subject: [PATCH] Add authentication flags for list-qt-commercial, add tests for coverage --- .github/workflows/check.yml | 69 +- aqt/commercial.py | 105 +-- aqt/helper.py | 23 + aqt/installer.py | 155 ++-- aqt/settings.ini | 2 +- azure-pipelines.yml | 24 +- ci/generate_azure_pipelines_matrices.py | 50 +- ci/steps.yml | 937 ++++++++++++------------ tests/test_cli.py | 37 +- tests/test_commercial.py | 416 +++++++++++ tests/test_install.py | 101 --- tests/test_list.py | 16 - 12 files changed, 1141 insertions(+), 794 deletions(-) create mode 100644 tests/test_commercial.py diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 9ff3bc71..be26c267 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -15,37 +15,40 @@ jobs: check_document: name: Check packaging 📦 runs-on: ubuntu-22.04 + env: + AQT_TEST_EMAIL: ${{ secrets.TEST_EMAIL }} + AQT_TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }} steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 20 - fetch-tags: true - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Install tools - run: | - pip install -U pip - pip install tox tox-gh-actions coveralls coverage[toml] - - name: Test - run: | - tox - coverage xml -o cobertura.xml - env: - PYTEST_ADDOPTS: --cov-config=pyproject.toml --cov --cov-append --verbose - - name: Upload Coverage - run: coveralls --service=github - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/setup-java@v4 - if: ( github.event_name == 'push' ) && ( github.ref == 'refs/heads/master' ) - with: - distribution: 'temurin' - java-version: '21' - - name: Send coverage to codacy - if: ( github.event_name == 'push' ) && ( github.ref == 'refs/heads/master' ) - run: | - java -jar ci/codacy-coverage-reporter.jar report -l Python -t ${PROJECT_TOKEN} -r cobertura.xml - env: - PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} + - uses: actions/checkout@v4 + with: + fetch-depth: 20 + fetch-tags: true + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install tools + run: | + pip install -U pip + pip install tox tox-gh-actions coveralls coverage[toml] + - name: Test + run: | + tox + coverage xml -o cobertura.xml + env: + PYTEST_ADDOPTS: --cov-config=pyproject.toml --cov --cov-append --verbose + - name: Upload Coverage + run: coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/setup-java@v4 + if: ( github.event_name == 'push' ) && ( github.ref == 'refs/heads/master' ) + with: + distribution: 'temurin' + java-version: '21' + - name: Send coverage to codacy + if: ( github.event_name == 'push' ) && ( github.ref == 'refs/heads/master' ) + run: | + java -jar ci/codacy-coverage-reporter.jar report -l Python -t ${PROJECT_TOKEN} -r cobertura.xml + env: + PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} diff --git a/aqt/commercial.py b/aqt/commercial.py index e144f796..792dca57 100644 --- a/aqt/commercial.py +++ b/aqt/commercial.py @@ -9,7 +9,15 @@ from defusedxml import ElementTree from aqt.exceptions import DiskAccessNotPermitted -from aqt.helper import Settings, get_os_name, get_qt_account_path, get_qt_installer_name, safely_run, safely_run_save_output +from aqt.helper import ( + Settings, + extract_auth, + get_os_name, + get_qt_account_path, + get_qt_installer_name, + safely_run, + safely_run_save_output, +) from aqt.metadata import Version @@ -103,19 +111,14 @@ def gather_packages(self, installer_path: str) -> None: version_str = self._get_version_string() base_package = f"qt.qt{self.version.major}.{version_str}" - cmd = [ - installer_path, - "--accept-licenses", - "--accept-obligations", - "--confirm-command", - "--default-answer", - "search", - base_package, - ] + cmd = [installer_path, "--accept-licenses", "--accept-obligations", "--confirm-command", "--default-answer"] if self.username and self.password: cmd.extend(["--email", self.username, "--pw", self.password]) + cmd.append("search") + cmd.append(base_package) + try: output = safely_run_save_output(cmd, Settings.qt_installer_timeout) @@ -190,7 +193,7 @@ def __init__( output_dir: Optional[str] = None, logger: Optional[Logger] = None, base_url: str = "https://download.qt.io", - override: Optional[list[str]] = None, + override: Optional[List[str]] = None, modules: Optional[List[str]] = None, no_unattended: bool = False, ): @@ -198,14 +201,21 @@ def __init__( self.target = target self.arch = arch or "" self.version = Version(version) if version else Version("0.0.0") - self.username = username - self.password = password self.output_dir = output_dir self.logger = logger or getLogger(__name__) self.base_url = base_url self.modules = modules self.no_unattended = no_unattended + # Extract credentials from override if present + if override: + extracted_username, extracted_password, self.override = extract_auth(override) + self.username = extracted_username or username + self.password = extracted_password or password + else: + self.username = username + self.password = password + # Set OS-specific properties self.os_name = get_os_name() self._installer_filename = get_qt_installer_name() @@ -247,15 +257,15 @@ def build_command( if not no_unattended: cmd.extend(["--accept-licenses", "--accept-obligations", "--confirm-command"]) + # Add authentication if provided + if username and password: + cmd.extend(["--email", username, "--pw", password]) + if override: # When using override, still include unattended flags unless disabled cmd.extend(override) return cmd - # Add authentication if provided - if username and password: - cmd.extend(["--email", username, "--pw", password]) - # Add output directory if specified if output_dir: cmd.extend(["--root", str(Path(output_dir).resolve())]) @@ -271,36 +281,34 @@ def install(self) -> None: """Run the Qt installation process.""" if ( not self.qt_account.exists() - and not (self.username and self.password) + and (not self.username or not self.password) and not os.environ.get("QT_INSTALLER_JWT_TOKEN") ): raise RuntimeError( "No Qt account credentials found. Provide username and password or ensure qtaccount.ini exists." ) - # Check output directory if specified - if self.output_dir: - output_path = Path(self.output_dir) / str(self.version) - if output_path.exists(): - if Settings.qt_installer_overwritetargetdirectory.lower() == "yes": - self.logger.warning(f"Target directory {output_path} exists - removing as overwrite is enabled") - try: - import shutil - - shutil.rmtree(output_path) - except (OSError, PermissionError) as e: - raise DiskAccessNotPermitted(f"Failed to remove existing target directory {output_path}: {str(e)}") - else: - msg = ( - f"Target directory {output_path} already exists. " - "Set overwrite_target_directory='Yes' in settings.ini to overwrite, or select another directory." - ) - raise DiskAccessNotPermitted(msg) - # Setup cache directory cache_path = Path(Settings.qt_installer_cache_path) cache_path.mkdir(parents=True, exist_ok=True) + # Setup output directory and validate access + output_dir = Path(self.output_dir) if self.output_dir else Path(os.getcwd()) / "Qt" + version_dir = output_dir / str(self.version) + qt_base_dir = output_dir + + if qt_base_dir.exists(): + if Settings.qt_installer_overwritetargetdirectory.lower() == "yes": + self.logger.warning(f"Target directory {qt_base_dir} exists - removing as overwrite is enabled") + try: + import shutil + + if version_dir.exists(): + shutil.rmtree(version_dir) + except (OSError, PermissionError) as e: + raise DiskAccessNotPermitted(f"Failed to remove existing version directory {version_dir}: {str(e)}") + + # Setup temp directory import shutil temp_dir = Settings.qt_installer_temp_path @@ -316,7 +324,16 @@ def install(self) -> None: try: cmd = [] if self.override: - cmd = self.build_command(str(installer_path), override=self.override, no_unattended=self.no_unattended) + if not self.username or not self.password: + self.username, self.password, self.override = extract_auth(self.override) + + cmd = self.build_command( + str(installer_path), + override=self.override, + no_unattended=self.no_unattended, + username=self.username, + password=self.password, + ) else: # Initialize package manager and gather packages self.package_manager.gather_packages(str(installer_path)) @@ -325,20 +342,18 @@ def install(self) -> None: str(installer_path.absolute()), username=self.username, password=self.password, - output_dir=self.output_dir, + output_dir=str(qt_base_dir.absolute()), no_unattended=self.no_unattended, ) - cmd = [ - *base_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] self.logger.info(f"Running: {cmd}") - safely_run(cmd, Settings.qt_installer_timeout) + except Exception as e: - self.logger.error(f"Installation failed with exit code {e.__str__()}") + self.logger.error(f"Installation failed: {str(e)}") raise finally: self.logger.info("Qt installation completed successfully") diff --git a/aqt/helper.py b/aqt/helper.py index 92976ac9..412b39a3 100644 --- a/aqt/helper.py +++ b/aqt/helper.py @@ -644,3 +644,26 @@ def safely_run_save_output(cmd: List[str], timeout: int) -> Any: return result except Exception: raise + + +def extract_auth(args: List[str]) -> Tuple[str | None, str | None, List[str] | None]: + username = None + password = None + i = 0 + while i < len(args): + if args[i] == "--email": + if i + 1 < len(args): + username = args[i + 1] + del args[i : i + 2] + else: + del args[i] + continue + elif args[i] == "--pw": + if i + 1 < len(args): + password = args[i + 1] + del args[i : i + 2] + else: + del args[i] + continue + i += 1 + return username, password, args diff --git a/aqt/installer.py b/aqt/installer.py index ccd515aa..d1e4780b 100644 --- a/aqt/installer.py +++ b/aqt/installer.py @@ -59,6 +59,7 @@ MyQueueListener, Settings, downloadBinaryFile, + extract_auth, get_hash, get_os_name, get_qt_installer_name, @@ -676,42 +677,43 @@ def run_install_qt_commercial(self, args: InstallArgParser) -> None: """Execute commercial Qt installation""" self.show_aqt_version() - if args.override: - commercial_installer = CommercialInstaller( - target="", # Empty string as placeholder - arch="", - version=None, - logger=self.logger, - base_url=args.base if args.base is not None else Settings.baseurl, - override=args.override, - no_unattended=not Settings.qt_installer_unattended, - ) - else: - if not all([args.target, args.arch, args.version]): - raise CliInputError("target, arch, and version are required") - - commercial_installer = CommercialInstaller( - target=args.target, - arch=args.arch, - version=args.version, - username=args.user, - password=args.password, - output_dir=args.outputdir, - logger=self.logger, - base_url=args.base if args.base is not None else Settings.baseurl, - no_unattended=not Settings.qt_installer_unattended, - modules=args.modules, - ) - try: + if args.override: + username, password, override_args = extract_auth(args.override) + commercial_installer = CommercialInstaller( + target="", # Empty string as placeholder + arch="", + version=None, + logger=self.logger, + base_url=args.base if args.base is not None else Settings.baseurl, + override=override_args, + no_unattended=not Settings.qt_installer_unattended, + username=username or args.user, + password=password or args.password, + ) + else: + if not all([args.target, args.arch, args.version]): + raise CliInputError("target, arch, and version are required") + + commercial_installer = CommercialInstaller( + target=args.target, + arch=args.arch, + version=args.version, + username=args.user, + password=args.password, + output_dir=args.outputdir, + logger=self.logger, + base_url=args.base if args.base is not None else Settings.baseurl, + no_unattended=not Settings.qt_installer_unattended, + modules=args.modules, + ) + commercial_installer.install() Settings.qt_installer_cleanup() - except DiskAccessNotPermitted: - # Let DiskAccessNotPermitted propagate up without additional logging - raise except Exception as e: - self.logger.error(f"Commercial installation failed: {str(e)}") - raise + self.logger.error(f"Error installing commercial installer {str(e)}") + finally: + self.logger.info("Done") def show_help(self, args=None): """Display help message""" @@ -851,30 +853,31 @@ def __call__(self, parser, namespace, values, option_string=None) -> None: ) self._set_common_options(install_qt_commercial_parser) - def _make_list_qt_commercial_parser(self, subparsers: argparse._SubParsersAction) -> None: - """Creates a subparser for listing Qt commercial packages""" - list_parser = subparsers.add_parser( - "list-qt-commercial", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog="Examples:\n" - "$ aqt list-qt-commercial # list all available packages\n" - "$ aqt list-qt-commercial gcc_64 # search for specific archs\n" - "$ aqt list-qt-commercial 6.8.1 # search for specific versions\n" - "$ aqt list-qt-commercial qtquick3d # search for specific packages\n" - "$ aqt list-qt-commercial gcc_64 6.8.1 # search for multiple terms at once\n", + def _set_list_qt_commercial_parser(self, list_qt_commercial_parser: argparse.ArgumentParser) -> None: + """Configure parser for list-qt-commercial command with flexible argument handling.""" + list_qt_commercial_parser.set_defaults(func=self.run_list_qt_commercial) + + list_qt_commercial_parser.add_argument( + "--user", + help="Qt account username", ) - list_parser.add_argument( + list_qt_commercial_parser.add_argument( + "--password", + help="Qt account password", + ) + + # Capture all remaining arguments as search terms + list_qt_commercial_parser.add_argument( "search_terms", - nargs="*", - help="Optional search terms to pass to the installer search command. If not provided, lists all packages", + nargs="*", # Zero or more arguments + help="Search terms (all non-option arguments are treated as search terms)", ) - list_parser.set_defaults(func=self.run_list_qt_commercial) def run_list_qt_commercial(self, args) -> None: - """Execute Qt commercial package listing""" + """Execute Qt commercial package listing.""" self.show_aqt_version() - # Create temporary directory to download installer + # Create temporary directory for installer import shutil from pathlib import Path @@ -893,6 +896,7 @@ def run_list_qt_commercial(self, args) -> None: self.logger.info(f"Downloading Qt installer to {installer_path}") base_url = Settings.baseurl url = f"{base_url}/official_releases/online_installers/{installer_filename}" + import requests response = requests.get(url, stream=True, timeout=Settings.qt_installer_timeout) @@ -905,25 +909,23 @@ def run_list_qt_commercial(self, args) -> None: if get_os_name() != "windows": os.chmod(installer_path, 0o500) - # Build search command - cmd = [ - str(installer_path), - "--accept-licenses", - "--accept-obligations", - "--confirm-command", - "search", - "" if not args.search_terms else " ".join(args.search_terms), - ] - - # Run search and display output + # Build command + cmd = [str(installer_path), "--accept-licenses", "--accept-obligations", "--confirm-command"] + + if args.user and args.password: + cmd.extend(["--email", args.user, "--pw", args.password]) + + cmd.append("search") + + # Add all search terms if present + if args.search_terms: + cmd.extend(args.search_terms) + + # Run search output = safely_run_save_output(cmd, Settings.qt_installer_timeout) - # Process and print the output properly if output.stdout: - # Print the actual output with proper newlines - print(output.stdout) - - # If there are any errors, print them as warnings + self.logger.info(output.stdout) if output.stderr: for line in output.stderr.splitlines(): self.logger.warning(line) @@ -931,7 +933,6 @@ def run_list_qt_commercial(self, args) -> None: except Exception as e: self.logger.error(f"Failed to list Qt commercial packages: {e}") finally: - # Clean up Settings.qt_installer_cleanup() def _warn_on_deprecated_command(self, old_name: str, new_name: str) -> None: @@ -992,13 +993,18 @@ def make_parser_list_sde(cmd: str, desc: str, cmd_type: str): self._set_install_qt_commercial_parser, argparse.RawTextHelpFormatter, ) + make_parser_it( + "list-qt-commercial", + "Search packages using Qt commercial", + self._set_list_qt_commercial_parser, + None, + ) make_parser_sde("install-doc", "Install documentation.", self.run_install_doc, False) make_parser_sde("install-example", "Install examples.", self.run_install_example, False) make_parser_sde("install-src", "Install source.", self.run_install_src, True, is_add_modules=False) # Create list command parsers self._make_list_qt_parser(subparsers) - self._make_list_qt_commercial_parser(subparsers) self._make_list_tool_parser(subparsers) make_parser_list_sde("list-doc", "List documentation archives available (use with install-doc)", "doc") make_parser_list_sde("list-example", "List example archives available (use with install-example)", "examples") @@ -1018,11 +1024,16 @@ def _make_list_qt_parser(self, subparsers: argparse._SubParsersAction): '$ 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 --spec 5.9 --modules latest clang_64 # print modules for latest 5.9\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 --archives 5.9.0 clang_64 # list archives in base Qt installation\n" - "$ aqt list-qt mac desktop --archives 5.14.0 clang_64 debug_info # list archives in debug_info module\n" - "$ aqt list-qt all_os wasm --arch 6.8.1 # print architectures for Qt WASM 6.8.1\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 --archives 5.9.0 clang_64 # list archives in base Qt " + "installation\n" + "$ aqt list-qt mac desktop --archives 5.14.0 clang_64 debug_info # list archives in debug_info " + "module\n" + "$ aqt list-qt all_os wasm --arch 6.8.1 # print architectures for Qt WASM " + "6.8.1\n", ) list_parser.add_argument( "host", diff --git a/aqt/settings.ini b/aqt/settings.ini index 75124334..16de5dec 100644 --- a/aqt/settings.ini +++ b/aqt/settings.ini @@ -23,7 +23,7 @@ INSECURE_NOT_FOR_PRODUCTION_ignore_hash : False unattended : True installer_timeout : 1800 operation_does_not_exist_error : Ignore -overwrite_target_directory : Yes +overwrite_target_directory : No stop_processes_for_updates : Ignore installation_error_with_cancel : Ignore installation_error_with_ignore : Ignore diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0bd7904b..a36ca26c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -7,13 +7,13 @@ jobs: pool: vmImage: 'ubuntu-22.04' steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.12' - addToPath: true - - bash: python ci/generate_azure_pipelines_matrices.py - name: mtrx - displayName: Generate test matrices and set variables in Azure Pipelines + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.12' + addToPath: true + - bash: python ci/generate_azure_pipelines_matrices.py + name: mtrx + displayName: Generate test matrices and set variables in Azure Pipelines - job: Mac dependsOn: MatricesGenerator @@ -22,9 +22,9 @@ jobs: strategy: matrix: $[ dependencies.MatricesGenerator.outputs['mtrx.mac'] ] steps: - - script: brew install p7zip - displayName: Install 7zip - - template: ci/steps.yml + - script: brew install p7zip + displayName: Install 7zip + - template: ci/steps.yml - job: Windows dependsOn: MatricesGenerator @@ -37,7 +37,7 @@ jobs: startYear: $[format('{0:yyyy}', pipeline.startTime)] startMonth: $[format('{0:MM}', pipeline.startTime)] steps: - - template: ci/steps.yml + - template: ci/steps.yml - job: Linux dependsOn: MatricesGenerator @@ -46,7 +46,7 @@ jobs: strategy: matrix: $[ dependencies.MatricesGenerator.outputs['mtrx.linux'] ] steps: - - template: ci/steps.yml + - template: ci/steps.yml #- job: Linux_ARM64 # dependsOn: MatricesGenerator diff --git a/ci/generate_azure_pipelines_matrices.py b/ci/generate_azure_pipelines_matrices.py index 5fdf560c..35952315 100644 --- a/ci/generate_azure_pipelines_matrices.py +++ b/ci/generate_azure_pipelines_matrices.py @@ -3,7 +3,6 @@ """ import collections import json -import secrets as random import re from itertools import product from typing import Dict, Optional @@ -46,7 +45,8 @@ def __init__( is_autodesktop: bool = False, tool_options: Optional[Dict[str, str]] = None, check_output_cmd: Optional[str] = None, - emsdk_version: str = "sdk-fastcomp-1.38.27-64bit@3.1.29", # did not change for safety, created func self.emsdk_version() + emsdk_version: str = "sdk-fastcomp-1.38.27-64bit@3.1.29", + # did not change for safety, created func self.emsdk_version() autodesk_arch_folder: Optional[str] = None, ): self.command = command @@ -167,13 +167,13 @@ def __init__(self, platform, build_jobs): BuildJob("install-qt", qt_version, "mac", "desktop", "clang_64", "macos") ) mac_build_jobs.append(BuildJob( - "install-qt", - "6.2.0", - "mac", - "desktop", - "clang_64", - "macos", - module="qtcharts qtnetworkauth", )) + "install-qt", + "6.2.0", + "mac", + "desktop", + "clang_64", + "macos", + module="qtcharts qtnetworkauth", )) # Windows Desktop for qt_version in qt_versions: @@ -211,7 +211,7 @@ def __init__(self, platform, build_jobs): # Extra modules test linux_build_jobs.extend( [ - BuildJob( + BuildJob( # Archives stored as .7z "install-src", "6.1.0", "linux", "desktop", "gcc_64", "gcc_64", subarchives="qtlottie", # Fail the job if this path does not exist: @@ -235,10 +235,12 @@ def __init__(self, platform, build_jobs): "install-example", "6.1.3", "linux", "desktop", "gcc_64", "gcc_64", subarchives="qtdoc", module="qtcharts", # Fail the job if these paths do not exist: - check_output_cmd="ls -lh ./Examples/Qt-6.1.3/charts/ ./Examples/Qt-6.1.3/demos/ ./Examples/Qt-6.1.3/tutorials/", + check_output_cmd="ls -lh ./Examples/Qt-6.1.3/charts/ ./Examples/Qt-6.1.3/demos/ " + "./Examples/Qt-6.1.3/tutorials/", ), # test for list commands - BuildJob('list-qt', '6.1.0', 'linux', 'desktop', 'gcc_64', '', spec=">6.0,<6.1.1", list_options={'HAS_WASM': "False"}), + BuildJob('list-qt', '6.1.0', 'linux', 'desktop', 'gcc_64', '', spec=">6.0,<6.1.1", + list_options={'HAS_WASM': "False"}), BuildJob('list-qt', '6.1.0', 'linux', 'android', 'android_armv7', '', spec=">6.0,<6.1.1", list_options={}), ] ) @@ -271,7 +273,8 @@ def __init__(self, platform, build_jobs): # WASM post 6.7.x linux_build_jobs.append( BuildJob("install-qt", "6.7.3", "all_os", "wasm", "wasm_multithread", "wasm_multithread", - is_autodesktop=True, emsdk_version=f"sdk-{BuildJob.emsdk_version_for_qt("6.7.3")}-64bit", autodesk_arch_folder="gcc_64") + is_autodesktop=True, emsdk_version=f"sdk-{BuildJob.emsdk_version_for_qt("6.7.3")}-64bit", + autodesk_arch_folder="gcc_64") ) for job_queue, host, desk_arch, target, qt_version in ( (linux_build_jobs, "all_os", "linux_gcc_64", "wasm", qt_versions[0]), @@ -281,7 +284,8 @@ def __init__(self, platform, build_jobs): for wasm_arch in ("wasm_singlethread", "wasm_multithread"): job_queue.append( BuildJob("install-qt", qt_version, host, target, wasm_arch, wasm_arch, - is_autodesktop=True, emsdk_version=f"sdk-{BuildJob.emsdk_version_for_qt(qt_version)}-64bit", autodesk_arch_folder=desk_arch) + is_autodesktop=True, emsdk_version=f"sdk-{BuildJob.emsdk_version_for_qt(qt_version)}-64bit", + autodesk_arch_folder=desk_arch) ) # mobile SDK @@ -335,6 +339,20 @@ def __init__(self, platform, build_jobs): BuildJob("install-tool", "", "mac", "desktop", "", "", tool_options=tool_options_mac) ) +# Commercial +windows_build_jobs.append( + BuildJob("install-qt-commercial", qt_version="6.8.1", target="desktop", arch="win64_msvc2022_64", + archdir="win64_msvc2022_64", module="qtquick3d") +) +linux_build_jobs.append( + BuildJob("install-qt-commercial", qt_version="6.8.1", target="desktop", arch="linux_gcc_64", + archdir="linux_gcc_64", module="qtquick3d") +) +mac_build_jobs.append( + BuildJob("install-qt-commercial", qt_version="6.8.1", target="desktop", arch="clang_64", + archdir="clang_64", module="qtquick3d") +) + matrices = {} for platform_build_job in all_platform_build_jobs: @@ -375,8 +393,8 @@ def __init__(self, platform, build_jobs): ("OUTPUT_DIR", build_job.output_dir if build_job.output_dir else ""), ("QT_BINDIR", build_job.qt_bindir()), ("WIN_QT_BINDIR", build_job.win_qt_bindir()), - ("EMSDK_VERSION", (build_job.emsdk_version+"@main").split('@')[0]), - ("EMSDK_TAG", (build_job.emsdk_version+"@main").split('@')[1]), + ("EMSDK_VERSION", (build_job.emsdk_version + "@main").split('@')[0]), + ("EMSDK_TAG", (build_job.emsdk_version + "@main").split('@')[1]), ("WIN_AUTODESK_QT_BINDIR", build_job.win_autodesk_qt_bindir()), ("TOOL1_ARGS", build_job.tool_options.get("TOOL1_ARGS", "")), ("LIST_TOOL1_CMD", build_job.tool_options.get("LIST_TOOL1_CMD", "")), diff --git a/ci/steps.yml b/ci/steps.yml index ca09a461..3ecbf9d9 100644 --- a/ci/steps.yml +++ b/ci/steps.yml @@ -1,496 +1,503 @@ +env: + AQT_TEST_EMAIL: ${{ secrets.TEST_EMAIL }} + AQT_TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }} steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: $(PYTHON_VERSION) - architecture: 'x64' - - powershell: | - pip install -e . - displayName: install package +- task: UsePythonVersion@0 + inputs: + versionSpec: $(PYTHON_VERSION) + architecture: 'x64' +- powershell: | + pip install -e . + displayName: install package - # Install linux dependencies - - bash: | - sudo apt-get update - sudo apt-get install -y libgl1-mesa-dev libxkbcommon-x11-0 - if [[ "$(SUBCOMMAND)" == "install-tool" ]]; then - sudo apt-get install -y libxcb-{icccm4,image0,keysyms1,randr0,render-util0,shape0,sync1,xfixes0,xinerama0,xkb1} - fi - condition: and(eq(variables['TARGET'], 'desktop'), eq(variables['Agent.OS'], 'Linux')) - displayName: install test dependency for Linux +# Install linux dependencies +- bash: | + sudo apt-get update + sudo apt-get install -y libgl1-mesa-dev libxkbcommon-x11-0 + if [[ "$(SUBCOMMAND)" == "install-tool" || "$(SUBCOMMAND)" == "install-qt-commercial" ]]; then + sudo apt-get install -y libxcb-{icccm4,image0,keysyms1,randr0,render-util0,shape0,sync1,xfixes0,xinerama0,xkb1} + fi + condition: and(eq(variables['TARGET'], 'desktop'), eq(variables['Agent.OS'], 'Linux')) + displayName: install test dependency for Linux - # Run Aqt - ##---------------------------------------------------- - ## we insert sleep in random 1sec < duration < 60sec to reduce - ## download server load. - - bash: | - set -ex - number=$RANDOM - let "number %= 60" || true - let "number += 1" - sleep $number - mkdir Qt - cd Qt - if [[ "$(SUBCOMMAND)" == "install-qt" ]]; then - opt="" - if [[ "$(QT_BASE_MIRROR)" != "" ]]; then - opt+=" -b $(QT_BASE_MIRROR)" - fi - if [[ "$(MODULE)" != "" ]]; then - opt+=" -m $(MODULE)" - fi - if [[ "$(OUTPUT_DIR)" != "" ]]; then - opt+=" --outputdir $(OUTPUT_DIR)" - sudo mkdir -p "$(OUTPUT_DIR)" - sudo chown $(whoami) "$(OUTPUT_DIR)" - fi - if [[ "$(SUBARCHIVES)" != "" ]]; then - opt+=" --archives $(SUBARCHIVES)" - fi - if [[ "$(IS_AUTODESKTOP)" == "True" ]]; then - opt+=" --autodesktop" - elif [[ "$(TARGET)" == "android" || "$(TARGET)" == "ios" ]]; then - if [[ "$(HOST)" == "windows" ]]; then - python -m aqt install-qt $(HOST) desktop $(QT_VERSION) mingw81_64 --archives qtbase - else - # qtdeclarative contains `qmlimportscanner`: necessary for ios builds, Qt6+ - python -m aqt install-qt $(HOST) desktop $(QT_VERSION) --archives qtbase qtdeclarative - fi - fi - if [[ "$(SPEC)" == "" ]]; then - python -m aqt install-qt $(HOST) $(TARGET) $(QT_VERSION) $(ARCH) $opt +# Run Aqt +##---------------------------------------------------- +## we insert sleep in random 1sec < duration < 60sec to reduce +## download server load. +- bash: | + set -ex + number=$RANDOM + let "number %= 60" || true + let "number += 1" + sleep $number + mkdir Qt + cd Qt + if [[ "$(SUBCOMMAND)" == "install-qt-commercial" ]]; then + opt+=" --user $(AQT_TEST_EMAIL) --password $(AQT_TEST_PASSWORD)" + python -m aqt install-qt-commercial $opt $(TARGET) $(ARCH) $(QT_VERSION) --m $(MODULE) + fi + if [[ "$(SUBCOMMAND)" == "install-qt" ]]; then + opt="" + if [[ "$(QT_BASE_MIRROR)" != "" ]]; then + opt+=" -b $(QT_BASE_MIRROR)" + fi + if [[ "$(MODULE)" != "" ]]; then + opt+=" -m $(MODULE)" + fi + if [[ "$(OUTPUT_DIR)" != "" ]]; then + opt+=" --outputdir $(OUTPUT_DIR)" + sudo mkdir -p "$(OUTPUT_DIR)" + sudo chown $(whoami) "$(OUTPUT_DIR)" + fi + if [[ "$(SUBARCHIVES)" != "" ]]; then + opt+=" --archives $(SUBARCHIVES)" + fi + if [[ "$(IS_AUTODESKTOP)" == "True" ]]; then + opt+=" --autodesktop" + elif [[ "$(TARGET)" == "android" || "$(TARGET)" == "ios" ]]; then + if [[ "$(HOST)" == "windows" ]]; then + python -m aqt install-qt $(HOST) desktop $(QT_VERSION) mingw81_64 --archives qtbase else - python -m aqt install-qt $(HOST) $(TARGET) "$(SPEC)" $(ARCH) $opt - fi - if [[ "$(OUTPUT_DIR)" != "" ]]; then - # Use 'strings' to read binary - echo "Verify patched value of qt_prfxpath" - [[ "$(strings $(QT_BINDIR)/qmake | grep qt_prfxpath | cut -d '=' -f 2)" == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] - echo "Verify patched value of qt_epfxpath" - [[ "$(strings $(QT_BINDIR)/qmake | grep qt_epfxpath | cut -d '=' -f 2)" == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] - echo "Verify patched value of qt_hpfxpath" - [[ "$(strings $(QT_BINDIR)/qmake | grep qt_hpfxpath | cut -d '=' -f 2)" == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] - - # Use 'qmake -query' to check paths - echo "Hide qt.conf so it doesn't interfere with test" - mv $(QT_BINDIR)/qt.conf $(QT_BINDIR)/_qt.conf - #export PATH=$(QT_BINDIR):$PATH - echo "Require that qt_epfxpath was set to '$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)'" - [[ $($(QT_BINDIR)/qmake -query QT_INSTALL_PREFIX) == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] - echo "Require that qt_prfxpath was set to '$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)'" - [[ $($(QT_BINDIR)/qmake -query QT_INSTALL_PREFIX/dev) == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] - echo "Require that qt_hpfxpath was set to '$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)'" - [[ $($(QT_BINDIR)/qmake -query QT_HOST_PREFIX) == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] + # qtdeclarative contains `qmlimportscanner`: necessary for ios builds, Qt6+ + python -m aqt install-qt $(HOST) desktop $(QT_VERSION) --archives qtbase qtdeclarative fi fi - if [[ "$(SUBCOMMAND)" == "list" ]]; then - aqt list-qt $(HOST) # print all targets for host - aqt list-tool $(HOST) $(TARGET) # print all tools for host/target - aqt list-tool $(HOST) $(TARGET) qt3dstudio_runtime_240 # print all tool variant names for qt3dstudio - aqt list-tool $(HOST) $(TARGET) qt3dstudio_runtime_240 --long # print tool variant names, versions, release dates - if [[ "$(TARGET)" == "desktop" ]]; then - aqt list-tool $(HOST) $(TARGET) tools_qtcreator # print all tool variant names for qtcreator - aqt list-tool $(HOST) $(TARGET) tools_qtcreator -l # print tool variant names, versions, release dates - fi - aqt list-qt $(HOST) $(TARGET) # print all versions of Qt - aqt list-qt $(HOST) $(TARGET) --spec "$(SPEC)" # print all versions of Qt in SimpleSpec - ver=$(aqt list-qt $(HOST) $(TARGET) --spec "$(SPEC)" --latest-version) # latest Qt in SimpleSpec - [ $ver == $(QT_VERSION) ] # latest version in SPEC must be QT_VERSION - aqt list-qt $(HOST) $(TARGET) --spec "$(SPEC)" --modules latest $(ARCH) # print modules for latest in SimpleSpec - aqt list-qt $(HOST) $(TARGET) --modules $(QT_VERSION) $(ARCH) # print modules for version/host/target/arch - aqt list-qt $(HOST) $(TARGET) --arch $(QT_VERSION) # print architectures for version/host/target - if [[ "$(TARGET)" == "desktop" ]]; then - if [[ "$(HAS_WASM)" == "True" ]]; then # fail if 'wasm_32' is not in the list - aqt list-qt $(HOST) $(TARGET) --arch $(QT_VERSION) | grep -w -q "wasm_32" - else # fail if 'wasm_32' is in the list - ! aqt list-qt $(HOST) $(TARGET) --arch $(QT_VERSION) | grep -w -q "wasm_32" - fi - - # Extra check for Qt 6.5.0 WASM: - for host in mac linux windows; do - export WASM_ARCHES=$(aqt list-qt $host desktop --arch 6.5.0) - echo $WASM_ARCHES | grep -w -q "wasm_singlethread" # fail if 'wasm_singlethread' is not in the list - echo $WASM_ARCHES | grep -w -q "wasm_multithread" # fail if 'wasm_multithread' is not in the list - done - fi - aqt list-qt $(HOST) $(TARGET) --spec "$(SPEC)" --arch latest - - # list-src/doc/example - aqt list-src $(HOST) $(QT_VERSION) # print source archives available for host/desktop/version - aqt list-doc $(HOST) $(QT_VERSION) # print docs archives available for host/desktop/version - aqt list-doc $(HOST) $(QT_VERSION) --modules # print docs modules available for host/desktop/version - aqt list-example $(HOST) $(QT_VERSION) # print example archives available for host/desktop/version - aqt list-example $(HOST) $(QT_VERSION) --modules # print example modules available for host/desktop/version + if [[ "$(SPEC)" == "" ]]; then + python -m aqt install-qt $(HOST) $(TARGET) $(QT_VERSION) $(ARCH) $opt + else + python -m aqt install-qt $(HOST) $(TARGET) "$(SPEC)" $(ARCH) $opt fi - if [[ "$(SUBCOMMAND)" == "install-src" ]]; then - python -m aqt $(SUBCOMMAND) $(HOST) $(QT_VERSION) --archives $(SUBARCHIVES) - $(CHECK_OUTPUT_CMD) + if [[ "$(OUTPUT_DIR)" != "" ]]; then + # Use 'strings' to read binary + echo "Verify patched value of qt_prfxpath" + [[ "$(strings $(QT_BINDIR)/qmake | grep qt_prfxpath | cut -d '=' -f 2)" == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] + echo "Verify patched value of qt_epfxpath" + [[ "$(strings $(QT_BINDIR)/qmake | grep qt_epfxpath | cut -d '=' -f 2)" == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] + echo "Verify patched value of qt_hpfxpath" + [[ "$(strings $(QT_BINDIR)/qmake | grep qt_hpfxpath | cut -d '=' -f 2)" == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] + + # Use 'qmake -query' to check paths + echo "Hide qt.conf so it doesn't interfere with test" + mv $(QT_BINDIR)/qt.conf $(QT_BINDIR)/_qt.conf + #export PATH=$(QT_BINDIR):$PATH + echo "Require that qt_epfxpath was set to '$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)'" + [[ $($(QT_BINDIR)/qmake -query QT_INSTALL_PREFIX) == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] + echo "Require that qt_prfxpath was set to '$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)'" + [[ $($(QT_BINDIR)/qmake -query QT_INSTALL_PREFIX/dev) == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] + echo "Require that qt_hpfxpath was set to '$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)'" + [[ $($(QT_BINDIR)/qmake -query QT_HOST_PREFIX) == "$(OUTPUT_DIR)/$(QT_VERSION)/$(ARCHDIR)" ]] fi - if [[ "$(SUBCOMMAND)" =~ ^install-(doc|example)$ ]]; then - opt="" - if [[ "$(MODULE)" != "" ]]; then - opt+=" -m $(MODULE)" - fi - python -m aqt $(SUBCOMMAND) $(HOST) $(QT_VERSION) --archives $(SUBARCHIVES) $opt - $(CHECK_OUTPUT_CMD) + fi + if [[ "$(SUBCOMMAND)" == "list" ]]; then + aqt list-qt $(HOST) # print all targets for host + aqt list-tool $(HOST) $(TARGET) # print all tools for host/target + aqt list-tool $(HOST) $(TARGET) qt3dstudio_runtime_240 # print all tool variant names for qt3dstudio + aqt list-tool $(HOST) $(TARGET) qt3dstudio_runtime_240 --long # print tool variant names, versions, release dates + if [[ "$(TARGET)" == "desktop" ]]; then + aqt list-tool $(HOST) $(TARGET) tools_qtcreator # print all tool variant names for qtcreator + aqt list-tool $(HOST) $(TARGET) tools_qtcreator -l # print tool variant names, versions, release dates fi - if [[ "$(SUBCOMMAND)" == "install-tool" ]]; then - opt="" - if [[ "$(OUTPUT_DIR)" != "" ]]; then - opt+=" --outputdir $(OUTPUT_DIR)" - sudo mkdir -p "$(OUTPUT_DIR)" - sudo chown $(whoami) "$(OUTPUT_DIR)" + aqt list-qt $(HOST) $(TARGET) # print all versions of Qt + aqt list-qt $(HOST) $(TARGET) --spec "$(SPEC)" # print all versions of Qt in SimpleSpec + ver=$(aqt list-qt $(HOST) $(TARGET) --spec "$(SPEC)" --latest-version) # latest Qt in SimpleSpec + [ $ver == $(QT_VERSION) ] # latest version in SPEC must be QT_VERSION + aqt list-qt $(HOST) $(TARGET) --spec "$(SPEC)" --modules latest $(ARCH) # print modules for latest in SimpleSpec + aqt list-qt $(HOST) $(TARGET) --modules $(QT_VERSION) $(ARCH) # print modules for version/host/target/arch + aqt list-qt $(HOST) $(TARGET) --arch $(QT_VERSION) # print architectures for version/host/target + if [[ "$(TARGET)" == "desktop" ]]; then + if [[ "$(HAS_WASM)" == "True" ]]; then # fail if 'wasm_32' is not in the list + aqt list-qt $(HOST) $(TARGET) --arch $(QT_VERSION) | grep -w -q "wasm_32" + else # fail if 'wasm_32' is in the list + ! aqt list-qt $(HOST) $(TARGET) --arch $(QT_VERSION) | grep -w -q "wasm_32" fi - python -m aqt $(SUBCOMMAND) $(HOST) $(TARGET) $(TOOL1_ARGS) $opt - $(LIST_TOOL1_CMD) - echo "Testing $(TOOL1_ARGS) with '$(TEST_TOOL1_CMD)'" - $(TEST_TOOL1_CMD) - python -m aqt $(SUBCOMMAND) $(HOST) $(TARGET) $(TOOL2_ARGS) $opt - $(LIST_TOOL2_CMD) - echo "Testing $(TOOL2_ARGS) with '$(TEST_TOOL2_CMD)'" - $(TEST_TOOL2_CMD) + + # Extra check for Qt 6.5.0 WASM: + for host in mac linux windows; do + export WASM_ARCHES=$(aqt list-qt $host desktop --arch 6.5.0) + echo $WASM_ARCHES | grep -w -q "wasm_singlethread" # fail if 'wasm_singlethread' is not in the list + echo $WASM_ARCHES | grep -w -q "wasm_multithread" # fail if 'wasm_multithread' is not in the list + done fi - workingDirectory: $(Build.BinariesDirectory) - env: - AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini - LOG_CFG: $(Build.SourcesDirectory)/ci/logging.ini - displayName: Run Aqt - - ##---------------------------------------------------- - # for Android target - - bash: | - set -ex - if [[ "$(Agent.OS)" == "Linux" ]]; then - wget https://dl.google.com/android/repository/android-ndk-r21e-linux-x86_64.zip - unzip android-ndk-r21e-linux-x86_64.zip + aqt list-qt $(HOST) $(TARGET) --spec "$(SPEC)" --arch latest + + # list-src/doc/example + aqt list-src $(HOST) $(QT_VERSION) # print source archives available for host/desktop/version + aqt list-doc $(HOST) $(QT_VERSION) # print docs archives available for host/desktop/version + aqt list-doc $(HOST) $(QT_VERSION) --modules # print docs modules available for host/desktop/version + aqt list-example $(HOST) $(QT_VERSION) # print example archives available for host/desktop/version + aqt list-example $(HOST) $(QT_VERSION) --modules # print example modules available for host/desktop/version + fi + if [[ "$(SUBCOMMAND)" == "install-src" ]]; then + python -m aqt $(SUBCOMMAND) $(HOST) $(QT_VERSION) --archives $(SUBARCHIVES) + $(CHECK_OUTPUT_CMD) + fi + if [[ "$(SUBCOMMAND)" =~ ^install-(doc|example)$ ]]; then + opt="" + if [[ "$(MODULE)" != "" ]]; then + opt+=" -m $(MODULE)" fi - if [[ "$(Agent.OS)" == "Darwin" ]]; then - wget https://dl.google.com/android/repository/android-ndk-r21e-darwin-x86_64.zip - unzip android-ndk-r21e-darwin-x86_64.zip + python -m aqt $(SUBCOMMAND) $(HOST) $(QT_VERSION) --archives $(SUBARCHIVES) $opt + $(CHECK_OUTPUT_CMD) + fi + if [[ "$(SUBCOMMAND)" == "install-tool" ]]; then + opt="" + if [[ "$(OUTPUT_DIR)" != "" ]]; then + opt+=" --outputdir $(OUTPUT_DIR)" + sudo mkdir -p "$(OUTPUT_DIR)" + sudo chown $(whoami) "$(OUTPUT_DIR)" fi - export ANDROID_NDK_ROOT=$(Build.SourcesDirectory)/android-ndk-r21e - mkdir $(Build.BinariesDirectory)/tests - (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/accelbubble.7z) - export PATH=$(QT_BINDIR):$PATH - qmake $(Build.BinariesDirectory)/tests/accelbubble - make - condition: | - and( - eq(variables['TARGET'], 'android'), - or( - eq(variables['Agent.OS'], 'Linux'), - eq(variables['Agent.OS'], 'Darwin') - ), - ne(variables['SUBCOMMAND'], 'list-qt'), - ne(variables['SUBCOMMAND'], 'install-tool') - ) - displayName: Build accelbubble example application to test for android + python -m aqt $(SUBCOMMAND) $(HOST) $(TARGET) $(TOOL1_ARGS) $opt + $(LIST_TOOL1_CMD) + echo "Testing $(TOOL1_ARGS) with '$(TEST_TOOL1_CMD)'" + $(TEST_TOOL1_CMD) + python -m aqt $(SUBCOMMAND) $(HOST) $(TARGET) $(TOOL2_ARGS) $opt + $(LIST_TOOL2_CMD) + echo "Testing $(TOOL2_ARGS) with '$(TEST_TOOL2_CMD)'" + $(TEST_TOOL2_CMD) + fi + workingDirectory: $(Build.BinariesDirectory) + env: + AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini + LOG_CFG: $(Build.SourcesDirectory)/ci/logging.ini + displayName: Run Aqt - ##---------------------------------------------------- - # for iOS target - - bash: | - set -ex - mkdir $(Build.BinariesDirectory)/tests - (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/accelbubble.7z) - export PATH=$(QT_BINDIR):$PATH - qmake $(Build.BinariesDirectory)/tests/accelbubble - make - condition: | - and(eq(variables['TARGET'], 'ios'), - ne(variables['SUBCOMMAND'], 'list'), - ne(variables['SUBCOMMAND'], 'install-tool')) - displayName: Build accelbubble example application to test for ios +##---------------------------------------------------- +# for Android target +- bash: | + set -ex + if [[ "$(Agent.OS)" == "Linux" ]]; then + wget https://dl.google.com/android/repository/android-ndk-r21e-linux-x86_64.zip + unzip android-ndk-r21e-linux-x86_64.zip + fi + if [[ "$(Agent.OS)" == "Darwin" ]]; then + wget https://dl.google.com/android/repository/android-ndk-r21e-darwin-x86_64.zip + unzip android-ndk-r21e-darwin-x86_64.zip + fi + export ANDROID_NDK_ROOT=$(Build.SourcesDirectory)/android-ndk-r21e + mkdir $(Build.BinariesDirectory)/tests + (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/accelbubble.7z) + export PATH=$(QT_BINDIR):$PATH + qmake $(Build.BinariesDirectory)/tests/accelbubble + make + condition: | + and( + eq(variables['TARGET'], 'android'), + or( + eq(variables['Agent.OS'], 'Linux'), + eq(variables['Agent.OS'], 'Darwin') + ), + ne(variables['SUBCOMMAND'], 'list-qt'), + ne(variables['SUBCOMMAND'], 'install-tool') + ) + displayName: Build accelbubble example application to test for android - ##---------------------------------------------------- - # Cache Powershell Modules in $MODULES_FOLDER - - task: Cache@2 - inputs: - key: '"pwsh modules" | "$(startYear)" | "$(startMonth)"' - path: $(MODULES_FOLDER) - cacheHitVar: PSModules_IsCached - condition: | - and(eq(variables['Agent.OS'], 'Windows_NT'), - eq(variables['SUBCOMMAND'], 'install-qt')) - displayName: Cache Powershell Modules - # On cache miss: Download Powershell Modules to $MODULES_FOLDER - - powershell: | - Install-PackageProvider NuGet -Force - Import-PackageProvider NuGet -Force - Set-PSRepository -Name PSGallery -InstallationPolicy Trusted - md -p $(MODULES_FOLDER) # mkdir - Save-Module -Name Pscx -AllowPrerelease -Path $(MODULES_FOLDER) - condition: | - and(eq(variables['Agent.OS'], 'Windows_NT'), - eq(variables['SUBCOMMAND'], 'install-qt'), - ne(variables.PSModules_IsCached, 'true')) - displayName: Download Powershell Modules for Windows on cache miss +##---------------------------------------------------- +# for iOS target +- bash: | + set -ex + mkdir $(Build.BinariesDirectory)/tests + (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/accelbubble.7z) + export PATH=$(QT_BINDIR):$PATH + qmake $(Build.BinariesDirectory)/tests/accelbubble + make + condition: | + and(eq(variables['TARGET'], 'ios'), + ne(variables['SUBCOMMAND'], 'list'), + ne(variables['SUBCOMMAND'], 'install-tool')) + displayName: Build accelbubble example application to test for ios - ##---------------------------------------------------- - # determine Windows build system - - powershell: | - if ('$(ARCH)' -like '*msvc*') { - Write-Host '##vso[task.setvariable variable=TOOLCHAIN]MSVC' - } - if ('$(ARCH)' -like '*mingw*') { - Write-Host '##vso[task.setvariable variable=TOOLCHAIN]MINGW' - } - if ('$(ARCH)' -like 'win64_msvc*') { - Write-Host '##vso[task.setvariable variable=ARCHITECTURE]amd64' - } else { - Write-Host '##vso[task.setvariable variable=ARCHITECTURE]x86' - } - if ('$(ARCH)' -like '*msvc*') { - Write-Host '##vso[task.setvariable variable=VSVER]2022' - } - cd $(WIN_QT_BINDIR) - unzip $(Build.SourcesDirectory)\ci\jom_1_1_3.zip - condition: | - and(eq(variables['Agent.OS'], 'Windows_NT'), - eq(variables['SUBCOMMAND'], 'install-qt')) - displayName: Detect toolchain for Windows and update PATH +##---------------------------------------------------- +# Cache Powershell Modules in $MODULES_FOLDER +- task: Cache@2 + inputs: + key: '"pwsh modules" | "$(startYear)" | "$(startMonth)"' + path: $(MODULES_FOLDER) + cacheHitVar: PSModules_IsCached + condition: | + and(eq(variables['Agent.OS'], 'Windows_NT'), + eq(variables['SUBCOMMAND'], 'install-qt')) + displayName: Cache Powershell Modules +# On cache miss: Download Powershell Modules to $MODULES_FOLDER +- powershell: | + Install-PackageProvider NuGet -Force + Import-PackageProvider NuGet -Force + Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + md -p $(MODULES_FOLDER) # mkdir + Save-Module -Name Pscx -AllowPrerelease -Path $(MODULES_FOLDER) + condition: | + and(eq(variables['Agent.OS'], 'Windows_NT'), + eq(variables['SUBCOMMAND'], 'install-qt'), + ne(variables.PSModules_IsCached, 'true')) + displayName: Download Powershell Modules for Windows on cache miss - # When no modules - - script: | - set -ex - mkdir $(Build.BinariesDirectory)/tests - (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/helloworld.7z) - export PATH=$(QT_BINDIR):$PATH - qmake $(Build.BinariesDirectory)/tests/helloworld - make - condition: | - and( - eq( variables['TARGET'], 'desktop' ), - not( startsWith( variables['ARCH'], 'wasm_' ) ), - or( - eq(variables['Agent.OS'], 'Linux'), - eq(variables['Agent.OS'], 'Darwin') - ), - eq(variables['MODULE'], ''), - eq(variables['SUBCOMMAND'], 'install-qt') - ) - displayName: Build test with qmake for Linux and macOS w/o extra module - # Update the MSVC build step to use the new compiler flags - - powershell: | - if ( $env:TOOLCHAIN -eq 'MSVC' ) { - # Load modules from cache - $Env:PSModulePath = '$(MODULES_FOLDER)', $Env:PSModulePath -join [System.IO.Path]::PathSeparator - Write-Host $Env:PSModulePath - Import-Module "Pscx" - Import-Module "VSSetup" +##---------------------------------------------------- +# determine Windows build system +- powershell: | + if ('$(ARCH)' -like '*msvc*') { + Write-Host '##vso[task.setvariable variable=TOOLCHAIN]MSVC' + } + if ('$(ARCH)' -like '*mingw*') { + Write-Host '##vso[task.setvariable variable=TOOLCHAIN]MINGW' + } + if ('$(ARCH)' -like 'win64_msvc*') { + Write-Host '##vso[task.setvariable variable=ARCHITECTURE]amd64' + } else { + Write-Host '##vso[task.setvariable variable=ARCHITECTURE]x86' + } + if ('$(ARCH)' -like '*msvc*') { + Write-Host '##vso[task.setvariable variable=VSVER]2022' + } + cd $(WIN_QT_BINDIR) + unzip $(Build.SourcesDirectory)\ci\jom_1_1_3.zip + condition: | + and(eq(variables['Agent.OS'], 'Windows_NT'), + eq(variables['SUBCOMMAND'], 'install-qt')) + displayName: Detect toolchain for Windows and update PATH - Import-VisualStudioVars -VisualStudioVersion $(VSVER) -Architecture $(ARCHITECTURE) - $env:Path += ";$(WIN_QT_BINDIR)" - mkdir $(Build.BinariesDirectory)\tests - cd $(Build.BinariesDirectory)\tests - 7z x $(Build.SourcesDirectory)\ci\helloworld.7z - cd .. - qmake $(Build.BinariesDirectory)\tests\helloworld - jom - } elseif ( $env:TOOLCHAIN -eq 'MINGW' ) { - python -m aqt install-tool $(if (($QT_BASE_MIRROR + "") -ne "") { "-b $QT_BASE_MIRROR" } else {""}) ` - --outputdir $(Build.BinariesDirectory)/Qt $(HOST) desktop $(MINGW_TOOL_NAME) qt.tools.$(MINGW_VARIANT) - if ($?) { - Write-Host 'Successfully installed tools_mingw' - } else { - throw 'Failed to install tools_mingw' - } - Set-Item -Path Env:Path -Value ("$(Build.BinariesDirectory)\Qt\Tools\$(MINGW_FOLDER)\bin;$(WIN_QT_BINDIR);" + $Env:Path) - Write-Host "Path == " + $env:Path - mkdir $(Build.BinariesDirectory)\tests - cd $(Build.BinariesDirectory)\tests - 7z x $(Build.SourcesDirectory)\ci\helloworld.7z - cd .. - qmake $(Build.BinariesDirectory)\tests\helloworld - mingw32-make - } - condition: | - and( - eq(variables['Agent.OS'], 'Windows_NT'), - eq(variables['MODULE'], ''), - eq(variables['SUBCOMMAND'], 'install-qt') - ) - displayName: build test with qmake w/o extra module - env: - AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini - LOG_CFG: $(Build.SourcesDirectory)/ci/logging.ini - -# When --archives non-empty - - script: | - set -ex - rm -rf $(Build.BinariesDirectory)/tests - mkdir $(Build.BinariesDirectory)/tests - (cd $(Build.BinariesDirectory)/tests && 7zr x $(Build.SourcesDirectory)/ci/helloworld_qttools.7z) - export PATH=$(QT_BINDIR):$PATH - qmake -d $(Build.BinariesDirectory)/tests/helloworld_qttools - make - condition: | - and( - eq( variables['TARGET'], 'desktop' ), - not( startsWith( variables['ARCH'], 'wasm_' ) ), - or( - eq(variables['Agent.OS'], 'Linux'), - eq(variables['Agent.OS'], 'Darwin') - ), - contains(variables['SUBARCHIVES'], 'qttools'), - eq(variables['SUBCOMMAND'], 'install-qt') - ) - displayName: build test with qmake for Linux and macOS with specific Qt modules (QT += uitools) - - powershell: | - if ( $env:TOOLCHAIN -eq 'MSVC' ) { - # Load modules from cache - $Env:PSModulePath = '$(MODULES_FOLDER)', $Env:PSModulePath -join [System.IO.Path]::PathSeparator - Write-Host $Env:PSModulePath - Import-Module "Pscx" - Import-Module "VSSetup" - - Import-VisualStudioVars -VisualStudioVersion $(VSVER) -Architecture $(ARCHITECTURE) - $env:Path += ";$(WIN_QT_BINDIR)" - try { rm -r -fo $(Build.BinariesDirectory)\tests } catch { $Error.Clear() } - mkdir $(Build.BinariesDirectory)\tests - cd $(Build.BinariesDirectory)\tests - 7z x $(Build.SourcesDirectory)\ci\helloworld_qttools.7z - qmake -d $(Build.BinariesDirectory)\tests\helloworld_qttools - nmake - } elseif ( $env:TOOLCHAIN -eq 'MINGW' ) { - Set-Item -Path Env:Path -Value ("$(Build.BinariesDirectory)\Qt\Tools\$(MINGW_FOLDER)\bin;$(WIN_QT_BINDIR);" + $Env:Path) - Write-Host "Path == " + $env:Path - if (![bool] (Get-Command -ErrorAction Ignore -Type Application mingw32-make)) { - python -m aqt install-tool $(if (($QT_BASE_MIRROR + "") -ne "") { "-b $QT_BASE_MIRROR" } else {""}) ` - --outputdir $(Build.BinariesDirectory)/Qt $(HOST) desktop $(MINGW_TOOL_NAME) qt.tools.$(MINGW_VARIANT) - if ($?) { - Write-Host 'Successfully installed tools_mingw' - } else { - throw 'Failed to install tools_mingw' - } - } - try { rm -r -fo $(Build.BinariesDirectory)\tests } catch { $Error.Clear() } - mkdir $(Build.BinariesDirectory)\tests - cd $(Build.BinariesDirectory)\tests - 7z x $(Build.SourcesDirectory)\ci\helloworld_qttools.7z - qmake -d $(Build.BinariesDirectory)\tests\helloworld_qttools - mingw32-make - } - condition: | - and( - eq( variables['Agent.OS'], 'Windows_NT'), - contains(variables['SUBARCHIVES'], 'qttools'), - eq(variables['SUBCOMMAND'], 'install-qt') - ) - displayName: build test with qmake with specific Qt modules (QT += uitools) - env: - AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini - LOG_CFG: $(Build.SourcesDirectory)/ci/logging.ini - - - powershell: | +# When no modules +- script: | + set -ex + mkdir $(Build.BinariesDirectory)/tests + (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/helloworld.7z) + export PATH=$(QT_BINDIR):$PATH + qmake $(Build.BinariesDirectory)/tests/helloworld + make + condition: | + and( + eq( variables['TARGET'], 'desktop' ), + not( startsWith( variables['ARCH'], 'wasm_' ) ), + or( + eq(variables['Agent.OS'], 'Linux'), + eq(variables['Agent.OS'], 'Darwin') + ), + eq(variables['MODULE'], ''), + eq(variables['SUBCOMMAND'], 'install-qt') + ) + displayName: Build test with qmake for Linux and macOS w/o extra module +# Update the MSVC build step to use the new compiler flags +- powershell: | + if ( $env:TOOLCHAIN -eq 'MSVC' ) { # Load modules from cache $Env:PSModulePath = '$(MODULES_FOLDER)', $Env:PSModulePath -join [System.IO.Path]::PathSeparator Write-Host $Env:PSModulePath Import-Module "Pscx" Import-Module "VSSetup" - + Import-VisualStudioVars -VisualStudioVersion $(VSVER) -Architecture $(ARCHITECTURE) $env:Path += ";$(WIN_QT_BINDIR)" - echo Add Qt to PATH: $env:PATH - mkdir $(Build.BinariesDirectory)/tests - cd $(Build.BinariesDirectory)/tests - 7z x $(Build.SourcesDirectory)/ci/redditclient.7z - cd .. - qmake $(Build.BinariesDirectory)\tests\redditclient - nmake - condition: | - and( - eq(variables['Agent.OS'], 'Windows_NT'), - eq(variables['TOOLCHAIN'], 'MSVC'), - ne(variables['MODULE'], ''), - ne(variables['VSVER'], '2022') - ) - displayName: build test with qmake with MSVC with extra module - - bash: | - set -ex - mkdir $(Build.BinariesDirectory)/tests - (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/redditclient.7z) - if [[ "6" -eq "${QT_VERSION:0:1}" ]]; then - (cd $(Build.BinariesDirectory)/tests/redditclient;patch -i redditclient_6.diff -p1) - fi - export PATH=$(QT_BINDIR):$PATH - qmake $(Build.BinariesDirectory)/tests/redditclient - make - condition: | - and( - eq( variables['TARGET'], 'desktop'), - or( - eq(variables['Agent.OS'], 'Linux'), - eq(variables['Agent.OS'], 'Darwin') - ), - ne(variables['MODULE'], ''), - eq(variables['SUBCOMMAND'], 'install-qt') - ) - displayName: Build test with qmake for Linux and macOS with extra module - - ##---------------------------------------------------- - # wasm_32/single/multithread on linux and mac - - script: | - set -uex - git clone --depth=1 --branch=$(EMSDK_TAG) https://github.com/emscripten-core/emsdk.git - cd emsdk - ./emsdk install $(EMSDK_VERSION) - ./emsdk activate --embedded $(EMSDK_VERSION) - source $(Build.BinariesDirectory)/emsdk/emsdk_env.sh - mkdir $(Build.BinariesDirectory)/tests - if [[ $(QT_VERSION) = 6* ]]; then - OPENGLWINDOW_7Z="openglwindow_qt6.7z" - else - OPENGLWINDOW_7Z="openglwindow.7z" - fi - (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/$OPENGLWINDOW_7Z) - export PATH=$(QT_BINDIR):$PATH - qmake $(Build.BinariesDirectory)/tests/openglwindow - make - workingDirectory: $(Build.BinariesDirectory) - condition: | - and( - startsWith( variables['ARCH'], 'wasm_' ), - or( - eq(variables['Agent.OS'], 'Linux'), - eq(variables['Agent.OS'], 'Darwin') - ) - ) - displayName: 'Build WebAssembler sample project on mac/linux' - - # wasm_32/single/multithread on Windows cmd.exe - - powershell: | - git clone --depth=1 --branch=$(EMSDK_TAG) https://github.com/emscripten-core/emsdk.git - cd emsdk - .\emsdk install $(EMSDK_VERSION) - .\emsdk activate --embedded $(EMSDK_VERSION) - .\emsdk_env.bat mkdir $(Build.BinariesDirectory)\tests cd $(Build.BinariesDirectory)\tests - $env:Path += ";$(WIN_QT_BINDIR)" - echo "Add Qt/qmake to PATH at $(WIN_QT_BINDIR):" - echo "$env:Path" - if ('$(QT_VERSION)' -like '6*') { - 7z x $(Build.SourcesDirectory)\ci\openglwindow_qt6.7z - echo "Inlined qmake.bat command is: $(WIN_AUTODESK_QT_BINDIR)\qmake -qtconf $(WIN_QT_BINDIR)\target_qt.conf $(Build.BinariesDirectory)\tests\openglwindow" - $(WIN_AUTODESK_QT_BINDIR)\qmake.exe -qtconf "$(WIN_QT_BINDIR)\target_qt.conf" $(Build.BinariesDirectory)\tests\openglwindow + 7z x $(Build.SourcesDirectory)\ci\helloworld.7z + cd .. + qmake $(Build.BinariesDirectory)\tests\helloworld + jom + } elseif ( $env:TOOLCHAIN -eq 'MINGW' ) { + python -m aqt install-tool $(if (($QT_BASE_MIRROR + "") -ne "") { "-b $QT_BASE_MIRROR" } else {""}) ` + --outputdir $(Build.BinariesDirectory)/Qt $(HOST) desktop $(MINGW_TOOL_NAME) qt.tools.$(MINGW_VARIANT) + if ($?) { + Write-Host 'Successfully installed tools_mingw' } else { - 7z x $(Build.SourcesDirectory)\ci\openglwindow.7z - echo "Qt5: run qmake.exe" - qmake $(Build.BinariesDirectory)\tests\openglwindow - } - if ($false -eq $?) { - Write-Error "qmake failed." - Write-Host "##vso[task.logissue type=error]qmake failed." - exit(1) + throw 'Failed to install tools_mingw' } - make - if ($false -eq $?) { - Write-Error "make failed." - Write-Host "##vso[task.logissue type=error]nmake failed." - exit(1) + Set-Item -Path Env:Path -Value ("$(Build.BinariesDirectory)\Qt\Tools\$(MINGW_FOLDER)\bin;$(WIN_QT_BINDIR);" + $Env:Path) + Write-Host "Path == " + $env:Path + mkdir $(Build.BinariesDirectory)\tests + cd $(Build.BinariesDirectory)\tests + 7z x $(Build.SourcesDirectory)\ci\helloworld.7z + cd .. + qmake $(Build.BinariesDirectory)\tests\helloworld + mingw32-make + } + condition: | + and( + eq(variables['Agent.OS'], 'Windows_NT'), + eq(variables['MODULE'], ''), + eq(variables['SUBCOMMAND'], 'install-qt') + ) + displayName: build test with qmake w/o extra module + env: + AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini + LOG_CFG: $(Build.SourcesDirectory)/ci/logging.ini + +# When --archives non-empty +- script: | + set -ex + rm -rf $(Build.BinariesDirectory)/tests + mkdir $(Build.BinariesDirectory)/tests + (cd $(Build.BinariesDirectory)/tests && 7zr x $(Build.SourcesDirectory)/ci/helloworld_qttools.7z) + export PATH=$(QT_BINDIR):$PATH + qmake -d $(Build.BinariesDirectory)/tests/helloworld_qttools + make + condition: | + and( + eq( variables['TARGET'], 'desktop' ), + not( startsWith( variables['ARCH'], 'wasm_' ) ), + or( + eq(variables['Agent.OS'], 'Linux'), + eq(variables['Agent.OS'], 'Darwin') + ), + contains(variables['SUBARCHIVES'], 'qttools'), + eq(variables['SUBCOMMAND'], 'install-qt') + ) + displayName: build test with qmake for Linux and macOS with specific Qt modules (QT += uitools) +- powershell: | + if ( $env:TOOLCHAIN -eq 'MSVC' ) { + # Load modules from cache + $Env:PSModulePath = '$(MODULES_FOLDER)', $Env:PSModulePath -join [System.IO.Path]::PathSeparator + Write-Host $Env:PSModulePath + Import-Module "Pscx" + Import-Module "VSSetup" + + Import-VisualStudioVars -VisualStudioVersion $(VSVER) -Architecture $(ARCHITECTURE) + $env:Path += ";$(WIN_QT_BINDIR)" + try { rm -r -fo $(Build.BinariesDirectory)\tests } catch { $Error.Clear() } + mkdir $(Build.BinariesDirectory)\tests + cd $(Build.BinariesDirectory)\tests + 7z x $(Build.SourcesDirectory)\ci\helloworld_qttools.7z + qmake -d $(Build.BinariesDirectory)\tests\helloworld_qttools + nmake + } elseif ( $env:TOOLCHAIN -eq 'MINGW' ) { + Set-Item -Path Env:Path -Value ("$(Build.BinariesDirectory)\Qt\Tools\$(MINGW_FOLDER)\bin;$(WIN_QT_BINDIR);" + $Env:Path) + Write-Host "Path == " + $env:Path + if (![bool] (Get-Command -ErrorAction Ignore -Type Application mingw32-make)) { + python -m aqt install-tool $(if (($QT_BASE_MIRROR + "") -ne "") { "-b $QT_BASE_MIRROR" } else {""}) ` + --outputdir $(Build.BinariesDirectory)/Qt $(HOST) desktop $(MINGW_TOOL_NAME) qt.tools.$(MINGW_VARIANT) + if ($?) { + Write-Host 'Successfully installed tools_mingw' + } else { + throw 'Failed to install tools_mingw' + } } - workingDirectory: $(Build.BinariesDirectory) - condition: | - and( - startsWith( variables['ARCH'], 'wasm_' ), - eq( variables['Agent.OS'], 'Windows_NT' ) + try { rm -r -fo $(Build.BinariesDirectory)\tests } catch { $Error.Clear() } + mkdir $(Build.BinariesDirectory)\tests + cd $(Build.BinariesDirectory)\tests + 7z x $(Build.SourcesDirectory)\ci\helloworld_qttools.7z + qmake -d $(Build.BinariesDirectory)\tests\helloworld_qttools + mingw32-make + } + condition: | + and( + eq( variables['Agent.OS'], 'Windows_NT'), + contains(variables['SUBARCHIVES'], 'qttools'), + eq(variables['SUBCOMMAND'], 'install-qt') + ) + displayName: build test with qmake with specific Qt modules (QT += uitools) + env: + AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini + LOG_CFG: $(Build.SourcesDirectory)/ci/logging.ini + +- powershell: | + # Load modules from cache + $Env:PSModulePath = '$(MODULES_FOLDER)', $Env:PSModulePath -join [System.IO.Path]::PathSeparator + Write-Host $Env:PSModulePath + Import-Module "Pscx" + Import-Module "VSSetup" + + Import-VisualStudioVars -VisualStudioVersion $(VSVER) -Architecture $(ARCHITECTURE) + $env:Path += ";$(WIN_QT_BINDIR)" + echo Add Qt to PATH: $env:PATH + mkdir $(Build.BinariesDirectory)/tests + cd $(Build.BinariesDirectory)/tests + 7z x $(Build.SourcesDirectory)/ci/redditclient.7z + cd .. + qmake $(Build.BinariesDirectory)\tests\redditclient + nmake + condition: | + and( + eq(variables['Agent.OS'], 'Windows_NT'), + eq(variables['TOOLCHAIN'], 'MSVC'), + ne(variables['MODULE'], ''), + ne(variables['VSVER'], '2022') + ) + displayName: build test with qmake with MSVC with extra module +- bash: | + set -ex + mkdir $(Build.BinariesDirectory)/tests + (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/redditclient.7z) + if [[ "6" -eq "${QT_VERSION:0:1}" ]]; then + (cd $(Build.BinariesDirectory)/tests/redditclient;patch -i redditclient_6.diff -p1) + fi + export PATH=$(QT_BINDIR):$PATH + qmake $(Build.BinariesDirectory)/tests/redditclient + make + condition: | + and( + eq( variables['TARGET'], 'desktop'), + or( + eq(variables['Agent.OS'], 'Linux'), + eq(variables['Agent.OS'], 'Darwin') + ), + ne(variables['MODULE'], ''), + eq(variables['SUBCOMMAND'], 'install-qt') + ) + displayName: Build test with qmake for Linux and macOS with extra module + +##---------------------------------------------------- +# wasm_32/single/multithread on linux and mac +- script: | + set -uex + git clone --depth=1 --branch=$(EMSDK_TAG) https://github.com/emscripten-core/emsdk.git + cd emsdk + ./emsdk install $(EMSDK_VERSION) + ./emsdk activate --embedded $(EMSDK_VERSION) + source $(Build.BinariesDirectory)/emsdk/emsdk_env.sh + mkdir $(Build.BinariesDirectory)/tests + if [[ $(QT_VERSION) = 6* ]]; then + OPENGLWINDOW_7Z="openglwindow_qt6.7z" + else + OPENGLWINDOW_7Z="openglwindow.7z" + fi + (cd $(Build.BinariesDirectory)/tests; 7zr x $(Build.SourcesDirectory)/ci/$OPENGLWINDOW_7Z) + export PATH=$(QT_BINDIR):$PATH + qmake $(Build.BinariesDirectory)/tests/openglwindow + make + workingDirectory: $(Build.BinariesDirectory) + condition: | + and( + startsWith( variables['ARCH'], 'wasm_' ), + or( + eq(variables['Agent.OS'], 'Linux'), + eq(variables['Agent.OS'], 'Darwin') ) - displayName: 'Build WebAssembler sample project on windows' + ) + displayName: 'Build WebAssembler sample project on mac/linux' + +# wasm_32/single/multithread on Windows cmd.exe +- powershell: | + git clone --depth=1 --branch=$(EMSDK_TAG) https://github.com/emscripten-core/emsdk.git + cd emsdk + .\emsdk install $(EMSDK_VERSION) + .\emsdk activate --embedded $(EMSDK_VERSION) + .\emsdk_env.bat + mkdir $(Build.BinariesDirectory)\tests + cd $(Build.BinariesDirectory)\tests + $env:Path += ";$(WIN_QT_BINDIR)" + echo "Add Qt/qmake to PATH at $(WIN_QT_BINDIR):" + echo "$env:Path" + if ('$(QT_VERSION)' -like '6*') { + 7z x $(Build.SourcesDirectory)\ci\openglwindow_qt6.7z + echo "Inlined qmake.bat command is: $(WIN_AUTODESK_QT_BINDIR)\qmake -qtconf $(WIN_QT_BINDIR)\target_qt.conf $(Build.BinariesDirectory)\tests\openglwindow" + $(WIN_AUTODESK_QT_BINDIR)\qmake.exe -qtconf "$(WIN_QT_BINDIR)\target_qt.conf" $(Build.BinariesDirectory)\tests\openglwindow + } else { + 7z x $(Build.SourcesDirectory)\ci\openglwindow.7z + echo "Qt5: run qmake.exe" + qmake $(Build.BinariesDirectory)\tests\openglwindow + } + if ($false -eq $?) { + Write-Error "qmake failed." + Write-Host "##vso[task.logissue type=error]qmake failed." + exit(1) + } + make + if ($false -eq $?) { + Write-Error "make failed." + Write-Host "##vso[task.logissue type=error]nmake failed." + exit(1) + } + workingDirectory: $(Build.BinariesDirectory) + condition: | + and( + startsWith( variables['ARCH'], 'wasm_' ), + eq( variables['Agent.OS'], 'Windows_NT' ) + ) + displayName: 'Build WebAssembler sample project on windows' diff --git a/tests/test_cli.py b/tests/test_cli.py index a6577f9d..62616975 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,4 +1,3 @@ -import platform import re import sys from pathlib import Path @@ -16,9 +15,8 @@ def expected_help(actual, prefix=None): expected = ( "usage: aqt [-h] [-c CONFIG]\n" - " {install-qt,install-tool,install-qt-commercial,install-doc,install-example," - "install-src," - "list-qt,list-qt-commercial,list-tool,list-doc,list-example,list-src,help,version}\n" + " {install-qt,install-tool,install-qt-commercial,list-qt-commercial,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" @@ -34,9 +32,8 @@ def expected_help(actual, prefix=None): " install-* subcommands are commands that install components\n" " list-* subcommands are commands that show available components\n" "\n" - " {install-qt,install-tool,install-qt-commercial,install-doc,install-example," - "install-src,list-qt,list-qt-commercial," - "list-tool,list-doc,list-example,list-src,help,version}\n" + " {install-qt,install-tool,install-qt-commercial,list-qt-commercial,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", ) @@ -523,29 +520,3 @@ def test_get_autodesktop_dir_and_arch_non_android( ), "Expected autodesktop install message." elif expect["instruct"]: assert any("You can install" in line for line in err_lines), "Expected install instruction message." - - -@pytest.mark.parametrize( - "cmd, expected_arch, expected_err", - [ - pytest.param( - "install-qt-commercial desktop {} 6.8.0", - {"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"}, - "No Qt account credentials found. Either provide --user and --password or", - ), - ], -) -def test_cli_login_qt_commercial(capsys, monkeypatch, cmd, expected_arch, expected_err): - """Test commercial Qt installation command""" - # Detect current platform - current_platform = platform.system().lower() - arch = expected_arch[current_platform] - cmd = cmd.format(arch) - - cli = Cli() - cli._setup_settings() - result = cli.run(cmd.split()) - - _, err = capsys.readouterr() - assert str(err).find(expected_err) - assert not result == 0 diff --git a/tests/test_commercial.py b/tests/test_commercial.py new file mode 100644 index 00000000..aaa09ff7 --- /dev/null +++ b/tests/test_commercial.py @@ -0,0 +1,416 @@ +import os +import shutil +import sys +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Dict, List, Optional + +import pytest +import requests + +from aqt.commercial import CommercialInstaller, QtPackageInfo, QtPackageManager +from aqt.exceptions import DiskAccessNotPermitted +from aqt.helper import Settings, get_qt_account_path +from aqt.installer import Cli +from aqt.metadata import Version + + +class CompletedProcess: + def __init__(self, args, returncode): + self.args = args + self.returncode = returncode + self.stdout = None + self.stderr = None + + +# Test data +MOCK_XML_RESPONSE = """ + + + +""" + +TEST_EMAIL = os.getenv("AQT_TEST_EMAIL") +TEST_PASSWORD = os.getenv("AQT_TEST_PASSWORD") + + +class MockResponse: + def __init__(self, status_code: int = 200, content: bytes = b"", text: str = "", headers: Dict = None): + self.status_code = status_code + self.content = content + self.text = text + self.headers = headers or {} + self.ok = status_code == 200 + + def raise_for_status(self): + if not self.ok: + raise requests.HTTPError(f"HTTP Error: {self.status_code}") + + def iter_content(self, chunk_size=None): + 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 +def commercial_installer(): + return CommercialInstaller( + target="desktop", + arch="gcc_64", + version="6.8.0", + username=TEST_EMAIL, + password=TEST_PASSWORD, + output_dir="./test_output", + ) + + +@pytest.mark.enable_socket +@pytest.mark.parametrize( + "cmd, expected_arch, expected_err", + [ + pytest.param( + "install-qt-commercial desktop {} 6.8.0", + {"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"}, + "No Qt account credentials found", + ), + ], +) +def test_cli_login_qt_commercial(capsys, monkeypatch, cmd, expected_arch, expected_err): + """Test commercial Qt installation command""" + # Detect current platform + current_platform = sys.platform.lower() + arch = expected_arch[current_platform] + cmd = cmd.format(arch) + + if get_qt_account_path().exists(): + os.remove(get_qt_account_path()) + + cli = Cli() + cli._setup_settings() + cli.run(cmd.split()) + + out, err = capsys.readouterr() + assert expected_err in err or expected_arch in out + + +def test_package_manager_init(): + """Test QtPackageManager initialization""" + manager = QtPackageManager( + arch="gcc_64", + version=Version("6.8.0"), + target="desktop", + username=TEST_EMAIL, + password=TEST_PASSWORD, + ) + assert manager.arch == "gcc_64" + assert str(manager.version) == "6.8.0" + assert manager.target == "desktop" + assert manager.username == TEST_EMAIL + assert manager.password == TEST_PASSWORD + + +@pytest.mark.parametrize( + "xml_content, expected_packages", + [ + ( + MOCK_XML_RESPONSE, + [ + QtPackageInfo(name="qt.qt6.680.gcc_64", displayname="Desktop gcc", version="6.8.0-0-202312011"), + QtPackageInfo(name="qt.qt6.680.addons.qtquick3d", displayname="Qt Quick 3D", version="6.8.0-0-202312011"), + ], + ) + ], +) +def test_parse_packages_xml(xml_content: str, expected_packages: List[QtPackageInfo]): + """Test parsing of package XML data""" + manager = QtPackageManager(arch="gcc_64", version=Version("6.8.0"), target="desktop") + manager._parse_packages_xml(xml_content) + + assert len(manager.packages) == len(expected_packages) + for actual, expected in zip(manager.packages, expected_packages): + assert actual.name == expected.name + assert actual.displayname == expected.displayname + assert actual.version == expected.version + + +def test_commercial_installer_auto_answers(): + """Test generation of auto-answer options""" + auto_answers = CommercialInstaller.get_auto_answers() + assert "OperationDoesNotExistError=Ignore" in auto_answers + assert "OverwriteTargetDirectory=No" in auto_answers + assert "telemetry-question=No" in auto_answers + + +@pytest.mark.parametrize( + "installer_path, override, username, password, output_dir, no_unattended, expected_cmd", + [ + ( + "/path/to/installer", + None, + "user", + "pass", + "./output", + False, + [ + "/path/to/installer", + "--accept-licenses", + "--accept-obligations", + "--confirm-command", + "--email", + "user", + "--pw", + "pass", + "--root", + str(Path("./output").absolute()), + "--auto-answer", + CommercialInstaller.get_auto_answers(), + ], + ), + ( + "/path/to/installer", + ["--override", "arg1", "arg2"], + None, + None, + None, + True, + ["/path/to/installer", "--override", "arg1", "arg2"], + ), + ], +) +def test_build_command( + installer_path: str, + override: Optional[List[str]], + username: Optional[str], + password: Optional[str], + output_dir: Optional[str], + no_unattended: bool, + expected_cmd: List[str], +): + """Test building of installer command""" + cmd = CommercialInstaller.build_command( + installer_path, + override=override, + username=username, + password=password, + output_dir=output_dir, + no_unattended=no_unattended, + ) + assert cmd == expected_cmd + + +@pytest.mark.enable_socket +def test_commercial_installer_download(monkeypatch, commercial_installer): + """Test downloading of commercial installer""" + + def mock_requests_get(*args, **kwargs): + return MockResponse(content=b"installer_content") + + monkeypatch.setattr(requests, "get", mock_requests_get) + + with TemporaryDirectory() as temp_dir: + target_path = Path(temp_dir) / "qt-installer" + commercial_installer.download_installer(target_path, timeout=60) + assert target_path.exists() + assert target_path.read_bytes() == b"installer_content" + + +@pytest.mark.parametrize( + "modules, expected_command", + [ + (None, ["install", "qt.qt6.680.gcc_64"]), + (["qtquick3d"], ["install", "qt.qt6.680.gcc_64", "qt.qt6.680.addons.qtquick3d"]), + (["all"], ["install", "qt.qt6.680.gcc_64", "qt.qt6.680.addons.qtquick3d"]), + ], +) +def test_get_install_command(monkeypatch, modules: Optional[List[str]], expected_command: List[str]): + """Test generation of install commands""" + manager = QtPackageManager(arch="gcc_64", version=Version("6.8.0"), target="desktop") + + def mock_gather_packages(self, installer_path: str) -> None: + self.packages = [ + QtPackageInfo(name="qt.qt6.680.gcc_64", displayname="Desktop gcc", version="6.8.0"), + QtPackageInfo(name="qt.qt6.680.addons.qtquick3d", displayname="Qt Quick 3D", version="6.8.0"), + ] + + monkeypatch.setattr(QtPackageManager, "gather_packages", mock_gather_packages) + + command = manager.get_install_command(modules, "./temp") + assert command == expected_command + + +@pytest.mark.enable_socket +@pytest.mark.parametrize( + "cmd, arch_dict, details, expected_command", + [ + ( + "install-qt-commercial desktop {} 6.8.1 " "--outputdir ./install-qt-commercial --user {} --password {}", + {"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"}, + ["./install-qt-commercial", "qt6", "681"], + "qt-unified-{}-x64-online.run --email ******** --pw ******** --root {} " + "--accept-licenses --accept-obligations " + "--confirm-command " + "--auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=No," + "stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore," + "AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}", + ), + ( + "install-qt-commercial desktop {} 6.8.1 --outputdir ./install-qt-commercial --user {} --password {}", + {"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"}, + ["./install-qt-commercial", "qt6", "681"], + "qt-unified-{}-x64-online.run --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( + capsys, monkeypatch, cmd: str, arch_dict: dict[str, str], details: list[str], expected_command: str +) -> None: + """Test commercial Qt installation command""" + + def mock_safely_run(*args, **kwargs): + return CompletedProcess(args=args[0], returncode=0) + + monkeypatch.setattr("aqt.commercial.safely_run", mock_safely_run) + + current_platform = sys.platform.lower() + arch = arch_dict[current_platform] + + abs_out = Path(details[0]).absolute() + + # Get the email and password from the test parameters + email = TEST_EMAIL + password = TEST_PASSWORD + + formatted_cmd = cmd.format(arch, email, password) + formatted_expected = expected_command.format(current_platform, abs_out, *details[1:], arch) + + cli = Cli() + cli._setup_settings() + + # First test the normal installation command + try: + cli.run(formatted_cmd.split()) + except AttributeError: + out = " ".join(capsys.readouterr()) + assert str(out).find(formatted_expected) >= 0 + + abs_out.joinpath(f"6.8.{str(details[2])[-1]}").mkdir(exist_ok=True, parents=True) + + # Create a new command with the temp directory + new_cmd = ( + f"install-qt-commercial desktop {arch} 6.8.{str(details[2])[-1]} --outputdir {abs_out} --user {email} " + f"--password {password}" + ) + + # This should raise DiskAccessNotPermitted only for the first test (680) + if details[2] == "680": + with pytest.raises(DiskAccessNotPermitted) as exc_info: + cli.run(new_cmd.split()) + assert "Target directory" in str(exc_info.value) + assert "already exists" in str(exc_info.value) + else: + cli.run(new_cmd.split()) + + def modify_qt_config(content): + """ + Takes content of INI file as string and returns modified content + """ + lines = content.splitlines() + in_qt_commercial = False + modified = [] + + for line in lines: + # Check if we're entering qtcommercial section + if line.strip() == "[qtcommercial]": + in_qt_commercial = True + + # If in qtcommercial section, look for the target line + if in_qt_commercial and "overwrite_target_directory : No" in line: + line = "overwrite_target_directory : Yes" + elif in_qt_commercial and "overwrite_target_directory : Yes" in line: + line = "overwrite_target_directory : No" + + modified.append(line) + + return "\n".join(modified) + + script_dir = os.path.dirname(os.path.abspath(__file__)) + config_path = os.path.join(script_dir, "../aqt/settings.ini") + + with open(config_path, "r") as f: + content = f.read() + + modified_content = modify_qt_config(content) + + with open(config_path, "w") as f: + f.write(modified_content) + Settings._initialize() + + shutil.rmtree(abs_out) + + +@pytest.mark.enable_socket +@pytest.mark.parametrize( + "args, expected_ret", + [ + ( + [ + "list-qt-commercial", + "--user", + TEST_EMAIL, + "--password", + TEST_PASSWORD, + "6.8.1", + ], + "Downloading", + ), + ( + [ + "install-qt-commercial", + "--override", + "search", + "6.8.0", + "--email", + TEST_EMAIL, + "--pw", + TEST_PASSWORD, + ], + "installing", + ), + ], +) +def test_list_qt_commercial(capsys, args, expected_ret): + """Test error handling in list-qt-commercial command""" + cli = Cli() + cli._setup_settings() + cli.run(args) + + out, err = capsys.readouterr() + + assert expected_ret in err or expected_ret in out + + +@pytest.mark.parametrize( + "args, expected_error", + [ + (["list-qt-commercial", "--bad-flag"], "usage: aqt [-h] [-c CONFIG]"), + ], +) +def test_list_qt_commercial_errors(capsys, args, expected_error): + """Test error handling in list-qt-commercial command""" + cli = Cli() + with pytest.raises(SystemExit): + cli.run(args) + _, err = capsys.readouterr() + assert expected_error in err diff --git a/tests/test_install.py b/tests/test_install.py index 02c0d787..585e2716 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -2054,104 +2054,3 @@ def mock_get_url(url: str, *args, **kwargs) -> str: sys.stderr.write(err) assert expect_out.match(err), err - - -class CompletedProcess: - def __init__(self, args, returncode): - self.args = args - self.returncode = returncode - self.stdout = None - self.stderr = None - - -@pytest.mark.enable_socket -@pytest.mark.parametrize( - "cmd, arch_dict, details, expected_command", - [ - ( - "install-qt-commercial desktop {} 6.8.0 " "--outputdir ./install-qt-commercial " "--user {} --password {}", - {"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"}, - ["./install-qt-commercial", "qt6", "680"], - "qt-unified-{}-x64-online.run --email ******** --pw ******** --root {} " - "--accept-licenses --accept-obligations " - "--confirm-command " - "--auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=No," - "stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore," - "AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}", - ), - ( - "install-qt-commercial desktop {} 6.8.1 " "--outputdir ./install-qt-commercial " "--user {} --password {}", - {"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"}, - ["./install-qt-commercial", "qt6", "681"], - "qt-unified-{}-x64-online.run --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( - capsys, monkeypatch, cmd: str, arch_dict: dict[str, str], details: list[str], expected_command: str -) -> None: - """Test commercial Qt installation command""" - - # Mock subprocess.run instead of run_static_subprocess_dynamically - def mock_subprocess_run(*args, **kwargs): - # This will be called instead of the real subprocess.run - return CompletedProcess(args=args[0], returncode=0) - - # Patch subprocess.run directly - monkeypatch.setattr("subprocess.run", mock_subprocess_run) - - current_platform = sys.platform.lower() - arch = arch_dict[current_platform] - - abs_out = Path(details[0]).absolute() - - formatted_cmd = cmd.format(arch, "vofab76634@gholar.com", "WxK43TdWCTmxsrrpnsWbjPfPXVq3mtLK") - formatted_expected = expected_command.format(current_platform, abs_out, *details[1:], arch) - - cli = Cli() - cli._setup_settings() - - try: - cli.run(formatted_cmd.split()) - except AttributeError: - out = " ".join(capsys.readouterr()) - assert str(out).find(formatted_expected) >= 0 - - def modify_qt_config(content): - """ - Takes content of INI file as string and returns modified content - """ - lines = content.splitlines() - in_qt_commercial = False - modified = [] - - for line in lines: - # Check if we're entering qtcommercial section - if line.strip() == "[qtcommercial]": - in_qt_commercial = True - - # If in qtcommercial section, look for the target line - if in_qt_commercial and "overwrite_target_directory : No" in line: - line = "overwrite_target_directory : Yes" - elif in_qt_commercial and "overwrite_target_directory : Yes" in line: - line = "overwrite_target_directory : No" - - modified.append(line) - - return "\n".join(modified) - - script_dir = os.path.dirname(os.path.abspath(__file__)) - config_path = os.path.join(script_dir, "../aqt/settings.ini") - - with open(config_path, "r") as f: - content = f.read() - - modified_content = modify_qt_config(content) - - with open(config_path, "w") as f: - f.write(modified_content) diff --git a/tests/test_list.py b/tests/test_list.py index 15d08241..be4ce88c 100644 --- a/tests/test_list.py +++ b/tests/test_list.py @@ -1336,19 +1336,3 @@ def test_find_installed_qt_mingw_dir(expected_result: str, installed_files: List actual_result = QtRepoProperty.find_installed_desktop_qt_dir(host, base_path, Version(qt_ver)) assert (actual_result.name if actual_result else None) == expected_result - - -# Test error cases -@pytest.mark.parametrize( - "args, expected_error", - [ - (["list-qt-commercial", "--bad-flag"], "usage: aqt [-h] [-c CONFIG]"), - ], -) -def test_list_qt_commercial_errors(capsys, args, expected_error): - """Test error handling in list-qt-commercial command""" - cli = Cli() - with pytest.raises(SystemExit): - cli.run(args) - _, err = capsys.readouterr() - assert expected_error in err