diff --git a/.github/workflows/macos-static-avalonia-native.yml b/.github/workflows/macos-static-avalonia-native.yml new file mode 100644 index 0000000..2184252 --- /dev/null +++ b/.github/workflows/macos-static-avalonia-native.yml @@ -0,0 +1,199 @@ +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: Build macOS static graphics libraries + run: bash ./scripts/build-macos-static-graphics.sh all + + - name: Build static AvaloniaNative + run: bash ./scripts/build-macos-avalonia-native-static.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/Shiroka.Avalonia.StaticLink.csproj \ + -c Release \ + -o artifacts/nuget \ + /p:PackageVersion=${{ steps.version.outputs.package_version }} \ + /p:Version=${{ steps.version.outputs.package_version }} + + - 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" + pwsh ./scripts/create-avalonia-smoke.ps1 ` + -ProjectDir $projectDir ` + -NuGetSource (Resolve-Path "artifacts/nuget") ` + -StaticLinkVersion "${{ needs.pack-experimental.outputs.package_version }}" + 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: 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 diff --git a/.github/workflows/nuget-static-graphics.yml b/.github/workflows/nuget-static-graphics.yml index 6536a1a..4398728 100644 --- a/.github/workflows/nuget-static-graphics.yml +++ b/.github/workflows/nuget-static-graphics.yml @@ -301,52 +301,6 @@ jobs: } Get-ChildItem -Path $publishDir -Recurse -File | ForEach-Object { $_.FullName } - - name: Bundle macOS smoke app - if: ${{ startsWith(matrix.rid, 'osx-') }} - shell: pwsh - run: | - $ErrorActionPreference = "Stop" - $publishDir = Resolve-Path "artifacts/smoke-${{ matrix.rid }}" - $bundleRoot = Join-Path $publishDir "AvaloniaStaticLinkSmoke.app" - $contentsDir = Join-Path $bundleRoot "Contents" - $macosDir = Join-Path $contentsDir "MacOS" - New-Item -ItemType Directory -Path $macosDir -Force | Out-Null - - Get-ChildItem -Path $publishDir -Force | Where-Object { $_.Name -ne "AvaloniaStaticLinkSmoke.app" } | ForEach-Object { - Move-Item -LiteralPath $_.FullName -Destination $macosDir -Force - } - - @" - - - - - CFBundleExecutable - AvaloniaStaticLinkSmoke - CFBundleIdentifier - com.shiroka.avalonia-staticlink-smoke - CFBundleName - AvaloniaStaticLinkSmoke - CFBundleDisplayName - Avalonia StaticLink Smoke - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleVersion - ${{ github.run_number }} - LSMinimumSystemVersion - 12.0 - NSHighResolutionCapable - - NSPrincipalClass - NSApplication - - - "@ | Set-Content -Path (Join-Path $contentsDir "Info.plist") -Encoding UTF8 - - Get-ChildItem -Path $publishDir -Recurse -File | ForEach-Object { $_.FullName } - - name: Upload Avalonia smoke app uses: actions/upload-artifact@v4 with: diff --git a/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj b/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj index 0f55afa..31b8b03 100644 --- a/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj +++ b/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj @@ -6,7 +6,7 @@ false false Shiroka.Avalonia.StaticLink - 3.119.2-7151.6 + 3.119.2-7151.7 greepar greepar Static SkiaSharp, HarfBuzzSharp, and ANGLE libraries for Avalonia NativeAOT single-file publishing. diff --git a/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets index f53cce0..847690a 100644 --- a/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets +++ b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets @@ -49,21 +49,25 @@ + + - - + + + + <_ShirokaAvaloniaStaticLinkFilesToDelete Include="$(PublishDir)libHarfBuzzSharp.dylib" /> <_ShirokaAvaloniaStaticLinkFilesToDelete Include="$(PublishDir)libSkiaSharp.dylib" /> + <_ShirokaAvaloniaStaticLinkFilesToDelete Include="$(PublishDir)libAvaloniaNative.dylib" /> diff --git a/scripts/build-macos-avalonia-native-static.sh b/scripts/build-macos-avalonia-native-static.sh new file mode 100644 index 0000000..2f7ba1d --- /dev/null +++ b/scripts/build-macos-avalonia-native-static.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +set -euo pipefail + +RID="${RID:-osx-arm64}" +AVALONIA_VERSION="${AVALONIA_VERSION:-11.3.14}" +WORK_DIR="${WORK_DIR:-$PWD/External/NativeStatic/.work}" +OUTPUT_DIR="${OUTPUT_DIR:-$PWD/External/NativeStatic/$RID/native}" +BUILD_JOBS="${BUILD_JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 4)}" + +case "$RID" in + osx-arm64) ARCH="arm64" ;; + osx-x64) ARCH="x86_64" ;; + *) echo "Unsupported RID: $RID" >&2; exit 2 ;; +esac + +mkdir -p "$WORK_DIR" "$OUTPUT_DIR" + +src="$WORK_DIR/Avalonia-$AVALONIA_VERSION" +if [[ ! -d "$src/.git" ]]; then + rm -rf "$src" + git clone --depth 1 --branch "$AVALONIA_VERSION" https://github.com/AvaloniaUI/Avalonia.git "$src" +fi + +project="$src/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj" +build_dir="$WORK_DIR/avalonia-native-$RID" +rm -rf "$build_dir" +mkdir -p "$build_dir" + +xcodebuild \ + -project "$project" \ + -target Avalonia.Native.OSX \ + -configuration Release \ + -sdk macosx \ + -arch "$ARCH" \ + CONFIGURATION_BUILD_DIR="$build_dir" \ + ONLY_ACTIVE_ARCH=NO \ + CODE_SIGNING_ALLOWED=NO \ + MACH_O_TYPE=staticlib \ + EXECUTABLE_PREFIX=lib \ + EXECUTABLE_EXTENSION=a \ + PRODUCT_NAME=AvaloniaNative \ + CLANG_ENABLE_MODULES=YES \ + GCC_GENERATE_DEBUGGING_SYMBOLS=NO \ + -jobs "$BUILD_JOBS" + +candidate="$(find "$build_dir" -maxdepth 2 -type f -name 'libAvaloniaNative.a' -print -quit)" +if [[ -z "$candidate" ]]; then + echo "Failed to find libAvaloniaNative.a in $build_dir" >&2 + find "$build_dir" -maxdepth 3 -type f -print >&2 + exit 1 +fi + +cp "$candidate" "$OUTPUT_DIR/libAvaloniaNative.a" +echo "Wrote $OUTPUT_DIR/libAvaloniaNative.a"