From 39129fce03532dd7fa9e67c48a66792a962b65c1 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Tue, 10 Dec 2024 19:01:06 -0500 Subject: [PATCH 1/9] Fix parallel desktop installation detection --- action/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action/src/main.ts b/action/src/main.ts index 4aac05d2..28961da9 100644 --- a/action/src/main.ts +++ b/action/src/main.ts @@ -84,7 +84,7 @@ const locateQtArchDir = (installDir: string): string => { const requiresParallelDesktop = qtArchDirs.filter((archPath) => { const archDir = path.basename(archPath); const versionDir = path.basename(path.join(archPath, "..")); - return versionDir.match(/^6\.\d+\.\d+$/) && archDir.match(/^(android*|ios|wasm*|msvc*_arm64)$/); + return versionDir.match(/^6\.\d+\.\d+$/) && archDir.match(/^(android.*|ios|wasm.*|msvc.*_arm64)$/); }); if (requiresParallelDesktop.length) { // NOTE: if multiple mobile/wasm installations coexist, this may not select the desired directory From 46f1e48bd72c877496ca3c64fe065185dba64fea Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Tue, 10 Dec 2024 20:14:26 -0500 Subject: [PATCH 2/9] Set QT_HOST_PATH when applicable --- action/src/main.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/action/src/main.ts b/action/src/main.ts index 28961da9..dd89b660 100644 --- a/action/src/main.ts +++ b/action/src/main.ts @@ -71,7 +71,7 @@ const flaggedList = (flag: string, listArgs: readonly string[]): string[] => { return listArgs.length ? [flag, ...listArgs] : []; }; -const locateQtArchDir = (installDir: string): string => { +const locateQtArchDir = (installDir: string): [string, boolean] => { // For 6.4.2/gcc, qmake is at 'installDir/6.4.2/gcc_64/bin/qmake'. // This makes a list of all the viable arch directories that contain a qmake file. const qtArchDirs = glob @@ -88,12 +88,12 @@ const locateQtArchDir = (installDir: string): string => { }); if (requiresParallelDesktop.length) { // NOTE: if multiple mobile/wasm installations coexist, this may not select the desired directory - return requiresParallelDesktop[0]; + return [requiresParallelDesktop[0], true]; } else if (!qtArchDirs.length) { throw Error(`Failed to locate a Qt installation directory in ${installDir}`); } else { // NOTE: if multiple Qt installations exist, this may not select the desired directory - return qtArchDirs[0]; + return [qtArchDirs[0], false]; } }; @@ -455,7 +455,7 @@ const run = async (): Promise => { } // Set environment variables/outputs for binaries if (inputs.isInstallQtBinaries) { - const qtPath = locateQtArchDir(inputs.dir); + const [qtPath, requiresParallelDesktop] = locateQtArchDir(inputs.dir); // Set outputs core.setOutput("qtPath", qtPath); @@ -474,6 +474,11 @@ const run = async (): Promise => { core.exportVariable("QT_ROOT_DIR", qtPath); core.exportVariable("QT_PLUGIN_PATH", path.resolve(qtPath, "plugins")); core.exportVariable("QML2_IMPORT_PATH", path.resolve(qtPath, "qml")); + if (requiresParallelDesktop) { + const qmakePath = path.resolve(qtPath, "bin", "qmake"); + const queryResult = await getExecOutput(`${qmakePath} -query QT_HOST_PREFIX`); + core.exportVariable("QT_HOST_PATH", path.normalize(queryResult.stdout.trim())); + } core.addPath(path.resolve(qtPath, "bin")); } } From ccb6cdbc3e3dc5588287f290fb5307499a1d15af Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Tue, 10 Dec 2024 21:51:34 -0500 Subject: [PATCH 3/9] lint --- action/src/main.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/action/src/main.ts b/action/src/main.ts index dd89b660..0c379891 100644 --- a/action/src/main.ts +++ b/action/src/main.ts @@ -84,7 +84,9 @@ const locateQtArchDir = (installDir: string): [string, boolean] => { const requiresParallelDesktop = qtArchDirs.filter((archPath) => { const archDir = path.basename(archPath); const versionDir = path.basename(path.join(archPath, "..")); - return versionDir.match(/^6\.\d+\.\d+$/) && archDir.match(/^(android.*|ios|wasm.*|msvc.*_arm64)$/); + return ( + versionDir.match(/^6\.\d+\.\d+$/) && archDir.match(/^(android.*|ios|wasm.*|msvc.*_arm64)$/) + ); }); if (requiresParallelDesktop.length) { // NOTE: if multiple mobile/wasm installations coexist, this may not select the desired directory From 99c242a43413cf86f4c2a3eceb77736a39af9505 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Tue, 10 Dec 2024 22:27:05 -0500 Subject: [PATCH 4/9] Fix macOS tests --- .github/workflows/test.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8b28d07f..cc5f8707 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -146,17 +146,14 @@ jobs: ls "${QT_ROOT_DIR}/bin/" | grep qmake - name: Switch macOS Xcode version with older Qt versions - if: ${{ matrix.qt.version && (startsWith(matrix.os, 'macos-13') || startsWith(matrix.os, 'macos-14')) }} + if: ${{ matrix.qt.version && startsWith(matrix.os, 'macos-13') }} shell: pwsh env: QT_VERSION: ${{ matrix.qt.version }} run: | - if ([version]$env:QT_VERSION -ge [version]"6.5.3") { - # GitHub macOS 13/14 runners use Xcode 15.0.x by default which has a known linker issue causing crashes if the artifact is run on macOS <= 12 - sudo xcode-select --switch /Applications/Xcode_15.2.app - } else { - # Keep older Qt versions on Xcode 14 due to concern over QTBUG-117484 - sudo xcode-select --switch /Applications/Xcode_14.3.1.app + if ([version]$env:QT_VERSION -lt [version]'6.5.3') { + # Workaround for QTBUG-117225 + sudo xcode-select --switch /Applications/Xcode_14.3.1.app } - name: Configure test project on windows From c8d2244b7cd3dd6a42c8ba63e697cdac379c361a Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Wed, 11 Dec 2024 23:00:03 -0500 Subject: [PATCH 5/9] Handle errors when querying qmake --- action/src/main.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/action/src/main.ts b/action/src/main.ts index 0c379891..1aef35e2 100644 --- a/action/src/main.ts +++ b/action/src/main.ts @@ -478,8 +478,10 @@ const run = async (): Promise => { core.exportVariable("QML2_IMPORT_PATH", path.resolve(qtPath, "qml")); if (requiresParallelDesktop) { const qmakePath = path.resolve(qtPath, "bin", "qmake"); - const queryResult = await getExecOutput(`${qmakePath} -query QT_HOST_PREFIX`); - core.exportVariable("QT_HOST_PATH", path.normalize(queryResult.stdout.trim())); + const val = await getExecOutput(`${qmakePath} -query QT_HOST_PREFIX`).catch(() => null); + if (val?.exitCode === 0) { + core.exportVariable("QT_HOST_PATH", path.normalize(val.stdout.trim())); + } } core.addPath(path.resolve(qtPath, "bin")); } From 5232a9f33752ae60b18ef405b472eaa8902f0b28 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Thu, 12 Dec 2024 19:11:01 -0500 Subject: [PATCH 6/9] Read host prefix from target_qt.conf instead of querying qmake --- action/src/main.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/action/src/main.ts b/action/src/main.ts index 1aef35e2..df2eaa5c 100644 --- a/action/src/main.ts +++ b/action/src/main.ts @@ -477,10 +477,12 @@ const run = async (): Promise => { core.exportVariable("QT_PLUGIN_PATH", path.resolve(qtPath, "plugins")); core.exportVariable("QML2_IMPORT_PATH", path.resolve(qtPath, "qml")); if (requiresParallelDesktop) { - const qmakePath = path.resolve(qtPath, "bin", "qmake"); - const val = await getExecOutput(`${qmakePath} -query QT_HOST_PREFIX`).catch(() => null); - if (val?.exitCode === 0) { - core.exportVariable("QT_HOST_PATH", path.normalize(val.stdout.trim())); + const hostPrefix = await fs.promises + .readFile(path.join(qtPath, "bin", "target_qt.conf"), "utf8") + .then((data) => data.match(/^HostPrefix=(.*)$/m)?.[1].trim() ?? "") + .catch(() => ""); + if (hostPrefix) { + core.exportVariable("QT_HOST_PATH", path.resolve(qtPath, "bin", hostPrefix)); } } core.addPath(path.resolve(qtPath, "bin")); From dc5cb24c9bfa9d61e2753baefa04006748d302e0 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Fri, 13 Dec 2024 01:24:43 -0500 Subject: [PATCH 7/9] Add test for QT_HOST_PATH --- .github/workflows/test-android.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/test-android.yml b/.github/workflows/test-android.yml index 77bde40b..2afd90db 100644 --- a/.github/workflows/test-android.yml +++ b/.github/workflows/test-android.yml @@ -52,6 +52,22 @@ jobs: example-archives: ${{ matrix.qt.example-archives }} example-modules: ${{ matrix.qt.example-modules }} + - name: Test QT_HOST_PATH + if: ${{ startsWith(matrix.qt.version, '6.') }} + shell: pwsh + run: | + $qmakeHostPrefix = [string](Resolve-Path -Path (qmake -query QT_HOST_PREFIX)) + if ($env:QT_HOST_PATH -ne $qmakeHostPrefix) { + throw "QT_HOST_PATH should match qmake's QT_HOST_PREFIX." + } + if ($env:QT_HOST_PATH -eq $env:QT_ROOT_DIR) { + throw "QT_HOST_PATH and QT_ROOT_DIR should be different." + } + if ((Split-Path -Path $env:QT_HOST_PATH -Parent) -ne (Split-Path -Path $env:QT_ROOT_DIR -Parent)) { + throw "QT_HOST_PATH and QT_ROOT_DIR should have the same parent directory." + } + Write-Host "All tests passed!" + - name: Install Android NDK shell: bash # Links to NDK are at https://github.com/android/ndk/wiki/Unsupported-Downloads From f8fda12bbedee5075997a8609ccbdd14f7c0653f Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Thu, 16 Jan 2025 18:50:11 -0500 Subject: [PATCH 8/9] Support ARM64 hosts --- README.md | 4 +++- action/src/main.ts | 30 +++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f613350b..a5acf843 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ This is the host platform of the Qt version you will be installing. It's unlikel For example, if you are building on Linux and targeting desktop, you would set host to `linux`. If you are building on Linux and targeting android, you would set host to `linux` also. The host platform is the platform that your application will build on, not its target platform. -Possible values: `windows`, `mac`, or `linux` +Possible values: `windows`, `windows_arm64`, `mac`, `linux` or `linux_arm64` Defaults to the current platform it is being run on. @@ -64,6 +64,8 @@ Windows w/ Qt >= 5.15 && Qt < 6.8: `win64_msvc2019_64` Windows w/ Qt >= 6.8: `win64_msvc2022_64` +Windows (ARM64) w/ Qt >= 6.8: `win64_msvc2022_arm64` + Android: `android_armv7` ### `dir` diff --git a/action/src/main.ts b/action/src/main.ts index df2eaa5c..6c28ceb5 100644 --- a/action/src/main.ts +++ b/action/src/main.ts @@ -71,21 +71,23 @@ const flaggedList = (flag: string, listArgs: readonly string[]): string[] => { return listArgs.length ? [flag, ...listArgs] : []; }; -const locateQtArchDir = (installDir: string): [string, boolean] => { +const locateQtArchDir = (installDir: string, host: string): [string, boolean] => { // For 6.4.2/gcc, qmake is at 'installDir/6.4.2/gcc_64/bin/qmake'. // This makes a list of all the viable arch directories that contain a qmake file. const qtArchDirs = glob .sync(`${installDir}/[0-9]*/*/bin/qmake*`) .map((s) => path.resolve(s, "..", "..")); - // For Qt6 mobile and wasm installations, and Qt6 Windows on ARM installations, + // For Qt6 mobile and wasm installations, and Qt6 Windows on ARM cross-compiled installations, // a standard desktop Qt installation must exist alongside the requested architecture. // In these cases, we must select the first item that ends with 'android*', 'ios', 'wasm*' or 'msvc*_arm64'. const requiresParallelDesktop = qtArchDirs.filter((archPath) => { const archDir = path.basename(archPath); const versionDir = path.basename(path.join(archPath, "..")); return ( - versionDir.match(/^6\.\d+\.\d+$/) && archDir.match(/^(android.*|ios|wasm.*|msvc.*_arm64)$/) + versionDir.match(/^6\.\d+\.\d+$/) && + (archDir.match(/^(android.*|ios|wasm.*)$/) || + (archDir.match(/^msvc.*_arm64$/) && host !== "windows_arm64")) ); }); if (requiresParallelDesktop.length) { @@ -106,7 +108,7 @@ const isAutodesktopSupported = async (): Promise => { }; class Inputs { - readonly host: "windows" | "mac" | "linux"; + readonly host: "windows" | "windows_arm64" | "mac" | "linux" | "linux_arm64"; readonly target: "desktop" | "android" | "ios"; readonly version: string; readonly arch: string; @@ -144,7 +146,7 @@ class Inputs { if (!host) { switch (process.platform) { case "win32": { - this.host = "windows"; + this.host = process.arch === "arm64" ? "windows_arm64" : "windows"; break; } case "darwin": { @@ -152,16 +154,24 @@ class Inputs { break; } default: { - this.host = "linux"; + this.host = process.arch === "arm64" ? "linux_arm64" : "linux"; break; } } } else { // Make sure host is one of the allowed values - if (host === "windows" || host === "mac" || host === "linux") { + if ( + host === "windows" || + host === "windows_arm64" || + host === "mac" || + host === "linux" || + host === "linux_arm64" + ) { this.host = host; } else { - throw TypeError(`host: "${host}" is not one of "windows" | "mac" | "linux"`); + throw TypeError( + `host: "${host}" is not one of "windows" | "windows_arm64" | "mac" | "linux" | "linux_arm64"` + ); } } @@ -200,6 +210,8 @@ class Inputs { } else { this.arch = "win64_msvc2017_64"; } + } else if (this.host === "windows_arm64") { + this.arch = "win64_msvc2022_arm64"; } } @@ -457,7 +469,7 @@ const run = async (): Promise => { } // Set environment variables/outputs for binaries if (inputs.isInstallQtBinaries) { - const [qtPath, requiresParallelDesktop] = locateQtArchDir(inputs.dir); + const [qtPath, requiresParallelDesktop] = locateQtArchDir(inputs.dir, inputs.host); // Set outputs core.setOutput("qtPath", qtPath); From ccf95d9524af497983ed5b0f506f503ef0d5dfb5 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Sat, 18 Jan 2025 17:27:40 -0500 Subject: [PATCH 9/9] Add test for Ubuntu ARM64 runner --- .github/workflows/test.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cc5f8707..3bd58beb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -96,6 +96,11 @@ jobs: qt: tools-only-build: true add-tools-to-path: false + - os: ubuntu-24.04-arm + qt: + version: "6.8.1" + requested: "6.8.1" + modules: qtwebengine qtpositioning qtwebchannel steps: @@ -123,7 +128,7 @@ jobs: dir: ${{ matrix.dir }} modules: ${{ matrix.qt.modules }} version: ${{ matrix.qt.requested }} - tools: tools_ifw tools_qtcreator,qt.tools.qtcreator + tools: tools_ifw tools_qtcreator_gui,qt.tools.qtcreator_gui cache: ${{ matrix.cache == 'cached' }} - name: Install Qt with options and specified aqtversion @@ -134,7 +139,7 @@ jobs: dir: ${{ matrix.dir }} modules: ${{ matrix.qt.modules }} version: ${{ matrix.qt.requested }} - tools: tools_ifw tools_qtcreator,qt.tools.qtcreator + tools: tools_ifw tools_qtcreator_gui,qt.tools.qtcreator_gui cache: ${{ matrix.cache == 'cached' }} - name: Test QT_ROOT_DIR @@ -228,7 +233,7 @@ jobs: with: dir: ${{ matrix.dir }} tools-only: true - tools: tools_ifw tools_qtcreator,qt.tools.qtcreator tools_cmake tools_ninja tools_conan + tools: tools_ifw tools_qtcreator_gui,qt.tools.qtcreator_gui tools_cmake tools_ninja tools_conan add-tools-to-path: ${{ matrix.qt.add-tools-to-path }} cache: ${{ matrix.cache == 'cached' }} @@ -240,7 +245,7 @@ jobs: set -x # tools_ifw: use `archivegen` to test that Tools/QtInstallerFramework/4.7/bin is added to path - # tools_qtcreator: use `qbs` to test that Tools/QtCreator/bin or "Qt Creator.app/Contents/MacOS/" is added to path + # tools_qtcreator_gui: use `qbs` to test that Tools/QtCreator/bin or "Qt Creator.app/Contents/MacOS/" is added to path # tools_cmake: test that Tools/CMake/bin or Tools/CMake/CMake.app/Contents/bin is added to path # tools_ninja: test that Tools/Ninja is added to path # tools_conan: test that Tools/Conan is added to path @@ -270,7 +275,7 @@ jobs: [[ -e "../Qt/Tools/Conan/conan" ]] # tools_ifw: use `archivegen` to test that Tools/QtInstallerFramework/4.7/bin is not added to path - # tools_qtcreator: use `qbs` to test that Tools/QtCreator/bin or "Qt Creator.app/Contents/MacOS/" is not added to path + # tools_qtcreator_gui: use `qbs` to test that Tools/QtCreator/bin or "Qt Creator.app/Contents/MacOS/" is not added to path # tools_cmake: test that Tools/CMake/bin or Tools/CMake/CMake.app/Contents/bin is not added to path # tools_ninja: test that Tools/Ninja is not added to path # tools_conan: test that Tools/Conan is not added to path