Author SHA1 Message Date
geruipu 5f21d670b0 Add linux musl static graphics support 2026-07-01 18:26:31 +08:00
geruipu 294b2f67d4 Simplify README usage 2026-06-21 13:10:19 +08:00
geruipu ff4458d089 StaticLink.Avalonia baseline 2026-06-21 12:37:51 +08:00
11 changed files with 680 additions and 369 deletions

No files matched your search

@@ -1,256 +0,0 @@
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
+111 -9
View File
@@ -20,6 +20,7 @@ on:
env:
SKIASHARP_VERSION: "3.119.2"
ANGLE_BRANCH: "7151"
DOTNET_ALPINE_IMAGE: mcr.microsoft.com/dotnet/sdk:10.0-alpine
jobs:
build-linux:
@@ -33,22 +34,59 @@ jobs:
- rid: linux-arm64
target_cpu: arm64
runner: ubuntu-24.04-arm
- rid: linux-musl-x64
target_cpu: x64
runner: ubuntu-latest
- rid: linux-musl-arm64
target_cpu: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Install native build dependencies
if: ${{ !startsWith(matrix.rid, 'linux-musl-') }}
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
if: ${{ !startsWith(matrix.rid, 'linux-musl-') }}
env:
RID: ${{ matrix.rid }}
TARGET_CPU: ${{ matrix.target_cpu }}
LLVM_AR: llvm-ar
run: scripts/build-linux-static-graphics.sh all
- name: Build Linux musl static graphics libraries
if: ${{ startsWith(matrix.rid, 'linux-musl-') }}
run: |
docker run --rm \
-v "$PWD:/workspace" \
-w /workspace \
-e RID="${{ matrix.rid }}" \
-e TARGET_CPU="${{ matrix.target_cpu }}" \
-e OUTPUT_DIR="/workspace/External/NativeStatic/${{ matrix.rid }}/native" \
-e SKIASHARP_VERSION="$SKIASHARP_VERSION" \
-e ANGLE_BRANCH="$ANGLE_BRANCH" \
-e LLVM_AR=llvm-ar \
-e DEPOT_TOOLS_BOOTSTRAP_PYTHON3=0 \
-e VPYTHON_BYPASS="manually managed python not supported by chrome operations" \
"$DOTNET_ALPINE_IMAGE" \
sh -lc '
set -e
apk add --no-cache bash ca-certificates git python3 py3-httplib2 py3-pysocks py3-six clang compiler-rt lld llvm g++ ninja pkgconf fontconfig-dev glib-dev mesa-dev libx11-dev libxcb-dev libxext-dev pciutils-dev linux-headers perl curl tar xz patch gcompat libc6-compat
python3 - <<'"'"'PY'"'"'
import pathlib
import httplib2
path = pathlib.Path(httplib2.__file__).with_name("socks.py")
if not path.exists():
path.write_text("from socks import *\n")
PY
bash scripts/build-linux-static-graphics.sh all
'
- name: Upload Linux static archives
uses: actions/upload-artifact@v4
with:
@@ -64,6 +102,9 @@ jobs:
- rid: win-x64
target_cpu: x64
msvc_arch: x64
- rid: win-x86
target_cpu: x86
msvc_arch: x86
- rid: win-arm64
target_cpu: arm64
msvc_arch: amd64_arm64
@@ -124,7 +165,7 @@ jobs:
runner: macos-14
- rid: osx-arm64
target_cpu: arm64
runner: macos-latest
runner: macos-15
runs-on: ${{ matrix.runner }}
env:
RID: ${{ matrix.rid }}
@@ -137,6 +178,19 @@ jobs:
- name: Install native build dependencies
run: brew install ninja
- name: Install Xcode Metal toolchain
run: |
set -euo pipefail
cat > /tmp/staticlink-metal-check.metal <<'EOF'
#include <metal_stdlib>
using namespace metal;
kernel void staticlink_metal_check(device float* values [[buffer(0)]], uint id [[thread_position_in_grid]]) { values[id] = 0; }
EOF
if ! xcrun metal -c /tmp/staticlink-metal-check.metal -o /tmp/staticlink-metal-check.air >/dev/null 2>&1; then
sudo xcodebuild -downloadComponent MetalToolchain
fi
xcrun metal -c /tmp/staticlink-metal-check.metal -o /tmp/staticlink-metal-check.air >/dev/null
- name: Build macOS static graphics libraries
run: bash ./scripts/build-macos-static-graphics.sh all
@@ -159,13 +213,13 @@ jobs:
runner: macos-14
avalonia_version: 11.3.14
- rid: osx-arm64
runner: macos-latest
runner: macos-15
avalonia_version: 11.3.14
- rid: osx-x64
runner: macos-14
avalonia_version: 12.0.3
- rid: osx-arm64
runner: macos-latest
runner: macos-15
avalonia_version: 12.0.3
runs-on: ${{ matrix.runner }}
env:
@@ -231,7 +285,7 @@ jobs:
- name: Move static archives into RID directories
run: |
for rid in win-x64 win-arm64 linux-x64 linux-arm64 osx-x64 osx-arm64; do
for rid in win-x64 win-x86 win-arm64 linux-x64 linux-arm64 linux-musl-x64 linux-musl-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"
@@ -243,7 +297,7 @@ jobs:
- name: Verify static archives
run: |
for rid in linux-x64 linux-arm64 osx-x64 osx-arm64; do
for rid in linux-x64 linux-arm64 linux-musl-x64 linux-musl-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
@@ -251,13 +305,14 @@ jobs:
test -f External/NativeStatic/$rid/native/libGLESv2_static.a
done
for rid in win-x64 win-arm64; do
for rid in win-x64 win-x86 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
test -f External/NativeStatic/win-x86/native/skia_x86_stdcall_thunks.lib
- name: Pack NuGet package
run: |
@@ -308,6 +363,9 @@ jobs:
- rid: win-x64
runner: windows-2022
msvc_arch: x64
- rid: win-x86
runner: windows-2022
msvc_arch: x86
- rid: win-arm64
runner: windows-2022
msvc_arch: amd64_arm64
@@ -315,12 +373,16 @@ jobs:
runner: ubuntu-latest
- rid: linux-arm64
runner: ubuntu-24.04-arm
- rid: linux-musl-x64
runner: ubuntu-latest
- rid: linux-musl-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
runner: macos-15
avalonia_version: 11.3.14
native_version: 11.3.14.1
- rid: osx-x64
@@ -328,7 +390,7 @@ jobs:
avalonia_version: 12.0.3
native_version: 12.0.3.1
- rid: osx-arm64
runner: macos-latest
runner: macos-15
avalonia_version: 12.0.3
native_version: 12.0.3.1
runs-on: ${{ matrix.runner }}
@@ -342,7 +404,7 @@ jobs:
arch: ${{ matrix.msvc_arch }}
- name: Install Linux publish dependencies
if: ${{ startsWith(matrix.rid, 'linux-') }}
if: ${{ startsWith(matrix.rid, 'linux-') && !startsWith(matrix.rid, 'linux-musl-') }}
run: |
sudo apt-get update
sudo apt-get install -y clang lld zlib1g-dev libfontconfig1-dev
@@ -359,6 +421,7 @@ jobs:
path: artifacts/nuget
- name: Publish Avalonia single-file smoke app
if: ${{ !startsWith(matrix.rid, 'linux-musl-') }}
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
@@ -385,6 +448,23 @@ jobs:
/p:StripSymbols=true `
/p:PublishDir="$PWD/artifacts/smoke-${{ matrix.rid }}/"
- name: Publish Avalonia musl smoke app
if: ${{ startsWith(matrix.rid, 'linux-musl-') }}
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]
$projectDir = Join-Path $PWD "artifacts/avalonia-smoke-src"
pwsh ./scripts/create-avalonia-smoke.ps1 -ProjectDir $projectDir -NuGetSource "/workspace/artifacts/nuget" -StaticLinkVersion $version -AvaloniaVersion "11.3.14"
docker run --rm `
-v "${{ github.workspace }}:/workspace" `
-w /workspace `
"${{ env.DOTNET_ALPINE_IMAGE }}" `
sh -c "apk add --no-cache clang lld build-base zlib-dev fontconfig-dev && dotnet publish /workspace/artifacts/avalonia-smoke-src/AvaloniaStaticLinkSmoke.csproj -c Release -r ${{ matrix.rid }} --configfile /workspace/artifacts/avalonia-smoke-src/NuGet.config /p:PublishAot=true /p:SelfContained=true /p:PublishSingleFile=true /p:StripSymbols=true /p:PublishDir=/workspace/artifacts/smoke-${{ matrix.rid }}/"
- name: Verify no dynamic graphics libraries remain
shell: pwsh
run: |
@@ -398,6 +478,28 @@ jobs:
}
Get-ChildItem -Path $publishDir -Recurse -File | ForEach-Object { $_.FullName }
- name: Verify musl ELF dependencies
if: ${{ startsWith(matrix.rid, 'linux-musl-') }}
shell: bash
run: |
set -euo pipefail
docker run --rm \
-v "${{ github.workspace }}:/workspace" \
-w /workspace \
alpine:latest \
sh -c 'apk add --no-cache binutils pax-utils && app="artifacts/smoke-${{ matrix.rid }}/AvaloniaStaticLinkSmoke" && readelf -l "$app" | grep "Requesting program interpreter: /lib/ld-musl-" && scanelf -n "$app" && ! scanelf -n "$app" | grep -E "ld-linux|ld64.so"'
- name: Run Linux musl Avalonia smoke app
if: ${{ startsWith(matrix.rid, 'linux-musl-') }}
shell: bash
run: |
set -euo pipefail
docker run --rm \
-v "${{ github.workspace }}:/workspace" \
-w /workspace/artifacts/smoke-${{ matrix.rid }} \
alpine:latest \
sh -c 'apk add --no-cache xvfb-run libstdc++ libgcc fontconfig freetype ttf-dejavu libx11 libxcb libxext libxrender libxi libxrandr libxcursor libxfixes libxinerama libice libsm mesa-gl mesa-egl && fc-cache -f && fc-match sans && chmod +x ./AvaloniaStaticLinkSmoke && timeout 15s xvfb-run -a ./AvaloniaStaticLinkSmoke || code=$?; if [ "${code:-0}" = 124 ] || [ "${code:-0}" = 143 ]; then echo "Avalonia musl smoke app stayed alive for 15 seconds."; exit 0; fi; exit "${code:-0}"'
- name: Run macOS Avalonia smoke app
if: ${{ startsWith(matrix.rid, 'osx-') }}
shell: bash
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>true</IsPackable>
<NoBuild>true</NoBuild>
<IncludeBuildOutput>false</IncludeBuildOutput>
@@ -20,7 +20,7 @@
<ItemGroup>
<None Include="../StaticGraphics/README.md" Pack="true" PackagePath="README.md" />
<None Include="lib/net10.0/_._" Pack="true" PackagePath="lib/net10.0/_._" />
<None Include="lib/net8.0/_._" Pack="true" PackagePath="lib/net8.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" />
File renamed without changes.
+12 -45
View File
@@ -1,19 +1,6 @@
# 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`
Static native libraries for Avalonia single-file NativeAOT publishing.
## Install
@@ -21,10 +8,16 @@ Repository: <https://github.com/greepar/StaticLink.Avalonia>
dotnet add package StaticLink.Avalonia
```
For fully static macOS output, also reference the AvaloniaNative package that matches your Avalonia version:
Or add it manually:
```bash
dotnet add package StaticLink.Avalonia.Native --version 11.3.14.1
```xml
<PackageReference Include="StaticLink.Avalonia" Version="3.119.2-7151.10" />
```
For macOS, also reference `StaticLink.Avalonia.Native`. This package is macOS-only and must match your Avalonia version because it contains `libAvaloniaNative.a`.
```xml
<PackageReference Include="StaticLink.Avalonia.Native" Version="11.3.14.1" />
```
## Publish
@@ -37,32 +30,6 @@ dotnet publish -c Release -r win-x64 \
-p:StripSymbols=true
```
Use the RID you need, for example `linux-x64`, `linux-arm64`, `osx-arm64`, or `osx-x64`.
Use the RID you need, such as `win-x86`, `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.
For macOS, avoid the Metal renderer for fully static output. Use OpenGL or Software.
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>true</IsPackable>
<NoBuild>true</NoBuild>
<IncludeBuildOutput>false</IncludeBuildOutput>
@@ -24,13 +24,16 @@
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
<None Include="lib/net10.0/_._" Pack="true" PackagePath="lib/net10.0/_._" />
<None Include="lib/net8.0/_._" Pack="true" PackagePath="lib/net8.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-x86/native/**/*" Pack="true" PackagePath="static/win-x86/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/linux-musl-x64/native/**/*" Pack="true" PackagePath="static/linux-musl-x64/native/%(RecursiveDir)%(Filename)%(Extension)" />
<None Include="../../External/NativeStatic/linux-musl-arm64/native/**/*" Pack="true" PackagePath="static/linux-musl-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>
@@ -1,21 +1,25 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_StaticLinkAvaloniaRid Condition="'$(RuntimeIdentifier)' == 'win-x64'">win-x64</_StaticLinkAvaloniaRid>
<_StaticLinkAvaloniaRid Condition="'$(RuntimeIdentifier)' == 'win-x86'">win-x86</_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)' == 'linux-musl-x64'">linux-musl-x64</_StaticLinkAvaloniaRid>
<_StaticLinkAvaloniaRid Condition="'$(RuntimeIdentifier)' == 'linux-musl-arm64'">linux-musl-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>
<LinkerFlavor Condition="'$(LinkerFlavor)' == '' and ('$(RuntimeIdentifier)' == 'linux-x64' or '$(RuntimeIdentifier)' == 'linux-arm64' or '$(RuntimeIdentifier)' == 'linux-musl-x64' or '$(RuntimeIdentifier)' == 'linux-musl-arm64')">lld</LinkerFlavor>
</PropertyGroup>
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'win-x64' or '$(RuntimeIdentifier)' == 'win-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'win-x64' or '$(RuntimeIdentifier)' == 'win-x86' 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_x86_stdcall_thunks.lib" Condition="'$(RuntimeIdentifier)' == 'win-x86' and Exists('$(_StaticLinkAvaloniaNativeRoot)/skia_x86_stdcall_thunks.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')" />
@@ -30,7 +34,51 @@
<LinkerArg Include="d3d12.lib;d3dcompiler.lib" />
</ItemGroup>
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'linux-x64' or '$(RuntimeIdentifier)' == 'linux-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<Target Name="StaticLinkAvaloniaAddWindowsDirectPInvokes"
BeforeTargets="WriteIlcRspFileForCompilation"
Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'win-x64' or '$(RuntimeIdentifier)' == 'win-x86' or '$(RuntimeIdentifier)' == 'win-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<PropertyGroup>
<_StaticLinkAvaloniaDirectPInvokeList>$(NativeIntermediateOutputPath)staticlink-avalonia.directpinvokes.txt</_StaticLinkAvaloniaDirectPInvokeList>
</PropertyGroup>
<ItemGroup>
<_StaticLinkAvaloniaDirectPInvoke Include="libHarfBuzzSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.lib')" />
<_StaticLinkAvaloniaDirectPInvoke Include="libHarfBuzzSharp.dll" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.lib')" />
<_StaticLinkAvaloniaDirectPInvoke Include="libSkiaSharp" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/SkiaSharp.lib')" />
<_StaticLinkAvaloniaDirectPInvoke Include="libSkiaSharp.dll" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/SkiaSharp.lib')" />
<_StaticLinkAvaloniaDirectPInvoke Include="libGLESv2" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.lib')" />
<_StaticLinkAvaloniaDirectPInvoke Include="libGLESv2.dll" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.lib')" />
<_StaticLinkAvaloniaDirectPInvoke Include="av_libglesv2" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.lib')" />
<_StaticLinkAvaloniaDirectPInvoke Include="av_libglesv2.dll" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libGLESv2_static.lib')" />
</ItemGroup>
<WriteLinesToFile File="$(_StaticLinkAvaloniaDirectPInvokeList)" Lines="@(_StaticLinkAvaloniaDirectPInvoke)" Overwrite="true" />
<ItemGroup>
<DirectPInvoke Include="@(_StaticLinkAvaloniaDirectPInvoke)" />
<DirectPInvokeList Include="$(_StaticLinkAvaloniaDirectPInvokeList)" />
</ItemGroup>
</Target>
<Target Name="StaticLinkAvaloniaAddWindowsNativeLibraries"
BeforeTargets="LinkNative"
Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'win-x64' or '$(RuntimeIdentifier)' == 'win-x86' or '$(RuntimeIdentifier)' == 'win-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != ''">
<ItemGroup>
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.lib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libHarfBuzzSharp.lib')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/skia_x86_stdcall_thunks.lib" Condition="'$(RuntimeIdentifier)' == 'win-x86' and Exists('$(_StaticLinkAvaloniaNativeRoot)/skia_x86_stdcall_thunks.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>
</Target>
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'linux-x64' or '$(RuntimeIdentifier)' == 'linux-arm64' or '$(RuntimeIdentifier)' == 'linux-musl-x64' or '$(RuntimeIdentifier)' == 'linux-musl-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')" />
@@ -43,7 +91,7 @@
<NativeSystemLibrary Include="fontconfig" />
</ItemGroup>
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'linux-x64' or '$(RuntimeIdentifier)' == 'linux-arm64') and '$(_StaticLinkAvaloniaNativeRoot)' != '' and '$(LinkerFlavor)' == 'lld'">
<ItemGroup Condition="'$(PublishAot)' == 'true' and ('$(RuntimeIdentifier)' == 'linux-x64' or '$(RuntimeIdentifier)' == 'linux-arm64' or '$(RuntimeIdentifier)' == 'linux-musl-x64' or '$(RuntimeIdentifier)' == 'linux-musl-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>
File renamed without changes.
+12 -45
View File
@@ -1,19 +1,6 @@
# 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`
Static native libraries for Avalonia single-file NativeAOT publishing.
## Install
@@ -21,10 +8,16 @@ Repository: <https://github.com/greepar/StaticLink.Avalonia>
dotnet add package StaticLink.Avalonia
```
For fully static macOS output, also reference the AvaloniaNative package that matches your Avalonia version:
Or add it manually:
```bash
dotnet add package StaticLink.Avalonia.Native --version 11.3.14.1
```xml
<PackageReference Include="StaticLink.Avalonia" Version="3.119.2-7151.10" />
```
For macOS, also reference `StaticLink.Avalonia.Native`. This package is macOS-only and must match your Avalonia version because it contains `libAvaloniaNative.a`.
```xml
<PackageReference Include="StaticLink.Avalonia.Native" Version="11.3.14.1" />
```
## Publish
@@ -37,32 +30,6 @@ dotnet publish -c Release -r win-x64 \
-p:StripSymbols=true
```
Use the RID you need, for example `linux-x64`, `linux-arm64`, `osx-arm64`, or `osx-x64`.
Use the RID you need, such as `win-x86`, `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.
For macOS, avoid the Metal renderer for fully static output. Use OpenGL or Software.
+206 -2
View File
@@ -37,6 +37,10 @@ require_cmd() {
fi
}
is_musl_rid() {
[[ "$RID" == linux-musl-* ]]
}
ensure_tools() {
require_cmd git
require_cmd python3
@@ -50,12 +54,84 @@ ensure_tools() {
ensure_depot_tools() {
local depot_dir="$WORK_DIR/depot_tools"
local python_bin_dir
python_bin_dir="$(dirname "$(command -v python3)")"
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"
if is_musl_rid; then
initialize_depot_tools_system_python "$depot_dir"
patch_depot_tools_python_deps "$depot_dir"
fi
export PATH="$python_bin_dir:$depot_dir:$PATH"
}
initialize_depot_tools_system_python() {
local depot_dir="$1"
local python_bin_dir
python_bin_dir="$(dirname "$(command -v python3)")"
if [[ -d "$depot_dir" ]]; then
python3 - "$depot_dir" "$python_bin_dir" <<'PY'
import os
import pathlib
import sys
depot_dir = pathlib.Path(sys.argv[1]).resolve()
python_bin_dir = pathlib.Path(sys.argv[2]).resolve()
marker = depot_dir / "python3_bin_reldir.txt"
marker.write_text(os.path.relpath(python_bin_dir, depot_dir) + "\n")
PY
fi
}
patch_depot_tools_python_deps() {
local depot_dir="$1"
local gsutil_dir="$depot_dir/external_bin/gsutil/gsutil_4.68/gsutil"
local gsutil_third_party="$gsutil_dir/third_party"
if [[ -d "$gsutil_dir" && ! -f "$gsutil_dir/six.py" ]]; then
python3 - "$gsutil_dir/six.py" <<'PY'
import pathlib
import shutil
import six
import sys
src = pathlib.Path(six.__file__)
dest = pathlib.Path(sys.argv[1])
shutil.copyfile(src, dest)
PY
fi
if [[ -d "$gsutil_third_party" && ! -f "$gsutil_third_party/six.py" ]]; then
python3 - "$gsutil_third_party/six.py" <<'PY'
import pathlib
import shutil
import six
import sys
src = pathlib.Path(six.__file__)
dest = pathlib.Path(sys.argv[1])
shutil.copyfile(src, dest)
PY
fi
}
prepend_python_module_path() {
local module="$1"
local module_dir
module_dir="$(python3 - "$module" <<'PY'
import importlib
import pathlib
import sys
module = importlib.import_module(sys.argv[1])
print(pathlib.Path(module.__file__).parent)
PY
)"
export PYTHONPATH="$module_dir:${PYTHONPATH:-}"
}
sync_skiasharp() {
@@ -186,16 +262,28 @@ build_angle() {
cd "$src"
apply_angle_patches "$src"
python3 scripts/bootstrap.py
if is_musl_rid; then
initialize_depot_tools_system_python "$src/third_party/depot_tools"
patch_depot_tools_python_deps "$src/third_party/depot_tools"
prepend_python_module_path six
prepare_angle_gcs_artifacts "$src"
fi
prepare_musl_clang_runtime
prepare_musl_libstdcxx_headers
gclient sync -f -D -R
local out_dir="$src/out/linux-static-$TARGET_CPU"
local angle_is_clang="false"
if is_musl_rid; then
angle_is_clang="true"
fi
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
is_clang = $angle_is_clang
treat_warnings_as_errors = false
use_custom_libcxx = false
use_sysroot = false
@@ -208,6 +296,7 @@ angle_enable_swiftshader = false
angle_enable_vulkan = false
angle_enable_wgpu = false
EOF_ARGS
append_angle_musl_gn_args "$out_dir/args.gn"
gn gen "$out_dir"
ninja -C "$out_dir" -j "$BUILD_JOBS" libANGLE_static libGLESv2_static
@@ -262,6 +351,121 @@ PY
fi
}
prepare_angle_gcs_artifacts() {
local src="$1"
download_angle_gcs_artifact \
angle-glslang-validator \
"$src/tools/glslang/glslang_validator.sha1" \
"$src/tools/glslang/glslang_validator"
download_angle_gcs_artifact \
angle-flex-bison \
"$src/tools/flex-bison/linux/bison.sha1" \
"$src/tools/flex-bison/linux/bison"
download_angle_gcs_artifact \
angle-flex-bison \
"$src/tools/flex-bison/linux/flex.sha1" \
"$src/tools/flex-bison/linux/flex"
}
download_angle_gcs_artifact() {
local bucket="$1"
local sha_file="$2"
local output="$3"
if [[ -f "$sha_file" && ! -f "$output" ]]; then
local sha
sha="$(tr -d '[:space:]' <"$sha_file")"
curl -fsSL "https://storage.googleapis.com/$bucket/$sha" -o "$output"
chmod +x "$output"
fi
}
append_angle_musl_gn_args() {
local args_file="$1"
if is_musl_rid; then
cat >>"$args_file" <<'EOF_ARGS'
clang_base_path = "/usr"
clang_use_chrome_plugins = false
EOF_ARGS
fi
}
prepare_musl_clang_runtime() {
if ! is_musl_rid; then
return 0
fi
local arch
local gnu_triple
case "$TARGET_CPU" in
x64)
arch="x86_64"
gnu_triple="x86_64-unknown-linux-gnu"
;;
arm64)
arch="aarch64"
gnu_triple="aarch64-unknown-linux-gnu"
;;
*)
echo "Unsupported musl ANGLE target_cpu for clang runtime: $TARGET_CPU" >&2
return 1
;;
esac
local src
src="$(find /usr/lib/llvm* /usr/lib/clang -path "*/${arch}-alpine-linux-musl/libclang_rt.builtins-${arch}.a" -print -quit 2>/dev/null || true)"
if [[ -z "$src" ]]; then
echo "Could not find Alpine compiler-rt builtins for $arch" >&2
return 1
fi
local clang_version
clang_version="$(clang -print-resource-dir | awk -F/ '{print $NF}')"
local dest_dir="/usr/lib/clang/$clang_version/lib/$gnu_triple"
mkdir -p "$dest_dir"
ln -sf "$src" "$dest_dir/libclang_rt.builtins.a"
}
prepare_musl_libstdcxx_headers() {
if ! is_musl_rid; then
return 0
fi
local arch
local musl_triple
case "$TARGET_CPU" in
x64)
arch="x86_64"
musl_triple="x86_64-linux-musl"
;;
arm64)
arch="aarch64"
musl_triple="aarch64-linux-musl"
;;
*)
echo "Unsupported musl ANGLE target_cpu for libstdc++ headers: $TARGET_CPU" >&2
return 1
;;
esac
local cxx_root
cxx_root="$(find /usr/include/c++ -mindepth 1 -maxdepth 1 -type d -print -quit 2>/dev/null || true)"
if [[ -z "$cxx_root" ]]; then
echo "Could not find libstdc++ include root" >&2
return 1
fi
local src_dir="$cxx_root/${arch}-alpine-linux-musl"
local dest_dir="$cxx_root/$musl_triple"
if [[ ! -d "$src_dir" ]]; then
echo "Could not find Alpine libstdc++ target headers: $src_dir" >&2
return 1
fi
ln -sfn "$src_dir" "$dest_dir"
}
copy_first_existing() {
local dest="$1"
shift
+280 -4
View File
@@ -72,6 +72,275 @@ function Copy-FirstExisting($Destination, [string[]]$Candidates) {
throw "None of the expected files exist for ${Destination}: $($Candidates -join ', ')"
}
function Split-ParameterList($Parameters) {
if ([string]::IsNullOrWhiteSpace($Parameters)) {
return @()
}
$items = [System.Collections.Generic.List[string]]::new()
$start = 0
$depth = 0
for ($i = 0; $i -lt $Parameters.Length; $i++) {
$ch = $Parameters[$i]
if ($ch -eq '[' -or $ch -eq '(' -or $ch -eq '<') {
$depth++
} elseif ($ch -eq ']' -or $ch -eq ')' -or $ch -eq '>') {
if ($depth -gt 0) { $depth-- }
} elseif ($ch -eq ',' -and $depth -eq 0) {
$items.Add($Parameters.Substring($start, $i - $start).Trim())
$start = $i + 1
}
}
$items.Add($Parameters.Substring($start).Trim())
return $items | Where-Object { $_ }
}
function Get-X86NativeParameterSize($Parameter) {
$parameter = [regex]::Replace($Parameter, '/\*.*?\*/', '').Trim()
$parameter = [regex]::Replace($parameter, '\[[^\]]+\]\s*', '').Trim()
$parameter = [regex]::Replace($parameter, '\b(ref|out|in)\b\s*', '').Trim()
if (-not $parameter) { return 0 }
$parts = $parameter -split '\s+'
if ($parts.Length -gt 1) {
$type = ($parts[0..($parts.Length - 2)] -join ' ')
} else {
$type = $parts[0]
}
$type = $type.Trim()
if ($script:X86GeneratedStructSizes -and $script:X86GeneratedStructSizes.ContainsKey($type)) {
return $script:X86GeneratedStructSizes[$type]
}
if ($type.Contains('*') -or $type.EndsWith('[]') -or $type -eq 'String' -or $type -eq 'string' -or $type.EndsWith('Delegate')) {
return 4
}
switch -Regex ($type) {
'^(Int64|UInt64|long|ulong|Double|double)$' { return 8 }
default { return 4 }
}
}
function Align-X86Size($Size, $Alignment) {
if ($Alignment -le 1) { return $Size }
return [int]([Math]::Ceiling($Size / [double]$Alignment) * $Alignment)
}
function Get-X86ManagedTypeLayout($Type, $KnownSizes) {
if ($Type.Contains('*') -or $Type.Contains('delegate*') -or $Type.EndsWith('[]') -or $Type.EndsWith('Delegate')) {
return @{ Size = 4; Alignment = 4 }
}
switch -Regex ($Type) {
'^(Byte|SByte|bool)$' { return @{ Size = 1; Alignment = 1 } }
'^(Int16|UInt16|short|ushort|Char)$' { return @{ Size = 2; Alignment = 2 } }
'^(Int64|UInt64|long|ulong|Double|double)$' { return @{ Size = 8; Alignment = 4 } }
default {
if ($KnownSizes.ContainsKey($Type)) {
return @{ Size = $KnownSizes[$Type]; Alignment = 4 }
}
return @{ Size = 4; Alignment = 4 }
}
}
}
function Get-X86GeneratedStructSizes($BindingFiles) {
$structFields = @{}
foreach ($bindingFile in $BindingFiles) {
$lines = Get-Content -Path $bindingFile
$current = $null
$depth = 0
$includeStack = @($true)
foreach ($line in $lines) {
if ($line -match '^\s*#if\s+USE_LIBRARY_IMPORT\b') {
$includeStack += $includeStack[-1]
continue
}
if ($line -match '^\s*#else\b') {
if ($includeStack.Count -gt 1) {
$parentActive = $includeStack[-2]
$includeStack[-1] = $parentActive -and (-not $includeStack[-1])
}
continue
}
if ($line -match '^\s*#endif\b') {
if ($includeStack.Count -gt 1) {
$includeStack = @($includeStack[0..($includeStack.Count - 2)])
}
continue
}
if (-not $includeStack[-1]) {
continue
}
if (-not $current -and $line -match '\bstruct\s+([A-Za-z0-9_]+)\b') {
$current = $Matches[1]
$structFields[$current] = [System.Collections.Generic.List[string]]::new()
$depth = ([regex]::Matches($line, '\{')).Count - ([regex]::Matches($line, '\}')).Count
continue
}
if ($current) {
if ($depth -eq 1 -and $line -match '^\s*public\s+(.+?)\s+[A-Za-z0-9_]+;\s*$') {
$fieldType = ([regex]::Replace($Matches[1].Trim(), '#if.*$', '')).Trim()
$structFields[$current].Add($fieldType)
}
$depth += ([regex]::Matches($line, '\{')).Count
$depth -= ([regex]::Matches($line, '\}')).Count
if ($depth -le 0) {
$current = $null
}
}
}
}
$sizes = @{}
$pending = @($structFields.Keys)
while ($pending.Count -gt 0) {
$next = @()
$progress = $false
foreach ($name in $pending) {
$offset = 0
$maxAlign = 1
$resolved = $true
foreach ($fieldType in $structFields[$name]) {
if ($structFields.ContainsKey($fieldType) -and -not $sizes.ContainsKey($fieldType)) {
$resolved = $false
break
}
$layout = Get-X86ManagedTypeLayout $fieldType $sizes
$maxAlign = [Math]::Max($maxAlign, [Math]::Min($layout.Alignment, 4))
$offset = Align-X86Size $offset ([Math]::Min($layout.Alignment, 4))
$offset += $layout.Size
}
if ($resolved) {
$sizes[$name] = Align-X86Size $offset $maxAlign
$progress = $true
} else {
$next += $name
}
}
if (-not $progress) { break }
$pending = $next
}
return $sizes
}
function Get-X86PInvokeThunks($BindingFiles, $DefinedSymbols) {
$thunks = @{}
$signaturePattern = 'internal static (?:extern|partial)\s+.+?\s+((?:sk|gr|hb)_[A-Za-z0-9_]+)\s*\((.*?)\);'
$script:X86GeneratedStructSizes = Get-X86GeneratedStructSizes $BindingFiles
foreach ($bindingFile in $BindingFiles) {
if (-not (Test-Path $bindingFile)) {
throw "Missing binding file for win-x86 thunk generation: $bindingFile"
}
$content = Get-Content -Raw -Path $bindingFile
foreach ($match in [regex]::Matches($content, $signaturePattern, [System.Text.RegularExpressions.RegexOptions]::Singleline)) {
$symbol = "_$($match.Groups[1].Value)"
if (-not $DefinedSymbols.ContainsKey($symbol)) {
continue
}
$bytes = 0
foreach ($parameter in Split-ParameterList $match.Groups[2].Value) {
$bytes += Get-X86NativeParameterSize $parameter
}
$decorated = "${symbol}@${bytes}"
$thunks[$decorated] = [pscustomobject]@{
Symbol = $symbol
Bytes = $bytes
Decorated = $decorated
}
}
}
return $thunks.Values | Sort-Object Symbol, Bytes
}
function New-WinX86SkiaStdcallThunks([string[]]$InputLibraries, [string[]]$BindingFiles, $Destination) {
if ($TargetCpu -ne "x86") {
return
}
$llvmNm = Get-Command llvm-nm.exe -ErrorAction SilentlyContinue
if (-not $llvmNm) {
$llvmNm = Get-Command llvm-nm -ErrorAction SilentlyContinue
}
if (-not $llvmNm) {
throw "Missing required command: llvm-nm"
}
Require-Command ml.exe
Require-Command lib.exe
$thunkDir = Join-Path $WorkDir "win-x86-skia-stdcall-thunks"
New-Item -ItemType Directory -Path $thunkDir -Force | Out-Null
$asmPath = Join-Path $thunkDir "skia_x86_stdcall_thunks.asm"
$objPath = Join-Path $thunkDir "skia_x86_stdcall_thunks.obj"
$definedSymbols = @{}
foreach ($library in $InputLibraries) {
& $llvmNm.Source --defined-only $library |
ForEach-Object {
if ($_ -match '\sT\s(_(?:sk|gr|hb)_[A-Za-z0-9_]+)$') {
$definedSymbols[$Matches[1]] = $true
}
}
}
if (-not $definedSymbols.Count) {
throw "No SkiaSharp/HarfBuzzSharp C API symbols found in $($InputLibraries -join ', ')"
}
$thunks = @(Get-X86PInvokeThunks $BindingFiles $definedSymbols)
if (-not $thunks) {
throw "No win-x86 stdcall thunks generated from $($BindingFiles -join ', ')"
}
$lines = [System.Collections.Generic.List[string]]::new()
$lines.Add("OPTION CASEMAP:NONE")
$lines.Add(".386")
$lines.Add(".model flat")
foreach ($symbol in ($thunks | Select-Object -ExpandProperty Symbol -Unique)) {
$lines.Add("EXTERN ${symbol}:PROC")
}
$lines.Add("_TEXT SEGMENT")
foreach ($thunk in $thunks) {
$lines.Add("PUBLIC $($thunk.Decorated)")
$lines.Add("$($thunk.Decorated) PROC")
for ($offset = 0; $offset -lt $thunk.Bytes; $offset += 4) {
$lines.Add(" push DWORD PTR [esp+$($thunk.Bytes)]")
}
$lines.Add(" call $($thunk.Symbol)")
if ($thunk.Bytes -gt 0) {
$lines.Add(" add esp, $($thunk.Bytes)")
}
$lines.Add(" ret $($thunk.Bytes)")
$lines.Add("$($thunk.Decorated) ENDP")
}
$lines.Add("_TEXT ENDS")
$lines.Add("END")
Set-Content -Path $asmPath -Value $lines -Encoding ASCII
& ml.exe /nologo /c /coff /safeseh /Fo$objPath $asmPath
if ($LASTEXITCODE -ne 0) {
throw "Failed to assemble win-x86 Skia stdcall thunks."
}
& lib.exe /nologo /out:$Destination $objPath
if ($LASTEXITCODE -ne 0) {
throw "Failed to archive win-x86 Skia stdcall thunks."
}
Write-Host "Wrote $Destination"
}
function Sync-SkiaSharp {
$src = Join-Path $WorkDir "SkiaSharp-$SkiaSharpVersion"
if (-not (Test-Path (Join-Path $src ".git"))) {
@@ -146,7 +415,7 @@ skia_use_system_libpng = false
skia_use_system_libwebp = false
skia_use_system_zlib = false
skia_use_vulkan = false
skia_use_xps = false
skia_use_xps = true
extra_cflags = [ "-DSKIA_C_DLL" ]
extra_cflags_cc = [ "/GR" ]
"@ | Set-Content -Path (Join-Path $outDir "args.gn") -Encoding ASCII
@@ -159,9 +428,12 @@ extra_cflags_cc = [ "/GR" ]
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") @(
$skiaLib = Join-Path $OutputDir "skia.lib"
$skiaSharpLib = Join-Path $OutputDir "SkiaSharp.lib"
$harfbuzzLib = Join-Path $OutputDir "libHarfBuzzSharp.lib"
Copy-FirstExisting $skiaLib @((Join-Path $outDir "skia.lib"), (Join-Path $outDir "obj\skia.lib"))
Copy-FirstExisting $skiaSharpLib @((Join-Path $outDir "SkiaSharp.lib"), (Join-Path $outDir "obj\SkiaSharp.lib"))
Copy-FirstExisting $harfbuzzLib @(
(Join-Path $outDir "libHarfBuzzSharp.lib"),
(Join-Path $outDir "HarfBuzzSharp.lib"),
(Join-Path $outDir "obj\libHarfBuzzSharp.lib"),
@@ -169,6 +441,10 @@ extra_cflags_cc = [ "/GR" ]
(Join-Path $outDir "obj\HarfBuzzSharp\libHarfBuzzSharp.lib"),
(Join-Path $outDir "obj\HarfBuzzSharp\HarfBuzzSharp.lib")
)
New-WinX86SkiaStdcallThunks `
-InputLibraries @($skiaLib, $skiaSharpLib, $harfbuzzLib) `
-BindingFiles @((Join-Path $src "binding\SkiaSharp\SkiaApi.generated.cs"), (Join-Path $src "binding\HarfBuzzSharp\HarfBuzzApi.generated.cs")) `
-Destination (Join-Path $OutputDir "skia_x86_stdcall_thunks.lib")
}
function Sync-Angle {