From 572d14040d0625f10830ac27a96a78c8c4e2b6ba Mon Sep 17 00:00:00 2001 From: greepar Date: Thu, 23 Jul 2026 19:40:46 +0800 Subject: [PATCH] Add SkiaSharp 4.150.1 static graphics release --- .../nuget-static-graphics-repack.yml | 2 +- .github/workflows/nuget-static-graphics.yml | 344 +++++++----------- .github/workflows/static-graphics-linux.yml | 64 ++++ .github/workflows/static-graphics-macos.yml | 74 ++++ .github/workflows/static-graphics-musl.yml | 93 +++++ .../workflows/static-graphics-preflight.yml | 81 +++++ .github/workflows/static-graphics-windows.yml | 92 +++++ .../patches/angle-chromium-7151.patch | 64 ---- .../patches/angle-chromium-7922.patch | 64 ++++ NuGet/StaticGraphics/README.md | 2 +- .../StaticGraphics/StaticLink.Avalonia.csproj | 4 +- README.md | 76 +++- Smoke/AvaloniaStaticLinkSmoke/App.axaml | 7 + Smoke/AvaloniaStaticLinkSmoke/App.axaml.cs | 23 ++ .../AvaloniaStaticLinkSmoke.csproj | 26 ++ .../AvaloniaStaticLinkSmoke/MainWindow.axaml | 13 + .../MainWindow.axaml.cs | 11 + Smoke/AvaloniaStaticLinkSmoke/Program.cs | 54 +++ scripts/build-linux-static-graphics.sh | 128 ++++--- scripts/build-macos-static-graphics.sh | 12 +- scripts/build-windows-static-graphics.ps1 | 29 +- scripts/create-avalonia-smoke.ps1 | 148 +------- 22 files changed, 938 insertions(+), 473 deletions(-) create mode 100644 .github/workflows/static-graphics-linux.yml create mode 100644 .github/workflows/static-graphics-macos.yml create mode 100644 .github/workflows/static-graphics-musl.yml create mode 100644 .github/workflows/static-graphics-preflight.yml create mode 100644 .github/workflows/static-graphics-windows.yml delete mode 100644 External/NativeStatic/patches/angle-chromium-7151.patch create mode 100644 External/NativeStatic/patches/angle-chromium-7922.patch create mode 100644 Smoke/AvaloniaStaticLinkSmoke/App.axaml create mode 100644 Smoke/AvaloniaStaticLinkSmoke/App.axaml.cs create mode 100644 Smoke/AvaloniaStaticLinkSmoke/AvaloniaStaticLinkSmoke.csproj create mode 100644 Smoke/AvaloniaStaticLinkSmoke/MainWindow.axaml create mode 100644 Smoke/AvaloniaStaticLinkSmoke/MainWindow.axaml.cs create mode 100644 Smoke/AvaloniaStaticLinkSmoke/Program.cs diff --git a/.github/workflows/nuget-static-graphics-repack.yml b/.github/workflows/nuget-static-graphics-repack.yml index 22e0c1f..52174e9 100644 --- a/.github/workflows/nuget-static-graphics-repack.yml +++ b/.github/workflows/nuget-static-graphics-repack.yml @@ -6,7 +6,7 @@ on: source_version: description: Existing package version containing the static libraries required: true - default: 3.119.2-7151.11 + default: 4.150.1-7922.1 type: string publish_to_nuget: description: Publish package to nuget.org diff --git a/.github/workflows/nuget-static-graphics.yml b/.github/workflows/nuget-static-graphics.yml index 4ecb7a4..b67d601 100644 --- a/.github/workflows/nuget-static-graphics.yml +++ b/.github/workflows/nuget-static-graphics.yml @@ -18,193 +18,46 @@ on: - "staticlink-avalonia-v*" env: - SKIASHARP_VERSION: "3.119.2" - ANGLE_BRANCH: "7151" - DOTNET_ALPINE_IMAGE: mcr.microsoft.com/dotnet/sdk:10.0-alpine + DOTNET_ALPINE_IMAGE: mcr.microsoft.com/dotnet/sdk:10.0.302-alpine3.23 jobs: + preflight: + uses: ./.github/workflows/static-graphics-preflight.yml + build-linux: - strategy: - fail-fast: false - matrix: - include: - - rid: linux-x64 - target_cpu: x64 - runner: ubuntu-latest - - rid: linux-arm64 - target_cpu: arm64 - runner: ubuntu-24.04-arm - - 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 + needs: preflight + uses: ./.github/workflows/static-graphics-linux.yml + with: + skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }} + angle_branch: ${{ needs.preflight.outputs.angle_branch }} - - 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: - name: native-static-${{ matrix.rid }} - path: External/NativeStatic/${{ matrix.rid }}/* - if-no-files-found: error + build-musl: + needs: preflight + uses: ./.github/workflows/static-graphics-musl.yml + with: + skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }} + angle_branch: ${{ needs.preflight.outputs.angle_branch }} + dotnet_alpine_image: mcr.microsoft.com/dotnet/sdk:10.0.302-alpine3.23 build-windows: - 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 - 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 + needs: preflight + uses: ./.github/workflows/static-graphics-windows.yml + with: + skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }} + angle_branch: ${{ needs.preflight.outputs.angle_branch }} build-macos: - strategy: - fail-fast: false - matrix: - include: - - rid: osx-x64 - target_cpu: x64 - runner: macos-14 - - rid: osx-arm64 - target_cpu: arm64 - runner: macos-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 - 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 + needs: preflight + uses: ./.github/workflows/static-graphics-macos.yml + with: + skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }} + angle_branch: ${{ needs.preflight.outputs.angle_branch }} pack: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - build-linux + - build-musl - build-windows - build-macos permissions: @@ -215,7 +68,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: "10.0.x" + dotnet-version: "10.0.302" - name: Download static archives uses: actions/download-artifact@v4 @@ -279,23 +132,6 @@ jobs: artifacts/nuget/*.snupkg if-no-files-found: error - - name: Publish to nuget.org - if: ${{ startsWith(github.ref, 'refs/tags/staticlink-avalonia-v') || (github.event_name == 'workflow_dispatch' && inputs.publish_to_nuget == true) }} - env: - NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} - run: | - if [ -z "$NUGET_API_KEY" ]; then - echo "NUGET_API_KEY secret is not configured. Skipping publish." - exit 0 - fi - - for package in artifacts/nuget/*.nupkg; do - dotnet nuget push "$package" \ - --source "https://api.nuget.org/v3/index.json" \ - --api-key "$NUGET_API_KEY" \ - --skip-duplicate - done - smoke-avalonia: needs: pack strategy: @@ -312,15 +148,15 @@ jobs: runner: windows-2022 msvc_arch: amd64_arm64 - rid: linux-x64 - runner: ubuntu-latest + runner: ubuntu-24.04 - rid: linux-arm64 runner: ubuntu-24.04-arm - rid: linux-musl-x64 - runner: ubuntu-latest + runner: ubuntu-24.04 - rid: linux-musl-arm64 runner: ubuntu-24.04-arm - rid: osx-x64 - runner: macos-14 + runner: macos-15 avalonia_version: 11.3.14 - rid: osx-arm64 runner: macos-15 @@ -339,12 +175,12 @@ jobs: 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 + 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 uses: actions/setup-dotnet@v4 with: - dotnet-version: "10.0.x" + dotnet-version: "10.0.302" - name: Download NuGet package uses: actions/download-artifact@v4 @@ -405,6 +241,56 @@ jobs: } 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 if: ${{ startsWith(matrix.rid, 'linux-musl-') }} shell: bash @@ -413,7 +299,7 @@ jobs: docker run --rm \ -v "${{ github.workspace }}:/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"' - name: Run Linux musl Avalonia smoke app @@ -424,7 +310,7 @@ jobs: docker run --rm \ -v "${{ github.workspace }}:/workspace" \ -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}"' - name: Run macOS Avalonia smoke app @@ -474,21 +360,65 @@ jobs: path: artifacts/smoke-${{ matrix.rid }}/** if-no-files-found: error - release: - runs-on: ubuntu-latest + publish-nuget: + runs-on: ubuntu-24.04 needs: - pack - 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) }} permissions: contents: write steps: - uses: actions/checkout@v4 - - name: Download release artifacts + - name: Download NuGet artifact uses: actions/download-artifact@v4 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 + merge-multiple: false - name: Prepare release files run: | @@ -504,19 +434,21 @@ jobs: - name: Publish GitHub Release env: GH_TOKEN: ${{ github.token }} + PACKAGE_VERSION: ${{ needs.preflight.outputs.package_version }} run: | if [ "${GITHUB_REF_TYPE}" = "tag" ]; then release_tag="${GITHUB_REF_NAME}" title="${GITHUB_REF_NAME}" else - release_tag="staticlink-avalonia-smoke-${GITHUB_RUN_NUMBER}" - title="StaticLink.Avalonia smoke ${GITHUB_RUN_NUMBER}" + release_tag="staticlink-avalonia-v${PACKAGE_VERSION}" + title="StaticLink.Avalonia ${PACKAGE_VERSION}" fi if gh release view "$release_tag" >/dev/null 2>&1; then gh release upload "$release_tag" release-files/* --clobber else gh release create "$release_tag" release-files/* \ + --target "$GITHUB_SHA" \ --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 diff --git a/.github/workflows/static-graphics-linux.yml b/.github/workflows/static-graphics-linux.yml new file mode 100644 index 0000000..7a72668 --- /dev/null +++ b/.github/workflows/static-graphics-linux.yml @@ -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: "4.150.1" + 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 diff --git a/.github/workflows/static-graphics-macos.yml b/.github/workflows/static-graphics-macos.yml new file mode 100644 index 0000000..46d0986 --- /dev/null +++ b/.github/workflows/static-graphics-macos.yml @@ -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: "4.150.1" + 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 + 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 diff --git a/.github/workflows/static-graphics-musl.yml b/.github/workflows/static-graphics-musl.yml new file mode 100644 index 0000000..728b27f --- /dev/null +++ b/.github/workflows/static-graphics-musl.yml @@ -0,0 +1,93 @@ +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: "4.150.1" + 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 + git config --global --add safe.directory "*" + 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 diff --git a/.github/workflows/static-graphics-preflight.yml b/.github/workflows/static-graphics-preflight.yml new file mode 100644 index 0000000..6519768 --- /dev/null +++ b/.github/workflows/static-graphics-preflight.yml @@ -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 diff --git a/.github/workflows/static-graphics-windows.yml b/.github/workflows/static-graphics-windows.yml new file mode 100644 index 0000000..a6799e0 --- /dev/null +++ b/.github/workflows/static-graphics-windows.yml @@ -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: "4.150.1" + 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 diff --git a/External/NativeStatic/patches/angle-chromium-7151.patch b/External/NativeStatic/patches/angle-chromium-7151.patch deleted file mode 100644 index b99971c..0000000 --- a/External/NativeStatic/patches/angle-chromium-7151.patch +++ /dev/null @@ -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', diff --git a/External/NativeStatic/patches/angle-chromium-7922.patch b/External/NativeStatic/patches/angle-chromium-7922.patch new file mode 100644 index 0000000..2325737 --- /dev/null +++ b/External/NativeStatic/patches/angle-chromium-7922.patch @@ -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 checkedArraySize = elementSize; + checkedArraySize *= getOutermostArraySize(); +- constArray = new TConstantUnion[checkedArraySize.ValueOrDie()]; ++ constArray = new TConstantUnion[static_cast(checkedArraySize.ValueOrDie())]; + + size_t elementOffset = 0u; diff --git a/NuGet/StaticGraphics/README.md b/NuGet/StaticGraphics/README.md index 25b8557..6d87c98 100644 --- a/NuGet/StaticGraphics/README.md +++ b/NuGet/StaticGraphics/README.md @@ -11,7 +11,7 @@ dotnet add package StaticLink.Avalonia Or add it manually: ```xml - + ``` For macOS, also reference `StaticLink.Avalonia.Native`. This package is macOS-only and must match your Avalonia version because it contains `libAvaloniaNative.a`. diff --git a/NuGet/StaticGraphics/StaticLink.Avalonia.csproj b/NuGet/StaticGraphics/StaticLink.Avalonia.csproj index e80b992..69ca529 100644 --- a/NuGet/StaticGraphics/StaticLink.Avalonia.csproj +++ b/NuGet/StaticGraphics/StaticLink.Avalonia.csproj @@ -6,7 +6,7 @@ false false StaticLink.Avalonia - 3.119.2-7151.12 + 4.150.1-7922.1 greepar greepar Static SkiaSharp, HarfBuzzSharp, and ANGLE libraries for Avalonia NativeAOT single-file publishing. @@ -19,7 +19,7 @@ - + diff --git a/README.md b/README.md index af84e2f..d5da5b6 100644 --- a/README.md +++ b/README.md @@ -4,30 +4,73 @@ Static native libraries for Avalonia single-file NativeAOT publishing. ## Install -```bash -dotnet add package StaticLink.Avalonia -``` +Choose the static graphics package that matches the Avalonia and SkiaSharp major versions used by your application. -Or add it manually: +| Avalonia version | SkiaSharp version | `StaticLink.Avalonia` version | +| --- | --- | --- | +| 11.3.14 | 2.88.9 | `2.88.9-7151.10` | +| 11.3.14 | 3.119.4 | `3.119.4-7922.1` | +| 12.1.0 | 3.119.4 | `3.119.4-7922.1` | +| 12.1.0 | 4.150.1 | `4.150.1-7922.1` | + +### Avalonia 11 with SkiaSharp 2 ```xml - + + + + + + ``` -`3.119.2-7151.12` supports Avalonia 11. If you need the SkiaSharp 2 line for an Avalonia 11 project, use this optional package variant: +### Avalonia 11 with SkiaSharp 3 ```xml - + + + + + + ``` -`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`. +### Avalonia 12 with SkiaSharp 3 ```xml + + + + + + +``` + +### Avalonia 12 with SkiaSharp 4 + +```xml + + + + + + +``` + +### macOS + +For macOS, also reference `StaticLink.Avalonia.Native`. This package contains `libAvaloniaNative.a`, so its version must match the Avalonia version used by the application. + +```xml + + + + ``` +Add only the `StaticLink.Avalonia.Native` reference matching your Avalonia version. + ## Publish ```bash @@ -47,3 +90,16 @@ For macOS, avoid the Metal renderer for fully static output. Use OpenGL or Softw `.github/workflows/nuget-avalonia-native.yml` runs daily and checks the latest stable `Avalonia` version on NuGet.org. If `StaticLink.Avalonia.Native..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. diff --git a/Smoke/AvaloniaStaticLinkSmoke/App.axaml b/Smoke/AvaloniaStaticLinkSmoke/App.axaml new file mode 100644 index 0000000..a3d1b68 --- /dev/null +++ b/Smoke/AvaloniaStaticLinkSmoke/App.axaml @@ -0,0 +1,7 @@ + + + + + diff --git a/Smoke/AvaloniaStaticLinkSmoke/App.axaml.cs b/Smoke/AvaloniaStaticLinkSmoke/App.axaml.cs new file mode 100644 index 0000000..d886bd6 --- /dev/null +++ b/Smoke/AvaloniaStaticLinkSmoke/App.axaml.cs @@ -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(); + } +} diff --git a/Smoke/AvaloniaStaticLinkSmoke/AvaloniaStaticLinkSmoke.csproj b/Smoke/AvaloniaStaticLinkSmoke/AvaloniaStaticLinkSmoke.csproj new file mode 100644 index 0000000..d57fc4f --- /dev/null +++ b/Smoke/AvaloniaStaticLinkSmoke/AvaloniaStaticLinkSmoke.csproj @@ -0,0 +1,26 @@ + + + + + WinExe + net10.0 + enable + enable + true + true + true + true + true + false + true + 11.3.14 + + + + + + + + + + diff --git a/Smoke/AvaloniaStaticLinkSmoke/MainWindow.axaml b/Smoke/AvaloniaStaticLinkSmoke/MainWindow.axaml new file mode 100644 index 0000000..934b673 --- /dev/null +++ b/Smoke/AvaloniaStaticLinkSmoke/MainWindow.axaml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/Smoke/AvaloniaStaticLinkSmoke/MainWindow.axaml.cs b/Smoke/AvaloniaStaticLinkSmoke/MainWindow.axaml.cs new file mode 100644 index 0000000..43167f1 --- /dev/null +++ b/Smoke/AvaloniaStaticLinkSmoke/MainWindow.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace AvaloniaStaticLinkSmoke; + +public sealed partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); + } +} diff --git a/Smoke/AvaloniaStaticLinkSmoke/Program.cs b/Smoke/AvaloniaStaticLinkSmoke/Program.cs new file mode 100644 index 0000000..435f50c --- /dev/null +++ b/Smoke/AvaloniaStaticLinkSmoke/Program.cs @@ -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() + .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; + } +} diff --git a/scripts/build-linux-static-graphics.sh b/scripts/build-linux-static-graphics.sh index 9a621ca..8f8efac 100755 --- a/scripts/build-linux-static-graphics.sh +++ b/scripts/build-linux-static-graphics.sh @@ -6,8 +6,8 @@ WORK_DIR="${WORK_DIR:-$ROOT_DIR/External/NativeStatic/.work}" TARGET_CPU="${TARGET_CPU:-x64}" RID="${RID:-linux-$TARGET_CPU}" OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}" -SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.2}" -ANGLE_BRANCH="${ANGLE_BRANCH:-7151}" +SKIASHARP_VERSION="${SKIASHARP_VERSION:-4.150.1}" +ANGLE_BRANCH="${ANGLE_BRANCH:-7922}" BUILD_JOBS="${BUILD_JOBS:-$(nproc)}" ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}" LLVM_AR="${LLVM_AR:-llvm-ar-19}" @@ -20,8 +20,8 @@ Usage: scripts/build-linux-static-graphics.sh [skia|angle|all] Environment: WORK_DIR Source/build cache directory. Default: External/NativeStatic/.work OUTPUT_DIR Final static library directory. Default: External/NativeStatic/linux-$TARGET_CPU - SKIASHARP_VERSION SkiaSharp release branch version. Default: 3.119.2 - ANGLE_BRANCH ANGLE chromium branch. Default: 7151 + SKIASHARP_VERSION SkiaSharp release branch version. Default: 4.150.1 + ANGLE_BRANCH ANGLE chromium branch. Default: 7922 TARGET_CPU GN target_cpu. Default: x64. Supported: x64, arm64 RID Output RID. Default: linux-$TARGET_CPU BUILD_JOBS Ninja parallelism. Default: nproc @@ -149,11 +149,17 @@ sync_skiasharp() { prepare_skia_git_sync_deps() { local sync_deps="$1/tools/git-sync-deps" python3 - "$sync_deps" <<'PY' +import re import pathlib import sys path = pathlib.Path(sys.argv[1]) 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)" new = " for args in list_of_arg_lists:\n git_checkout_to_directory(*args)" if old in text: @@ -267,15 +273,15 @@ build_angle() { patch_depot_tools_python_deps "$src/third_party/depot_tools" prepend_python_module_path six prepare_angle_gcs_artifacts "$src" + prepare_musl_clang_toolchain 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" + local angle_is_clang="true" + if [[ "$TARGET_CPU" == "arm64" ]] && ! is_musl_rid; then + angle_is_clang="false" fi mkdir -p "$out_dir" cat >"$out_dir/args.gn" <>"$out_dir/args.gn" + fi gn gen "$out_dir" ninja -C "$out_dir" -j "$BUILD_JOBS" libANGLE_static libGLESv2_static @@ -380,53 +387,6 @@ download_angle_gcs_artifact() { 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 @@ -466,6 +426,60 @@ prepare_musl_libstdcxx_headers() { 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() { local dest="$1" shift diff --git a/scripts/build-macos-static-graphics.sh b/scripts/build-macos-static-graphics.sh index fbee727..12a6d82 100755 --- a/scripts/build-macos-static-graphics.sh +++ b/scripts/build-macos-static-graphics.sh @@ -6,8 +6,8 @@ WORK_DIR="${WORK_DIR:-$ROOT_DIR/External/NativeStatic/.work}" TARGET_CPU="${TARGET_CPU:-arm64}" RID="${RID:-osx-$TARGET_CPU}" OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}" -SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.2}" -ANGLE_BRANCH="${ANGLE_BRANCH:-7151}" +SKIASHARP_VERSION="${SKIASHARP_VERSION:-4.150.1}" +ANGLE_BRANCH="${ANGLE_BRANCH:-7922}" BUILD_JOBS="${BUILD_JOBS:-$(sysctl -n hw.ncpu)}" ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}" SKIA_DEPS_RETRIES="${SKIA_DEPS_RETRIES:-3}" @@ -68,11 +68,17 @@ sync_skiasharp() { prepare_skia_git_sync_deps() { local sync_deps="$1/tools/git-sync-deps" python3 - "$sync_deps" <<'PY' +import re import pathlib import sys path = pathlib.Path(sys.argv[1]) 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)" new = " for args in list_of_arg_lists:\n git_checkout_to_directory(*args)" if old in text: @@ -112,10 +118,12 @@ build_skia() { cat >"$out_dir/args.gn" <" -} +$escapedNuGetSource = [System.Security.SecurityElement]::Escape($NuGetSource) +$escapedAvaloniaVersion = [System.Security.SecurityElement]::Escape($AvaloniaVersion) +$escapedStaticLinkVersion = [System.Security.SecurityElement]::Escape($StaticLinkVersion) +$escapedStaticLinkNativeVersion = [System.Security.SecurityElement]::Escape($StaticLinkNativeVersion) -$staticLinkReference = "" -if ($StaticLinkVersion) { - $staticLinkReference = "`n " -} +@" + + + $escapedAvaloniaVersion + $escapedStaticLinkVersion + $escapedStaticLinkNativeVersion + + +"@ | Set-Content -Path (Join-Path $ProjectDir "SmokeVersions.props") -Encoding UTF8 @" - + "@ | Set-Content -Path (Join-Path $ProjectDir "NuGet.config") -Encoding UTF8 - -@" - - - WinExe - net10.0 - enable - enable - true - true - true - true - true - false - true - - - - - - $staticLinkReference$nativeReference - - -"@ | 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() - .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 - -@" - - - - - -"@ | 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 - -@" - - - - - - - - -"@ | Set-Content -Path (Join-Path $ProjectDir "MainWindow.axaml") -Encoding UTF8