Skip to content

Commit 3c7fcb6

Browse files
committed
ci: add scratch native arm64 workflow
1 parent f680771 commit 3c7fcb6

2 files changed

Lines changed: 391 additions & 0 deletions

File tree

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
name: Build Windows ARM64 Native
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- arm64-native-from-scratch
8+
tags:
9+
- 'v*'
10+
11+
permissions:
12+
contents: write
13+
14+
jobs:
15+
build-windows-arm64-native:
16+
name: Build Native ARM64
17+
runs-on: windows-latest
18+
env:
19+
boost_version: "1.91.0"
20+
libt_version: "2.0.13"
21+
qbt_version: "5.2.3"
22+
qt_version: "6.10.3"
23+
vcpkg_triplet: "arm64-windows-static-release"
24+
boost_root: "${{ github.workspace }}/../boost"
25+
libtorrent_root: "${{ github.workspace }}/../libtorrent-msvc-arm64"
26+
qt_target_root: "${{ github.workspace }}/../qt-msvc-arm64"
27+
28+
steps:
29+
- name: Checkout repository
30+
uses: actions/checkout@v4
31+
32+
- name: Setup MSVC cross toolchain
33+
uses: ilammy/msvc-dev-cmd@v1
34+
with:
35+
arch: amd64_arm64
36+
37+
- name: Install build tools
38+
shell: pwsh
39+
run: |
40+
choco install ninja
41+
42+
- name: Setup vcpkg
43+
uses: lukka/run-vcpkg@v11
44+
with:
45+
vcpkgDirectory: C:/vcpkg
46+
doNotUpdateVcpkg: true
47+
48+
- name: Normalize vcpkg paths
49+
shell: pwsh
50+
run: |
51+
if (-not $env:RUNVCPKG_VCPKG_ROOT) {
52+
throw "RUNVCPKG_VCPKG_ROOT is not set"
53+
}
54+
55+
$vcpkgRoot = $env:RUNVCPKG_VCPKG_ROOT -replace '\\', '/'
56+
"VCPKG_ROOT=$vcpkgRoot" >> $env:GITHUB_ENV
57+
"VCPKG_CMAKE_ROOT=$vcpkgRoot" >> $env:GITHUB_ENV
58+
59+
- name: Install vcpkg dependencies
60+
shell: pwsh
61+
run: |
62+
$packages = @(
63+
"openssl:${{ env.vcpkg_triplet }}",
64+
"zlib:${{ env.vcpkg_triplet }}"
65+
)
66+
67+
& "$env:VCPKG_ROOT/vcpkg.exe" upgrade --no-dry-run
68+
& "$env:VCPKG_ROOT/vcpkg.exe" install --clean-after-build @packages
69+
70+
- name: Build source URLs
71+
shell: pwsh
72+
run: |
73+
$boostVersion = "${{ env.boost_version }}"
74+
$boostVersionUnderscored = $boostVersion -replace '\.', '_'
75+
"BOOST_URL=https://archives.boost.io/release/$boostVersion/source/boost_${boostVersionUnderscored}.7z" >> $env:GITHUB_ENV
76+
"QBT_URL=https://github.com/qbittorrent/qBittorrent/archive/refs/tags/release-${{ env.qbt_version }}.tar.gz" >> $env:GITHUB_ENV
77+
78+
- name: Download Boost
79+
shell: pwsh
80+
run: |
81+
aria2c "$env:BOOST_URL" -d "${{ runner.temp }}" -o "boost.7z"
82+
7z x "${{ runner.temp }}/boost.7z" -o"${{ github.workspace }}/.."
83+
Move-Item "${{ github.workspace }}/../boost_*" "${{ env.boost_root }}"
84+
85+
- name: Install Qt host tools
86+
uses: jurplel/install-qt-action@v4
87+
with:
88+
aqtversion: '==3.3.*'
89+
version: "${{ env.qt_version }}"
90+
archives: 'qtbase qttools'
91+
set-env: 'false'
92+
dir: 'C:'
93+
94+
- name: Show tool versions
95+
shell: pwsh
96+
run: |
97+
cmake --version
98+
ninja --version
99+
100+
- name: Build Qt for ARM64
101+
shell: pwsh
102+
run: |
103+
$env:Path = "C:\Qt\${{ env.qt_version }}\msvc2022_64\bin;" + $env:Path
104+
$vcpkgRoot = $env:VCPKG_ROOT
105+
106+
git clone https://github.com/qt/qt5.git qt6
107+
Set-Location qt6
108+
git checkout "v${{ env.qt_version }}"
109+
./configure -init-submodules -submodules qtbase,qtsvg
110+
Remove-Item CMakeCache.txt -ErrorAction SilentlyContinue
111+
112+
./configure -opensource -confirm-license -nomake tests -nomake examples `
113+
-static -static-runtime -release -xplatform win32-arm64-msvc `
114+
-prefix "${{ env.qt_target_root }}" `
115+
-qt-host-path "C:/Qt/${{ env.qt_version }}/msvc2022_64" `
116+
-system-zlib -schannel -qt-sqlite -sql-sqlite -no-sql-mysql -no-sql-odbc -no-sql-psql -c++std c++20 -- `
117+
-DCMAKE_PREFIX_PATH="$vcpkgRoot/installed/${{ env.vcpkg_triplet }}" `
118+
-DCMAKE_TOOLCHAIN_FILE="$vcpkgRoot/scripts/buildsystems/vcpkg.cmake" `
119+
-DVCPKG_TARGET_TRIPLET=${{ env.vcpkg_triplet }}
120+
121+
cmake --build .
122+
cmake --install .
123+
124+
- name: Build libtorrent for ARM64
125+
shell: pwsh
126+
run: |
127+
git clone `
128+
--branch v${{ env.libt_version }} `
129+
--depth 1 `
130+
--recurse-submodules `
131+
https://github.com/arvidn/libtorrent.git
132+
133+
Set-Location libtorrent
134+
$boostRoot = (Get-Item "${{ env.boost_root }}").FullName.Replace('\\', '/')
135+
$libtorrentRoot = (Resolve-Path "${{ env.libtorrent_root }}").Path.Replace('\\', '/')
136+
$toolchain = "$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
137+
138+
cmake `
139+
-B build `
140+
-G Ninja `
141+
-DCMAKE_BUILD_TYPE=Release `
142+
-DCMAKE_INSTALL_PREFIX="$libtorrentRoot" `
143+
-DCMAKE_TOOLCHAIN_FILE="$toolchain" `
144+
-DBOOST_ROOT="$boostRoot" `
145+
-DBUILD_SHARED_LIBS=OFF `
146+
-Ddeprecated-functions=OFF `
147+
-Dstatic_runtime=ON `
148+
-DVCPKG_TARGET_TRIPLET=${{ env.vcpkg_triplet }}
149+
150+
cmake --build build
151+
cmake --install build
152+
153+
- name: Build qBittorrent for ARM64
154+
shell: pwsh
155+
run: |
156+
$qtTargetRoot = (Get-Item "${{ env.qt_target_root }}").FullName.Replace('\\', '/')
157+
$hostQtRoot = "C:/Qt/${{ env.qt_version }}/msvc2022_64"
158+
$vcpkgInstalled = "$env:VCPKG_ROOT/installed/${{ env.vcpkg_triplet }}"
159+
$boostRoot = (Get-Item "${{ env.boost_root }}").FullName.Replace('\\', '/')
160+
$libtorrentDir = (Get-Item "${{ env.libtorrent_root }}").FullName.Replace('\\', '/') + '/lib/cmake/LibtorrentRasterbar'
161+
162+
$env:Path = "$qtTargetRoot/bin;$env:Path"
163+
$env:LDFLAGS += " /FORCE:MULTIPLE"
164+
165+
aria2c "$env:QBT_URL"
166+
tar -xf "qbittorrent-release-${{ env.qbt_version }}.tar.gz"
167+
Set-Location "qbittorrent-release-${{ env.qbt_version }}"
168+
169+
$fixCode = @"
170+
# Forced Global Promotion Hotfix
171+
find_package(ZLIB REQUIRED)
172+
if(TARGET ZLIB::ZLIB)
173+
set_property(TARGET ZLIB::ZLIB PROPERTY IMPORTED_GLOBAL TRUE)
174+
endif()
175+
find_package(OpenSSL REQUIRED)
176+
if(TARGET OpenSSL::SSL)
177+
set_property(TARGET OpenSSL::SSL PROPERTY IMPORTED_GLOBAL TRUE)
178+
endif()
179+
if(TARGET OpenSSL::Crypto)
180+
set_property(TARGET OpenSSL::Crypto PROPERTY IMPORTED_GLOBAL TRUE)
181+
endif()
182+
"@
183+
184+
$checkPackagesPath = "cmake/Modules/CheckPackages.cmake"
185+
$originalContent = Get-Content $checkPackagesPath -Raw
186+
Set-Content $checkPackagesPath -Value ($fixCode + "`n" + $originalContent)
187+
188+
cmake `
189+
-B build `
190+
-G Ninja `
191+
-DCMAKE_BUILD_TYPE=Release `
192+
-DCMAKE_PREFIX_PATH="$qtTargetRoot;$hostQtRoot;$vcpkgInstalled" `
193+
-DQT_HOST_PATH="$hostQtRoot" `
194+
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
195+
-DVCPKG_MANIFEST_MODE=OFF `
196+
-DBOOST_ROOT="$boostRoot" `
197+
-DLibtorrentRasterbar_DIR="$libtorrentDir" `
198+
-DMSVC_RUNTIME_DYNAMIC=OFF `
199+
-DSTACKTRACE=OFF `
200+
-DQT_NO_PRIVATE_MODULE_WARNING=ON `
201+
-DVCPKG_TARGET_TRIPLET=${{ env.vcpkg_triplet }}
202+
203+
cmake --build build
204+
205+
- name: Package artifact
206+
shell: pwsh
207+
run: |
208+
New-Item -ItemType Directory -Path upload -Force | Out-Null
209+
Copy-Item "qbittorrent-release-${{ env.qbt_version }}/build/qbittorrent.exe" upload
210+
Copy-Item "qbittorrent-release-${{ env.qbt_version }}/dist/windows/qt.conf" upload
211+
Set-Location upload
212+
7z a "qbittorrent_${{ env.qbt_version }}_arm64.zip" *
213+
Move-Item "qbittorrent_${{ env.qbt_version }}_arm64.zip" ..
214+
215+
- name: Upload build artifact
216+
uses: actions/upload-artifact@v4
217+
with:
218+
name: qBittorrent_${{ env.qbt_version }}_arm64_native
219+
path: upload
220+
221+
- name: Publish tagged release
222+
if: startsWith(github.ref, 'refs/tags/')
223+
uses: softprops/action-gh-release@v2
224+
with:
225+
tag_name: v${{ env.qbt_version }}
226+
name: qBittorrent ${{ env.qbt_version }} (Windows ARM64 Native)
227+
files: qbittorrent_${{ env.qbt_version }}_arm64.zip
228+
draft: false
229+
prerelease: false
230+
body: |
231+
Native Windows ARM64 build produced by ci_windows_arm64_native.yaml
232+
233+
Versions:
234+
- qBittorrent: ${{ env.qbt_version }}
235+
- libtorrent: ${{ env.libt_version }}
236+
- Boost: ${{ env.boost_version }}
237+
- Qt: ${{ env.qt_version }}
238+
env:
239+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
240+
241+
- name: Publish branch prerelease
242+
if: github.event_name == 'push' && github.ref == 'refs/heads/arm64-native-from-scratch'
243+
uses: softprops/action-gh-release@v2
244+
with:
245+
tag_name: native-arm64-scratch-${{ github.run_number }}
246+
target_commitish: ${{ github.sha }}
247+
name: qBittorrent native ARM64 scratch build ${{ github.run_number }}
248+
files: qbittorrent_${{ env.qbt_version }}_arm64.zip
249+
draft: false
250+
prerelease: true
251+
body: |
252+
Scratch native ARM64 build from branch arm64-native-from-scratch.
253+
Commit: ${{ github.sha }}
254+
env:
255+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

NATIVE_WORKFLOW_EXPLANATION.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Native ARM64 Workflow Explanation
2+
3+
This branch contains a fresh GitHub Actions workflow intended to build qBittorrent as a native Windows ARM64 executable using the MSVC ARM64 cross toolchain on GitHub's standard x64 Windows runner.
4+
5+
## Why this workflow exists
6+
7+
The older workflow evolved through many fixes and experiments. This file is a clean rebuild that keeps the useful parts:
8+
9+
- one visible version block
10+
- one consistent vcpkg triplet
11+
- a single MSVC-based ARM64 build path
12+
- explicit release and prerelease publishing rules
13+
14+
The new workflow file is:
15+
16+
- `.github/workflows/ci_windows_arm64_native.yaml`
17+
18+
## How the workflow is organized
19+
20+
### 1. Triggers
21+
22+
The workflow runs when:
23+
24+
- you push to branch `arm64-native-from-scratch`
25+
- you push a tag like `v5.2.3`
26+
- you start it manually with `workflow_dispatch`
27+
28+
This lets you test safely on the scratch branch without changing the old workflow.
29+
30+
### 2. Version block
31+
32+
At the top of the job you will see:
33+
34+
```yaml
35+
boost_version: "1.91.0"
36+
libt_version: "2.0.13"
37+
qbt_version: "5.2.3"
38+
qt_version: "6.10.3"
39+
```
40+
41+
This is the main update surface. If you want to build a newer qBittorrent or Qt version later, this is the first place you edit.
42+
43+
### 3. Toolchain setup
44+
45+
The workflow uses:
46+
47+
- `ilammy/msvc-dev-cmd` to load the MSVC ARM64 cross compiler
48+
- `lukka/run-vcpkg` to use the preinstalled vcpkg tree on the runner
49+
- `ninja` for faster builds
50+
51+
Important detail: the build still happens on GitHub's normal x64 Windows machine, but the compiler target is ARM64, so the produced `qbittorrent.exe` is a native ARM64 executable.
52+
53+
### 4. Dependency installation
54+
55+
The workflow builds against one vcpkg triplet only:
56+
57+
- `arm64-windows-static-release`
58+
59+
That triplet is used for:
60+
61+
- OpenSSL
62+
- zlib
63+
- Qt configuration
64+
- libtorrent
65+
- qBittorrent
66+
67+
The goal is to avoid mixing dependency models.
68+
69+
### 5. Qt build
70+
71+
The workflow installs:
72+
73+
- host Qt tools for x64 from `install-qt-action`
74+
- target Qt for ARM64 by building Qt from source
75+
76+
The host Qt is needed because the build system runs helper tools on the x64 runner while compiling target libraries for ARM64.
77+
78+
### 6. libtorrent build
79+
80+
libtorrent is built from source with the same vcpkg triplet and the same ARM64 target model as qBittorrent.
81+
82+
### 7. qBittorrent build
83+
84+
qBittorrent is built from the official source tarball for the selected `qbt_version`.
85+
86+
One compatibility hotfix is still used:
87+
88+
- the workflow prepends a small `find_package(OpenSSL)` and `find_package(ZLIB)` block into qBittorrent's `CheckPackages.cmake`
89+
90+
That hotfix is kept because it was already needed in your existing working builds to make imported CMake targets visible consistently.
91+
92+
### 8. Packaging
93+
94+
The workflow currently packages:
95+
96+
- `qbittorrent.exe`
97+
- qBittorrent's own `dist/windows/qt.conf`
98+
99+
This matches the packaging pattern already used in the existing workflow.
100+
101+
### 9. Publishing behavior
102+
103+
There are two publishing modes:
104+
105+
- tagged releases for `v*` tags
106+
- prereleases for pushes to `arm64-native-from-scratch`
107+
108+
That means every push on the scratch branch can produce a downloadable test artifact without touching your main release tags.
109+
110+
## Which old files still matter
111+
112+
For this new native MSVC workflow, the important files are:
113+
114+
- `.github/workflows/ci_windows_arm64_native.yaml`
115+
- `README.md`
116+
117+
Useful to keep for now as reference:
118+
119+
- `.github/workflows/ci_windows_arm64.yaml`
120+
- `.github/workflows/ci_mingw_arm64.yaml`
121+
122+
## Which old files are probably removable later
123+
124+
These are not needed by the new native MSVC workflow itself:
125+
126+
- `build.sh`
127+
- `Toolchain-llvm-mingw.cmake`
128+
- `patches/`
129+
- root-level `qt.conf`
130+
- `changelog.txt`
131+
132+
However, I recommend not deleting them until the new workflow has produced at least one known-good artifact. They are still useful as comparison material.
133+
134+
## Important caution
135+
136+
This new workflow can make the build logic cleaner, but it may not fix an upstream qBittorrent runtime regression by itself. If qBittorrent 5.2.3 still crashes after startup, that may still be an application-level issue rather than a workflow issue.

0 commit comments

Comments
 (0)