28 changed files with 1303 additions and 558 deletions

No files matched your search

+278
View File
@@ -0,0 +1,278 @@
name: staticlink-avalonia-native
on:
schedule:
- cron: "17 3 * * *"
workflow_dispatch:
inputs:
avalonia_version:
description: Avalonia version to build; empty uses the latest stable NuGet version
required: false
type: string
force_build:
description: Build even when this StaticLink.Avalonia.Native version already exists
required: true
default: false
type: boolean
publish_to_nuget:
description: Publish with NuGet Trusted Publishing after smoke tests
required: true
default: true
type: boolean
concurrency:
group: staticlink-avalonia-native
cancel-in-progress: false
jobs:
detect:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
avalonia_version: ${{ steps.version.outputs.avalonia_version }}
package_version: ${{ steps.version.outputs.package_version }}
should_build: ${{ steps.version.outputs.should_build }}
steps:
- name: Detect unpublished Avalonia version
id: version
shell: bash
env:
REQUESTED_VERSION: ${{ inputs.avalonia_version }}
FORCE_BUILD: ${{ inputs.force_build }}
run: |
set -euo pipefail
avalonia_version="$REQUESTED_VERSION"
if [ -z "$avalonia_version" ]; then
avalonia_version="$(curl --fail --silent --show-error --location \
https://api.nuget.org/v3-flatcontainer/avalonia/index.json \
| jq -r '[.versions[] | select(contains("-") | not)] | last')"
fi
if [ -z "$avalonia_version" ] || [ "$avalonia_version" = "null" ]; then
echo "Could not determine the latest stable Avalonia version." >&2
exit 1
fi
package_version="$avalonia_version.1"
package_url="https://api.nuget.org/v3-flatcontainer/staticlink.avalonia.native/$package_version/staticlink.avalonia.native.$package_version.nupkg"
should_build=true
if [ "$FORCE_BUILD" != "true" ] && curl --fail --silent --location --output /dev/null "$package_url"; then
should_build=false
fi
echo "Avalonia version: $avalonia_version"
echo "StaticLink.Avalonia.Native version: $package_version"
echo "Build required: $should_build"
echo "avalonia_version=$avalonia_version" >> "$GITHUB_OUTPUT"
echo "package_version=$package_version" >> "$GITHUB_OUTPUT"
echo "should_build=$should_build" >> "$GITHUB_OUTPUT"
build:
needs: detect
if: ${{ needs.detect.outputs.should_build == 'true' }}
strategy:
fail-fast: false
matrix:
include:
- rid: osx-x64
runner: macos-14
- rid: osx-arm64
runner: macos-15
runs-on: ${{ matrix.runner }}
permissions:
contents: read
env:
RID: ${{ matrix.rid }}
AVALONIA_VERSION: ${{ needs.detect.outputs.avalonia_version }}
WORK_DIR: ${{ github.workspace }}/External/AvaloniaNative/.work
OUTPUT_DIR: ${{ github.workspace }}/External/AvaloniaNative/${{ matrix.rid }}/native
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.rid }}
path: |
External/AvaloniaNative/${{ matrix.rid }}/native/libAvaloniaNative.a
External/AvaloniaNative/${{ matrix.rid }}/native/macos_font_preinit.o
if-no-files-found: error
pack:
needs:
- detect
- build
if: ${{ needs.detect.outputs.should_build == 'true' }}
runs-on: ubuntu-latest
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 AvaloniaNative archives
uses: actions/download-artifact@v4
with:
pattern: native-avalonia-*
path: artifacts/native
merge-multiple: false
- name: Prepare package inputs
shell: bash
run: |
set -euo pipefail
for rid in osx-x64 osx-arm64; do
mkdir -p "External/AvaloniaNative/$rid/native"
cp "artifacts/native/native-avalonia-$rid/libAvaloniaNative.a" "External/AvaloniaNative/$rid/native/"
cp "artifacts/native/native-avalonia-$rid/macos_font_preinit.o" "External/AvaloniaNative/$rid/native/"
done
- name: Pack StaticLink.Avalonia.Native
run: |
dotnet pack NuGet/AvaloniaNative/StaticLink.Avalonia.Native.csproj \
-c Release \
-o artifacts/nuget \
/p:PackageVersion=${{ needs.detect.outputs.package_version }} \
/p:Version=${{ needs.detect.outputs.package_version }}
- name: Upload NuGet package
uses: actions/upload-artifact@v4
with:
name: staticlink-avalonia-native-${{ needs.detect.outputs.package_version }}
path: artifacts/nuget/*.nupkg
if-no-files-found: error
smoke:
needs:
- detect
- pack
if: ${{ needs.detect.outputs.should_build == 'true' }}
strategy:
fail-fast: false
matrix:
include:
- rid: osx-x64
runner: macos-14
- rid: osx-arm64
runner: macos-15
runs-on: ${{ matrix.runner }}
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- 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-native-${{ needs.detect.outputs.package_version }}
path: artifacts/nuget
- name: Publish Avalonia NativeAOT smoke app
shell: pwsh
run: |
$projectDir = Join-Path $PWD "artifacts/avalonia-native-smoke-src"
pwsh ./scripts/create-avalonia-smoke.ps1 `
-ProjectDir $projectDir `
-NuGetSource (Resolve-Path "artifacts/nuget") `
-AvaloniaVersion "${{ needs.detect.outputs.avalonia_version }}" `
-StaticLinkNativeVersion "${{ needs.detect.outputs.package_version }}"
dotnet publish (Join-Path $projectDir "AvaloniaStaticLinkSmoke.csproj") `
-c Release `
-r "${{ matrix.rid }}" `
--configfile (Join-Path $projectDir "NuGet.config") `
/p:PublishDir="$PWD/artifacts/smoke-${{ matrix.rid }}/"
- name: Verify AvaloniaNative is statically linked
shell: bash
run: |
set -euo pipefail
test ! -f "artifacts/smoke-${{ matrix.rid }}/libAvaloniaNative.dylib"
test -x "artifacts/smoke-${{ matrix.rid }}/AvaloniaStaticLinkSmoke"
- name: Run Avalonia smoke app
shell: bash
run: |
set -euo pipefail
cd "artifacts/smoke-${{ matrix.rid }}"
python3 - <<'PY'
import subprocess
import sys
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()
print(output or "")
print("Avalonia smoke app stayed alive for 15 seconds.")
sys.exit(0)
print(output or "")
if proc.returncode == 0:
sys.exit(0)
sys.exit(proc.returncode if proc.returncode > 0 else 1)
PY
publish:
needs:
- detect
- smoke
if: ${{ needs.detect.outputs.should_build == 'true' && (github.event_name == 'schedule' || inputs.publish_to_nuget == true) }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Download NuGet package
uses: actions/download-artifact@v4
with:
name: staticlink-avalonia-native-${{ needs.detect.outputs.package_version }}
path: artifacts/nuget
- name: Validate Trusted Publishing configuration
env:
NUGET_USER: ${{ vars.NUGET_USER }}
run: test -n "$NUGET_USER"
- name: NuGet login
id: login
uses: NuGet/login@v1
with:
user: ${{ vars.NUGET_USER }}
- name: Publish to nuget.org
run: |
dotnet nuget push artifacts/nuget/*.nupkg \
--source https://www.nuget.org/api/v2/package \
--api-key "${{ steps.login.outputs.NUGET_API_KEY }}" \
--skip-duplicate
@@ -0,0 +1,106 @@
name: staticlink-avalonia-repack
on:
workflow_dispatch:
inputs:
source_version:
description: Existing package version containing the static libraries
required: true
default: 3.119.4-7922.1
type: string
publish_to_nuget:
description: Publish package to nuget.org
required: true
default: false
type: boolean
jobs:
repack:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"
- name: Extract static libraries from existing package
env:
SOURCE_VERSION: ${{ inputs.source_version }}
run: |
python3 - <<'PY'
import os
import pathlib
import urllib.request
import zipfile
version = os.environ["SOURCE_VERSION"]
package = pathlib.Path("artifacts/source.nupkg")
package.parent.mkdir(parents=True, exist_ok=True)
urllib.request.urlretrieve(
f"https://api.nuget.org/v3-flatcontainer/staticlink.avalonia/{version}/staticlink.avalonia.{version}.nupkg",
package,
)
output = pathlib.Path("External/NativeStatic")
with zipfile.ZipFile(package) as archive:
for entry in archive.infolist():
name = entry.filename.replace("\\", "/")
if not name.startswith("static/") or name.endswith("/"):
continue
destination = output / name.removeprefix("static/")
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_bytes(archive.read(entry))
PY
- name: Verify static archives
run: |
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
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-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: dotnet pack NuGet/StaticGraphics/StaticLink.Avalonia.csproj -c Release -o artifacts/nuget
- name: Verify portable NuGet paths
run: |
package=$(find artifacts/nuget -name 'StaticLink.Avalonia.*.nupkg' ! -name 'StaticLink.Avalonia.Native.*.nupkg' -print -quit)
test -n "$package"
if unzip -Z1 "$package" | grep -F '\'; then
echo "NuGet package contains Windows-only backslash paths."
exit 1
fi
unzip -Z1 "$package" | grep -Fx 'buildTransitive/StaticLink.Avalonia.props'
unzip -Z1 "$package" | grep -Fx 'buildTransitive/StaticLink.Avalonia.targets'
- name: Upload NuGet artifact
uses: actions/upload-artifact@v4
with:
name: staticlink-avalonia-repacked-nuget
path: artifacts/nuget/*.nupkg
if-no-files-found: error
- name: Publish to nuget.org
if: ${{ inputs.publish_to_nuget == true }}
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
test -n "$NUGET_API_KEY"
dotnet nuget push artifacts/nuget/*.nupkg \
--source "https://api.nuget.org/v3/index.json" \
--api-key "$NUGET_API_KEY" \
--skip-duplicate
+152 -293
View File
@@ -18,247 +18,48 @@ on:
- "staticlink-avalonia-v*" - "staticlink-avalonia-v*"
env: env:
SKIASHARP_VERSION: "3.119.2" DOTNET_ALPINE_IMAGE: mcr.microsoft.com/dotnet/sdk:10.0.302-alpine3.23
ANGLE_BRANCH: "7151"
DOTNET_ALPINE_IMAGE: mcr.microsoft.com/dotnet/sdk:10.0-alpine
jobs: jobs:
preflight:
uses: ./.github/workflows/static-graphics-preflight.yml
build-linux: build-linux:
strategy: needs: preflight
fail-fast: false uses: ./.github/workflows/static-graphics-linux.yml
matrix: with:
include: skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }}
- rid: linux-x64 angle_branch: ${{ needs.preflight.outputs.angle_branch }}
target_cpu: x64
runner: ubuntu-latest
- 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 build-musl:
if: ${{ !startsWith(matrix.rid, 'linux-musl-') }} needs: preflight
run: | uses: ./.github/workflows/static-graphics-musl.yml
sudo apt-get update with:
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 skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }}
angle_branch: ${{ needs.preflight.outputs.angle_branch }}
- name: Build Linux static graphics libraries dotnet_alpine_image: mcr.microsoft.com/dotnet/sdk:10.0.302-alpine3.23
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:
name: native-static-${{ matrix.rid }}
path: External/NativeStatic/${{ matrix.rid }}/*
if-no-files-found: error
build-windows: build-windows:
strategy: needs: preflight
fail-fast: false uses: ./.github/workflows/static-graphics-windows.yml
matrix: with:
include: skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }}
- rid: win-x64 angle_branch: ${{ needs.preflight.outputs.angle_branch }}
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
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: build-macos:
strategy: needs: preflight
fail-fast: false uses: ./.github/workflows/static-graphics-macos.yml
matrix: with:
include: skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }}
- rid: osx-x64 angle_branch: ${{ needs.preflight.outputs.angle_branch }}
target_cpu: x64
runner: macos-14
- rid: osx-arm64
target_cpu: arm64
runner: macos-15
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: 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
- 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-15
avalonia_version: 11.3.14
- rid: osx-x64
runner: macos-14
avalonia_version: 12.0.3
- rid: osx-arm64
runner: macos-15
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: pack:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- build-linux - build-linux
- build-musl
- build-windows - build-windows
- build-macos - build-macos
- build-macos-avalonia-native
permissions: permissions:
contents: read contents: read
steps: steps:
@@ -267,7 +68,7 @@ jobs:
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: "10.0.x" dotnet-version: "10.0.302"
- name: Download static archives - name: Download static archives
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
@@ -276,13 +77,6 @@ jobs:
path: External/NativeStatic path: External/NativeStatic
merge-multiple: false 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 - name: Move static archives into RID directories
run: | run: |
for rid in win-x64 win-x86 win-arm64 linux-x64 linux-arm64 linux-musl-x64 linux-musl-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
@@ -315,18 +109,19 @@ jobs:
test -f External/NativeStatic/win-x86/native/skia_x86_stdcall_thunks.lib test -f External/NativeStatic/win-x86/native/skia_x86_stdcall_thunks.lib
- name: Pack NuGet package - name: Pack NuGet package
run: dotnet pack NuGet/StaticGraphics/StaticLink.Avalonia.csproj -c Release -o artifacts/nuget
- name: Verify portable NuGet paths
shell: bash
run: | run: |
dotnet pack NuGet/StaticGraphics/StaticLink.Avalonia.csproj -c Release -o artifacts/nuget package=$(find artifacts/nuget -name 'StaticLink.Avalonia.*.nupkg' ! -name 'StaticLink.Avalonia.Native.*.nupkg' -print -quit)
for version in 11.3.14 12.0.3; do test -n "$package"
mkdir -p External/NativeStatic/osx-x64/native External/NativeStatic/osx-arm64/native if unzip -Z1 "$package" | grep -F '\'; then
cp External/AvaloniaNative/native-avalonia-$version-osx-x64/* External/NativeStatic/osx-x64/native/ echo "NuGet package contains Windows-only backslash paths."
cp External/AvaloniaNative/native-avalonia-$version-osx-arm64/* External/NativeStatic/osx-arm64/native/ exit 1
dotnet pack NuGet/AvaloniaNative/StaticLink.Avalonia.Native.csproj \ fi
-c Release \ unzip -Z1 "$package" | grep -Fx 'buildTransitive/StaticLink.Avalonia.props'
-o artifacts/nuget \ unzip -Z1 "$package" | grep -Fx 'buildTransitive/StaticLink.Avalonia.targets'
/p:PackageVersion=$version.1 \
/p:Version=$version.1
done
- name: Upload NuGet artifact - name: Upload NuGet artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -337,23 +132,6 @@ jobs:
artifacts/nuget/*.snupkg artifacts/nuget/*.snupkg
if-no-files-found: error 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: smoke-avalonia:
needs: pack needs: pack
strategy: strategy:
@@ -370,29 +148,19 @@ jobs:
runner: windows-2022 runner: windows-2022
msvc_arch: amd64_arm64 msvc_arch: amd64_arm64
- rid: linux-x64 - rid: linux-x64
runner: ubuntu-latest runner: ubuntu-24.04
- rid: linux-arm64 - rid: linux-arm64
runner: ubuntu-24.04-arm runner: ubuntu-24.04-arm
- rid: linux-musl-x64 - rid: linux-musl-x64
runner: ubuntu-latest runner: ubuntu-24.04
- rid: linux-musl-arm64 - rid: linux-musl-arm64
runner: ubuntu-24.04-arm runner: ubuntu-24.04-arm
- rid: osx-x64 - rid: osx-x64
runner: macos-14 runner: macos-15
avalonia_version: 11.3.14 avalonia_version: 11.3.14
native_version: 11.3.14.1
- rid: osx-arm64 - rid: osx-arm64
runner: macos-15 runner: macos-15
avalonia_version: 11.3.14 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-15
avalonia_version: 12.0.3
native_version: 12.0.3.1
runs-on: ${{ matrix.runner }} runs-on: ${{ matrix.runner }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -407,12 +175,12 @@ jobs:
if: ${{ startsWith(matrix.rid, 'linux-') && !startsWith(matrix.rid, 'linux-musl-') }} if: ${{ startsWith(matrix.rid, 'linux-') && !startsWith(matrix.rid, 'linux-musl-') }}
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y clang lld zlib1g-dev libfontconfig1-dev sudo apt-get install -y clang lld xvfb zlib1g-dev libfontconfig1-dev libfreetype6 libgl1 libice6 libsm6 libx11-6 libxcb1 libxcursor1 libxext6 libxfixes3 libxinerama1 libxrandr2 libxrender1 libxi6
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: "10.0.x" dotnet-version: "10.0.302"
- name: Download NuGet package - name: Download NuGet package
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
@@ -429,15 +197,10 @@ jobs:
if (-not $package) { throw "StaticLink NuGet package was not downloaded." } if (-not $package) { throw "StaticLink NuGet package was not downloaded." }
if ($package.BaseName -notmatch '^StaticLink\.Avalonia\.(.+)$') { throw "Unexpected package name: $($package.Name)" } if ($package.BaseName -notmatch '^StaticLink\.Avalonia\.(.+)$') { throw "Unexpected package name: $($package.Name)" }
$version = $Matches[1] $version = $Matches[1]
$nativeVersion = "${{ matrix.native_version }}"
$avaloniaVersion = "${{ matrix.avalonia_version }}" $avaloniaVersion = "${{ matrix.avalonia_version }}"
if (-not $avaloniaVersion) { $avaloniaVersion = "11.3.14" } 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" $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 pwsh ./scripts/create-avalonia-smoke.ps1 -ProjectDir $projectDir -NuGetSource (Resolve-Path "artifacts/nuget") -StaticLinkVersion $version -AvaloniaVersion $avaloniaVersion
dotnet publish (Join-Path $projectDir "AvaloniaStaticLinkSmoke.csproj") ` dotnet publish (Join-Path $projectDir "AvaloniaStaticLinkSmoke.csproj") `
-c Release ` -c Release `
-r "${{ matrix.rid }}" ` -r "${{ matrix.rid }}" `
@@ -470,7 +233,7 @@ jobs:
run: | run: |
$publishDir = "artifacts/smoke-${{ matrix.rid }}" $publishDir = "artifacts/smoke-${{ matrix.rid }}"
$blocked = Get-ChildItem -Path $publishDir -Recurse -File | Where-Object { $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") $_.Name -in @("libSkiaSharp.dll", "libHarfBuzzSharp.dll", "av_libglesv2.dll", "libSkiaSharp.so", "libHarfBuzzSharp.so", "libSkiaSharp.dylib", "libHarfBuzzSharp.dylib")
} }
if ($blocked) { if ($blocked) {
$blocked | ForEach-Object { Write-Error "Unexpected dynamic graphics library: $($_.FullName)" } $blocked | ForEach-Object { Write-Error "Unexpected dynamic graphics library: $($_.FullName)" }
@@ -478,6 +241,56 @@ jobs:
} }
Get-ChildItem -Path $publishDir -Recurse -File | ForEach-Object { $_.FullName } Get-ChildItem -Path $publishDir -Recurse -File | ForEach-Object { $_.FullName }
- name: Verify Windows executable architecture
if: ${{ startsWith(matrix.rid, 'win-') }}
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$app = Join-Path $PWD "artifacts/smoke-${{ matrix.rid }}/AvaloniaStaticLinkSmoke.exe"
$expectedMachine = @{
"win-x64" = "8664 machine"
"win-x86" = "14C machine"
"win-arm64" = "AA64 machine"
}["${{ matrix.rid }}"]
$headers = & dumpbin /headers $app | Out-String
if ($LASTEXITCODE -ne 0 -or $headers -notmatch [regex]::Escape($expectedMachine)) {
throw "Expected $expectedMachine in PE headers for $app.`n$headers"
}
- name: Run Windows Avalonia smoke app
if: ${{ matrix.rid == 'win-x64' || matrix.rid == 'win-x86' }}
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$publishDir = Join-Path $PWD "artifacts/smoke-${{ matrix.rid }}"
$app = Join-Path $publishDir "AvaloniaStaticLinkSmoke.exe"
$process = Start-Process -FilePath $app -WorkingDirectory $publishDir -PassThru
if ($process.WaitForExit(15000)) {
if ($process.ExitCode -eq 0) {
Write-Host "Avalonia Windows smoke app exited normally."
exit 0
}
throw "Avalonia Windows smoke app failed with exit code $($process.ExitCode)."
}
Stop-Process -Id $process.Id -Force
Write-Host "Avalonia Windows smoke app stayed alive for 15 seconds."
- name: Run Linux Avalonia smoke app
if: ${{ startsWith(matrix.rid, 'linux-') && !startsWith(matrix.rid, 'linux-musl-') }}
shell: bash
run: |
set +e
cd "artifacts/smoke-${{ matrix.rid }}"
chmod +x ./AvaloniaStaticLinkSmoke
timeout 15s xvfb-run -a ./AvaloniaStaticLinkSmoke
code=$?
if [ "$code" = 124 ] || [ "$code" = 143 ]; then
echo "Avalonia Linux smoke app stayed alive for 15 seconds."
exit 0
fi
exit "$code"
- name: Verify musl ELF dependencies - name: Verify musl ELF dependencies
if: ${{ startsWith(matrix.rid, 'linux-musl-') }} if: ${{ startsWith(matrix.rid, 'linux-musl-') }}
shell: bash shell: bash
@@ -486,7 +299,7 @@ jobs:
docker run --rm \ docker run --rm \
-v "${{ github.workspace }}:/workspace" \ -v "${{ github.workspace }}:/workspace" \
-w /workspace \ -w /workspace \
alpine:latest \ alpine:3.23 \
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"' 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 - name: Run Linux musl Avalonia smoke app
@@ -497,7 +310,7 @@ jobs:
docker run --rm \ docker run --rm \
-v "${{ github.workspace }}:/workspace" \ -v "${{ github.workspace }}:/workspace" \
-w /workspace/artifacts/smoke-${{ matrix.rid }} \ -w /workspace/artifacts/smoke-${{ matrix.rid }} \
alpine:latest \ alpine:3.23 \
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}"' 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 - name: Run macOS Avalonia smoke app
@@ -547,21 +360,65 @@ jobs:
path: artifacts/smoke-${{ matrix.rid }}/** path: artifacts/smoke-${{ matrix.rid }}/**
if-no-files-found: error if-no-files-found: error
release: publish-nuget:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- pack - pack
- smoke-avalonia - smoke-avalonia
steps:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.302"
- name: Download NuGet package
uses: actions/download-artifact@v4
with:
name: staticlink-avalonia-nuget
path: artifacts/nuget
- 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
release:
runs-on: ubuntu-24.04
needs:
- preflight
- pack
- smoke-avalonia
- publish-nuget
if: ${{ startsWith(github.ref, 'refs/tags/staticlink-avalonia-v') || (github.event_name == 'workflow_dispatch' && inputs.publish_to_release == true) }} if: ${{ startsWith(github.ref, 'refs/tags/staticlink-avalonia-v') || (github.event_name == 'workflow_dispatch' && inputs.publish_to_release == true) }}
permissions: permissions:
contents: write contents: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Download release artifacts - name: Download NuGet artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: staticlink-avalonia-nuget
path: release-artifacts/staticlink-avalonia-nuget
- name: Download smoke artifacts
uses: actions/download-artifact@v4
with:
pattern: avalonia-smoke-*
path: release-artifacts path: release-artifacts
merge-multiple: false
- name: Prepare release files - name: Prepare release files
run: | run: |
@@ -577,19 +434,21 @@ jobs:
- name: Publish GitHub Release - name: Publish GitHub Release
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
PACKAGE_VERSION: ${{ needs.preflight.outputs.package_version }}
run: | run: |
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
release_tag="${GITHUB_REF_NAME}" release_tag="${GITHUB_REF_NAME}"
title="${GITHUB_REF_NAME}" title="${GITHUB_REF_NAME}"
else else
release_tag="staticlink-avalonia-smoke-${GITHUB_RUN_NUMBER}" release_tag="staticlink-avalonia-v${PACKAGE_VERSION}"
title="StaticLink.Avalonia smoke ${GITHUB_RUN_NUMBER}" title="StaticLink.Avalonia ${PACKAGE_VERSION}"
fi fi
if gh release view "$release_tag" >/dev/null 2>&1; then if gh release view "$release_tag" >/dev/null 2>&1; then
gh release upload "$release_tag" release-files/* --clobber gh release upload "$release_tag" release-files/* --clobber
else else
gh release create "$release_tag" release-files/* \ gh release create "$release_tag" release-files/* \
--target "$GITHUB_SHA" \
--title "$title" \ --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." --notes "NuGet package plus Avalonia 11.3.14 NativeAOT single-file smoke apps for win-x64, win-x86, win-arm64, linux-x64, linux-arm64, linux-musl-x64, linux-musl-arm64, osx-x64, and osx-arm64."
fi fi
@@ -0,0 +1,64 @@
name: static-graphics-linux
on:
workflow_call:
inputs:
skiasharp_version:
required: true
type: string
angle_branch:
required: true
type: string
workflow_dispatch:
inputs:
skiasharp_version:
required: true
default: "3.119.4"
type: string
angle_branch:
required: true
default: "7922"
type: string
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- rid: linux-x64
target_cpu: x64
runner: ubuntu-24.04
- rid: linux-arm64
target_cpu: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
env:
SKIASHARP_VERSION: ${{ inputs.skiasharp_version }}
ANGLE_BRANCH: ${{ inputs.angle_branch }}
RID: ${{ matrix.rid }}
TARGET_CPU: ${{ matrix.target_cpu }}
LLVM_AR: llvm-ar
steps:
- uses: actions/checkout@v4
- name: Restore native build cache
uses: actions/cache@v4
with:
path: External/NativeStatic/.work
key: native-work-linux-${{ matrix.rid }}-${{ inputs.skiasharp_version }}-${{ inputs.angle_branch }}-${{ hashFiles('External/NativeStatic/patches/*.patch', 'scripts/build-linux-static-graphics.sh') }}
- 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
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
@@ -0,0 +1,74 @@
name: static-graphics-macos
on:
workflow_call:
inputs:
skiasharp_version:
required: true
type: string
angle_branch:
required: true
type: string
workflow_dispatch:
inputs:
skiasharp_version:
required: true
default: "3.119.4"
type: string
angle_branch:
required: true
default: "7922"
type: string
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- rid: osx-x64
target_cpu: x64
- rid: osx-arm64
target_cpu: arm64
runs-on: macos-15
env:
SKIASHARP_VERSION: ${{ inputs.skiasharp_version }}
ANGLE_BRANCH: ${{ inputs.angle_branch }}
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: Restore native build cache
uses: actions/cache@v4
with:
path: External/NativeStatic/.work
key: native-work-macos-${{ matrix.rid }}-${{ inputs.skiasharp_version }}-${{ inputs.angle_branch }}-${{ hashFiles('External/NativeStatic/patches/*.patch', 'scripts/build-macos-static-graphics.sh') }}
- 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
- 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
@@ -0,0 +1,92 @@
name: static-graphics-musl
on:
workflow_call:
inputs:
skiasharp_version:
required: true
type: string
angle_branch:
required: true
type: string
dotnet_alpine_image:
required: true
type: string
workflow_dispatch:
inputs:
skiasharp_version:
required: true
default: "3.119.4"
type: string
angle_branch:
required: true
default: "7922"
type: string
dotnet_alpine_image:
required: true
default: mcr.microsoft.com/dotnet/sdk:10.0.302-alpine3.23
type: string
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- rid: linux-musl-x64
target_cpu: x64
runner: ubuntu-24.04
- rid: linux-musl-arm64
target_cpu: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
env:
SKIASHARP_VERSION: ${{ inputs.skiasharp_version }}
ANGLE_BRANCH: ${{ inputs.angle_branch }}
DOTNET_ALPINE_IMAGE: ${{ inputs.dotnet_alpine_image }}
steps:
- uses: actions/checkout@v4
- name: Restore native build cache
uses: actions/cache@v4
with:
path: External/NativeStatic/.work
key: native-work-musl-${{ matrix.rid }}-${{ inputs.skiasharp_version }}-${{ inputs.angle_branch }}-${{ hashFiles('External/NativeStatic/patches/*.patch', 'scripts/build-linux-static-graphics.sh') }}
- name: Build Linux musl static graphics libraries
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: Normalize native build cache ownership
run: sudo chown -R "$(id -u):$(id -g)" External/NativeStatic/.work
- name: Upload Linux musl static archives
uses: actions/upload-artifact@v4
with:
name: native-static-${{ matrix.rid }}
path: External/NativeStatic/${{ matrix.rid }}/*
if-no-files-found: error
@@ -0,0 +1,81 @@
name: static-graphics-preflight
on:
workflow_call:
outputs:
package_version:
value: ${{ jobs.validate.outputs.package_version }}
skiasharp_version:
value: ${{ jobs.validate.outputs.skiasharp_version }}
angle_branch:
value: ${{ jobs.validate.outputs.angle_branch }}
workflow_dispatch:
jobs:
validate:
runs-on: ubuntu-24.04
outputs:
package_version: ${{ steps.versions.outputs.package_version }}
skiasharp_version: ${{ steps.versions.outputs.skiasharp_version }}
angle_branch: ${{ steps.versions.outputs.angle_branch }}
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.302"
- name: Read and validate versions
id: versions
shell: bash
run: |
python3 - <<'PY'
import os
import pathlib
import xml.etree.ElementTree as ET
project = pathlib.Path("NuGet/StaticGraphics/StaticLink.Avalonia.csproj")
root = ET.parse(project).getroot()
package_version = root.findtext(".//Version")
skia_reference = next(
item.attrib["Version"]
for item in root.findall(".//PackageReference")
if item.attrib.get("Include") == "SkiaSharp"
)
version_base, revision = package_version.rsplit(".", 1)
skiasharp_version, angle_branch = version_base.split("-", 1)
if not revision.isdigit() or skiasharp_version != skia_reference:
raise SystemExit(f"Inconsistent package version: {package_version}, SkiaSharp: {skia_reference}")
if os.environ.get("GITHUB_REF_TYPE") == "tag":
expected_tag = f"staticlink-avalonia-v{package_version}"
if os.environ.get("GITHUB_REF_NAME") != expected_tag:
raise SystemExit(f"Release tag must be {expected_tag}")
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
output.write(f"package_version={package_version}\n")
output.write(f"skiasharp_version={skiasharp_version}\n")
output.write(f"angle_branch={angle_branch}\n")
PY
- name: Validate upstream refs and ANGLE patch
env:
SKIASHARP_VERSION: ${{ steps.versions.outputs.skiasharp_version }}
ANGLE_BRANCH: ${{ steps.versions.outputs.angle_branch }}
shell: bash
run: |
set -euo pipefail
git ls-remote --exit-code --heads https://github.com/mono/SkiaSharp.git "refs/heads/release/${SKIASHARP_VERSION}"
git clone --depth 1 --branch "chromium/${ANGLE_BRANCH}" https://github.com/google/angle.git /tmp/staticlink-angle
git -C /tmp/staticlink-angle apply --check "$GITHUB_WORKSPACE/External/NativeStatic/patches/angle-chromium-${ANGLE_BRANCH}.patch"
- name: Validate scripts
shell: bash
run: |
set -euo pipefail
bash -n scripts/build-linux-static-graphics.sh
bash -n scripts/build-macos-static-graphics.sh
pwsh -NoProfile -Command '$files = @("scripts/build-windows-static-graphics.ps1", "scripts/create-avalonia-smoke.ps1"); foreach ($file in $files) { $errors = $null; [System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path $file), [ref]$null, [ref]$errors) | Out-Null; if ($errors.Count) { $errors | Format-List | Out-String; exit 1 } }'
- name: Compile smoke project
run: dotnet build Smoke/AvaloniaStaticLinkSmoke/AvaloniaStaticLinkSmoke.csproj -c Release -p:PublishAot=false
@@ -0,0 +1,92 @@
name: static-graphics-windows
on:
workflow_call:
inputs:
skiasharp_version:
required: true
type: string
angle_branch:
required: true
type: string
workflow_dispatch:
inputs:
skiasharp_version:
required: true
default: "3.119.4"
type: string
angle_branch:
required: true
default: "7922"
type: string
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- 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
runs-on: windows-2022
env:
SKIASHARP_VERSION: ${{ inputs.skiasharp_version }}
ANGLE_BRANCH: ${{ inputs.angle_branch }}
steps:
- uses: actions/checkout@v4
- name: Restore native build cache
uses: actions/cache@v4
with:
path: External/NativeStatic/.work
key: native-work-windows-${{ matrix.rid }}-${{ inputs.skiasharp_version }}-${{ inputs.angle_branch }}-${{ hashFiles('External/NativeStatic/patches/*.patch', 'scripts/build-windows-static-graphics.ps1') }}
- 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
+1
View File
@@ -4,3 +4,4 @@ artifacts/
*.nupkg *.nupkg
*.snupkg *.snupkg
External/NativeStatic/.work/ External/NativeStatic/.work/
External/AvaloniaNative/.work/
-64
View File
@@ -1,64 +0,0 @@
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',
+64
View File
@@ -0,0 +1,64 @@
diff --git a/DEPS b/DEPS
index 8ba99a6..1a5e1a5 100644
--- a/DEPS
+++ b/DEPS
@@ -768,9 +768,4 @@ deps = {
},
- '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': {
@@ -795,9 +790,4 @@ deps = {
- 'third_party/dawn': {
- 'url': Var('dawn_git') + '/dawn.git' + '@' + Var('dawn_revision'),
- 'condition': 'checkout_angle_dawn_deps and not build_with_chromium'
- },
-
'third_party/depot_tools': {
'url': Var('chromium_git') + '/chromium/tools/depot_tools.git@94e89b10b92cc9d6e58fc8d1b6474b7d29e8a114',
@@ -875,9 +865,4 @@ deps = {
},
- 'third_party/llvm/src': {
- 'url': Var('chromium_git') + '/external/github.com/llvm/llvm-project@67790d3fb06b184b7be9c4d3e36ab01f0ac4198e',
- 'condition': 'checkout_angle_cl_deps and not build_with_chromium',
- },
-
'third_party/llvm-build/Release+Asserts': {
'dep_type': 'gcs',
@@ -1217,9 +1202,4 @@ deps = {
},
- 'third_party/SwiftShader': {
- 'url': Var('swiftshader_git') + '/SwiftShader@f3e464b1e56795940f826a972b39e19bd9f6ac6c',
- 'condition': 'not build_with_chromium',
- },
-
'third_party/turbine/cipd': {
'packages': [
@@ -1233,8 +1213,4 @@ deps = {
},
- '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@f2b9fece7c6772c09379b3a6938dbcd2573f2a3e',
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 710bba9..33d4a64 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -881,5 +881,5 @@ const TConstantUnion *TIntermAggregate::getConstantValue() const
angle::base::CheckedNumeric<size_t> checkedArraySize = elementSize;
checkedArraySize *= getOutermostArraySize();
- constArray = new TConstantUnion[checkedArraySize.ValueOrDie()];
+ constArray = new TConstantUnion[static_cast<size_t>(checkedArraySize.ValueOrDie())];
size_t elementOffset = 0u;
+12
View File
@@ -0,0 +1,12 @@
# StaticLink.Avalonia.Native
Static `AvaloniaNative` libraries for macOS Avalonia NativeAOT publishing.
The package version matches the Avalonia version with a `.1` suffix. For example, use `StaticLink.Avalonia.Native` version `12.1.0.1` with Avalonia `12.1.0`.
```xml
<PackageReference Include="Avalonia" Version="12.1.0" />
<PackageReference Include="StaticLink.Avalonia.Native" Version="12.1.0.1" />
```
The package supports `osx-x64` and `osx-arm64` NativeAOT publishing.
@@ -6,7 +6,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput> <IncludeBuildOutput>false</IncludeBuildOutput>
<SuppressDependenciesWhenPacking>false</SuppressDependenciesWhenPacking> <SuppressDependenciesWhenPacking>false</SuppressDependenciesWhenPacking>
<PackageId>StaticLink.Avalonia.Native</PackageId> <PackageId>StaticLink.Avalonia.Native</PackageId>
<Version>11.3.14.1</Version> <Version>0.0.0</Version>
<Authors>greepar</Authors> <Authors>greepar</Authors>
<Company>greepar</Company> <Company>greepar</Company>
<Description>Static AvaloniaNative library for macOS Avalonia NativeAOT publishing.</Description> <Description>Static AvaloniaNative library for macOS Avalonia NativeAOT publishing.</Description>
@@ -19,14 +19,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="../StaticGraphics/README.md" Pack="true" PackagePath="README.md" /> <None Include="README.md" Pack="true" PackagePath="README.md" />
<None Include="lib/net8.0/_._" Pack="true" PackagePath="lib/net8.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.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.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="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/AvaloniaNative/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/AvaloniaNative/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/AvaloniaNative/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" /> <None Include="../../External/AvaloniaNative/osx-arm64/native/macos_font_preinit.o" Pack="true" PackagePath="static/osx-arm64/native/macos_font_preinit.o" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -18,6 +18,8 @@
<LinkerArg Include="-framework CoreFoundation" /> <LinkerArg Include="-framework CoreFoundation" />
<LinkerArg Include="-framework CoreGraphics" /> <LinkerArg Include="-framework CoreGraphics" />
<LinkerArg Include="-framework Foundation" /> <LinkerArg Include="-framework Foundation" />
<LinkerArg Include="-framework IOSurface" />
<LinkerArg Include="-framework Metal" />
<LinkerArg Include="-framework OpenGL" /> <LinkerArg Include="-framework OpenGL" />
<LinkerArg Include="-framework QuartzCore" /> <LinkerArg Include="-framework QuartzCore" />
<LinkerArg Include="-framework UniformTypeIdentifiers" /> <LinkerArg Include="-framework UniformTypeIdentifiers" />
+1 -1
View File
@@ -11,7 +11,7 @@ dotnet add package StaticLink.Avalonia
Or add it manually: Or add it manually:
```xml ```xml
<PackageReference Include="StaticLink.Avalonia" Version="3.119.2-7151.10" /> <PackageReference Include="StaticLink.Avalonia" Version="3.119.4-7922.1" />
``` ```
For macOS, also reference `StaticLink.Avalonia.Native`. This package is macOS-only and must match your Avalonia version because it contains `libAvaloniaNative.a`. For macOS, also reference `StaticLink.Avalonia.Native`. This package is macOS-only and must match your Avalonia version because it contains `libAvaloniaNative.a`.
@@ -6,7 +6,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput> <IncludeBuildOutput>false</IncludeBuildOutput>
<SuppressDependenciesWhenPacking>false</SuppressDependenciesWhenPacking> <SuppressDependenciesWhenPacking>false</SuppressDependenciesWhenPacking>
<PackageId>StaticLink.Avalonia</PackageId> <PackageId>StaticLink.Avalonia</PackageId>
<Version>3.119.2-7151.10</Version> <Version>3.119.4-7922.1</Version>
<Authors>greepar</Authors> <Authors>greepar</Authors>
<Company>greepar</Company> <Company>greepar</Company>
<Description>Static SkiaSharp, HarfBuzzSharp, and ANGLE libraries for Avalonia NativeAOT single-file publishing.</Description> <Description>Static SkiaSharp, HarfBuzzSharp, and ANGLE libraries for Avalonia NativeAOT single-file publishing.</Description>
@@ -19,7 +19,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="SkiaSharp" Version="3.119.2" /> <PackageReference Include="SkiaSharp" Version="3.119.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+28 -1
View File
@@ -11,9 +11,17 @@ dotnet add package StaticLink.Avalonia
Or add it manually: Or add it manually:
```xml ```xml
<PackageReference Include="StaticLink.Avalonia" Version="3.119.2-7151.10" /> <PackageReference Include="StaticLink.Avalonia" Version="3.119.4-7922.1" />
``` ```
`3.119.4-7922.1` supports Avalonia 11. If you need the SkiaSharp 2 line for an Avalonia 11 project, use this optional package variant:
```xml
<PackageReference Include="StaticLink.Avalonia" Version="2.88.9-7151.10" />
```
`2.88.9-7151.10` is only for Avalonia 11 projects that specifically want SkiaSharp 2.
For macOS, also reference `StaticLink.Avalonia.Native`. This package is macOS-only and must match your Avalonia version because it contains `libAvaloniaNative.a`. For macOS, also reference `StaticLink.Avalonia.Native`. This package is macOS-only and must match your Avalonia version because it contains `libAvaloniaNative.a`.
```xml ```xml
@@ -33,3 +41,22 @@ dotnet publish -c Release -r win-x64 \
Use the RID you need, such as `win-x86`, `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 macOS, avoid the Metal renderer for fully static output. Use OpenGL or Software. For macOS, avoid the Metal renderer for fully static output. Use OpenGL or Software.
## Native Package Automation
`.github/workflows/nuget-avalonia-native.yml` runs daily and checks the latest stable `Avalonia` version on NuGet.org. If `StaticLink.Avalonia.Native.<AvaloniaVersion>.1` does not exist, it builds both macOS architectures from the matching Avalonia source tag, runs NativeAOT smoke tests, and publishes the package with NuGet Trusted Publishing.
Configure NuGet.org Trusted Publishing for this repository and the `nuget-avalonia-native.yml` workflow, then add the NuGet.org account name as the repository variable `NUGET_USER`. No long-lived NuGet API key is used by this workflow.
## Static Graphics Automation
`.github/workflows/nuget-static-graphics.yml` runs a fast version, upstream-ref, patch, script, and smoke-project preflight before starting native builds. It then calls independent Windows, Linux, Linux musl, and macOS workflows, packs their artifacts, runs all NativeAOT smoke tests, and only then publishes NuGet and GitHub Release assets.
The platform workflows can also be dispatched independently when diagnosing a single platform:
- `static-graphics-windows.yml`
- `static-graphics-linux.yml`
- `static-graphics-musl.yml`
- `static-graphics-macos.yml`
The release workflow derives the SkiaSharp version and ANGLE branch from `NuGet/StaticGraphics/StaticLink.Avalonia.csproj`. Native source and build directories are cached by platform, RID, version, patch, and build script.
+7
View File
@@ -0,0 +1,7 @@
<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>
@@ -0,0 +1,23 @@
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();
}
}
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="SmokeVersions.props" Condition="Exists('SmokeVersions.props')" />
<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>
<AvaloniaVersion Condition="'$(AvaloniaVersion)' == ''">11.3.14</AvaloniaVersion>
</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)" Condition="'$(StaticLinkVersion)' != ''" />
<PackageReference Include="StaticLink.Avalonia.Native" Version="$(StaticLinkNativeVersion)" Condition="'$(StaticLinkNativeVersion)' != ''" />
</ItemGroup>
</Project>
@@ -0,0 +1,13 @@
<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 static link smoke" FontSize="24" FontWeight="SemiBold" HorizontalAlignment="Center" />
<TextBlock Text="NativeAOT single-file publish" HorizontalAlignment="Center" />
</StackPanel>
</Border>
</Window>
@@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace AvaloniaStaticLinkSmoke;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
+54
View File
@@ -0,0 +1,54 @@
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.IsWindows())
{
builder = builder.With(new Win32PlatformOptions
{
RenderingMode =
[
Win32RenderingMode.AngleEgl,
Win32RenderingMode.Software
]
});
}
if (OperatingSystem.IsLinux())
{
builder = builder.With(new X11PlatformOptions
{
RenderingMode =
[
X11RenderingMode.Glx,
X11RenderingMode.Software
]
});
}
if (OperatingSystem.IsMacOS())
{
builder = builder.With(new AvaloniaNativePlatformOptions
{
RenderingMode =
[
AvaloniaNativeRenderingMode.OpenGl,
AvaloniaNativeRenderingMode.Software
]
});
}
return builder;
}
}
+71 -57
View File
@@ -6,8 +6,8 @@ WORK_DIR="${WORK_DIR:-$ROOT_DIR/External/NativeStatic/.work}"
TARGET_CPU="${TARGET_CPU:-x64}" TARGET_CPU="${TARGET_CPU:-x64}"
RID="${RID:-linux-$TARGET_CPU}" RID="${RID:-linux-$TARGET_CPU}"
OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}" OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}"
SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.2}" SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.4}"
ANGLE_BRANCH="${ANGLE_BRANCH:-7151}" ANGLE_BRANCH="${ANGLE_BRANCH:-7922}"
BUILD_JOBS="${BUILD_JOBS:-$(nproc)}" BUILD_JOBS="${BUILD_JOBS:-$(nproc)}"
ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}" ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}"
LLVM_AR="${LLVM_AR:-llvm-ar-19}" LLVM_AR="${LLVM_AR:-llvm-ar-19}"
@@ -20,8 +20,8 @@ Usage: scripts/build-linux-static-graphics.sh [skia|angle|all]
Environment: Environment:
WORK_DIR Source/build cache directory. Default: External/NativeStatic/.work WORK_DIR Source/build cache directory. Default: External/NativeStatic/.work
OUTPUT_DIR Final static library directory. Default: External/NativeStatic/linux-$TARGET_CPU OUTPUT_DIR Final static library directory. Default: External/NativeStatic/linux-$TARGET_CPU
SKIASHARP_VERSION SkiaSharp release branch version. Default: 3.119.2 SKIASHARP_VERSION SkiaSharp release branch version. Default: 3.119.4
ANGLE_BRANCH ANGLE chromium branch. Default: 7151 ANGLE_BRANCH ANGLE chromium branch. Default: 7922
TARGET_CPU GN target_cpu. Default: x64. Supported: x64, arm64 TARGET_CPU GN target_cpu. Default: x64. Supported: x64, arm64
RID Output RID. Default: linux-$TARGET_CPU RID Output RID. Default: linux-$TARGET_CPU
BUILD_JOBS Ninja parallelism. Default: nproc BUILD_JOBS Ninja parallelism. Default: nproc
@@ -149,11 +149,17 @@ sync_skiasharp() {
prepare_skia_git_sync_deps() { prepare_skia_git_sync_deps() {
local sync_deps="$1/tools/git-sync-deps" local sync_deps="$1/tools/git-sync-deps"
python3 - "$sync_deps" <<'PY' python3 - "$sync_deps" <<'PY'
import re
import pathlib import pathlib
import sys import sys
path = pathlib.Path(sys.argv[1]) path = pathlib.Path(sys.argv[1])
text = path.read_text() text = path.read_text()
deps_path = path.with_name("DEPS")
if deps_path.exists():
deps = deps_path.read_text()
deps = re.sub(r'^\s*"third_party/externals/dng_sdk"\s*:\s*"[^"]+",\s*\n', '', deps, flags=re.MULTILINE)
deps_path.write_text(deps)
old = " multithread(git_checkout_to_directory, list_of_arg_lists)" old = " multithread(git_checkout_to_directory, list_of_arg_lists)"
new = " for args in list_of_arg_lists:\n git_checkout_to_directory(*args)" new = " for args in list_of_arg_lists:\n git_checkout_to_directory(*args)"
if old in text: if old in text:
@@ -267,15 +273,15 @@ build_angle() {
patch_depot_tools_python_deps "$src/third_party/depot_tools" patch_depot_tools_python_deps "$src/third_party/depot_tools"
prepend_python_module_path six prepend_python_module_path six
prepare_angle_gcs_artifacts "$src" prepare_angle_gcs_artifacts "$src"
prepare_musl_clang_toolchain
fi fi
prepare_musl_clang_runtime
prepare_musl_libstdcxx_headers prepare_musl_libstdcxx_headers
gclient sync -f -D -R gclient sync -f -D -R
local out_dir="$src/out/linux-static-$TARGET_CPU" local out_dir="$src/out/linux-static-$TARGET_CPU"
local angle_is_clang="false" local angle_is_clang="true"
if is_musl_rid; then if [[ "$TARGET_CPU" == "arm64" ]] && ! is_musl_rid; then
angle_is_clang="true" angle_is_clang="false"
fi fi
mkdir -p "$out_dir" mkdir -p "$out_dir"
cat >"$out_dir/args.gn" <<EOF_ARGS cat >"$out_dir/args.gn" <<EOF_ARGS
@@ -296,8 +302,9 @@ angle_enable_swiftshader = false
angle_enable_vulkan = false angle_enable_vulkan = false
angle_enable_wgpu = false angle_enable_wgpu = false
EOF_ARGS EOF_ARGS
append_angle_musl_gn_args "$out_dir/args.gn" if is_musl_rid; then
printf 'clang_base_path = "%s/clang"\nclang_use_chrome_plugins = false\nclang_version = "%s"\n' "$WORK_DIR" "$(clang -dumpversion | cut -d. -f1)" >>"$out_dir/args.gn"
fi
gn gen "$out_dir" gn gen "$out_dir"
ninja -C "$out_dir" -j "$BUILD_JOBS" libANGLE_static libGLESv2_static ninja -C "$out_dir" -j "$BUILD_JOBS" libANGLE_static libGLESv2_static
@@ -380,53 +387,6 @@ download_angle_gcs_artifact() {
fi 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() { prepare_musl_libstdcxx_headers() {
if ! is_musl_rid; then if ! is_musl_rid; then
return 0 return 0
@@ -466,6 +426,60 @@ prepare_musl_libstdcxx_headers() {
ln -sfn "$src_dir" "$dest_dir" ln -sfn "$src_dir" "$dest_dir"
} }
prepare_musl_clang_toolchain() {
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: $TARGET_CPU" >&2
return 1
;;
esac
local clang_version
clang_version="$(clang -dumpversion | cut -d. -f1)"
local toolchain_dir="$WORK_DIR/clang"
local runtime
runtime="$(find /usr/lib/llvm* /usr/lib/clang -path "*${arch}*" -name 'libclang_rt.builtins*.a' -print -quit 2>/dev/null || true)"
if [[ -z "$runtime" ]]; then
echo "Could not find Alpine compiler-rt builtins for $arch" >&2
return 1
fi
mkdir -p "$toolchain_dir/bin" "$toolchain_dir/lib/clang/$clang_version/lib/$gnu_triple"
ln -sf "$runtime" "$toolchain_dir/lib/clang/$clang_version/lib/$gnu_triple/libclang_rt.builtins.a"
cat >"$toolchain_dir/bin/clang-wrapper.py" <<'PY'
#!/usr/bin/env python3
import os
import sys
blocked = (
"-fdiagnostics-show-inlining-chain",
"-fno-lifetime-dse",
"-fsanitize-ignore-for-ubsan-feature=",
)
args = [arg for arg in sys.argv[1:] if not arg.startswith(blocked)]
compiler = "/usr/bin/clang++" if os.path.basename(sys.argv[0]) == "clang++" else "/usr/bin/clang"
os.execv(compiler, [compiler, *args])
PY
chmod +x "$toolchain_dir/bin/clang-wrapper.py"
ln -sf clang-wrapper.py "$toolchain_dir/bin/clang"
ln -sf clang-wrapper.py "$toolchain_dir/bin/clang++"
for tool in llvm-ar llvm-ranlib llvm-nm; do
if command -v "$tool" >/dev/null 2>&1; then
ln -sf "$(command -v "$tool")" "$toolchain_dir/bin/$tool"
fi
done
}
copy_first_existing() { copy_first_existing() {
local dest="$1" local dest="$1"
shift shift
@@ -3,8 +3,8 @@ set -euo pipefail
RID="${RID:-osx-arm64}" RID="${RID:-osx-arm64}"
AVALONIA_VERSION="${AVALONIA_VERSION:-11.3.14}" AVALONIA_VERSION="${AVALONIA_VERSION:-11.3.14}"
WORK_DIR="${WORK_DIR:-$PWD/External/NativeStatic/.work}" WORK_DIR="${WORK_DIR:-$PWD/External/AvaloniaNative/.work}"
OUTPUT_DIR="${OUTPUT_DIR:-$PWD/External/NativeStatic/$RID/native}" OUTPUT_DIR="${OUTPUT_DIR:-$PWD/External/AvaloniaNative/$RID/native}"
BUILD_JOBS="${BUILD_JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 4)}" BUILD_JOBS="${BUILD_JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
case "$RID" in case "$RID" in
+10 -2
View File
@@ -6,8 +6,8 @@ WORK_DIR="${WORK_DIR:-$ROOT_DIR/External/NativeStatic/.work}"
TARGET_CPU="${TARGET_CPU:-arm64}" TARGET_CPU="${TARGET_CPU:-arm64}"
RID="${RID:-osx-$TARGET_CPU}" RID="${RID:-osx-$TARGET_CPU}"
OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}" OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}"
SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.2}" SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.4}"
ANGLE_BRANCH="${ANGLE_BRANCH:-7151}" ANGLE_BRANCH="${ANGLE_BRANCH:-7922}"
BUILD_JOBS="${BUILD_JOBS:-$(sysctl -n hw.ncpu)}" BUILD_JOBS="${BUILD_JOBS:-$(sysctl -n hw.ncpu)}"
ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}" ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}"
SKIA_DEPS_RETRIES="${SKIA_DEPS_RETRIES:-3}" SKIA_DEPS_RETRIES="${SKIA_DEPS_RETRIES:-3}"
@@ -68,11 +68,17 @@ sync_skiasharp() {
prepare_skia_git_sync_deps() { prepare_skia_git_sync_deps() {
local sync_deps="$1/tools/git-sync-deps" local sync_deps="$1/tools/git-sync-deps"
python3 - "$sync_deps" <<'PY' python3 - "$sync_deps" <<'PY'
import re
import pathlib import pathlib
import sys import sys
path = pathlib.Path(sys.argv[1]) path = pathlib.Path(sys.argv[1])
text = path.read_text() text = path.read_text()
deps_path = path.with_name("DEPS")
if deps_path.exists():
deps = deps_path.read_text()
deps = re.sub(r'^\s*"third_party/externals/dng_sdk"\s*:\s*"[^"]+",\s*\n', '', deps, flags=re.MULTILINE)
deps_path.write_text(deps)
old = " multithread(git_checkout_to_directory, list_of_arg_lists)" old = " multithread(git_checkout_to_directory, list_of_arg_lists)"
new = " for args in list_of_arg_lists:\n git_checkout_to_directory(*args)" new = " for args in list_of_arg_lists:\n git_checkout_to_directory(*args)"
if old in text: if old in text:
@@ -112,10 +118,12 @@ build_skia() {
cat >"$out_dir/args.gn" <<EOF_ARGS cat >"$out_dir/args.gn" <<EOF_ARGS
target_os = "mac" target_os = "mac"
target_cpu = "$TARGET_CPU" target_cpu = "$TARGET_CPU"
min_macos_version = "10.13"
is_official_build = true is_official_build = true
is_static_skiasharp = true is_static_skiasharp = true
skia_enable_tools = false skia_enable_tools = false
skia_enable_ganesh = true skia_enable_ganesh = true
skia_use_metal = true
skia_enable_pdf = false skia_enable_pdf = false
skia_enable_skottie = false skia_enable_skottie = false
skia_use_dng_sdk = false skia_use_dng_sdk = false
+8 -2
View File
@@ -7,8 +7,8 @@ $ErrorActionPreference = "Stop"
$RootDir = Split-Path -Parent $PSScriptRoot $RootDir = Split-Path -Parent $PSScriptRoot
$WorkDir = if ($env:WORK_DIR) { $env:WORK_DIR } else { Join-Path $RootDir "External\NativeStatic\.work" } $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" } $SkiaSharpVersion = if ($env:SKIASHARP_VERSION) { $env:SKIASHARP_VERSION } else { "3.119.4" }
$AngleBranch = if ($env:ANGLE_BRANCH) { $env:ANGLE_BRANCH } else { "7151" } $AngleBranch = if ($env:ANGLE_BRANCH) { $env:ANGLE_BRANCH } else { "7922" }
$TargetCpu = if ($env:TARGET_CPU) { $env:TARGET_CPU } else { "x64" } $TargetCpu = if ($env:TARGET_CPU) { $env:TARGET_CPU } else { "x64" }
$Rid = if ($env:RID) { $env:RID } else { "win-$TargetCpu" } $Rid = if ($env:RID) { $env:RID } else { "win-$TargetCpu" }
$OutputDir = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { Join-Path $RootDir "External\NativeStatic\$Rid" } $OutputDir = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { Join-Path $RootDir "External\NativeStatic\$Rid" }
@@ -356,6 +356,12 @@ function Sync-SkiaSharp {
function Prepare-SkiaGitSyncDeps($SkiaDir) { function Prepare-SkiaGitSyncDeps($SkiaDir) {
$syncDeps = Join-Path $SkiaDir "tools\git-sync-deps" $syncDeps = Join-Path $SkiaDir "tools\git-sync-deps"
$text = Get-Content -Path $syncDeps -Raw $text = Get-Content -Path $syncDeps -Raw
$depsPath = Join-Path $SkiaDir "DEPS"
if (Test-Path $depsPath) {
$depsText = Get-Content -Path $depsPath -Raw
$depsText = [regex]::Replace($depsText, '(?m)^\s*"third_party/externals/dng_sdk"\s*:\s*"[^"]+",\s*\r?\n', '')
Set-Content -Path $depsPath -Value $depsText -NoNewline -Encoding UTF8
}
$old = " multithread(git_checkout_to_directory, list_of_arg_lists)" $old = " multithread(git_checkout_to_directory, list_of_arg_lists)"
$new = " for args in list_of_arg_lists:`n git_checkout_to_directory(*args)" $new = " for args in list_of_arg_lists:`n git_checkout_to_directory(*args)"
if ($text.Contains($old)) { if ($text.Contains($old)) {
+23 -128
View File
@@ -5,8 +5,7 @@ param(
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[string]$NuGetSource, [string]$NuGetSource,
[Parameter(Mandatory = $true)] [string]$StaticLinkVersion = "",
[string]$StaticLinkVersion,
[string]$AvaloniaVersion = "11.3.14", [string]$AvaloniaVersion = "11.3.14",
@@ -15,140 +14,36 @@ param(
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
New-Item -ItemType Directory -Path $ProjectDir -Force | Out-Null $templateDir = Join-Path (Split-Path -Parent $PSScriptRoot) "Smoke/AvaloniaStaticLinkSmoke"
if (-not (Test-Path (Join-Path $templateDir "AvaloniaStaticLinkSmoke.csproj"))) {
$nativeReference = "" throw "Smoke project template was not found: $templateDir"
if ($StaticLinkNativeVersion) {
$nativeReference = "`n <PackageReference Include=`"StaticLink.Avalonia.Native`" Version=`"$StaticLinkNativeVersion`" />"
} }
New-Item -ItemType Directory -Path $ProjectDir -Force | Out-Null
Copy-Item (Join-Path $templateDir "*") -Destination $ProjectDir -Recurse -Force
$escapedNuGetSource = [System.Security.SecurityElement]::Escape($NuGetSource)
$escapedAvaloniaVersion = [System.Security.SecurityElement]::Escape($AvaloniaVersion)
$escapedStaticLinkVersion = [System.Security.SecurityElement]::Escape($StaticLinkVersion)
$escapedStaticLinkNativeVersion = [System.Security.SecurityElement]::Escape($StaticLinkNativeVersion)
@"
<Project>
<PropertyGroup>
<AvaloniaVersion>$escapedAvaloniaVersion</AvaloniaVersion>
<StaticLinkVersion>$escapedStaticLinkVersion</StaticLinkVersion>
<StaticLinkNativeVersion>$escapedStaticLinkNativeVersion</StaticLinkNativeVersion>
</PropertyGroup>
</Project>
"@ | Set-Content -Path (Join-Path $ProjectDir "SmokeVersions.props") -Encoding UTF8
@" @"
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<packageSources> <packageSources>
<clear /> <clear />
<add key="local-staticlink" value="$NuGetSource" /> <add key="local-staticlink" value="$escapedNuGetSource" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources> </packageSources>
</configuration> </configuration>
"@ | Set-Content -Path (Join-Path $ProjectDir "NuGet.config") -Encoding UTF8 "@ | 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