-
-
Notifications
You must be signed in to change notification settings - Fork 22
317 lines (291 loc) · 15.1 KB
/
Copy pathbuild_installer.yaml
File metadata and controls
317 lines (291 loc) · 15.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
name: Build Installer
# Same as build.yml (native + Rust + AOT publish for x64 and ARM64) but with an
# extra step that builds the WiX MSI installer from the publish output and
# uploads it as an artifact. Triggered manually from the Actions tab.
on:
workflow_dispatch:
inputs:
platform:
description: Platform(s) to build
type: choice
default: Both
options:
- Both
- x64
- ARM64
native:
description: Native DLLs (vcpkg + C++ + Rust)
type: choice
default: Build fresh
options:
- Build fresh
- Use committed (skip native build)
jobs:
# Turn the "platform" choice into the build matrix. The matrix context is not
# available in a job-level `if:`, so we compute the matrix here and the build
# job consumes it via fromJSON -- that way an UN-selected platform's runner
# never even starts (vs. starting and skipping every step).
setup:
name: Select platform(s)
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.pick.outputs.matrix }}
steps:
- id: pick
shell: pwsh
run: |
$x64 = '{"runner":"windows-latest","platform":"x64","rid":"win-x64","vcpkg-triplet":"x64-windows-release","install-root":"vcpkg_installed_x64","rust-target":"x86_64-pc-windows-msvc","publish-profile":"win-x64","toolset":"v145"}'
$arm = '{"runner":"windows-11-arm","platform":"ARM64","rid":"win-arm64","vcpkg-triplet":"arm64-windows-release","install-root":"vcpkg_installed_arm64","rust-target":"aarch64-pc-windows-msvc","publish-profile":"win-ARM64","toolset":"v143"}'
$items = switch ("${{ inputs.platform }}") {
'x64' { $x64 }
'ARM64' { $arm }
default { "$x64,$arm" }
}
Add-Content -Path $env:GITHUB_OUTPUT -Value "matrix={`"include`":[$items]}"
build:
needs: setup
strategy:
# Run the selected platform(s) in parallel. If one fails, let the other
# finish so you get failure information for both at the same time.
fail-fast: false
matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
runs-on: ${{ matrix.runner }}
name: Build Installer (${{ matrix.platform }})
env:
# vcpkg binary cache: stores compressed pre-built packages so vcpkg
# install is a cache-hit on repeat runs instead of a full recompile.
# "clear" resets default sources; "files,<path>,readwrite" adds our
# local folder as the only source.
VCPKG_BINARY_SOURCES: "clear;files,${{ github.workspace }}/vcpkg_cache,readwrite"
steps:
# -----------------------------------------------------------------------
# Checkout
# -----------------------------------------------------------------------
# This brings in the committed overlay port files at
# Src/build_libheif/ports/libheif/ and the custom triplet at
# Src/build_libheif/triplets/arm64-windows-release.cmake.
# Those files are what tell vcpkg to use dav1d instead of aom.
# -----------------------------------------------------------------------
- name: Checkout
uses: actions/checkout@v4
# -----------------------------------------------------------------------
# Toolchain setup
# -----------------------------------------------------------------------
- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
# Install the stable Rust toolchain and pre-add the cross-compilation
# target for this platform. On windows-latest the host IS x86_64 so
# the target is native; on windows-11-arm the host IS aarch64 so same.
#
# Everything from here through "Assemble External binaries" is the native
# build; it is skipped when the "native" input is set to reuse the DLLs
# already committed under Src/FlyPhotos/External/<Platform>/.
- name: Setup Rust
if: ${{ inputs.native == 'Build fresh' }}
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust-target }}
# -----------------------------------------------------------------------
# Caches
# -----------------------------------------------------------------------
# vcpkg binary cache -- keyed on the manifest + overlay port files.
# A change to any port file or vcpkg.json invalidates the cache and
# triggers a full recompile of the affected packages.
- name: Cache vcpkg binary packages
if: ${{ inputs.native == 'Build fresh' }}
uses: actions/cache@v4
with:
path: vcpkg_cache
key: vcpkg-${{ matrix.vcpkg-triplet }}-${{ hashFiles('Src/build_libheif/vcpkg.json', 'Src/build_libheif/vcpkg-configuration.json', 'Src/build_libheif/ports/libheif/**', 'Src/build_libheif/triplets/**') }}
restore-keys: |
vcpkg-${{ matrix.vcpkg-triplet }}-
# Rust build cache -- caches ~/.cargo/registry (downloaded crate source)
# and the target/ directory (compiled artifacts). Keyed automatically
# by Cargo.lock + rust-target so a dependency change invalidates it.
- name: Cache Rust build
if: ${{ inputs.native == 'Build fresh' }}
uses: Swatinem/rust-cache@v2
with:
workspaces: Src/fly_rust_bridge
key: ${{ matrix.rust-target }}
# -----------------------------------------------------------------------
# Step 1 -- vcpkg: heif.dll, dav1d.dll, libde265.dll, libpng16.dll
# -----------------------------------------------------------------------
# The overlay ports at Src/build_libheif/ports/ contain the patched
# libheif port that adds dav1d support (see how_to_patch_heif.md).
# --overlay-ports tells vcpkg to use those files instead of the built-in
# port registry -- without this flag vcpkg would build against aom.
# --overlay-triplets provides the custom arm64-windows-release triplet
# (vcpkg ships x64-windows-release built-in but not the ARM64 equivalent).
# --x-install-root isolates each platform's packages so they don't
# interfere with each other when both platforms run in parallel.
- name: Install vcpkg dependencies
if: ${{ inputs.native == 'Build fresh' }}
working-directory: Src/build_libheif
run: |
vcpkg install `
--triplet ${{ matrix.vcpkg-triplet }} `
--x-manifest-root=. `
--x-install-root=${{ matrix.install-root }} `
--overlay-ports=ports `
--overlay-triplets=triplets
# -----------------------------------------------------------------------
# Step 2 -- Native C++: FlyNativeLib.dll, FlyNativeLibHeif.dll,
# FlyContextMenuHelper.exe
# -----------------------------------------------------------------------
# PlatformToolset is per-runner (matrix.toolset): the windows-latest x64
# runner is on Visual Studio 2026 (v145), but windows-11-arm is still on
# Visual Studio 2022 (newest toolset v143) and has no v145. ATL ships with
# both images for their default toolset, so each compiles with its own.
# Build order matters: FlyNativeLib has no vcpkg dependency and must be
# built before FlyNativeLibHeif which links against heif.lib.
# Output lands in Src/<Platform>/Release/ (not Src/<Project>/<Platform>/Release/).
# The .vcxproj OutDir is $(ProjectDir)\..\$(Platform)\$(Configuration)\.
- name: Build FlyNativeLib
if: ${{ inputs.native == 'Build fresh' }}
working-directory: Src/FlyNativeLib
run: |
msbuild FlyNativeLib.vcxproj `
/p:Configuration=Release `
/p:Platform=${{ matrix.platform }} `
/p:PlatformToolset=${{ matrix.toolset }}
- name: Build FlyNativeLibHeif
if: ${{ inputs.native == 'Build fresh' }}
working-directory: Src/FlyNativeLibHeif
run: |
msbuild FlyNativeLibHeif.vcxproj `
/p:Configuration=Release `
/p:Platform=${{ matrix.platform }} `
/p:PlatformToolset=${{ matrix.toolset }}
- name: Build FlyContextMenuHelper
if: ${{ inputs.native == 'Build fresh' }}
working-directory: Src/FlyContextMenuHelper
run: |
msbuild FlyContextMenuHelper.vcxproj `
/p:Configuration=Release `
/p:Platform=${{ matrix.platform }} `
/p:PlatformToolset=${{ matrix.toolset }}
# -----------------------------------------------------------------------
# Step 3 -- Rust: fly_rust_bridge.dll (RAW decode + SVG render)
# -----------------------------------------------------------------------
# --release activates the optimised profile in Cargo.toml:
# opt-level=3, lto=true, codegen-units=1, panic=abort, strip=true.
# --target must be specified or cargo builds for the host and the DLL
# lands in target/release/ (no triplet subfolder) -- easy to mis-deploy.
- name: Build fly_rust_bridge
if: ${{ inputs.native == 'Build fresh' }}
working-directory: Src/fly_rust_bridge
run: |
cargo build --release --target ${{ matrix.rust-target }}
# -----------------------------------------------------------------------
# Step 4 -- Assemble External\<Platform>
# -----------------------------------------------------------------------
# Copies all required DLLs into the one folder that FlyPhotos.csproj
# reads from via: <ExternalBinaries Include="External\$(Platform)\*.*" />
# The FlattenDllsOnPublish MSBuild target then copies them into the
# publish root during dotnet publish.
#
# IMPORTANT: we do NOT clean the folder first. We copy each file
# individually so that if any copy source is missing the step fails
# with a clear error rather than silently succeeding with a partial set.
- name: Assemble External binaries
if: ${{ inputs.native == 'Build fresh' }}
shell: pwsh
run: |
$platform = "${{ matrix.platform }}"
$triplet = "${{ matrix.vcpkg-triplet }}"
$installRoot = "Src/build_libheif/${{ matrix.install-root }}"
$rustTarget = "${{ matrix.rust-target }}"
$externalDir = "Src/FlyPhotos/External/$platform"
New-Item -ItemType Directory -Path $externalDir -Force | Out-Null
# vcpkg DLLs: heif.dll, dav1d.dll, libde265.dll, libpng16.dll
$vcpkgBinDir = "$installRoot/$triplet/bin"
if (-not (Test-Path $vcpkgBinDir)) {
Write-Error "vcpkg bin dir not found: $vcpkgBinDir"
exit 1
}
Get-ChildItem "$vcpkgBinDir/*.dll" | Copy-Item -Destination $externalDir -Force
# Native C++ DLLs (OutDir = Src\<Platform>\Release\)
Copy-Item "Src/$platform/Release/FlyNativeLib.dll" -Destination $externalDir -Force
Copy-Item "Src/$platform/Release/FlyNativeLibHeif.dll" -Destination $externalDir -Force
Copy-Item "Src/$platform/Release/FlyContextMenuHelper.exe" -Destination $externalDir -Force
# Rust DLL
Copy-Item "Src/fly_rust_bridge/target/$rustTarget/release/fly_rust_bridge.dll" `
-Destination $externalDir -Force
# -----------------------------------------------------------------------
# Step 5 -- Verify External before publishing
# -----------------------------------------------------------------------
# Fail loudly here rather than producing a publish artifact that ships
# without a required DLL. dotnet publish would succeed but the app
# would crash at runtime when it tries to P/Invoke into the missing DLL.
- name: Verify External binaries
shell: pwsh
run: |
$externalDir = "Src/FlyPhotos/External/${{ matrix.platform }}"
$expected = @(
"heif.dll", "dav1d.dll", "libde265.dll", "libpng16.dll",
"FlyNativeLib.dll", "FlyNativeLibHeif.dll", "FlyContextMenuHelper.exe",
"fly_rust_bridge.dll"
)
$failed = $false
foreach ($f in $expected) {
if (Test-Path "$externalDir/$f") {
Write-Host " [OK] $f" -ForegroundColor Green
} else {
Write-Host " [MISSING] $f" -ForegroundColor Red
$failed = $true
}
}
if ($failed) { exit 1 }
# -----------------------------------------------------------------------
# Step 6 -- dotnet publish (AOT, self-contained, trimmed)
# -----------------------------------------------------------------------
# The publish profile is selected from the csproj property:
# <PublishProfile>Properties\PublishProfiles\win-$(Platform).pubxml</PublishProfile>
# The profile writes output to FlyPhotos/bin/win-x64/publish/ (or win-arm64).
# We do NOT use -o to override this -- the installer harvests the profile
# output path and would break if it changed.
#
# The RemoveAIMLFiles MSBuild target (defined in FlyPhotos.csproj) runs
# after publish and strips large Windows AI/ML DLLs that WindowsAppSDK
# includes but FlyPhotos does not use (~150 MB removed).
- name: Publish
working-directory: Src
run: |
dotnet publish FlyPhotos/FlyPhotos.csproj `
-c Release `
/p:Platform=${{ matrix.platform }} `
/p:PublishProfile=Properties/PublishProfiles/${{ matrix.publish-profile }}.pubxml
# -----------------------------------------------------------------------
# Step 7 -- Build the WiX MSI installer
# -----------------------------------------------------------------------
# Builds FlyPhotosSetup.wixproj (WiX v5, SDK-style) from the Step 6 publish
# output. No devenv -- `dotnet build` restores the WiX SDK from NuGet. The MSI
# version is the wixproj's own <BuildVersion> string. Output lands in
# Src/FlyPhotosSetup/bin/<Platform>/Release/FlyPhotosInstaller_<version>_<Platform>.msi.
- name: Build MSI installer
working-directory: Src
run: |
dotnet build FlyPhotosSetup/FlyPhotosSetup.wixproj `
-c Release `
/p:Platform=${{ matrix.platform }}
# -----------------------------------------------------------------------
# Step 8 -- Upload artifacts (publish folder + MSI)
# -----------------------------------------------------------------------
- name: Upload publish artifact
uses: actions/upload-artifact@v4
with:
name: FlyPhotos-${{ matrix.platform }}
path: Src/FlyPhotos/bin/${{ matrix.rid }}/publish/
# The wixproj names the output FlyPhotosInstaller_<version>_<platform>.msi
# (version from its <BuildVersion>); match it by pattern. On a fresh runner the
# Release folder is clean, so exactly one MSI matches.
- name: Upload MSI artifact
uses: actions/upload-artifact@v4
with:
name: FlyPhotos-${{ matrix.platform }}-MSI
path: Src/FlyPhotosSetup/bin/${{ matrix.platform }}/Release/FlyPhotosInstaller_*_${{ matrix.platform }}.msi