StaticLink.Avalonia baseline

This commit is contained in:
geruipu committed 2026-06-21 12:37:51 +08:00
commit ff4458d089
23 files changed
+2417

No files matched your search

@@ -0,0 +1,256 @@
name: macOS static AvaloniaNative experiment
on:
workflow_dispatch:
inputs:
publish_to_release:
description: Publish experimental artifacts to GitHub Release
required: true
default: true
type: boolean
permissions:
contents: write
jobs:
build-macos-native:
strategy:
fail-fast: false
matrix:
include:
- rid: osx-x64
runner: macos-14
target_cpu: x64
- rid: osx-arm64
runner: macos-latest
target_cpu: arm64
runs-on: ${{ matrix.runner }}
env:
RID: ${{ matrix.rid }}
TARGET_CPU: ${{ matrix.target_cpu }}
WORK_DIR: ${{ github.workspace }}/External/NativeStatic/.work
OUTPUT_DIR: ${{ github.workspace }}/External/NativeStatic/${{ matrix.rid }}/native
AVALONIA_VERSION: 11.3.14
steps:
- uses: actions/checkout@v4
- name: Setup .NET for Avalonia header generation
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
- name: Build static AvaloniaNative
run: bash ./scripts/build-macos-avalonia-native-static.sh
- name: Build macOS static graphics libraries
run: bash ./scripts/build-macos-static-graphics.sh all
- name: Build macOS font preinit object
run: bash ./scripts/build-macos-font-preinit.sh
- name: List native outputs
run: find "External/NativeStatic/${{ matrix.rid }}/native" -maxdepth 1 -type f -print
- name: Upload macOS native libraries
uses: actions/upload-artifact@v4
with:
name: macos-static-native-${{ matrix.rid }}
path: External/NativeStatic/${{ matrix.rid }}/native/*
if-no-files-found: error
pack-experimental:
runs-on: ubuntu-latest
needs: build-macos-native
outputs:
package_version: ${{ steps.version.outputs.package_version }}
steps:
- uses: actions/checkout@v4
- name: Download macOS native libraries
uses: actions/download-artifact@v4
with:
pattern: macos-static-native-*
path: artifacts/native
- name: Stage native libraries
run: |
mkdir -p External/NativeStatic/osx-x64/native External/NativeStatic/osx-arm64/native
cp artifacts/native/macos-static-native-osx-x64/* External/NativeStatic/osx-x64/native/
cp artifacts/native/macos-static-native-osx-arm64/* External/NativeStatic/osx-arm64/native/
find External/NativeStatic/osx-x64/native External/NativeStatic/osx-arm64/native -maxdepth 1 -type f -print
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"
- name: Compute experimental package version
id: version
run: |
version="3.119.2-7151.8-macavn.${GITHUB_RUN_NUMBER}"
echo "package_version=$version" >> "$GITHUB_OUTPUT"
echo "$version"
- name: Pack experimental NuGet package
run: |
dotnet pack NuGet/StaticGraphics/StaticLink.Avalonia.csproj \
-c Release \
-o artifacts/nuget \
/p:PackageVersion=${{ steps.version.outputs.package_version }} \
/p:Version=${{ steps.version.outputs.package_version }}
dotnet pack NuGet/AvaloniaNative/StaticLink.Avalonia.Native.csproj \
-c Release \
-o artifacts/nuget \
/p:PackageVersion=11.3.14-macavn.${{ github.run_number }} \
/p:Version=11.3.14-macavn.${{ github.run_number }}
- name: Upload experimental NuGet package
uses: actions/upload-artifact@v4
with:
name: macos-static-avalonia-native-nuget
path: artifacts/nuget/*.nupkg
if-no-files-found: error
smoke-macos:
strategy:
fail-fast: false
matrix:
include:
- rid: osx-x64
runner: macos-14
- rid: osx-arm64
runner: macos-latest
runs-on: ${{ matrix.runner }}
needs: pack-experimental
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"
- name: Download experimental NuGet package
uses: actions/download-artifact@v4
with:
name: macos-static-avalonia-native-nuget
path: artifacts/nuget
- name: Publish Avalonia smoke app
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$projectDir = Join-Path $PWD "artifacts/avalonia-smoke-src"
$nativePackage = Get-ChildItem -Path "artifacts/nuget" -Filter "StaticLink.Avalonia.Native.*.nupkg" | Select-Object -First 1
if (-not $nativePackage) { throw "StaticLink AvaloniaNative NuGet package was not downloaded." }
if ($nativePackage.BaseName -notmatch '^StaticLink\.Avalonia\.Native\.(.+)$') { throw "Unexpected native package name: $($nativePackage.Name)" }
$nativeVersion = $Matches[1]
pwsh ./scripts/create-avalonia-smoke.ps1 `
-ProjectDir $projectDir `
-NuGetSource (Resolve-Path "artifacts/nuget") `
-StaticLinkVersion "${{ needs.pack-experimental.outputs.package_version }}" `
-StaticLinkNativeVersion $nativeVersion
dotnet publish (Join-Path $projectDir "AvaloniaStaticLinkSmoke.csproj") `
-c Release `
-r "${{ matrix.rid }}" `
--configfile (Join-Path $projectDir "NuGet.config") `
/p:PublishAot=true `
/p:SelfContained=true `
/p:PublishSingleFile=true `
/p:StripSymbols=true `
/p:PublishDir="$PWD/artifacts/smoke-${{ matrix.rid }}/"
- name: Verify static macOS output
shell: pwsh
run: |
$publishDir = "artifacts/smoke-${{ matrix.rid }}"
$blocked = Get-ChildItem -Path $publishDir -Recurse -File | Where-Object {
$_.Name -in @("libAvaloniaNative.dylib", "libHarfBuzzSharp.dylib", "libSkiaSharp.dylib")
}
if ($blocked) {
$blocked | ForEach-Object { Write-Error "Unexpected dynamic library: $($_.FullName)" }
exit 1
}
Get-ChildItem -Path $publishDir -Recurse -File | ForEach-Object { $_.FullName }
- name: Run Avalonia smoke app
shell: bash
run: |
set -euo pipefail
cd "artifacts/smoke-${{ matrix.rid }}"
chmod +x ./AvaloniaStaticLinkSmoke
python3 - <<'PY'
import pathlib
import subprocess
import sys
log = pathlib.Path("avalonia-smoke-run.log")
proc = subprocess.Popen(
["./AvaloniaStaticLinkSmoke"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
try:
output, _ = proc.communicate(timeout=15)
except subprocess.TimeoutExpired:
proc.kill()
output, _ = proc.communicate()
log.write_text(output or "")
print(output or "")
print("Avalonia smoke app stayed alive for 15 seconds.")
sys.exit(0)
log.write_text(output or "")
print(output or "")
if proc.returncode == 0:
print("Avalonia smoke app exited normally.")
sys.exit(0)
print(f"Avalonia smoke app failed with exit code {proc.returncode}.", file=sys.stderr)
sys.exit(proc.returncode if proc.returncode > 0 else 1)
PY
- name: Upload macOS smoke app
uses: actions/upload-artifact@v4
with:
name: macos-static-avalonia-native-smoke-${{ matrix.rid }}
path: artifacts/smoke-${{ matrix.rid }}/**
if-no-files-found: error
release:
runs-on: ubuntu-latest
needs:
- pack-experimental
- smoke-macos
if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_to_release == true }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: release-artifacts
- name: Prepare release files
run: |
mkdir -p release-files
find release-artifacts/macos-static-avalonia-native-nuget -type f -name '*.nupkg' -exec cp {} release-files/ \;
for dir in release-artifacts/macos-static-avalonia-native-smoke-*; do
[ -d "$dir" ] || continue
name="$(basename "$dir")"
(cd "$dir" && zip -r "../../release-files/$name.zip" .)
done
ls -la release-files
- name: Publish experimental GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
release_tag="macos-static-avalonia-native-${GITHUB_RUN_NUMBER}"
if gh release view "$release_tag" >/dev/null 2>&1; then
gh release upload "$release_tag" release-files/* --clobber
else
gh release create "$release_tag" release-files/* \
--title "macOS static AvaloniaNative ${GITHUB_RUN_NUMBER}" \
--notes "Experimental macOS NativeAOT package with SkiaSharp, HarfBuzzSharp, and AvaloniaNative statically linked."
fi
+493
View File
@@ -0,0 +1,493 @@
name: staticlink-avalonia
on:
workflow_dispatch:
inputs:
publish_to_nuget:
description: Publish package to nuget.org
required: true
default: false
type: boolean
publish_to_release:
description: Publish NuGet package and smoke apps to GitHub Release
required: true
default: true
type: boolean
push:
tags:
- "staticlink-avalonia-v*"
env:
SKIASHARP_VERSION: "3.119.2"
ANGLE_BRANCH: "7151"
jobs:
build-linux:
strategy:
fail-fast: false
matrix:
include:
- rid: linux-x64
target_cpu: x64
runner: ubuntu-latest
- rid: linux-arm64
target_cpu: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Install native build dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang lld llvm g++ ninja-build pkg-config libfontconfig1-dev libglib2.0-dev libgl1-mesa-dev libpci-dev libx11-dev libxcb1-dev libxext-dev
- name: Build Linux static graphics libraries
env:
RID: ${{ matrix.rid }}
TARGET_CPU: ${{ matrix.target_cpu }}
LLVM_AR: llvm-ar
run: scripts/build-linux-static-graphics.sh all
- name: Upload Linux static archives
uses: actions/upload-artifact@v4
with:
name: native-static-${{ matrix.rid }}
path: External/NativeStatic/${{ matrix.rid }}/*
if-no-files-found: error
build-windows:
strategy:
fail-fast: false
matrix:
include:
- rid: win-x64
target_cpu: x64
msvc_arch: x64
- rid: win-arm64
target_cpu: arm64
msvc_arch: amd64_arm64
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
- name: Setup MSVC environment
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.msvc_arch }}
- name: Configure Chromium Visual Studio environment
shell: pwsh
run: |
"DEPOT_TOOLS_WIN_TOOLCHAIN=0" >> $env:GITHUB_ENV
"GYP_MSVS_VERSION=17.0" >> $env:GITHUB_ENV
"GYP_MSVS_OVERRIDE_PATH=$($env:VSINSTALLDIR.TrimEnd('\', '/'))" >> $env:GITHUB_ENV
- name: Install native build dependencies
shell: pwsh
run: |
git config --global core.longpaths true
choco install ninja llvm -y
- name: Validate Windows ANGLE inputs
shell: pwsh
env:
RID: ${{ matrix.rid }}
TARGET_CPU: ${{ matrix.target_cpu }}
DEPOT_TOOLS_WIN_TOOLCHAIN: 0
GYP_MSVS_VERSION: 17.0
run: .\scripts\build-windows-static-graphics.ps1 angle-preflight
- name: Build Windows static graphics libraries
shell: pwsh
env:
RID: ${{ matrix.rid }}
TARGET_CPU: ${{ matrix.target_cpu }}
DEPOT_TOOLS_WIN_TOOLCHAIN: 0
GYP_MSVS_VERSION: 17.0
run: .\scripts\build-windows-static-graphics.ps1 all
- name: Upload Windows static archives
uses: actions/upload-artifact@v4
with:
name: native-static-${{ matrix.rid }}
path: External/NativeStatic/${{ matrix.rid }}/*
if-no-files-found: error
build-macos:
strategy:
fail-fast: false
matrix:
include:
- rid: osx-x64
target_cpu: x64
runner: macos-14
- rid: osx-arm64
target_cpu: arm64
runner: macos-latest
runs-on: ${{ matrix.runner }}
env:
RID: ${{ matrix.rid }}
TARGET_CPU: ${{ matrix.target_cpu }}
WORK_DIR: ${{ github.workspace }}/External/NativeStatic/.work
OUTPUT_DIR: ${{ github.workspace }}/External/NativeStatic/${{ matrix.rid }}/native
steps:
- uses: actions/checkout@v4
- name: Install native build dependencies
run: brew install ninja
- name: Build macOS static graphics libraries
run: bash ./scripts/build-macos-static-graphics.sh all
- name: Build macOS font preinit object
run: bash scripts/build-macos-font-preinit.sh
- name: Upload macOS static archives
uses: actions/upload-artifact@v4
with:
name: native-static-${{ matrix.rid }}
path: External/NativeStatic/${{ matrix.rid }}/*
if-no-files-found: error
build-macos-avalonia-native:
strategy:
fail-fast: false
matrix:
include:
- rid: osx-x64
runner: macos-14
avalonia_version: 11.3.14
- rid: osx-arm64
runner: macos-latest
avalonia_version: 11.3.14
- rid: osx-x64
runner: macos-14
avalonia_version: 12.0.3
- rid: osx-arm64
runner: macos-latest
avalonia_version: 12.0.3
runs-on: ${{ matrix.runner }}
env:
RID: ${{ matrix.rid }}
WORK_DIR: ${{ github.workspace }}/External/NativeStatic/.work
OUTPUT_DIR: ${{ github.workspace }}/External/NativeStatic/${{ matrix.rid }}/native
AVALONIA_VERSION: ${{ matrix.avalonia_version }}
steps:
- uses: actions/checkout@v4
- name: Setup .NET for Avalonia header generation
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
10.0.x
- name: Build static AvaloniaNative
run: bash ./scripts/build-macos-avalonia-native-static.sh
- name: Build macOS font preinit object
run: bash scripts/build-macos-font-preinit.sh
- name: Upload static AvaloniaNative
uses: actions/upload-artifact@v4
with:
name: native-avalonia-${{ matrix.avalonia_version }}-${{ matrix.rid }}
path: |
External/NativeStatic/${{ matrix.rid }}/native/libAvaloniaNative.a
External/NativeStatic/${{ matrix.rid }}/native/macos_font_preinit.o
if-no-files-found: error
pack:
runs-on: ubuntu-latest
needs:
- build-linux
- build-windows
- build-macos
- build-macos-avalonia-native
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"
- name: Download static archives
uses: actions/download-artifact@v4
with:
pattern: native-static-*
path: External/NativeStatic
merge-multiple: false
- name: Download static AvaloniaNative archives
uses: actions/download-artifact@v4
with:
pattern: native-avalonia-*
path: External/AvaloniaNative
merge-multiple: false
- name: Move static archives into RID directories
run: |
for rid in win-x64 win-arm64 linux-x64 linux-arm64 osx-x64 osx-arm64; do
mkdir -p "External/NativeStatic/$rid"
mv "External/NativeStatic/native-static-$rid"/* "External/NativeStatic/$rid/"
rmdir "External/NativeStatic/native-static-$rid"
if [ ! -d "External/NativeStatic/$rid/native" ]; then
mkdir -p "External/NativeStatic/$rid/native"
find "External/NativeStatic/$rid" -maxdepth 1 -type f -exec mv {} "External/NativeStatic/$rid/native/" \;
fi
done
- name: Verify static archives
run: |
for rid in linux-x64 linux-arm64 osx-x64 osx-arm64; do
test -f External/NativeStatic/$rid/native/libskia.a
test -f External/NativeStatic/$rid/native/libSkiaSharp.a
test -f External/NativeStatic/$rid/native/libHarfBuzzSharp.a
test -f External/NativeStatic/$rid/native/libANGLE_static.a
test -f External/NativeStatic/$rid/native/libGLESv2_static.a
done
for rid in win-x64 win-arm64; do
test -f External/NativeStatic/$rid/native/skia.lib
test -f External/NativeStatic/$rid/native/SkiaSharp.lib
test -f External/NativeStatic/$rid/native/libHarfBuzzSharp.lib
test -f External/NativeStatic/$rid/native/libANGLE_static.lib
test -f External/NativeStatic/$rid/native/libGLESv2_static.lib
done
- name: Pack NuGet package
run: |
dotnet pack NuGet/StaticGraphics/StaticLink.Avalonia.csproj -c Release -o artifacts/nuget
for version in 11.3.14 12.0.3; do
mkdir -p External/NativeStatic/osx-x64/native External/NativeStatic/osx-arm64/native
cp External/AvaloniaNative/native-avalonia-$version-osx-x64/* External/NativeStatic/osx-x64/native/
cp External/AvaloniaNative/native-avalonia-$version-osx-arm64/* External/NativeStatic/osx-arm64/native/
dotnet pack NuGet/AvaloniaNative/StaticLink.Avalonia.Native.csproj \
-c Release \
-o artifacts/nuget \
/p:PackageVersion=$version.1 \
/p:Version=$version.1
done
- name: Upload NuGet artifact
uses: actions/upload-artifact@v4
with:
name: staticlink-avalonia-nuget
path: |
artifacts/nuget/*.nupkg
artifacts/nuget/*.snupkg
if-no-files-found: error
- name: Publish to nuget.org
if: ${{ startsWith(github.ref, 'refs/tags/staticlink-avalonia-v') || (github.event_name == 'workflow_dispatch' && inputs.publish_to_nuget == true) }}
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
if [ -z "$NUGET_API_KEY" ]; then
echo "NUGET_API_KEY secret is not configured. Skipping publish."
exit 0
fi
for package in artifacts/nuget/*.nupkg; do
dotnet nuget push "$package" \
--source "https://api.nuget.org/v3/index.json" \
--api-key "$NUGET_API_KEY" \
--skip-duplicate
done
smoke-avalonia:
needs: pack
strategy:
fail-fast: false
matrix:
include:
- rid: win-x64
runner: windows-2022
msvc_arch: x64
- rid: win-arm64
runner: windows-2022
msvc_arch: amd64_arm64
- rid: linux-x64
runner: ubuntu-latest
- rid: linux-arm64
runner: ubuntu-24.04-arm
- rid: osx-x64
runner: macos-14
avalonia_version: 11.3.14
native_version: 11.3.14.1
- rid: osx-arm64
runner: macos-latest
avalonia_version: 11.3.14
native_version: 11.3.14.1
- rid: osx-x64
runner: macos-14
avalonia_version: 12.0.3
native_version: 12.0.3.1
- rid: osx-arm64
runner: macos-latest
avalonia_version: 12.0.3
native_version: 12.0.3.1
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Setup MSVC environment
if: ${{ startsWith(matrix.rid, 'win-') }}
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.msvc_arch }}
- name: Install Linux publish dependencies
if: ${{ startsWith(matrix.rid, 'linux-') }}
run: |
sudo apt-get update
sudo apt-get install -y clang lld zlib1g-dev libfontconfig1-dev
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"
- name: Download NuGet package
uses: actions/download-artifact@v4
with:
name: staticlink-avalonia-nuget
path: artifacts/nuget
- name: Publish Avalonia single-file smoke app
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$package = Get-ChildItem -Path "artifacts/nuget" -Filter "StaticLink.Avalonia.*.nupkg" | Where-Object { $_.Name -notlike "StaticLink.Avalonia.Native.*" } | Select-Object -First 1
if (-not $package) { throw "StaticLink NuGet package was not downloaded." }
if ($package.BaseName -notmatch '^StaticLink\.Avalonia\.(.+)$') { throw "Unexpected package name: $($package.Name)" }
$version = $Matches[1]
$nativeVersion = "${{ matrix.native_version }}"
$avaloniaVersion = "${{ matrix.avalonia_version }}"
if (-not $avaloniaVersion) { $avaloniaVersion = "11.3.14" }
if ($nativeVersion) {
$nativePackage = Get-ChildItem -Path "artifacts/nuget" -Filter "StaticLink.Avalonia.Native.$nativeVersion.nupkg" | Select-Object -First 1
if (-not $nativePackage) { throw "StaticLink AvaloniaNative NuGet package $nativeVersion was not downloaded." }
}
$projectDir = Join-Path $PWD "artifacts/avalonia-smoke-src"
pwsh ./scripts/create-avalonia-smoke.ps1 -ProjectDir $projectDir -NuGetSource (Resolve-Path "artifacts/nuget") -StaticLinkVersion $version -AvaloniaVersion $avaloniaVersion -StaticLinkNativeVersion $nativeVersion
dotnet publish (Join-Path $projectDir "AvaloniaStaticLinkSmoke.csproj") `
-c Release `
-r "${{ matrix.rid }}" `
--configfile (Join-Path $projectDir "NuGet.config") `
/p:PublishAot=true `
/p:SelfContained=true `
/p:PublishSingleFile=true `
/p:StripSymbols=true `
/p:PublishDir="$PWD/artifacts/smoke-${{ matrix.rid }}/"
- name: Verify no dynamic graphics libraries remain
shell: pwsh
run: |
$publishDir = "artifacts/smoke-${{ matrix.rid }}"
$blocked = Get-ChildItem -Path $publishDir -Recurse -File | Where-Object {
$_.Name -in @("libSkiaSharp.dll", "libHarfBuzzSharp.dll", "av_libglesv2.dll", "libSkiaSharp.so", "libHarfBuzzSharp.so", "libAvaloniaNative.dylib", "libSkiaSharp.dylib", "libHarfBuzzSharp.dylib")
}
if ($blocked) {
$blocked | ForEach-Object { Write-Error "Unexpected dynamic graphics library: $($_.FullName)" }
exit 1
}
Get-ChildItem -Path $publishDir -Recurse -File | ForEach-Object { $_.FullName }
- name: Run macOS Avalonia smoke app
if: ${{ startsWith(matrix.rid, 'osx-') }}
shell: bash
run: |
set -euo pipefail
cd "artifacts/smoke-${{ matrix.rid }}"
chmod +x ./AvaloniaStaticLinkSmoke
python3 - <<'PY'
import pathlib
import subprocess
import sys
log = pathlib.Path("avalonia-smoke-run.log")
proc = subprocess.Popen(
["./AvaloniaStaticLinkSmoke"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
try:
output, _ = proc.communicate(timeout=15)
except subprocess.TimeoutExpired:
proc.kill()
output, _ = proc.communicate()
log.write_text(output or "")
print(output or "")
print("Avalonia smoke app stayed alive for 15 seconds.")
sys.exit(0)
log.write_text(output or "")
print(output or "")
if proc.returncode == 0:
print("Avalonia smoke app exited normally.")
sys.exit(0)
print(f"Avalonia smoke app failed with exit code {proc.returncode}.", file=sys.stderr)
sys.exit(proc.returncode if proc.returncode > 0 else 1)
PY
- name: Upload Avalonia smoke app
uses: actions/upload-artifact@v4
with:
name: avalonia-smoke-${{ matrix.rid }}
path: artifacts/smoke-${{ matrix.rid }}/**
if-no-files-found: error
release:
runs-on: ubuntu-latest
needs:
- pack
- smoke-avalonia
if: ${{ startsWith(github.ref, 'refs/tags/staticlink-avalonia-v') || (github.event_name == 'workflow_dispatch' && inputs.publish_to_release == true) }}
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download release artifacts
uses: actions/download-artifact@v4
with:
path: release-artifacts
- name: Prepare release files
run: |
mkdir -p release-files
find release-artifacts/staticlink-avalonia-nuget -type f \( -name '*.nupkg' -o -name '*.snupkg' \) -exec cp {} release-files/ \;
for dir in release-artifacts/avalonia-smoke-*; do
[ -d "$dir" ] || continue
name="$(basename "$dir")"
(cd "$dir" && zip -r "../../release-files/$name.zip" .)
done
ls -la release-files
- name: Publish GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
release_tag="${GITHUB_REF_NAME}"
title="${GITHUB_REF_NAME}"
else
release_tag="staticlink-avalonia-smoke-${GITHUB_RUN_NUMBER}"
title="StaticLink.Avalonia smoke ${GITHUB_RUN_NUMBER}"
fi
if gh release view "$release_tag" >/dev/null 2>&1; then
gh release upload "$release_tag" release-files/* --clobber
else
gh release create "$release_tag" release-files/* \
--title "$title" \
--notes "NuGet package plus Avalonia 11.3.14 NativeAOT single-file smoke apps for win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, and osx-arm64."
fi
+6
View File
@@ -0,0 +1,6 @@
bin/
obj/
artifacts/
*.nupkg
*.snupkg
External/NativeStatic/.work/
+16
View File
@@ -0,0 +1,16 @@
# Native Static Outputs
GitHub Actions writes platform static libraries here before packing the NuGet package.
Expected generated layout:
```text
External/NativeStatic/win-x64/native inputs
External/NativeStatic/win-arm64/native inputs
External/NativeStatic/linux-x64/native inputs
External/NativeStatic/linux-arm64/native inputs
External/NativeStatic/osx-x64/native inputs
External/NativeStatic/osx-arm64/native inputs
```
The repository keeps build patches here, but platform `.a` and `.lib` outputs are generated by the workflow and are not source files.
+64
View File
@@ -0,0 +1,64 @@
diff --git a/DEPS b/DEPS
index 9085ba513e..565840a832 100644
--- a/DEPS
+++ b/DEPS
@@ -696,11 +696,6 @@ deps = {
'dep_type': 'cipd',
},
- 'third_party/catapult': {
- 'url': Var('chromium_git') + '/catapult.git' + '@' + Var('catapult_revision'),
- 'condition': 'not build_with_chromium',
- },
-
# Cherry is a dEQP/VK-GL-CTS management GUI written in Go. We use it for viewing test results.
'third_party/cherry': {
'url': Var('android_git') + '/platform/external/cherry@4f8fb08d33ca5ff05a1c638f04c85bbb8d8b52cc',
@@ -722,12 +717,6 @@ deps = {
'condition': 'checkout_android and not build_with_chromium',
},
-
- 'third_party/dawn': {
- 'url': Var('dawn_git') + '/dawn.git' + '@' + Var('dawn_revision'),
- 'condition': 'not build_with_chromium'
- },
-
'third_party/depot_tools': {
'url': Var('chromium_git') + '/chromium/tools/depot_tools.git@1fcc527019d786502b02f71b8b764ee674a40953',
'condition': 'not build_with_chromium',
@@ -809,11 +798,6 @@ deps = {
'condition': 'not build_with_chromium',
},
- 'third_party/llvm/src': {
- 'url': Var('chromium_git') + '/external/github.com/llvm/llvm-project@1fd8d3fea53e6e4573cdce55bd38ef0a7813a442',
- 'condition': 'not build_with_chromium',
- },
-
'third_party/jdk': {
'packages': [
{
@@ -1000,11 +984,6 @@ deps = {
'condition': 'checkout_android and not build_with_chromium',
},
- 'third_party/SwiftShader': {
- 'url': Var('swiftshader_git') + '/SwiftShader@7905fa19e456df5aa8e2233a7ec5832c9c6c287b',
- 'condition': 'not build_with_chromium',
- },
-
'third_party/turbine/cipd': {
'packages': [
{
@@ -1016,10 +995,6 @@ deps = {
'dep_type': 'cipd',
},
- 'third_party/VK-GL-CTS/src': {
- 'url': Var('chromium_git') + '/external/github.com/KhronosGroup/VK-GL-CTS' + '@' + Var('vk_gl_cts_revision'),
- },
-
'third_party/vulkan-deps': {
'url': Var('chromium_git') + '/vulkan-deps@b9aab6c35f37e6532011ddab976c3ca71819c074',
'condition': 'not build_with_chromium',
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 greepar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>true</IsPackable>
<NoBuild>true</NoBuild>
<IncludeBuildOutput>false</IncludeBuildOutput>
<SuppressDependenciesWhenPacking>false</SuppressDependenciesWhenPacking>
<PackageId>StaticLink.Avalonia.Native</PackageId>
<Version>11.3.14.1</Version>
<Authors>greepar</Authors>
<Company>greepar</Company>
<Description>Static AvaloniaNative library for macOS Avalonia NativeAOT publishing.</Description>
<PackageTags>Avalonia;AvaloniaNative;NativeAOT;macOS;Static</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/greepar/StaticLink.Avalonia</RepositoryUrl>
<PackageProjectUrl>https://github.com/greepar/StaticLink.Avalonia</PackageProjectUrl>
</PropertyGroup>
<ItemGroup>
<None Include="../StaticGraphics/README.md" Pack="true" PackagePath="README.md" />
<None Include="lib/net10.0/_._" Pack="true" PackagePath="lib/net10.0/_._" />
<None Include="buildTransitive/StaticLink.Avalonia.Native.props" Pack="true" PackagePath="buildTransitive/$(PackageId).props" />
<None Include="buildTransitive/StaticLink.Avalonia.Native.targets" Pack="true" PackagePath="buildTransitive/$(PackageId).targets" />
<None Include="buildTransitive/StaticLink.Avalonia.Native.MacOSFontWarmup.cs" Pack="true" PackagePath="buildTransitive/StaticLink.Avalonia.Native.MacOSFontWarmup.cs" />
<None Include="../../External/NativeStatic/osx-x64/native/libAvaloniaNative.a" Condition="Exists('../../External/NativeStatic/osx-x64/native/libAvaloniaNative.a')" Pack="true" PackagePath="static/osx-x64/native/libAvaloniaNative.a" />
<None Include="../../External/NativeStatic/osx-x64/native/macos_font_preinit.o" Condition="Exists('../../External/NativeStatic/osx-x64/native/macos_font_preinit.o')" Pack="true" PackagePath="static/osx-x64/native/macos_font_preinit.o" />
<None Include="../../External/NativeStatic/osx-arm64/native/libAvaloniaNative.a" Condition="Exists('../../External/NativeStatic/osx-arm64/native/libAvaloniaNative.a')" Pack="true" PackagePath="static/osx-arm64/native/libAvaloniaNative.a" />
<None Include="../../External/NativeStatic/osx-arm64/native/macos_font_preinit.o" Condition="Exists('../../External/NativeStatic/osx-arm64/native/macos_font_preinit.o')" Pack="true" PackagePath="static/osx-arm64/native/macos_font_preinit.o" />
</ItemGroup>
</Project>
@@ -0,0 +1,50 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StaticLink.Avalonia.Native;
internal static class MacOSFontWarmup
{
[ModuleInitializer]
internal static void Initialize()
{
if (!OperatingSystem.IsMacOS())
{
return;
}
try
{
var nsApplication = objc_getClass("NSApplication");
var sharedApplication = sel_registerName("sharedApplication");
_ = objc_msgSend(nsApplication, sharedApplication);
var nsFont = objc_getClass("NSFont");
var systemFontOfSize = sel_registerName("systemFontOfSize:");
var systemFontOfSizeWidth = sel_registerName("systemFontOfSize:width:");
_ = objc_msgSend(nsFont, systemFontOfSize, 13.0);
_ = objc_msgSend(nsFont, systemFontOfSizeWidth, 13.0, 0.0);
}
catch
{
// Defensive AppKit font-cache warmup must never fail application startup.
}
}
[DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_getClass")]
private static extern IntPtr objc_getClass([MarshalAs(UnmanagedType.LPUTF8Str)] string name);
[DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "sel_registerName")]
private static extern IntPtr sel_registerName([MarshalAs(UnmanagedType.LPUTF8Str)] string name);
[DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_msgSend")]
private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector);
[DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_msgSend")]
private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, double value);
[DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_msgSend")]
private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, double value, double width);
}
@@ -0,0 +1,5 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<StaticLinkAvaloniaNativeRoot Condition="'$(StaticLinkAvaloniaNativeRoot)' == ''">$(MSBuildThisFileDirectory)../static/</StaticLinkAvaloniaNativeRoot>
</PropertyGroup>
</Project>
@@ -0,0 +1,61 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_StaticLinkAvaloniaNativePackageRid Condition="'$(RuntimeIdentifier)' == 'osx-x64'">osx-x64</_StaticLinkAvaloniaNativePackageRid>
<_StaticLinkAvaloniaNativePackageRid Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">osx-arm64</_StaticLinkAvaloniaNativePackageRid>
<_StaticLinkAvaloniaNativePackageRoot Condition="'$(_StaticLinkAvaloniaNativePackageRid)' != ''">$(StaticLinkAvaloniaNativeRoot)$(_StaticLinkAvaloniaNativePackageRid)/native</_StaticLinkAvaloniaNativePackageRoot>
</PropertyGroup>
<ItemGroup Condition="'$(PublishAot)' == 'true' and '$(_StaticLinkAvaloniaNativePackageRoot)' != ''">
<Compile Include="$(MSBuildThisFileDirectory)StaticLink.Avalonia.Native.MacOSFontWarmup.cs" Link="StaticLink.Avalonia.Native.MacOSFontWarmup.cs" Condition="Exists('$(MSBuildThisFileDirectory)StaticLink.Avalonia.Native.MacOSFontWarmup.cs')" />
<DirectPInvoke Include="libAvaloniaNative" />
<DirectPInvoke Include="libAvaloniaNative.dylib" />
<DirectPInvoke Include="AvaloniaNative" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativePackageRoot)/macos_font_preinit.o" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativePackageRoot)/libAvaloniaNative.a" />
<NativeSystemLibrary Include="c++" />
<LinkerArg Include="-framework AppKit" />
<LinkerArg Include="-framework Carbon" />
<LinkerArg Include="-framework CoreFoundation" />
<LinkerArg Include="-framework CoreGraphics" />
<LinkerArg Include="-framework Foundation" />
<LinkerArg Include="-framework OpenGL" />
<LinkerArg Include="-framework QuartzCore" />
<LinkerArg Include="-framework UniformTypeIdentifiers" />
</ItemGroup>
<Target Name="StaticLinkAvaloniaNativeAddMacOSDirectPInvokes"
BeforeTargets="WriteIlcRspFileForCompilation"
Condition="'$(PublishAot)' == 'true' and '$(_StaticLinkAvaloniaNativePackageRoot)' != ''">
<PropertyGroup>
<_StaticLinkAvaloniaNativeDirectPInvokeList>$(NativeIntermediateOutputPath)staticlink-avalonia-native.directpinvokes.txt</_StaticLinkAvaloniaNativeDirectPInvokeList>
</PropertyGroup>
<ItemGroup>
<_StaticLinkAvaloniaNativeDirectPInvoke Include="libAvaloniaNative" />
<_StaticLinkAvaloniaNativeDirectPInvoke Include="libAvaloniaNative.dylib" />
<_StaticLinkAvaloniaNativeDirectPInvoke Include="AvaloniaNative" />
</ItemGroup>
<MakeDir Directories="$(NativeIntermediateOutputPath)" />
<WriteLinesToFile File="$(_StaticLinkAvaloniaNativeDirectPInvokeList)" Lines="@(_StaticLinkAvaloniaNativeDirectPInvoke)" Overwrite="true" />
<ItemGroup>
<DirectPInvoke Include="libAvaloniaNative" />
<DirectPInvoke Include="libAvaloniaNative.dylib" />
<DirectPInvoke Include="AvaloniaNative" />
<DirectPInvokeList Include="$(_StaticLinkAvaloniaNativeDirectPInvokeList)" />
</ItemGroup>
<Error Condition="!Exists('$(_StaticLinkAvaloniaNativePackageRoot)/libAvaloniaNative.a')" Text="StaticLink.Avalonia.Native could not find $(_StaticLinkAvaloniaNativePackageRoot)/libAvaloniaNative.a. Check the package version and restore cache." />
<Error Condition="!Exists('$(_StaticLinkAvaloniaNativePackageRoot)/macos_font_preinit.o')" Text="StaticLink.Avalonia.Native could not find $(_StaticLinkAvaloniaNativePackageRoot)/macos_font_preinit.o. Check the package version and restore cache." />
<Message Importance="high" Text="StaticLink.Avalonia.Native Root: $(_StaticLinkAvaloniaNativePackageRoot)" />
<Message Importance="high" Text="StaticLink.Avalonia.Native DirectPInvoke: @(DirectPInvoke)" />
<Message Importance="high" Text="StaticLink.Avalonia.Native DirectPInvokeList: @(DirectPInvokeList)" />
<Message Importance="high" Text="StaticLink.Avalonia.Native NativeLibrary: @(NativeLibrary)" />
</Target>
<Target Name="StaticLinkAvaloniaNativeCleanDynamicAssets"
AfterTargets="Publish"
Condition="'$(PublishAot)' == 'true' and '$(_StaticLinkAvaloniaNativePackageRoot)' != ''">
<ItemGroup>
<_StaticLinkAvaloniaNativeFilesToDelete Include="$(PublishDir)libAvaloniaNative.dylib" />
</ItemGroup>
<Delete Files="@(_StaticLinkAvaloniaNativeFilesToDelete)" TreatErrorsAsWarnings="true" />
</Target>
</Project>
Whitespace-only changes.
+68
View File
@@ -0,0 +1,68 @@
# StaticLink.Avalonia
Static native libraries for Avalonia NativeAOT publishing.
This package links Avalonia's graphics/native dependencies from static archives so NativeAOT publish output does not need the usual SkiaSharp, HarfBuzzSharp, ANGLE, or AvaloniaNative runtime files next to the executable.
Repository: <https://github.com/greepar/StaticLink.Avalonia>
## Supported RIDs
- `win-x64`
- `win-arm64`
- `linux-x64`
- `linux-arm64`
- `osx-x64`
- `osx-arm64`
## Install
```bash
dotnet add package StaticLink.Avalonia
```
For fully static macOS output, also reference the AvaloniaNative package that matches your Avalonia version:
```bash
dotnet add package StaticLink.Avalonia.Native --version 11.3.14.1
```
## Publish
```bash
dotnet publish -c Release -r win-x64 \
-p:PublishAot=true \
-p:SelfContained=true \
-p:PublishSingleFile=true \
-p:StripSymbols=true
```
Use the RID you need, for example `linux-x64`, `linux-arm64`, `osx-arm64`, or `osx-x64`.
For Linux, `lld` is recommended:
```bash
dotnet publish -c Release -r linux-x64 \
-p:PublishAot=true \
-p:SelfContained=true \
-p:PublishSingleFile=true \
-p:StripSymbols=true \
-p:LinkerFlavor=lld
```
For macOS, avoid the Metal renderer for fully static output. Use OpenGL or Software:
```csharp
.With(new AvaloniaNativePlatformOptions
{
RenderingMode = [AvaloniaNativeRenderingMode.OpenGl, AvaloniaNativeRenderingMode.Software]
})
```
## Notes
- The package is intended for Avalonia `11.3.x` NativeAOT apps.
- `StaticLink.Avalonia.Native` is version-specific because `libAvaloniaNative.a` must match Avalonia's native ABI.
- The CI builds and smoke-publishes all six supported RIDs.
- macOS smoke tests run the published app on GitHub Actions and require it to stay alive for 15 seconds.
- Other native dependencies used by your app, such as SQLite or audio libraries, are outside this package's scope.
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>true</IsPackable>
<NoBuild>true</NoBuild>
<IncludeBuildOutput>false</IncludeBuildOutput>
<SuppressDependenciesWhenPacking>false</SuppressDependenciesWhenPacking>
<PackageId>StaticLink.Avalonia</PackageId>
<Version>3.119.2-7151.10</Version>
<Authors>greepar</Authors>
<Company>greepar</Company>
<Description>Static SkiaSharp, HarfBuzzSharp, and ANGLE libraries for Avalonia NativeAOT single-file publishing.</Description>
<PackageTags>Avalonia;SkiaSharp;ANGLE;NativeAOT;Windows;Linux;macOS;Static</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/greepar/StaticLink.Avalonia</RepositoryUrl>
<PackageProjectUrl>https://github.com/greepar/StaticLink.Avalonia</PackageProjectUrl>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="3.119.2" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
<None Include="lib/net10.0/_._" Pack="true" PackagePath="lib/net10.0/_._" />
<None Include="buildTransitive/StaticLink.Avalonia.props" Pack="true" PackagePath="buildTransitive/$(PackageId).props" />
<None Include="buildTransitive/StaticLink.Avalonia.targets" Pack="true" PackagePath="buildTransitive/$(PackageId).targets" />
<None Include="../../External/NativeStatic/win-x64/native/**/*" Pack="true" PackagePath="static/win-x64/native/%(RecursiveDir)%(Filename)%(Extension)" />
<None Include="../../External/NativeStatic/win-arm64/native/**/*" Pack="true" PackagePath="static/win-arm64/native/%(RecursiveDir)%(Filename)%(Extension)" />
<None Include="../../External/NativeStatic/linux-x64/native/**/*" Pack="true" PackagePath="static/linux-x64/native/%(RecursiveDir)%(Filename)%(Extension)" />
<None Include="../../External/NativeStatic/linux-arm64/native/**/*" Pack="true" PackagePath="static/linux-arm64/native/%(RecursiveDir)%(Filename)%(Extension)" />
<None Include="../../External/NativeStatic/osx-x64/native/**/*" Exclude="../../External/NativeStatic/osx-x64/native/libAvaloniaNative.a;../../External/NativeStatic/osx-x64/native/macos_font_preinit.o" Pack="true" PackagePath="static/osx-x64/native/%(RecursiveDir)%(Filename)%(Extension)" />
<None Include="../../External/NativeStatic/osx-arm64/native/**/*" Exclude="../../External/NativeStatic/osx-arm64/native/libAvaloniaNative.a;../../External/NativeStatic/osx-arm64/native/macos_font_preinit.o" Pack="true" PackagePath="static/osx-arm64/native/%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
</Project>
@@ -0,0 +1,5 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<StaticLinkAvaloniaRoot Condition="'$(StaticLinkAvaloniaRoot)' == ''">$(MSBuildThisFileDirectory)../static/</StaticLinkAvaloniaRoot>
</PropertyGroup>
</Project>
@@ -0,0 +1,118 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_StaticLinkAvaloniaRid Condition="'$(RuntimeIdentifier)' == 'win-x64'">win-x64</_StaticLinkAvaloniaRid>
<_StaticLinkAvaloniaRid Condition="'$(RuntimeIdentifier)' == 'win-arm64'">win-arm64</_StaticLinkAvaloniaRid>
<_StaticLinkAvaloniaRid Condition="'$(RuntimeIdentifier)' == 'linux-x64'">linux-x64</_StaticLinkAvaloniaRid>
<_StaticLinkAvaloniaRid Condition="'$(RuntimeIdentifier)' == 'linux-arm64'">linux-arm64</_StaticLinkAvaloniaRid>
<_StaticLinkAvaloniaRid Condition="'$(RuntimeIdentifier)' == 'osx-x64'">osx-x64</_StaticLinkAvaloniaRid>
<_StaticLinkAvaloniaRid Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">osx-arm64</_StaticLinkAvaloniaRid>
<_StaticLinkAvaloniaNativeRoot Condition="'$(_StaticLinkAvaloniaRid)' != ''">$(StaticLinkAvaloniaRoot)$(_StaticLinkAvaloniaRid)/native</_StaticLinkAvaloniaNativeRoot>
<LinkerFlavor Condition="'$(LinkerFlavor)' == '' and ('$(RuntimeIdentifier)' == 'linux-x64' or '$(RuntimeIdentifier)' == 'linux-arm64')">lld</LinkerFlavor>
</PropertyGroup>
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'win-x64' or '$(RuntimeIdentifier)' == 'win-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<DirectPInvoke Include="libHarfBuzzSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.lib')" />
<DirectPInvoke Include="libSkiaSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/SkiaSharp.lib')" />
<DirectPInvoke Include="libGLESv2" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.lib')" />
<DirectPInvoke Include="av_libglesv2" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.lib')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.lib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.lib')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/skia.lib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/skia.lib')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/SkiaSharp.lib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/SkiaSharp.lib')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libANGLE_static.lib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libANGLE_static.lib')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.lib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.lib')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libc++.lib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libc++.lib')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libc++abi.lib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libc++abi.lib')" />
<LinkerArg Include="d3d9.lib;dxguid.lib" />
<LinkerArg Include="dxgi.lib" />
<LinkerArg Include="Gdi32.lib;User32.lib" />
<LinkerArg Include="synchronization.lib" />
<LinkerArg Include="fontsub.lib;Ole32.lib;USP10.lib" />
<LinkerArg Include="d3d12.lib;d3dcompiler.lib" />
</ItemGroup>
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'linux-x64' or '$(RuntimeIdentifier)' == 'linux-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<DirectPInvoke Include="libHarfBuzzSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a')" />
<DirectPInvoke Include="libSkiaSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libskia.a" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libskia.a')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libANGLE_static.a" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libANGLE_static.a')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.a" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.a')" />
<NativeSystemLibrary Include="stdc++" />
<NativeSystemLibrary Include="gcc_eh" />
<NativeSystemLibrary Include="fontconfig" />
</ItemGroup>
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'linux-x64' or '$(RuntimeIdentifier)' == 'linux-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != '' and '$(LinkerFlavor)' == 'lld'">
<LinkerArg Include="-Wl,-z,nostart-stop-gc" />
<LinkerArg Include="-Wl,--allow-multiple-definition" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libskia.a')" />
</ItemGroup>
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'osx-x64' or '$(RuntimeIdentifier)' == 'osx-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<DirectPInvoke Include="libHarfBuzzSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a')" />
<DirectPInvoke Include="libSkiaSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libskia.a" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libskia.a')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a')" />
<NativeSystemLibrary Include="c++" />
<LinkerArg Include="-framework AppKit" />
<LinkerArg Include="-framework Carbon" />
<LinkerArg Include="-framework CoreFoundation" />
<LinkerArg Include="-framework CoreGraphics" />
<LinkerArg Include="-framework CoreText" />
<LinkerArg Include="-framework Foundation" />
<LinkerArg Include="-framework IOSurface" />
<LinkerArg Include="-framework Metal" />
<LinkerArg Include="-framework OpenGL" />
<LinkerArg Include="-framework QuartzCore" />
<LinkerArg Include="-framework UniformTypeIdentifiers" />
</ItemGroup>
<Target Name="StaticLinkAvaloniaDisableStaticExecutableOnApple"
BeforeTargets="LinkNative"
Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'osx-x64' or '$(RuntimeIdentifier)' == 'osx-arm64')">
<PropertyGroup>
<StaticExecutable>false</StaticExecutable>
</PropertyGroup>
<ItemGroup>
<LinkerArg Remove="-static" />
<LinkerArg Remove="-static-pie" />
</ItemGroup>
</Target>
<Target Name="StaticLinkAvaloniaValidateMacOSStaticLibraries"
BeforeTargets="LinkNative"
Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'osx-x64' or '$(RuntimeIdentifier)' == 'osx-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<Error Condition="!Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a')" Text="Missing static library: $(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a" />
<Error Condition="!Exists('$(_StaticLinkAvaloniaNativeRoot)/libskia.a')" Text="Missing static library: $(_StaticLinkAvaloniaNativeRoot)/libskia.a" />
<Error Condition="!Exists('$(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a')" Text="Missing static library: $(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a" />
</Target>
<Target Name="StaticLinkAvaloniaAddMacOSDirectPInvokes"
BeforeTargets="WriteIlcRspFileForCompilation"
Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'osx-x64' or '$(RuntimeIdentifier)' == 'osx-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<ItemGroup>
<DirectPInvoke Include="libHarfBuzzSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a')" />
<DirectPInvoke Include="libHarfBuzzSharp.dylib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.a')" />
<DirectPInvoke Include="libSkiaSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a')" />
<DirectPInvoke Include="libSkiaSharp.dylib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libSkiaSharp.a')" />
</ItemGroup>
<Message Importance="high" Text="StaticLink.Avalonia DirectPInvoke: @(DirectPInvoke)" />
<Message Importance="high" Text="StaticLink.Avalonia NativeLibrary: @(NativeLibrary)" />
</Target>
<Target Name="StaticLinkAvaloniaCleanDynamicAssets"
AfterTargets="Publish"
Condition="'$(PublishAot)' == 'true' and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<ItemGroup>
<_StaticLinkAvaloniaFilesToDelete Include="$(PublishDir)av_libglesv2.dll" />
<_StaticLinkAvaloniaFilesToDelete Include="$(PublishDir)libHarfBuzzSharp.dll" />
<_StaticLinkAvaloniaFilesToDelete Include="$(PublishDir)libSkiaSharp.dll" />
<_StaticLinkAvaloniaFilesToDelete Include="$(PublishDir)libHarfBuzzSharp.so" />
<_StaticLinkAvaloniaFilesToDelete Include="$(PublishDir)libSkiaSharp.so" />
<_StaticLinkAvaloniaFilesToDelete Include="$(PublishDir)libHarfBuzzSharp.dylib" />
<_StaticLinkAvaloniaFilesToDelete Include="$(PublishDir)libSkiaSharp.dylib" />
</ItemGroup>
<Delete Files="@(_StaticLinkAvaloniaFilesToDelete)" TreatErrorsAsWarnings="true" />
</Target>
</Project>
Whitespace-only changes.
+68
View File
@@ -0,0 +1,68 @@
# StaticLink.Avalonia
Static native libraries for Avalonia NativeAOT publishing.
This package links Avalonia's graphics/native dependencies from static archives so NativeAOT publish output does not need the usual SkiaSharp, HarfBuzzSharp, ANGLE, or AvaloniaNative runtime files next to the executable.
Repository: <https://github.com/greepar/StaticLink.Avalonia>
## Supported RIDs
- `win-x64`
- `win-arm64`
- `linux-x64`
- `linux-arm64`
- `osx-x64`
- `osx-arm64`
## Install
```bash
dotnet add package StaticLink.Avalonia
```
For fully static macOS output, also reference the AvaloniaNative package that matches your Avalonia version:
```bash
dotnet add package StaticLink.Avalonia.Native --version 11.3.14.1
```
## Publish
```bash
dotnet publish -c Release -r win-x64 \
-p:PublishAot=true \
-p:SelfContained=true \
-p:PublishSingleFile=true \
-p:StripSymbols=true
```
Use the RID you need, for example `linux-x64`, `linux-arm64`, `osx-arm64`, or `osx-x64`.
For Linux, `lld` is recommended:
```bash
dotnet publish -c Release -r linux-x64 \
-p:PublishAot=true \
-p:SelfContained=true \
-p:PublishSingleFile=true \
-p:StripSymbols=true \
-p:LinkerFlavor=lld
```
For macOS, avoid the Metal renderer for fully static output. Use OpenGL or Software:
```csharp
.With(new AvaloniaNativePlatformOptions
{
RenderingMode = [AvaloniaNativeRenderingMode.OpenGl, AvaloniaNativeRenderingMode.Software]
})
```
## Notes
- The package is intended for Avalonia `11.3.x` NativeAOT apps.
- `StaticLink.Avalonia.Native` is version-specific because `libAvaloniaNative.a` must match Avalonia's native ABI.
- The CI builds and smoke-publishes all six supported RIDs.
- macOS smoke tests run the published app on GitHub Actions and require it to stay alive for 15 seconds.
- Other native dependencies used by your app, such as SQLite or audio libraries, are outside this package's scope.
+329
View File
@@ -0,0 +1,329 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
WORK_DIR="${WORK_DIR:-$ROOT_DIR/External/NativeStatic/.work}"
TARGET_CPU="${TARGET_CPU:-x64}"
RID="${RID:-linux-$TARGET_CPU}"
OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}"
SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.2}"
ANGLE_BRANCH="${ANGLE_BRANCH:-7151}"
BUILD_JOBS="${BUILD_JOBS:-$(nproc)}"
ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}"
LLVM_AR="${LLVM_AR:-llvm-ar-19}"
SKIA_DEPS_RETRIES="${SKIA_DEPS_RETRIES:-3}"
usage() {
cat <<'USAGE'
Usage: scripts/build-linux-static-graphics.sh [skia|angle|all]
Environment:
WORK_DIR Source/build cache directory. Default: External/NativeStatic/.work
OUTPUT_DIR Final static library directory. Default: External/NativeStatic/linux-$TARGET_CPU
SKIASHARP_VERSION SkiaSharp release branch version. Default: 3.119.2
ANGLE_BRANCH ANGLE chromium branch. Default: 7151
TARGET_CPU GN target_cpu. Default: x64. Supported: x64, arm64
RID Output RID. Default: linux-$TARGET_CPU
BUILD_JOBS Ninja parallelism. Default: nproc
ANGLE_PATCH_DIR ANGLE patch directory. Default: External/NativeStatic/patches
LLVM_AR llvm-ar command used to expand thin archives. Default: llvm-ar-19
USAGE
}
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "Missing required command: $1" >&2
exit 1
fi
}
ensure_tools() {
require_cmd git
require_cmd python3
require_cmd clang
require_cmd clang++
require_cmd ar
require_cmd "$LLVM_AR"
require_cmd ninja
require_cmd pkg-config
}
ensure_depot_tools() {
local depot_dir="$WORK_DIR/depot_tools"
if [[ ! -d "$depot_dir/.git" ]]; then
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git "$depot_dir"
else
git -C "$depot_dir" pull --ff-only
fi
export PATH="$depot_dir:$PATH"
}
sync_skiasharp() {
local src="$WORK_DIR/SkiaSharp-$SKIASHARP_VERSION"
if [[ ! -d "$src/.git" ]]; then
git clone --depth 1 --branch "release/$SKIASHARP_VERSION" https://github.com/mono/SkiaSharp.git "$src"
else
git -C "$src" fetch --depth 1 origin "release/$SKIASHARP_VERSION"
git -C "$src" checkout -q FETCH_HEAD
fi
git -C "$src" submodule update --init --depth 1 externals/skia >&2
echo "$src"
}
prepare_skia_git_sync_deps() {
local sync_deps="$1/tools/git-sync-deps"
python3 - "$sync_deps" <<'PY'
import pathlib
import sys
path = pathlib.Path(sys.argv[1])
text = path.read_text()
old = " multithread(git_checkout_to_directory, list_of_arg_lists)"
new = " for args in list_of_arg_lists:\n git_checkout_to_directory(*args)"
if old in text:
path.write_text(text.replace(old, new))
PY
}
sync_skia_deps() {
local skia_dir="$1/externals/skia"
if [[ ! -x "$skia_dir/bin/gn" ]]; then
prepare_skia_git_sync_deps "$skia_dir"
local attempt
for attempt in $(seq 1 "$SKIA_DEPS_RETRIES"); do
if python3 "$skia_dir/tools/git-sync-deps"; then
return 0
fi
if [[ "$attempt" == "$SKIA_DEPS_RETRIES" ]]; then
return 1
fi
echo "git-sync-deps failed; retrying ($attempt/$SKIA_DEPS_RETRIES)..." >&2
sleep 10
done
fi
}
build_skia() {
ensure_tools
ensure_depot_tools
local src
src="$(sync_skiasharp)"
sync_skia_deps "$src"
local skia_dir="$src/externals/skia"
local out_dir="$skia_dir/out/linux-static-$TARGET_CPU"
mkdir -p "$out_dir" "$OUTPUT_DIR"
cat >"$out_dir/args.gn" <<EOF_ARGS
target_os = "linux"
target_cpu = "$TARGET_CPU"
is_official_build = true
is_static_skiasharp = true
skia_enable_tools = false
skia_enable_ganesh = true
skia_enable_pdf = false
skia_enable_skottie = false
skia_use_dng_sdk = false
skia_use_fontconfig = true
skia_use_freetype = true
skia_use_harfbuzz = false
skia_use_icu = false
skia_use_piex = false
skia_use_sfntly = false
skia_use_system_expat = false
skia_use_system_freetype2 = false
skia_use_system_libjpeg_turbo = false
skia_use_system_libpng = false
skia_use_system_libwebp = false
skia_use_system_zlib = false
skia_use_vulkan = false
skia_use_xps = false
cc = "clang"
cxx = "clang++"
ar = "ar"
extra_cflags = [
"-DSKIA_C_DLL",
"-DHAVE_SYSCALL_GETRANDOM",
"-DXML_DEV_URANDOM",
]
extra_cflags_cc = [ "-frtti" ]
extra_ldflags = [ "-static-libstdc++", "-static-libgcc" ]
EOF_ARGS
(cd "$skia_dir" && "$skia_dir/bin/gn" gen "$out_dir")
ninja -C "$out_dir" -j "$BUILD_JOBS" skia SkiaSharp HarfBuzzSharp
copy_first_existing "$OUTPUT_DIR/libskia.a" \
"$out_dir/libskia.a" \
"$out_dir/obj/libskia.a"
copy_first_existing "$OUTPUT_DIR/libSkiaSharp.a" \
"$out_dir/libSkiaSharp.a" \
"$out_dir/obj/libSkiaSharp.a"
copy_first_existing "$OUTPUT_DIR/libHarfBuzzSharp.a" \
"$out_dir/libHarfBuzzSharp.a" \
"$out_dir/obj/libHarfBuzzSharp.a"
}
sync_angle() {
local src="$WORK_DIR/ANGLE-$ANGLE_BRANCH"
if [[ ! -d "$src/.git" ]]; then
git clone --depth 1 --branch "chromium/$ANGLE_BRANCH" https://github.com/google/angle.git "$src"
else
git -C "$src" fetch --depth 1 origin "chromium/$ANGLE_BRANCH"
git -C "$src" checkout -q FETCH_HEAD
fi
echo "$src"
}
build_angle() {
ensure_tools
ensure_depot_tools
local src
src="$(sync_angle)"
mkdir -p "$OUTPUT_DIR"
cd "$src"
apply_angle_patches "$src"
python3 scripts/bootstrap.py
gclient sync -f -D -R
local out_dir="$src/out/linux-static-$TARGET_CPU"
mkdir -p "$out_dir"
cat >"$out_dir/args.gn" <<EOF_ARGS
target_os = "linux"
target_cpu = "$TARGET_CPU"
is_debug = false
is_component_build = false
is_clang = false
treat_warnings_as_errors = false
use_custom_libcxx = false
use_sysroot = false
use_lld = false
use_thin_lto = false
symbol_level = 0
angle_build_tests = false
build_angle_deqp_tests = false
angle_enable_swiftshader = false
angle_enable_vulkan = false
angle_enable_wgpu = false
EOF_ARGS
gn gen "$out_dir"
ninja -C "$out_dir" -j "$BUILD_JOBS" libANGLE_static libGLESv2_static
copy_full_archive_from_thin "$out_dir" "$OUTPUT_DIR/libANGLE_static.a" \
"$out_dir/obj/libANGLE_static.a" \
"$out_dir/obj/libANGLE_static/libANGLE_static.a" \
"$out_dir/libANGLE_static.a"
copy_full_archive_from_thin "$out_dir" "$OUTPUT_DIR/libGLESv2_static.a" \
"$out_dir/obj/libGLESv2_static.a" \
"$out_dir/obj/libGLESv2_static/libGLESv2_static.a" \
"$out_dir/libGLESv2_static.a"
}
apply_angle_patches() {
local src="$1"
local build_patch="$ANGLE_PATCH_DIR/angle-chromium-$ANGLE_BRANCH.patch"
local deps_patch="$ANGLE_PATCH_DIR/angle-chromium-$ANGLE_BRANCH-deps.patch"
if ! git -C "$src" grep -q 'angle_static_library("libANGLE_static")' -- BUILD.gn; then
python3 - "$src/BUILD.gn" <<'PY'
import pathlib
import re
import sys
path = pathlib.Path(sys.argv[1])
text = path.read_text()
targets = '''angle_static_library("libANGLE_static") {
complete_static_lib = true
public_deps = [ ":libANGLE" ]
}
angle_static_library("libANGLE_with_capture_static") {
complete_static_lib = true
public_deps = [ ":libANGLE_with_capture" ]
}
angle_static_library("libGLESv2_static") {
'''
text = re.sub(r'^angle_static_library\("libGLESv2_static"\) \{', targets, text, count=1, flags=re.M)
text = re.sub(r'^angle_static_library\("libGLESv2_static"\) \{\n sources = libglesv2_sources', 'angle_static_library("libGLESv2_static") {\n complete_static_lib = true\n sources = libglesv2_sources', text, count=1, flags=re.M)
path.write_text(text)
PY
fi
if [[ -f "$build_patch" ]] && git -C "$src" grep -q "'third_party/catapult'" -- DEPS; then
git -C "$src" apply "$build_patch"
elif [[ -f "$deps_patch" ]] && ! git -C "$src" apply --check "$deps_patch" >/dev/null 2>&1; then
:
elif [[ -f "$deps_patch" ]]; then
git -C "$src" apply "$deps_patch"
fi
}
copy_first_existing() {
local dest="$1"
shift
for src in "$@"; do
if [[ -f "$src" ]]; then
cp "$src" "$dest"
echo "Wrote $dest"
return 0
fi
done
echo "None of the expected files exist for $dest:" >&2
printf ' %s\n' "$@" >&2
return 1
}
copy_full_archive_from_thin() {
local base_dir="$1"
local dest="$2"
shift 2
local src
for src in "$@"; do
if [[ -f "$src" ]]; then
if [[ "$(head -c 7 "$src")" == "!<thin>" ]]; then
local tmp="$dest.tmp"
local rel_src="${src#$base_dir/}"
(
cd "$base_dir"
mapfile -t members < <("$LLVM_AR" t "$rel_src")
"$LLVM_AR" rcs "$tmp" "${members[@]}"
)
mv "$tmp" "$dest"
else
cp "$src" "$dest"
fi
echo "Wrote $dest"
return 0
fi
done
echo "None of the expected files exist for $dest:" >&2
printf ' %s\n' "$@" >&2
return 1
}
main() {
mkdir -p "$WORK_DIR" "$OUTPUT_DIR"
case "${1:-all}" in
skia) build_skia ;;
angle) build_angle ;;
all)
build_skia
build_angle
;;
-h|--help|help)
usage
;;
*)
usage >&2
exit 1
;;
esac
}
main "$@"
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -euo pipefail
RID="${RID:-osx-arm64}"
AVALONIA_VERSION="${AVALONIA_VERSION:-11.3.14}"
WORK_DIR="${WORK_DIR:-$PWD/External/NativeStatic/.work}"
OUTPUT_DIR="${OUTPUT_DIR:-$PWD/External/NativeStatic/$RID/native}"
BUILD_JOBS="${BUILD_JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
case "$RID" in
osx-arm64) ARCH="arm64" ;;
osx-x64) ARCH="x86_64" ;;
*) echo "Unsupported RID: $RID" >&2; exit 2 ;;
esac
mkdir -p "$WORK_DIR" "$OUTPUT_DIR"
src="$WORK_DIR/Avalonia-$AVALONIA_VERSION"
if [[ ! -d "$src/.git" ]]; then
rm -rf "$src"
git clone --depth 1 --branch "$AVALONIA_VERSION" https://github.com/AvaloniaUI/Avalonia.git "$src"
fi
if git -C "$src" config -f .gitmodules --get-regexp path | grep -q 'external/Numerge' && [[ ! -d "$src/external/Numerge/.git" ]]; then
git -C "$src" submodule update --init --depth 1 external/Numerge
fi
if [[ ! -f "$src/native/Avalonia.Native/inc/avalonia-native.h" ]]; then
bash "$src/native/Avalonia.Native/generate-headers.sh"
fi
project="$src/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj"
include_dir="$src/native/Avalonia.Native/inc"
build_dir="$WORK_DIR/avalonia-native-$RID"
rm -rf "$build_dir"
mkdir -p "$build_dir"
xcodebuild \
-project "$project" \
-target Avalonia.Native.OSX \
-configuration Release \
-sdk macosx \
-arch "$ARCH" \
CONFIGURATION_BUILD_DIR="$build_dir" \
ONLY_ACTIVE_ARCH=NO \
CODE_SIGNING_ALLOWED=NO \
MACH_O_TYPE=staticlib \
EXECUTABLE_PREFIX=lib \
EXECUTABLE_EXTENSION=a \
PRODUCT_NAME=AvaloniaNative \
HEADER_SEARCH_PATHS="$include_dir" \
CLANG_ENABLE_MODULES=YES \
GCC_GENERATE_DEBUGGING_SYMBOLS=NO \
-jobs "$BUILD_JOBS"
candidate="$(find "$build_dir" -maxdepth 2 -type f -name 'libAvaloniaNative.a' -print -quit)"
if [[ -z "$candidate" ]]; then
echo "Failed to find libAvaloniaNative.a in $build_dir" >&2
find "$build_dir" -maxdepth 3 -type f -print >&2
exit 1
fi
cp "$candidate" "$OUTPUT_DIR/libAvaloniaNative.a"
echo "Wrote $OUTPUT_DIR/libAvaloniaNative.a"
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
RID="${RID:-osx-arm64}"
OUTPUT_DIR="${OUTPUT_DIR:-$PWD/External/NativeStatic/$RID/native}"
case "$RID" in
osx-arm64) ARCH="arm64" ;;
osx-x64) ARCH="x86_64" ;;
*) echo "Unsupported RID: $RID" >&2; exit 2 ;;
esac
mkdir -p "$OUTPUT_DIR"
src="$OUTPUT_DIR/macos_font_preinit.m"
cat > "$src" <<'OBJC'
#import <AppKit/AppKit.h>
#import <objc/message.h>
#import <objc/runtime.h>
__attribute__((constructor(101)))
static void StaticLinkAvaloniaPreinitializeAppKitFonts(void)
{
@autoreleasepool
{
[NSApplication sharedApplication];
[NSFont systemFontOfSize:13.0];
((id (*)(id, SEL, CGFloat, CGFloat))objc_msgSend)([NSFont class], sel_registerName("systemFontOfSize:width:"), 13.0, 0.0);
}
}
OBJC
clang -arch "$ARCH" \
-mmacosx-version-min=12.0 \
-fobjc-arc \
-c "$src" \
-o "$OUTPUT_DIR/macos_font_preinit.o"
rm -f "$src"
echo "Wrote $OUTPUT_DIR/macos_font_preinit.o"
+240
View File
@@ -0,0 +1,240 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
WORK_DIR="${WORK_DIR:-$ROOT_DIR/External/NativeStatic/.work}"
TARGET_CPU="${TARGET_CPU:-arm64}"
RID="${RID:-osx-$TARGET_CPU}"
OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}"
SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.2}"
ANGLE_BRANCH="${ANGLE_BRANCH:-7151}"
BUILD_JOBS="${BUILD_JOBS:-$(sysctl -n hw.ncpu)}"
ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}"
SKIA_DEPS_RETRIES="${SKIA_DEPS_RETRIES:-3}"
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "Missing required command: $1" >&2
exit 1
fi
}
ensure_tools() {
require_cmd git
require_cmd python3
require_cmd clang
require_cmd clang++
require_cmd ar
require_cmd ninja
}
ensure_depot_tools() {
local depot_dir="$WORK_DIR/depot_tools"
if [[ ! -d "$depot_dir/.git" ]]; then
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git "$depot_dir"
else
git -C "$depot_dir" pull --ff-only
fi
export PATH="$depot_dir:$PATH"
}
copy_first_existing() {
local dest="$1"
shift
for src in "$@"; do
if [[ -f "$src" ]]; then
cp "$src" "$dest"
echo "Wrote $dest"
return 0
fi
done
echo "None of the expected files exist for $dest:" >&2
printf ' %s\n' "$@" >&2
return 1
}
sync_skiasharp() {
local src="$WORK_DIR/SkiaSharp-$SKIASHARP_VERSION"
if [[ ! -d "$src/.git" ]]; then
git clone --depth 1 --branch "release/$SKIASHARP_VERSION" https://github.com/mono/SkiaSharp.git "$src"
else
git -C "$src" fetch --depth 1 origin "release/$SKIASHARP_VERSION"
git -C "$src" checkout -q FETCH_HEAD
fi
git -C "$src" submodule update --init --depth 1 externals/skia >&2
echo "$src"
}
prepare_skia_git_sync_deps() {
local sync_deps="$1/tools/git-sync-deps"
python3 - "$sync_deps" <<'PY'
import pathlib
import sys
path = pathlib.Path(sys.argv[1])
text = path.read_text()
old = " multithread(git_checkout_to_directory, list_of_arg_lists)"
new = " for args in list_of_arg_lists:\n git_checkout_to_directory(*args)"
if old in text:
path.write_text(text.replace(old, new))
PY
}
sync_skia_deps() {
local skia_dir="$1"
prepare_skia_git_sync_deps "$skia_dir"
local attempt
for attempt in $(seq 1 "$SKIA_DEPS_RETRIES"); do
if python3 "$skia_dir/tools/git-sync-deps"; then
return 0
fi
if [[ "$attempt" == "$SKIA_DEPS_RETRIES" ]]; then
return 1
fi
echo "git-sync-deps failed; retrying ($attempt/$SKIA_DEPS_RETRIES)..." >&2
sleep 10
done
}
build_skia() {
ensure_tools
ensure_depot_tools
local src
src="$(sync_skiasharp)"
local skia_dir="$src/externals/skia"
if [[ ! -x "$skia_dir/bin/gn" ]]; then
sync_skia_deps "$skia_dir"
fi
local out_dir="$skia_dir/out/mac-static-$TARGET_CPU"
mkdir -p "$out_dir" "$OUTPUT_DIR"
cat >"$out_dir/args.gn" <<EOF_ARGS
target_os = "mac"
target_cpu = "$TARGET_CPU"
is_official_build = true
is_static_skiasharp = true
skia_enable_tools = false
skia_enable_ganesh = true
skia_enable_pdf = false
skia_enable_skottie = false
skia_use_dng_sdk = false
skia_use_fontconfig = false
skia_use_freetype = false
skia_use_harfbuzz = false
skia_use_icu = false
skia_use_piex = false
skia_use_sfntly = false
skia_use_system_expat = false
skia_use_system_libjpeg_turbo = false
skia_use_system_libpng = false
skia_use_system_libwebp = false
skia_use_system_zlib = false
skia_use_vulkan = false
skia_use_xps = false
cc = "clang"
cxx = "clang++"
ar = "ar"
extra_cflags = [ "-DSKIA_C_DLL" ]
extra_cflags_cc = [ "-frtti" ]
EOF_ARGS
(cd "$skia_dir" && "$skia_dir/bin/gn" gen "$out_dir")
ninja -C "$out_dir" -j "$BUILD_JOBS" skia SkiaSharp HarfBuzzSharp
copy_first_existing "$OUTPUT_DIR/libskia.a" "$out_dir/libskia.a" "$out_dir/obj/libskia.a"
copy_first_existing "$OUTPUT_DIR/libSkiaSharp.a" "$out_dir/libSkiaSharp.a" "$out_dir/obj/libSkiaSharp.a"
copy_first_existing "$OUTPUT_DIR/libHarfBuzzSharp.a" "$out_dir/libHarfBuzzSharp.a" "$out_dir/obj/libHarfBuzzSharp.a"
}
sync_angle() {
local src="$WORK_DIR/ANGLE-$ANGLE_BRANCH"
if [[ ! -d "$src/.git" ]]; then
git clone --depth 1 --branch "chromium/$ANGLE_BRANCH" https://github.com/google/angle.git "$src"
else
git -C "$src" fetch --depth 1 origin "chromium/$ANGLE_BRANCH"
git -C "$src" checkout -q FETCH_HEAD
fi
echo "$src"
}
apply_angle_patches() {
local src="$1"
local patch="$ANGLE_PATCH_DIR/angle-chromium-$ANGLE_BRANCH.patch"
if ! git -C "$src" grep -q 'angle_static_library("libANGLE_static")' -- BUILD.gn; then
python3 - "$src/BUILD.gn" <<'PY'
import pathlib
import re
import sys
path = pathlib.Path(sys.argv[1])
text = path.read_text()
targets = '''angle_static_library("libANGLE_static") {
complete_static_lib = true
public_deps = [ ":libANGLE" ]
}
angle_static_library("libANGLE_with_capture_static") {
complete_static_lib = true
public_deps = [ ":libANGLE_with_capture" ]
}
angle_static_library("libGLESv2_static") {
'''
text = re.sub(r'^angle_static_library\("libGLESv2_static"\) \{', targets, text, count=1, flags=re.M)
text = re.sub(r'^angle_static_library\("libGLESv2_static"\) \{\n sources = libglesv2_sources', 'angle_static_library("libGLESv2_static") {\n complete_static_lib = true\n sources = libglesv2_sources', text, count=1, flags=re.M)
path.write_text(text)
PY
fi
if [[ -f "$patch" ]] && git -C "$src" grep -q "'third_party/catapult'" -- DEPS; then
git -C "$src" apply "$patch"
fi
}
build_angle() {
ensure_tools
ensure_depot_tools
local src
src="$(sync_angle)"
mkdir -p "$OUTPUT_DIR"
apply_angle_patches "$src"
cd "$src"
python3 scripts/bootstrap.py
gclient sync -f -D -R
local out_dir="$src/out/mac-static-$TARGET_CPU"
mkdir -p "$out_dir"
cat >"$out_dir/args.gn" <<EOF_ARGS
target_os = "mac"
target_cpu = "$TARGET_CPU"
is_debug = false
is_component_build = false
is_clang = true
use_lld = false
use_thin_lto = false
symbol_level = 0
angle_build_tests = false
build_angle_deqp_tests = false
angle_enable_swiftshader = false
angle_enable_vulkan = false
angle_enable_wgpu = false
EOF_ARGS
gn gen "$out_dir"
ninja -C "$out_dir" -j "$BUILD_JOBS" libANGLE_static libGLESv2_static
copy_first_existing "$OUTPUT_DIR/libANGLE_static.a" "$out_dir/libANGLE_static.a" "$out_dir/obj/libANGLE_static.a" "$out_dir/obj/libANGLE_static/libANGLE_static.a"
copy_first_existing "$OUTPUT_DIR/libGLESv2_static.a" "$out_dir/libGLESv2_static.a" "$out_dir/obj/libGLESv2_static.a" "$out_dir/obj/libGLESv2_static/libGLESv2_static.a"
}
main() {
mkdir -p "$WORK_DIR" "$OUTPUT_DIR"
case "${1:-all}" in
skia) build_skia ;;
angle) build_angle ;;
all) build_skia; build_angle ;;
*) echo "Usage: scripts/build-macos-static-graphics.sh [skia|angle|all]" >&2; exit 1 ;;
esac
}
main "$@"
+290
View File
@@ -0,0 +1,290 @@
param(
[ValidateSet("skia", "angle", "angle-preflight", "all")]
[string]$Target = "all"
)
$ErrorActionPreference = "Stop"
$RootDir = Split-Path -Parent $PSScriptRoot
$WorkDir = if ($env:WORK_DIR) { $env:WORK_DIR } else { Join-Path $RootDir "External\NativeStatic\.work" }
$SkiaSharpVersion = if ($env:SKIASHARP_VERSION) { $env:SKIASHARP_VERSION } else { "3.119.2" }
$AngleBranch = if ($env:ANGLE_BRANCH) { $env:ANGLE_BRANCH } else { "7151" }
$TargetCpu = if ($env:TARGET_CPU) { $env:TARGET_CPU } else { "x64" }
$Rid = if ($env:RID) { $env:RID } else { "win-$TargetCpu" }
$OutputDir = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { Join-Path $RootDir "External\NativeStatic\$Rid" }
$BuildJobs = if ($env:BUILD_JOBS) { $env:BUILD_JOBS } else { [Environment]::ProcessorCount }
$AnglePatchDir = if ($env:ANGLE_PATCH_DIR) { $env:ANGLE_PATCH_DIR } else { Join-Path $RootDir "External\NativeStatic\patches" }
$SkiaDepsRetries = if ($env:SKIA_DEPS_RETRIES) { [int]$env:SKIA_DEPS_RETRIES } else { 3 }
if (-not $env:DEPOT_TOOLS_WIN_TOOLCHAIN) {
$env:DEPOT_TOOLS_WIN_TOOLCHAIN = "0"
}
if ($env:DEPOT_TOOLS_WIN_TOOLCHAIN -eq "0") {
if (-not $env:GYP_MSVS_VERSION) {
$env:GYP_MSVS_VERSION = "17.0"
}
if (-not $env:GYP_MSVS_OVERRIDE_PATH) {
if ($env:VSINSTALLDIR) {
$env:GYP_MSVS_OVERRIDE_PATH = $env:VSINSTALLDIR.TrimEnd("\", "/")
} else {
$vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe"
if (Test-Path $vswhere) {
$vsPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
if ($vsPath) {
$env:GYP_MSVS_OVERRIDE_PATH = $vsPath.TrimEnd("\", "/")
}
}
}
}
}
function Require-Command($Name) {
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
throw "Missing required command: $Name"
}
}
function Ensure-Tools {
Require-Command git
Require-Command python
Require-Command ninja
git config --global core.longpaths true
}
function Ensure-DepotTools {
$depotDir = Join-Path $WorkDir "depot_tools"
if (-not (Test-Path (Join-Path $depotDir ".git"))) {
$null = git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git $depotDir
} else {
$null = git -C $depotDir pull --ff-only
}
$env:PATH = "$depotDir;$env:PATH"
}
function Copy-FirstExisting($Destination, [string[]]$Candidates) {
foreach ($candidate in $Candidates) {
if (Test-Path $candidate) {
Copy-Item $candidate $Destination -Force
Write-Host "Wrote $Destination"
return
}
}
throw "None of the expected files exist for ${Destination}: $($Candidates -join ', ')"
}
function Sync-SkiaSharp {
$src = Join-Path $WorkDir "SkiaSharp-$SkiaSharpVersion"
if (-not (Test-Path (Join-Path $src ".git"))) {
$null = git -c core.longpaths=true clone --depth 1 --branch "release/$SkiaSharpVersion" https://github.com/mono/SkiaSharp.git $src
} else {
$null = git -C $src fetch --depth 1 origin "release/$SkiaSharpVersion"
$null = git -C $src checkout -q FETCH_HEAD
}
$null = git -C $src submodule update --init --depth 1 externals/skia
return $src
}
function Prepare-SkiaGitSyncDeps($SkiaDir) {
$syncDeps = Join-Path $SkiaDir "tools\git-sync-deps"
$text = Get-Content -Path $syncDeps -Raw
$old = " multithread(git_checkout_to_directory, list_of_arg_lists)"
$new = " for args in list_of_arg_lists:`n git_checkout_to_directory(*args)"
if ($text.Contains($old)) {
Set-Content -Path $syncDeps -Value $text.Replace($old, $new) -NoNewline -Encoding UTF8
}
}
function Invoke-SkiaGitSyncDeps($SkiaDir) {
Prepare-SkiaGitSyncDeps $SkiaDir
for ($attempt = 1; $attempt -le $SkiaDepsRetries; $attempt++) {
python (Join-Path $SkiaDir "tools\git-sync-deps")
if ($LASTEXITCODE -eq 0) {
return
}
if ($attempt -eq $SkiaDepsRetries) {
throw "git-sync-deps failed after $SkiaDepsRetries attempts"
}
Write-Warning "git-sync-deps failed; retrying ($attempt/$SkiaDepsRetries)..."
Start-Sleep -Seconds 10
}
}
function Build-Skia {
Ensure-Tools
Ensure-DepotTools
$src = Sync-SkiaSharp
$skiaDir = Join-Path $src "externals\skia"
if (-not (Test-Path (Join-Path $skiaDir "bin\gn.exe"))) {
Invoke-SkiaGitSyncDeps $skiaDir
}
$outDir = Join-Path $skiaDir "out\win-static-$TargetCpu"
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
@"
target_os = "win"
target_cpu = "$TargetCpu"
is_official_build = true
is_static_skiasharp = true
is_clang = true
skia_enable_tools = false
skia_enable_ganesh = true
skia_enable_pdf = false
skia_enable_skottie = false
skia_use_dng_sdk = false
skia_use_fontconfig = false
skia_use_freetype = false
skia_use_harfbuzz = false
skia_use_icu = false
skia_use_piex = false
skia_use_sfntly = false
skia_use_system_expat = false
skia_use_system_freetype2 = false
skia_use_system_libjpeg_turbo = false
skia_use_system_libpng = false
skia_use_system_libwebp = false
skia_use_system_zlib = false
skia_use_vulkan = false
skia_use_xps = false
extra_cflags = [ "-DSKIA_C_DLL" ]
extra_cflags_cc = [ "/GR" ]
"@ | Set-Content -Path (Join-Path $outDir "args.gn") -Encoding ASCII
Push-Location $skiaDir
try {
& (Join-Path $skiaDir "bin\gn.exe") gen $outDir
ninja -C $outDir -j $BuildJobs skia SkiaSharp HarfBuzzSharp
} finally {
Pop-Location
}
Copy-FirstExisting (Join-Path $OutputDir "skia.lib") @((Join-Path $outDir "skia.lib"), (Join-Path $outDir "obj\skia.lib"))
Copy-FirstExisting (Join-Path $OutputDir "SkiaSharp.lib") @((Join-Path $outDir "SkiaSharp.lib"), (Join-Path $outDir "obj\SkiaSharp.lib"))
Copy-FirstExisting (Join-Path $OutputDir "libHarfBuzzSharp.lib") @(
(Join-Path $outDir "libHarfBuzzSharp.lib"),
(Join-Path $outDir "HarfBuzzSharp.lib"),
(Join-Path $outDir "obj\libHarfBuzzSharp.lib"),
(Join-Path $outDir "obj\HarfBuzzSharp.lib"),
(Join-Path $outDir "obj\HarfBuzzSharp\libHarfBuzzSharp.lib"),
(Join-Path $outDir "obj\HarfBuzzSharp\HarfBuzzSharp.lib")
)
}
function Sync-Angle {
$src = Join-Path $WorkDir "ANGLE-$AngleBranch"
if (-not (Test-Path (Join-Path $src ".git"))) {
$null = git -c core.longpaths=true clone --depth 1 --branch "chromium/$AngleBranch" https://github.com/google/angle.git $src
} else {
$null = git -C $src fetch --depth 1 origin "chromium/$AngleBranch"
$null = git -C $src checkout -q FETCH_HEAD
}
return $src
}
function Apply-AnglePatches($Src) {
$buildFile = Join-Path $Src "BUILD.gn"
$buildText = Get-Content -Path $buildFile -Raw
if (-not $buildText.Contains('angle_static_library("libANGLE_static")')) {
$libAngleTargets = @'
angle_static_library("libANGLE_static") {
complete_static_lib = true
public_deps = [ ":libANGLE" ]
}
angle_static_library("libANGLE_with_capture_static") {
complete_static_lib = true
public_deps = [ ":libANGLE_with_capture" ]
}
angle_static_library("libGLESv2_static") {
'@
$buildText = [regex]::Replace($buildText, '(?m)^angle_static_library\("libGLESv2_static"\) \{', $libAngleTargets)
$buildText = [regex]::Replace($buildText, '(?m)^angle_static_library\("libGLESv2_static"\) \{\r?\n sources = libglesv2_sources', "angle_static_library(`"libGLESv2_static`") {`n complete_static_lib = true`n sources = libglesv2_sources")
Set-Content -Path $buildFile -Value $buildText -NoNewline -Encoding UTF8
}
$patch = Join-Path $AnglePatchDir "angle-chromium-$AngleBranch.patch"
$depsFile = Join-Path $Src "DEPS"
if ((Test-Path $patch) -and (Select-String -Path $depsFile -Pattern "'third_party/catapult'" -Quiet)) {
$null = git -C $Src apply $patch
if ($LASTEXITCODE -ne 0) {
throw "Failed to apply ANGLE patch: $patch"
}
}
}
function Assert-AngleVisualStudioVersion {
if ($env:VisualStudioVersion -and -not ($env:VisualStudioVersion -in @("17.0", "16.0", "15.0"))) {
throw "ANGLE $AngleBranch requires Visual Studio 2022/2019/2017, but VisualStudioVersion is $env:VisualStudioVersion. Use windows-2022 or a VS 2022 developer prompt."
}
if ($env:GYP_MSVS_OVERRIDE_PATH -and ($env:GYP_MSVS_OVERRIDE_PATH -match '\\Microsoft Visual Studio\\18\\')) {
throw "ANGLE $AngleBranch requires Visual Studio 2022/2019/2017, but GYP_MSVS_OVERRIDE_PATH points to $env:GYP_MSVS_OVERRIDE_PATH. Use windows-2022 or a VS 2022 developer prompt."
}
}
function Test-AnglePatch {
Require-Command git
Assert-AngleVisualStudioVersion
$src = Sync-Angle
Apply-AnglePatches $src
if (-not (Select-String -Path (Join-Path $src "BUILD.gn") -Pattern 'angle_static_library\("libANGLE_static"\)' -Quiet)) {
throw "ANGLE BUILD.gn preflight failed."
}
Write-Host "ANGLE preflight passed."
}
function Build-Angle {
Ensure-Tools
Assert-AngleVisualStudioVersion
Ensure-DepotTools
$src = Sync-Angle
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
Apply-AnglePatches $src
Push-Location $src
try {
python scripts/bootstrap.py
gclient sync -f -D -R
$outDir = Join-Path $src "out\win-static-$TargetCpu"
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
@"
target_os = "win"
target_cpu = "$TargetCpu"
is_debug = false
is_component_build = false
is_clang = true
use_lld = false
use_custom_libcxx = false
use_thin_lto = false
symbol_level = 0
angle_build_tests = false
build_angle_deqp_tests = false
angle_enable_swiftshader = false
angle_enable_vulkan = false
angle_enable_wgpu = false
"@ | Set-Content -Path (Join-Path $outDir "args.gn") -Encoding ASCII
gn gen $outDir
ninja -C $outDir -j $BuildJobs libANGLE_static libGLESv2_static
Copy-FirstExisting (Join-Path $OutputDir "libANGLE_static.lib") @((Join-Path $outDir "libANGLE_static.lib"), (Join-Path $outDir "obj\libANGLE_static.lib"), (Join-Path $outDir "obj\libANGLE_static\libANGLE_static.lib"))
Copy-FirstExisting (Join-Path $OutputDir "libGLESv2_static.lib") @((Join-Path $outDir "libGLESv2_static.lib"), (Join-Path $outDir "obj\libGLESv2_static.lib"), (Join-Path $outDir "obj\libGLESv2_static\libGLESv2_static.lib"))
foreach ($libcxxName in @("libc++.lib", "libc++abi.lib")) {
$libcxxPath = Join-Path $src "third_party\llvm-build\Release+Asserts\lib\$libcxxName"
if (Test-Path $libcxxPath) {
Copy-Item $libcxxPath (Join-Path $OutputDir $libcxxName) -Force
Write-Host "Wrote $(Join-Path $OutputDir $libcxxName)"
}
}
} finally {
Pop-Location
}
}
New-Item -ItemType Directory -Path $WorkDir -Force | Out-Null
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
switch ($Target) {
"skia" { Build-Skia }
"angle-preflight" { Test-AnglePatch }
"angle" { Build-Angle }
"all" { Build-Skia; Build-Angle }
}
+154
View File
@@ -0,0 +1,154 @@
param(
[Parameter(Mandatory = $true)]
[string]$ProjectDir,
[Parameter(Mandatory = $true)]
[string]$NuGetSource,
[Parameter(Mandatory = $true)]
[string]$StaticLinkVersion,
[string]$AvaloniaVersion = "11.3.14",
[string]$StaticLinkNativeVersion = ""
)
$ErrorActionPreference = "Stop"
New-Item -ItemType Directory -Path $ProjectDir -Force | Out-Null
$nativeReference = ""
if ($StaticLinkNativeVersion) {
$nativeReference = "`n <PackageReference Include=`"StaticLink.Avalonia.Native`" Version=`"$StaticLinkNativeVersion`" />"
}
@"
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="local-staticlink" value="$NuGetSource" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
"@ | Set-Content -Path (Join-Path $ProjectDir "NuGet.config") -Encoding UTF8
@"
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<PublishAot>true</PublishAot>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>true</PublishTrimmed>
<PublishReadyToRun>false</PublishReadyToRun>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="$AvaloniaVersion" />
<PackageReference Include="Avalonia.Desktop" Version="$AvaloniaVersion" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="$AvaloniaVersion" />
<PackageReference Include="StaticLink.Avalonia" Version="$StaticLinkVersion" />$nativeReference
</ItemGroup>
</Project>
"@ | Set-Content -Path (Join-Path $ProjectDir "AvaloniaStaticLinkSmoke.csproj") -Encoding UTF8
@"
using Avalonia;
namespace AvaloniaStaticLinkSmoke;
internal static class Program
{
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
public static AppBuilder BuildAvaloniaApp()
{
var builder = AppBuilder.Configure<App>()
.UsePlatformDetect()
.LogToTrace();
if (OperatingSystem.IsMacOS())
{
builder = builder.With(new AvaloniaNativePlatformOptions
{
RenderingMode = [AvaloniaNativeRenderingMode.Software]
});
}
return builder;
}
}
"@ | Set-Content -Path (Join-Path $ProjectDir "Program.cs") -Encoding UTF8
@"
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace AvaloniaStaticLinkSmoke;
public sealed partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
}
base.OnFrameworkInitializationCompleted();
}
}
"@ | Set-Content -Path (Join-Path $ProjectDir "App.axaml.cs") -Encoding UTF8
@"
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaStaticLinkSmoke.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
"@ | Set-Content -Path (Join-Path $ProjectDir "App.axaml") -Encoding UTF8
@"
using Avalonia.Controls;
namespace AvaloniaStaticLinkSmoke;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
"@ | Set-Content -Path (Join-Path $ProjectDir "MainWindow.axaml.cs") -Encoding UTF8
@"
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaStaticLinkSmoke.MainWindow"
Width="420"
Height="220"
Title="Avalonia StaticLink Smoke">
<Border Padding="24">
<StackPanel Spacing="8" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="Avalonia $AvaloniaVersion" FontSize="24" FontWeight="SemiBold" HorizontalAlignment="Center" />
<TextBlock Text="StaticLink NativeAOT smoke publish" HorizontalAlignment="Center" />
</StackPanel>
</Border>
</Window>
"@ | Set-Content -Path (Join-Path $ProjectDir "MainWindow.axaml") -Encoding UTF8