5 Commits
76 changed files with 4043 additions and 635 deletions

No files matched your search

-298
View File
@@ -1,298 +0,0 @@
name: staticlink-avalonia-native
on:
schedule:
- cron: "17 3 * * *"
workflow_dispatch:
inputs:
avalonia_version:
description: Avalonia version to build; empty checks the latest stable 11.x and 12.x versions
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
jobs:
detect:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
versions: ${{ steps.version.outputs.versions }}
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
if [ -n "$REQUESTED_VERSION" ]; then
candidate_versions=("$REQUESTED_VERSION")
else
nuget_versions="$(curl --fail --silent --show-error --location \
https://api.nuget.org/v3-flatcontainer/avalonia/index.json)"
candidate_versions=()
for major in 11 12; do
candidate_versions+=("$(jq -r --arg major "$major" \
'[.versions[] | select(startswith($major + ".") and (contains("-") | not))] | last' \
<<< "$nuget_versions")")
done
fi
versions='[]'
for avalonia_version in "${candidate_versions[@]}"; do
if [ -z "$avalonia_version" ] || [ "$avalonia_version" = "null" ]; then
echo "Could not determine a 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"
if [ "$FORCE_BUILD" != "true" ] && curl --fail --silent --location --output /dev/null "$package_url"; then
echo "Already published: $package_version"
continue
fi
echo "Build required: $package_version"
versions="$(jq -cn --argjson versions "$versions" \
--arg avalonia_version "$avalonia_version" \
--arg package_version "$package_version" \
'$versions + [{avalonia_version: $avalonia_version, package_version: $package_version}]')"
done
echo "versions=$versions" >> "$GITHUB_OUTPUT"
if [ "$versions" = '[]' ]; then
echo 'should_build=false' >> "$GITHUB_OUTPUT"
else
echo 'should_build=true' >> "$GITHUB_OUTPUT"
fi
build:
needs: detect
if: ${{ needs.detect.outputs.should_build == 'true' }}
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.detect.outputs.versions) }}
rid: [osx-x64, osx-arm64]
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: ${{ matrix.version.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.version.avalonia_version }}-${{ 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' }}
strategy:
matrix:
version: ${{ fromJSON(needs.detect.outputs.versions) }}
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-${{ matrix.version.avalonia_version }}-*
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-${{ matrix.version.avalonia_version }}-$rid/libAvaloniaNative.a" "External/AvaloniaNative/$rid/native/"
cp "artifacts/native/native-avalonia-${{ matrix.version.avalonia_version }}-$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=${{ matrix.version.package_version }} \
/p:Version=${{ matrix.version.package_version }}
- name: Upload NuGet package
uses: actions/upload-artifact@v4
with:
name: staticlink-avalonia-native-${{ matrix.version.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:
version: ${{ fromJSON(needs.detect.outputs.versions) }}
rid: [osx-x64, osx-arm64]
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-${{ matrix.version.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 "${{ matrix.version.avalonia_version }}" `
-StaticLinkNativeVersion "${{ matrix.version.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) }}
strategy:
matrix:
version: ${{ fromJSON(needs.detect.outputs.versions) }}
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-${{ matrix.version.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
+91 -40
View File
@@ -3,6 +3,18 @@ name: staticlink-avalonia
on:
workflow_dispatch:
inputs:
environment_only:
description: Validate the complete toolchain container without building release libraries
type: boolean
default: false
skiasharp_version:
description: Stable SkiaSharp version from this branch major; empty uses the branch version
type: string
default: ''
native_package_revision:
description: Published AvaloniaNative revision for independent graphics smoke tests
type: string
default: '1'
publish_to_nuget:
description: Publish package to nuget.org
required: true
@@ -11,7 +23,7 @@ on:
publish_to_release:
description: Publish NuGet package and smoke apps to GitHub Release
required: true
default: true
default: false
type: boolean
push:
tags:
@@ -20,46 +32,39 @@ on:
env:
DOTNET_ALPINE_IMAGE: mcr.microsoft.com/dotnet/sdk:10.0.302-alpine3.23
concurrency:
group: graphics-${{ github.ref }}-${{ inputs.skiasharp_version }}
cancel-in-progress: false
jobs:
environment:
permissions:
contents: read
packages: write
uses: ./.github/workflows/zig-toolchain-image.yml
preflight:
if: ${{ inputs.environment_only != true }}
uses: ./.github/workflows/static-graphics-preflight.yml
with:
skiasharp_version: ${{ inputs.skiasharp_version }}
build-linux:
needs: preflight
uses: ./.github/workflows/static-graphics-linux.yml
build-zig:
permissions:
contents: read
packages: write
needs: [preflight, environment]
uses: ./.github/workflows/static-graphics-zig-cross.yml
with:
skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }}
angle_branch: ${{ needs.preflight.outputs.angle_branch }}
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:
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:
needs: preflight
uses: ./.github/workflows/static-graphics-macos.yml
with:
skiasharp_version: ${{ needs.preflight.outputs.skiasharp_version }}
angle_branch: ${{ needs.preflight.outputs.angle_branch }}
source_lock: ${{ needs.preflight.outputs.source_lock }}
toolchain_image: ${{ needs.environment.outputs.image }}
pack:
runs-on: ubuntu-24.04
needs:
- build-linux
- build-musl
- build-windows
- build-macos
- preflight
- build-zig
permissions:
contents: read
steps:
@@ -109,7 +114,13 @@ jobs:
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
env:
SOURCE_LOCK_JSON: ${{ needs.preflight.outputs.source_lock }}
PACKAGE_VERSION: ${{ needs.preflight.outputs.package_version }}
SKIASHARP_VERSION: ${{ needs.preflight.outputs.skiasharp_version }}
run: |
python3 scripts/apply-release-version.py
dotnet pack NuGet/StaticGraphics/StaticLink.Avalonia.csproj -c Release -o artifacts/nuget
- name: Verify portable NuGet paths
shell: bash
@@ -123,6 +134,13 @@ jobs:
unzip -Z1 "$package" | grep -Fx 'buildTransitive/StaticLink.Avalonia.props'
unzip -Z1 "$package" | grep -Fx 'buildTransitive/StaticLink.Avalonia.targets'
- name: Upload release source lock
uses: actions/upload-artifact@v4
with:
name: graphics-release-source-lock
path: build/linux-toolchain/release.lock.json
if-no-files-found: error
- name: Upload NuGet artifact
uses: actions/upload-artifact@v4
with:
@@ -171,11 +189,16 @@ jobs:
with:
arch: ${{ matrix.msvc_arch }}
- name: Install LLVM linker for Zig Windows archives
if: ${{ startsWith(matrix.rid, 'win-') }}
shell: pwsh
run: choco install llvm -y
- name: Install Linux publish dependencies
if: ${{ startsWith(matrix.rid, 'linux-') && !startsWith(matrix.rid, 'linux-musl-') }}
run: |
sudo apt-get update
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
sudo apt-get install -y clang lld xvfb zlib1g-dev libfontconfig1-dev libffi-dev libwayland-dev libx11-dev libxext-dev libxcb1-dev libfreetype6 libgl1 libice6 libsm6 libx11-6 libxcb1 libxcursor1 libxext6 libxfixes3 libxinerama1 libxrandr2 libxrender1 libxi6
- name: Setup .NET
uses: actions/setup-dotnet@v4
@@ -200,7 +223,9 @@ jobs:
$avaloniaVersion = "${{ matrix.avalonia_version }}"
if (-not $avaloniaVersion) { $avaloniaVersion = "11.3.14" }
$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
$nativeVersion = ""
if ("${{ matrix.rid }}".StartsWith("osx-")) { $nativeVersion = "$avaloniaVersion.${{ inputs.native_package_revision || '1' }}" }
pwsh ./scripts/create-avalonia-smoke.ps1 -ProjectDir $projectDir -NuGetSource (Resolve-Path "artifacts/nuget") -StaticLinkVersion $version -AvaloniaVersion $avaloniaVersion -StaticLinkNativeVersion $nativeVersion
dotnet publish (Join-Path $projectDir "AvaloniaStaticLinkSmoke.csproj") `
-c Release `
-r "${{ matrix.rid }}" `
@@ -226,7 +251,7 @@ jobs:
-v "${{ github.workspace }}:/workspace" `
-w /workspace `
"${{ env.DOTNET_ALPINE_IMAGE }}" `
sh -c "apk add --no-cache clang lld build-base zlib-dev fontconfig-dev && dotnet publish /workspace/artifacts/avalonia-smoke-src/AvaloniaStaticLinkSmoke.csproj -c Release -r ${{ matrix.rid }} --configfile /workspace/artifacts/avalonia-smoke-src/NuGet.config /p:PublishAot=true /p:SelfContained=true /p:PublishSingleFile=true /p:StripSymbols=true /p:PublishDir=/workspace/artifacts/smoke-${{ matrix.rid }}/"
sh -c "apk add --no-cache clang lld build-base zlib-dev fontconfig-dev libffi-dev wayland-dev libx11-dev libxext-dev libxcb-dev && dotnet publish /workspace/artifacts/avalonia-smoke-src/AvaloniaStaticLinkSmoke.csproj -c Release -r ${{ matrix.rid }} --configfile /workspace/artifacts/avalonia-smoke-src/NuGet.config /p:PublishAot=true /p:SelfContained=true /p:PublishSingleFile=true /p:StripSymbols=true /p:PublishDir=/workspace/artifacts/smoke-${{ matrix.rid }}/"
- name: Verify no dynamic graphics libraries remain
shell: pwsh
@@ -354,11 +379,12 @@ jobs:
PY
- name: Upload Avalonia smoke app
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: avalonia-smoke-${{ matrix.rid }}
path: artifacts/smoke-${{ matrix.rid }}/**
if-no-files-found: error
if-no-files-found: warn
smoke-macos-metal:
runs-on: macos-15
@@ -392,7 +418,7 @@ jobs:
-NuGetSource (Resolve-Path "artifacts/nuget") `
-StaticLinkVersion $Matches[1] `
-AvaloniaVersion "12.1.0" `
-StaticLinkNativeVersion "12.1.0.1"
-StaticLinkNativeVersion "12.1.0.${{ inputs.native_package_revision || '1' }}"
dotnet publish (Join-Path $projectDir "AvaloniaStaticLinkSmoke.csproj") `
-c Release `
-r osx-arm64 `
@@ -451,7 +477,18 @@ jobs:
raise SystemExit(f"Avalonia {renderer} smoke app failed with exit code {result.returncode}.")
PY
- name: Upload macOS Metal smoke app
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: avalonia-smoke-osx-arm64-metal
path: artifacts/smoke-osx-arm64-metal/**
if-no-files-found: warn
publish-nuget:
permissions:
contents: read
id-token: write
runs-on: ubuntu-24.04
needs:
- pack
@@ -469,14 +506,21 @@ jobs:
name: staticlink-avalonia-nuget
path: artifacts/nuget
- name: NuGet Trusted Publishing login
id: login
if: ${{ vars.NUGET_USER != '' && (startsWith(github.ref, 'refs/tags/staticlink-avalonia-v') || inputs.publish_to_nuget == true) }}
uses: NuGet/login@v1
with:
user: ${{ vars.NUGET_USER }}
- 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 }}
NUGET_API_KEY: ${{ steps.login.outputs.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
echo "NUGET_API_KEY secret is not configured." >&2
exit 1
fi
for package in artifacts/nuget/*.nupkg; do
@@ -506,6 +550,12 @@ jobs:
name: staticlink-avalonia-nuget
path: release-artifacts/staticlink-avalonia-nuget
- name: Download release source lock
uses: actions/download-artifact@v4
with:
name: graphics-release-source-lock
path: release-artifacts/source-lock
- name: Download smoke artifacts
uses: actions/download-artifact@v4
with:
@@ -516,6 +566,7 @@ jobs:
- name: Prepare release files
run: |
mkdir -p release-files
cp release-artifacts/source-lock/release.lock.json release-files/
find release-artifacts/staticlink-avalonia-nuget -type f \( -name '*.nupkg' -o -name '*.snupkg' \) -exec cp {} release-files/ \;
for dir in release-artifacts/avalonia-smoke-*; do
[ -d "$dir" ] || continue
@@ -0,0 +1,28 @@
name: static-graphics-linux-debian-zig
on:
workflow_call:
inputs:
skiasharp_version:
required: true
type: string
source_lock:
type: string
default: ''
workflow_dispatch:
inputs:
skiasharp_version:
type: string
default: '4.150.1'
permissions:
contents: read
packages: write
jobs:
build:
uses: ./.github/workflows/static-graphics-zig-cross.yml
with:
skiasharp_version: ${{ inputs.skiasharp_version }}
source_lock: ${{ inputs.source_lock || '' }}
rids: '["linux-x64","linux-arm64","linux-musl-x64","linux-musl-arm64"]'
+13 -49
View File
@@ -1,4 +1,4 @@
name: static-graphics-linux
name: static-graphics-linux-zig
on:
workflow_call:
@@ -6,59 +6,23 @@ on:
skiasharp_version:
required: true
type: string
angle_branch:
required: true
source_lock:
type: string
default: ''
workflow_dispatch:
inputs:
skiasharp_version:
required: true
default: "4.150.1"
type: string
angle_branch:
required: true
default: "7922"
type: string
default: '4.150.1'
permissions:
contents: read
packages: write
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
uses: ./.github/workflows/static-graphics-zig-cross.yml
with:
skiasharp_version: ${{ inputs.skiasharp_version }}
source_lock: ${{ inputs.source_lock || '' }}
rids: '["linux-x64","linux-arm64"]'
+13 -59
View File
@@ -1,4 +1,4 @@
name: static-graphics-macos
name: static-graphics-macos-zig
on:
workflow_call:
@@ -6,69 +6,23 @@ on:
skiasharp_version:
required: true
type: string
angle_branch:
required: true
source_lock:
type: string
default: ''
workflow_dispatch:
inputs:
skiasharp_version:
required: true
default: "4.150.1"
type: string
angle_branch:
required: true
default: "7922"
type: string
default: '4.150.1'
permissions:
contents: read
packages: write
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
uses: ./.github/workflows/static-graphics-zig-cross.yml
with:
skiasharp_version: ${{ inputs.skiasharp_version }}
source_lock: ${{ inputs.source_lock || '' }}
rids: '["osx-x64","osx-arm64"]'
+13 -78
View File
@@ -1,4 +1,4 @@
name: static-graphics-musl
name: static-graphics-musl-zig
on:
workflow_call:
@@ -6,88 +6,23 @@ on:
skiasharp_version:
required: true
type: string
angle_branch:
required: true
type: string
dotnet_alpine_image:
required: true
source_lock:
type: string
default: ''
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
default: '4.150.1'
permissions:
contents: read
packages: write
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
uses: ./.github/workflows/static-graphics-zig-cross.yml
with:
skiasharp_version: ${{ inputs.skiasharp_version }}
source_lock: ${{ inputs.source_lock || '' }}
rids: '["linux-musl-x64","linux-musl-arm64"]'
@@ -2,7 +2,13 @@ name: static-graphics-preflight
on:
workflow_call:
inputs:
skiasharp_version:
type: string
default: ''
outputs:
source_lock:
value: ${{ jobs.validate.outputs.source_lock }}
package_version:
value: ${{ jobs.validate.outputs.package_version }}
skiasharp_version:
@@ -15,6 +21,7 @@ jobs:
validate:
runs-on: ubuntu-24.04
outputs:
source_lock: ${{ steps.versions.outputs.source_lock }}
package_version: ${{ steps.versions.outputs.package_version }}
skiasharp_version: ${{ steps.versions.outputs.skiasharp_version }}
angle_branch: ${{ steps.versions.outputs.angle_branch }}
@@ -29,7 +36,14 @@ jobs:
- name: Read and validate versions
id: versions
shell: bash
env:
REQUESTED_VERSION: ${{ inputs.skiasharp_version }}
run: |
if [ "${GITHUB_REF_TYPE:-}" = tag ] && [ -z "$REQUESTED_VERSION" ]; then
tag_version="${GITHUB_REF_NAME#staticlink-avalonia-v}"
REQUESTED_VERSION="${tag_version%%-*}"
fi
python3 scripts/resolve-release-lock.py --version "$REQUESTED_VERSION"
python3 - <<'PY'
import os
import pathlib
@@ -37,7 +51,10 @@ jobs:
project = pathlib.Path("NuGet/StaticGraphics/StaticLink.Avalonia.csproj")
root = ET.parse(project).getroot()
import json
lock = json.loads(pathlib.Path("build/linux-toolchain/release.lock.json").read_text())
package_version = root.findtext(".//Version")
package_version = lock["skiasharp_version"] + "-" + package_version.split("-", 1)[1]
skia_reference = next(
item.attrib["Version"]
for item in root.findall(".//PackageReference")
@@ -45,7 +62,7 @@ jobs:
)
version_base, revision = package_version.rsplit(".", 1)
skiasharp_version, angle_branch = version_base.split("-", 1)
if not revision.isdigit() or skiasharp_version != skia_reference:
if not revision.isdigit() or skiasharp_version != lock["skiasharp_version"]:
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}"
@@ -53,6 +70,7 @@ jobs:
raise SystemExit(f"Release tag must be {expected_tag}")
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
output.write("source_lock=" + json.dumps(lock, separators=(",", ":")) + "\n")
output.write(f"package_version={package_version}\n")
output.write(f"skiasharp_version={skiasharp_version}\n")
output.write(f"angle_branch={angle_branch}\n")
@@ -65,7 +83,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
git ls-remote --exit-code --heads https://github.com/mono/SkiaSharp.git "refs/heads/release/${SKIASHARP_VERSION}"
git ls-remote --exit-code --tags https://github.com/mono/SkiaSharp.git "refs/tags/v${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"
+13 -77
View File
@@ -1,4 +1,4 @@
name: static-graphics-windows
name: static-graphics-windows-zig
on:
workflow_call:
@@ -6,87 +6,23 @@ on:
skiasharp_version:
required: true
type: string
angle_branch:
required: true
source_lock:
type: string
default: ''
workflow_dispatch:
inputs:
skiasharp_version:
required: true
default: "4.150.1"
type: string
angle_branch:
required: true
default: "7922"
type: string
default: '4.150.1'
permissions:
contents: read
packages: write
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
uses: ./.github/workflows/static-graphics-zig-cross.yml
with:
skiasharp_version: ${{ inputs.skiasharp_version }}
source_lock: ${{ inputs.source_lock || '' }}
rids: '["win-x64","win-x86","win-arm64"]'
@@ -0,0 +1,111 @@
name: static-graphics-zig-cross-debian12
# All native archives are compiled with Zig on Linux; platform runners validate consumer apps.
on:
workflow_call:
inputs:
skiasharp_version:
type: string
required: true
toolchain_image:
type: string
default: ''
rids:
type: string
default: '["linux-x64","linux-arm64","linux-musl-x64","linux-musl-arm64","win-x64","win-x86","win-arm64","osx-x64","osx-arm64"]'
artifact_prefix:
type: string
default: native-static
source_lock:
type: string
required: true
workflow_dispatch:
inputs:
skiasharp_version:
description: Locked SkiaSharp release profile
type: string
default: '4.150.1'
permissions:
contents: read
packages: write
jobs:
image:
if: ${{ inputs.toolchain_image == '' }}
uses: ./.github/workflows/zig-toolchain-image.yml
build:
needs: image
if: ${{ always() && (needs.image.result == 'success' || inputs.toolchain_image != '') }}
permissions:
contents: read
packages: read
env:
TOOLCHAIN_IMAGE: ${{ inputs.toolchain_image || needs.image.outputs.image }}
timeout-minutes: 180
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
rid: ${{ fromJSON(inputs.rids || '["linux-x64","linux-arm64","linux-musl-x64","linux-musl-arm64","win-x64","win-x86","win-arm64","osx-x64","osx-arm64"]') }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Resolve immutable build inputs
env:
SOURCE_LOCK_JSON: ${{ inputs.source_lock }}
REQUESTED_VERSION: ${{ inputs.skiasharp_version }}
run: |
if [ -n "$SOURCE_LOCK_JSON" ]; then
python3 - <<'PY'
import json, os, pathlib
lock = json.loads(os.environ['SOURCE_LOCK_JSON'])
if lock['skiasharp_version'] != os.environ['REQUESTED_VERSION']:
raise SystemExit('Source lock does not match requested version')
pathlib.Path('build/linux-toolchain/release.lock.json').write_text(json.dumps(lock, indent=2) + '\n')
PY
else
python3 scripts/resolve-release-lock.py --version "$REQUESTED_VERSION"
fi
- name: Restore exact target cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: External/NativeStatic/.work
key: zig-debian12-v3-${{ inputs.skiasharp_version }}-${{ matrix.rid }}-${{ hashFiles('build/linux-toolchain/**', 'scripts/prepare-cross-sources.sh', 'scripts/build-linux-static-graphics.sh', 'External/NativeStatic/patches/*.patch') }}
- name: Pull immutable toolchain image
env:
GH_TOKEN: ${{ github.token }}
ACTOR: ${{ github.actor }}
run: |
printf '%s' "$GH_TOKEN" | docker login ghcr.io -u "$ACTOR" --password-stdin
docker pull "$TOOLCHAIN_IMAGE"
- name: Prepare locked sources and build native libraries
env:
CROSS_RID: ${{ matrix.rid }}
SKIASHARP_PROFILE: ${{ inputs.skiasharp_version }}
run: |
mkdir -p External/NativeStatic/.work/home
docker run --rm --user "$(id -u):$(id -g)" \
-e HOME=/src/External/NativeStatic/.work/home -e CROSS_RID -e SKIASHARP_PROFILE \
-e CCACHE_DIR=/src/External/NativeStatic/.work/ccache -e CCACHE_MAXSIZE=1G \
-e ZIG_GLOBAL_CACHE_DIR=/src/External/NativeStatic/.work/zig-global \
-v "$PWD:/src" -w /src --entrypoint bash "$TOOLCHAIN_IMAGE" -c '
set -euo pipefail
export SOURCE_LOCK="/src/build/linux-toolchain/release.lock.json"
source scripts/prepare-cross-sources.sh
python3 build/linux-toolchain/build-cross.py \
--target "$CROSS_RID" \
--sources-manifest "$WORK_DIR/cross-sources.json" \
--work /src/External/NativeStatic/.work/toolchains \
--output "/src/External/NativeStatic/zig-cross/$SKIASHARP_PROFILE/$CROSS_RID" --jobs 4
python3 build/linux-toolchain/package-zig.py \
--rid "$CROSS_RID" \
--input "/src/External/NativeStatic/zig-cross/$SKIASHARP_PROFILE/$CROSS_RID" \
--output "/src/External/NativeStatic/$CROSS_RID/native"
'
- name: Upload archives, link tests and input manifest
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ inputs.artifact_prefix || 'native-static' }}-${{ matrix.rid }}
path: External/NativeStatic/${{ matrix.rid }}/**
if-no-files-found: error
@@ -0,0 +1,55 @@
name: zig-recent-release-regression
on:
workflow_dispatch:
inputs:
versions:
description: JSON array of stable SkiaSharp versions from this branch major
type: string
default: '["4.150.3","4.150.5","4.153.1"]'
permissions:
contents: read
packages: write
jobs:
resolve:
runs-on: ubuntu-24.04
outputs:
releases: ${{ steps.locks.outputs.releases }}
steps:
- uses: actions/checkout@v4
- id: locks
env:
VERSIONS: ${{ inputs.versions }}
run: |
python3 - <<'PY'
import json, os, pathlib, subprocess
versions = json.loads(os.environ['VERSIONS'])
if not isinstance(versions, list) or not 1 <= len(versions) <= 5:
raise SystemExit('Expected one to five stable versions')
releases = []
for version in versions:
subprocess.run(['python3', 'scripts/resolve-release-lock.py', '--version', version], check=True)
lock = json.loads(pathlib.Path('build/linux-toolchain/release.lock.json').read_text())
releases.append({'version': version, 'lock': json.dumps(lock)})
with open(os.environ['GITHUB_OUTPUT'], 'a') as output:
output.write('releases=' + json.dumps(releases, separators=(',', ':')) + '\n')
PY
toolchain:
needs: resolve
uses: ./.github/workflows/zig-toolchain-image.yml
build:
needs: [resolve, toolchain]
strategy:
fail-fast: false
matrix:
release: ${{ fromJSON(needs.resolve.outputs.releases) }}
uses: ./.github/workflows/static-graphics-zig-cross.yml
with:
skiasharp_version: ${{ matrix.release.version }}
source_lock: ${{ matrix.release.lock }}
toolchain_image: ${{ needs.toolchain.outputs.image }}
artifact_prefix: regression-${{ matrix.release.version }}
+78
View File
@@ -0,0 +1,78 @@
name: pinned-zig-toolchain-image
on:
workflow_call:
inputs:
validation_rids:
type: string
default: '[]'
outputs:
image:
description: Immutable Debian 12 Zig toolchain image reference
value: ${{ jobs.image.outputs.image }}
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
image:
runs-on: ubuntu-24.04
timeout-minutes: 60
outputs:
image: ${{ steps.publish.outputs.image }}
steps:
- uses: actions/checkout@v4
- name: Publish or reuse exact toolchain image
id: publish
env:
GH_TOKEN: ${{ github.token }}
ACTOR: ${{ github.actor }}
REPOSITORY: ${{ github.repository }}
VALIDATION_RIDS: ${{ inputs.validation_rids || '[]' }}
BASE_HASH: ${{ hashFiles('build/linux-toolchain/Dockerfile') }}
RECIPE_HASH: ${{ hashFiles('build/linux-toolchain/Dockerfile*', 'build/linux-toolchain/*.py', 'build/linux-toolchain/*.c', 'build/linux-toolchain/*.gn.in', 'build/linux-toolchain/*lock.json', 'build/linux-toolchain/versions/*.json', 'build/linux-toolchain/patches/**') }}
run: |
set -euo pipefail
printf '%s' "$GH_TOKEN" | docker login ghcr.io -u "$ACTOR" --password-stdin
repository="ghcr.io/${REPOSITORY,,}-zig"
image="$repository:recipe-$RECIPE_HASH"
base="$repository:debian12-$BASE_HASH"
built_image=false
if ! docker pull "$image"; then
if ! docker pull "$base"; then
docker build --platform linux/amd64 \
--label "org.opencontainers.image.source=https://github.com/$REPOSITORY" \
-t "$base" build/linux-toolchain
docker push "$base"
docker pull "$base"
fi
base_digest="$(docker image inspect --format '{{index .RepoDigests 0}}' "$base")"
docker build --platform linux/amd64 \
--build-arg "TOOLCHAIN_IMAGE=$base_digest" \
--label "org.opencontainers.image.source=https://github.com/$REPOSITORY" \
-f build/linux-toolchain/Dockerfile.cross-probe \
-t "$image" build/linux-toolchain
built_image=true
fi
mkdir -p artifacts/toolchain-environment
docker run --rm --user "$(id -u):$(id -g)" \
-e HOME=/tmp -e DOTNET_CLI_HOME=/tmp -e ZIG_GLOBAL_CACHE_DIR=/tmp/zig-cache \
-v "$PWD/artifacts/toolchain-environment:/validation" \
--entrypoint python3 "$image" /opt/staticlink/validate-toolchain.py --output /validation --rids "$VALIDATION_RIDS"
if [ "$built_image" = true ]; then
docker push "$image"
docker pull "$image"
fi
digest="$(docker image inspect --format '{{index .RepoDigests 0}}' "$image")"
case "$digest" in "$repository"@sha256:*) ;; *) echo 'No immutable image digest' >&2; exit 1 ;; esac
echo "image=$digest" >> "$GITHUB_OUTPUT"
echo "Toolchain: $digest" >> "$GITHUB_STEP_SUMMARY"
- name: Upload environment validation report
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: toolchain-environment-validation
path: artifacts/toolchain-environment/
if-no-files-found: warn
+38
View File
@@ -0,0 +1,38 @@
# Manual artifact validation
Daily schedules are disabled during validation. No NuGet.org publication or
GitHub Release is requested by the manual dispatcher on main.
Before release builds, run nuget-static-graphics.yml on skia4 with
environment_only=true. This checks the actual container's Python/depot_tools,
.NET 8/10, SDKs, sysroots, and C/C++ compilation plus C executable linking for
all nine RIDs. Download toolchain-environment-validation to inspect the report.
The same environment gate runs before every graphics and native build and
must pass before the toolchain image is published for release compilation.
- skia3: nuget-static-graphics.yml; latest stable SkiaSharp 3.
- skia4: nuget-static-graphics.yml; latest stable SkiaSharp 4.
- avalonia-native: nuget-avalonia-native.yml; latest stable Avalonia 11 and 12.
Download staticlink-avalonia-nuget and avalonia-smoke-<RID> from each graphics run.
Download staticlink-avalonia-native-<version> and
avalonia-native-smoke-<AvaloniaVersion>-<RID> from the native run.
Native archives and source locks are also retained as Actions artifacts.
Graphics macOS smoke tests use the already published AvaloniaNative revision 1
by default, so the graphics builds do not require the experimental native
revision 2 to be published. The native branch validates its own local revision 2
package separately. To test both experimental packages together, put both
downloaded packages in a local NuGet feed and reference their exact versions.
Smoke executables are retained even if a later runtime validation fails.
An artifact is not evidence that all validation gates passed; check job results.
The daily-native-release workflow on main currently supports manual dispatch
only. Its three matrix jobs check out their own branch and dispatch independently.
Use force_build=true to rebuild published versions for testing.
After validation, enable the schedule on main only; non-default branches cannot
run their own GitHub cron schedules. Publication remains an explicit opt-in.
While publication is disabled, an unpublished package may be rebuilt on every
check; add successful-run tracking before enabling artifact-only daily builds
if avoiding repeated builds is required.
+9
View File
@@ -0,0 +1,9 @@
# Daily releases (not enabled)
This branch currently runs manual artifact validation only. The independent
three-branch dispatcher is on main and has no schedule yet. NuGet.org and
GitHub Release publication are disabled for the initial runs.
See [ACTION-TESTING.md](ACTION-TESTING.md) for artifact names, the macOS native
package used by graphics smoke tests, and the steps required before daily
builds are enabled.
@@ -19,6 +19,8 @@
</PropertyGroup>
<ItemGroup>
<None Include="../../External/AvaloniaNative/osx-x64/native/avalonia-native-build.json" Pack="true" PackagePath="buildMetadata/osx-x64.json" />
<None Include="../../External/AvaloniaNative/osx-arm64/native/avalonia-native-build.json" Pack="true" PackagePath="buildMetadata/osx-arm64.json" />
<None Include="README.md" Pack="true" PackagePath="README.md" />
<None Include="lib/net8.0/_._" Pack="true" PackagePath="lib/net8.0/_._" />
<None Include="buildTransitive/StaticLink.Avalonia.Native.props" Pack="true" PackagePath="buildTransitive/$(PackageId).props" />
@@ -6,7 +6,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<SuppressDependenciesWhenPacking>false</SuppressDependenciesWhenPacking>
<PackageId>StaticLink.Avalonia</PackageId>
<Version>4.150.1-7922.1</Version>
<Version>4.150.1-7922.2</Version>
<Authors>greepar</Authors>
<Company>greepar</Company>
<Description>Static SkiaSharp, HarfBuzzSharp, and ANGLE libraries for Avalonia NativeAOT single-file publishing.</Description>
@@ -24,6 +24,7 @@
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
<None Include="../../build/linux-toolchain/release.lock.json" Condition="Exists('../../build/linux-toolchain/release.lock.json')" Pack="true" PackagePath="buildMetadata/sources.lock.json" />
<None Include="lib/net8.0/_._" Pack="true" PackagePath="lib/net8.0/_._" />
<None Include="buildTransitive/StaticLink.Avalonia.props" Pack="true" PackagePath="buildTransitive/$(PackageId).props" />
<None Include="buildTransitive/StaticLink.Avalonia.targets" Pack="true" PackagePath="buildTransitive/$(PackageId).targets" />
@@ -163,4 +163,19 @@
</ItemGroup>
<Delete Files="@(_StaticLinkAvaloniaFilesToDelete)" TreatErrorsAsWarnings="true" />
</Target>
<Target Name="StaticLinkAvaloniaAddZigRuntime"
BeforeTargets="LinkNative"
Condition="'$(PublishAot)' == 'true' and Exists('$(_StaticLinkAvaloniaNativeRoot)/zig-build.json')">
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith('win-'))">
<CppLinker>lld-link</CppLinker>
</PropertyGroup>
<ItemGroup>
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/zig-runtime/*.lib" Condition="$(RuntimeIdentifier.StartsWith('win-'))" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/zig-runtime/*.a" Condition="!$(RuntimeIdentifier.StartsWith('win-'))" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libEGL_static.lib" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libEGL_static.lib')" />
<NativeLibrary Include="$(_StaticLinkAvaloniaNativeRoot)/libEGL_static.a" Condition="Exists('$(_StaticLinkAvaloniaNativeRoot)/libEGL_static.a')" />
<NativeSystemLibrary Include="X11;Xext;xcb;wayland-client;wayland-egl;ffi" Condition="$(RuntimeIdentifier.StartsWith('linux-'))" />
<LinkerArg Include="/lldmingw" Condition="$(RuntimeIdentifier.StartsWith('win-'))" />
</ItemGroup>
</Target>
</Project>
+2
View File
@@ -1,3 +1,5 @@
> 3/4 分支构建更新:原生图形库与 macOS AvaloniaNative 已统一为 Linux 上的 Zig 入口;Windows/macOS runner 用于消费者运行验收。配置与真实编译的验证范围见 [CROSS-COMPILE.md](build/linux-toolchain/CROSS-COMPILE.md),每日发布说明见 [DAILY-RELEASE.md](DAILY-RELEASE.md)。完整九平台回归与 Docker 镜像尚待 Action 实跑,不能视为已全部通过。
# StaticLink.Avalonia
Static native libraries for Avalonia single-file NativeAOT publishing.
+72
View File
@@ -0,0 +1,72 @@
{
"checked_at_utc": "2026-10-04",
"live_plan": [
{
"ref": "4.150.1-7922.1",
"skiasharp_version": "4.153.1",
"package_version": "4.153.1-7922.1"
}
],
"source_lock_checks": {
"3.119.2": {
"skiasharp_version": "3.119.2",
"skiasharp_revision": "2888c737ad016d584c74525e2d35db5097ea8576",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "927041a58f130e0dd0562ba86cb4170989ad39e9"
},
"3.119.4": {
"skiasharp_version": "3.119.4",
"skiasharp_revision": "f568ac94dd768ef9a2f593537cfde2dd0d348ef5",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "7dbfc07dd33181f84e0958afb7ee805c6c769f0b"
},
"4.150.2": {
"skiasharp_version": "4.150.2",
"skiasharp_revision": "fd1fdd5239f826514651a4ada5d7bb3dcec2fb43",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "b70519562e2d26d3cf56286d3a13ca25152cfae4"
},
"4.150.3": {
"skiasharp_version": "4.150.3",
"skiasharp_revision": "f8a404ff3375872d042404a29189ceb8e3001a41",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "eb86e4b3ee034085b62d3980b3d94629862441e6"
},
"4.150.5": {
"skiasharp_version": "4.150.5",
"skiasharp_revision": "16f82682f756217654eb1a152cdb639e358b8976",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "579fa81eeed6906732e27b7d95149538584dd20b"
},
"4.153.1": {
"skiasharp_version": "4.153.1",
"skiasharp_revision": "4783f51448f9b070dda4f87b83e941c9599e466e",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "45afab4f1f0921f3feb97f58cf89b136fffa85e6"
}
},
"checks_passed": [
"actionlint",
"python-json-yaml-bash-syntax",
"numeric-stable-version-selection",
"skip-published-version",
"freeze-skia2",
"real-nuget-discovery",
"immutable-tag-and-skia-gitlink-resolution",
"generated-package-version-and-exact-dependency"
],
"not_run": [
"full-build-of-the-six-new-versions",
"github-actions",
"docker-cold-build",
"windows-macos-runtime",
"dotnet-pack",
"nuget-publish"
]
}
+60
View File
@@ -0,0 +1,60 @@
# Zig cross compilation for SkiaSharp 3 / 4
All nine native targets use Zig 0.14.1 from Linux: linux-x64, linux-arm64,
linux-musl-x64, linux-musl-arm64, win-x64, win-x86, win-arm64, osx-x64, osx-arm64.
Windows uses the GNU ABI, without MSVC. macOS uses a checksum-pinned real SDK,
without Xcode. AvaloniaNative 11/12 also compiles with Zig on Linux.
## Local evidence (2026-10-04)
| Check | Result |
| --- | --- |
| 3.119.2, 3.119.4, 4.150.3, 4.150.5, 4.153.1 × nine GN configurations | 45 passed; these are configuration checks |
| All five versions, Windows x64 Skia/SkiaSharp/HarfBuzz compilation and executable link | Passed |
| 3.119.4 Windows x86 Skia and stdcall thunks | Compilation and raster/EGL links passed; fixed ANGLE archives reused |
| 3.119.4 Linux x64 | All six archives and both links passed; raster/UTF-8 execution passed |
| 3.119.4 Linux ARM64 | Three Skia archives and raster link passed; raster/UTF-8 execution passed under QEMU |
| 4.153.1 macOS ARM64 | Three Skia archives and raster executable link passed |
| AvaloniaNative 11.3.14 / 12.1.0, both macOS architectures | Four full compilations and SDK libc++ executable links passed |
Exact scope is recorded in `recent-version-validation.json`. Earlier baseline
results are retained separately. The raster probe checks 64 pixels and UTF-8
buffer ingestion; it does not prove complete font shaping or GPU operation.
There is no local Docker daemon or Windows/macOS runtime. The complete Debian
image, five releases × nine full builds, NativeAOT consumer applications and
actual Actions/NuGet publication have not been verified locally. Local builds
used the Linux host and pinned cross tools, not a running Debian container.
## Repeatable inputs and failure policy
`Dockerfile` pins Debian 12 by digest, apt by snapshot and depot_tools by commit.
`Dockerfile.cross-probe` is the production cross image despite its historical
name. It pins Zig, the macOS SDK, WinRT headers, availability runtime and Linux
sysroot packages by checksum. SDK extraction supports pinned Debian Python 3.11.
`zig-toolchain-image.yml` builds/reuses GHCR images by recipe digest and passes
an immutable image digest to downstream builds. Repository package write
permission is required. Source profiles pin SkiaSharp, Skia and ANGLE commits.
The existing exact per-target source/build cache has no fallback restores;
ccache additionally checks compiler contents. Cold downloads remain necessary.
`build-cross.py` adapts declared GN capabilities, rejects unknown compiler flags,
checks static targets, builds six archives and links raster and EGL/GLES probes.
`package-zig.py` requires archive hashes and successful links before packaging.
Windows x86 gets generated stdcall thunks. Captured Zig runtime libraries are
included for consumer linking. A successful C link does not establish NativeAOT
compatibility; the release workflow gates publication on consumer OS tests.
## Run
Use the `zig-recent-regression` manual Action for recent-version full matrices.
Use `static-graphics-zig-cross-debian12` for one pinned release and nine RIDs.
For already synced sources, run `build/linux-toolchain/build-cross.py --help`;
`--configure-only` configures without compiling, while `--component skia` checks
only the three Skia libraries and their executable link.
This common driver supports the tested source revisions. Future compiler,
SDK, GN or source changes can still require deliberate updates. Daily releases
fail closed and publish only after all required jobs pass. SkiaSharp 2 is frozen
and excluded from the new regression/release tracks.
+38
View File
@@ -0,0 +1,38 @@
# Multi-architecture index; deliberately update together with the snapshot/cache key.
FROM debian:bookworm-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251
ARG DEBIAN_SNAPSHOT=20260901T000000Z
ARG DEPOT_TOOLS_REVISION=8a5434051036b32412a2ecb10c213a72e3f3ccb9
ENV DEBIAN_FRONTEND=noninteractive \
DEPOT_TOOLS_UPDATE=0 \
DEPOT_TOOLS_REVISION=${DEPOT_TOOLS_REVISION} \
LLVM_AR=llvm-ar-16 \
BUILD_JOBS=4
# HTTP bootstrap uses Debian's signed Release metadata. Do not disable signature checks.
RUN rm -f /etc/apt/sources.list.d/debian.sources \
&& printf 'deb [check-valid-until=no] http://snapshot.debian.org/archive/debian/%s bookworm main\ndeb [check-valid-until=no] http://snapshot.debian.org/archive/debian-security/%s bookworm-security main\n' "$DEBIAN_SNAPSHOT" "$DEBIAN_SNAPSHOT" > /etc/apt/sources.list \
&& apt-get update \
&& printf 'Package: *\nPin: origin "snapshot.debian.org"\nPin-Priority: 1001\n' > /etc/apt/preferences.d/staticlink-snapshot \
&& apt-get dist-upgrade -y --allow-downgrades \
&& apt-get install -y --no-install-recommends \
ca-certificates git curl python3 python3-six python3-requests python3-httplib2 \
build-essential ccache clang-16 lld-16 llvm-16 libclang-rt-16-dev ninja-build \
pkg-config cmake binutils file unzip xz-utils bzip2 patch \
libfontconfig1-dev libglib2.0-dev libgl1-mesa-dev libpci-dev \
libx11-dev libxcb1-dev libxext-dev libwayland-dev libexpat1-dev libicu72 libssl3 \
&& for tool in clang clang++ llvm-ar llvm-ranlib llvm-nm lld ld.lld lld-link ld64.lld; do \
ln -s "/usr/bin/$tool-16" "/usr/local/bin/$tool"; \
done \
&& mkdir -p /opt/staticlink \
&& dpkg-query -W > /opt/staticlink/packages.lock \
&& rm -rf /var/lib/apt/lists/*
# Store the pinned checkout inside the image so the runner never selects depot_tools HEAD.
RUN git init /opt/depot_tools \
&& git -C /opt/depot_tools remote add origin https://chromium.googlesource.com/chromium/tools/depot_tools.git \
&& git -C /opt/depot_tools fetch --depth 1 origin "$DEPOT_TOOLS_REVISION" \
&& git -C /opt/depot_tools checkout --detach FETCH_HEAD
ENV DEPOT_TOOLS_DIR=/opt/depot_tools
WORKDIR /src
CMD ["bash"]
@@ -0,0 +1,61 @@
# Build this local base from Dockerfile first; it contains the pinned Debian toolchain.
ARG TOOLCHAIN_IMAGE=staticlink-debian12:probe-base
FROM mcr.microsoft.com/dotnet/sdk:10.0.302@sha256:72dd743782f2ae7e5476fd64f6a460045e3998dc862218b80e6944cba79a01b0 AS dotnet10
FROM mcr.microsoft.com/dotnet/sdk:8.0.416@sha256:8b5eece40d8a8050c09332eab1e65563fd4c928c5881e9e72bca7a4b0339f881 AS dotnet8
FROM python:3.12.12-slim-bookworm@sha256:593bd06efe90efa80dc4eee3948be7c0fde4134606dd40d8dd8dbcade98e669c AS python312
FROM ${TOOLCHAIN_IMAGE}
COPY --from=python312 /usr/local/ /usr/local/
ENV PYTHONPATH=/usr/lib/python3/dist-packages
RUN ldconfig && python3 -c 'import inspect, tarfile, six, requests, httplib2; assert "filter" in inspect.signature(tarfile.TarFile.extractall).parameters'
ARG TARGETARCH
RUN case "$TARGETARCH" in \
amd64) zig_arch=x86_64; zig_sha=24aeeec8af16c381934a6cd7d95c807a8cb2cf7df9fa40d359aa884195c4716c ;; \
arm64) zig_arch=aarch64; zig_sha=f7a654acc967864f7a050ddacfaa778c7504a0eca8d2b678839c21eea47c992b ;; \
*) echo "Unsupported host architecture: $TARGETARCH" >&2; exit 1 ;; \
esac \
&& curl --fail --location --retry 3 \
"https://ziglang.org/download/0.14.1/zig-$zig_arch-linux-0.14.1.tar.xz" -o /tmp/zig.tar.xz \
&& printf '%s /tmp/zig.tar.xz\n' "$zig_sha" | sha256sum --check \
&& mkdir /opt/zig \
&& tar -xJf /tmp/zig.tar.xz -C /opt/zig --strip-components=1 \
&& ln -s /opt/zig/zig /usr/local/bin/zig \
&& rm /tmp/zig.tar.xz
ARG MACOS_SDK_SHA256=c15cf0f3f17d714d1aa5a642da8e118db53d79429eb015771ba816aa7c6c1cbd
# Architecture-independent MinGW WinRT headers supplement Zig's SDK. Extract
# files only: do not install Ubuntu binaries or change Debian package resolution.
RUN curl --fail --location --retry 3 \
https://snapshot.ubuntu.com/ubuntu/20260828T000000Z/pool/universe/m/mingw-w64/mingw-w64-common_11.0.1-3build1_all.deb \
-o /tmp/mingw.deb \
&& printf '%s /tmp/mingw.deb\n' \
4a42fd30fbc3838c58c929d8a4882682fc12408bb7a0e6cafa913c0f0a57c187 | sha256sum --check \
&& mkdir /opt/mingw && dpkg-deb --extract /tmp/mingw.deb /opt/mingw \
&& rm /tmp/mingw.deb
RUN curl --fail --location --retry 3 \
https://github.com/joseluisq/macosx-sdks/releases/download/15.5/MacOSX15.5.sdk.tar.xz \
-o /tmp/macos-sdk.tar.xz \
&& printf '%s /tmp/macos-sdk.tar.xz\n' "$MACOS_SDK_SHA256" | sha256sum --check \
&& mkdir /opt/macos-sdk \
&& tar -xJf /tmp/macos-sdk.tar.xz -C /opt/macos-sdk \
&& rm /tmp/macos-sdk.tar.xz
RUN mkdir -p /opt/staticlink \
&& curl --fail --location --retry 3 \
https://raw.githubusercontent.com/llvm/llvm-project/llvmorg-19.1.7/compiler-rt/lib/builtins/os_version_check.c \
-o /opt/staticlink/os_version_check.c \
&& printf '%s /opt/staticlink/os_version_check.c\n' \
bc0b9f10e8d1a1ac5905d710f1c9fead23878e31233748d580393edc4e3bfc18 | sha256sum --check
ENTRYPOINT ["python3", "/opt/staticlink/probe-cross.py"]
COPY --from=dotnet8 /usr/share/dotnet /opt/dotnet
COPY --from=dotnet10 /usr/share/dotnet /opt/dotnet
ENV DOTNET_ROOT=/opt/dotnet PATH=/opt/dotnet:${PATH}
COPY fetch-linux-sysroots.py linux-sysroots*.lock.json /opt/staticlink/
RUN python3 /opt/staticlink/fetch-linux-sysroots.py --output /opt/sysroots \
--locks /opt/staticlink/linux-sysroots.lock.json /opt/staticlink/linux-sysroots-musl.lock.json
COPY *.py smoke-*.c sources.lock.json cross-toolchain.lock.json *.gn.in /opt/staticlink/
COPY patches/ /opt/staticlink/patches/
COPY versions/ /opt/staticlink/versions/
+20
View File
@@ -0,0 +1,20 @@
# Debian 12 Zig cross toolchain
The production native-build path is the pinned Debian 12 image plus Zig 0.14.1,
covering all nine GNU Linux, musl Linux, Windows and macOS targets. musl uses
checksum-locked target sysroots inside the same Debian image.
Read [CROSS-COMPILE.md](CROSS-COMPILE.md) for current build commands, exact local
validation scope and remaining Docker/runtime/NativeAOT verification. Read
[DAILY-RELEASE.md](../../DAILY-RELEASE.md) for daily 3/4 release configuration.
`Dockerfile` pins the base digest, apt snapshot and source-preparation tools.
`Dockerfile.cross-probe` adds Zig, macOS SDK, Windows headers, target Linux SDKs
and .NET SDKs. The historical filename is retained for existing references.
`zig-toolchain-image.yml` reuses recipe-tagged GHCR images and downstream jobs
pull their immutable digest. Source locks and archive/link manifests provide
traceability. No complete local Docker image build has been run.
Legacy platform helper scripts are retained for reference; production graphics
Actions now delegate to the common Zig cross-build workflow. SkiaSharp 2 remains
outside the new daily tracks.
+23
View File
@@ -0,0 +1,23 @@
target_os = "linux"
target_cpu = "@CPU@"
is_debug = false
is_component_build = false
is_clang = true
treat_warnings_as_errors = false
use_custom_libcxx = false
use_custom_libcxx_for_host = false
use_sysroot = false
use_lld = false
use_thin_lto = false
symbol_level = 0
angle_build_tests = false
build_angle_deqp_tests = false
angle_enable_swiftshader = false
angle_enable_vulkan = false
angle_enable_wgpu = false
clang_use_chrome_plugins = false
enable_rust = false
clang_version = "19"
# Chromium UI/ATK is not part of the standalone ANGLE library targets.
use_glib = false
+21
View File
@@ -0,0 +1,21 @@
target_os = "mac"
target_cpu = "@CPU@"
is_debug = false
is_component_build = false
is_clang = true
treat_warnings_as_errors = false
use_custom_libcxx = false
use_custom_libcxx_for_host = false
use_sysroot = false
use_lld = false
use_thin_lto = false
symbol_level = 0
angle_build_tests = false
build_angle_deqp_tests = false
angle_enable_swiftshader = false
angle_enable_vulkan = false
angle_enable_wgpu = false
clang_use_chrome_plugins = false
enable_rust = false
use_system_xcode = true
clang_version = "19"
+25
View File
@@ -0,0 +1,25 @@
target_os = "win"
target_cpu = "@CPU@"
is_debug = false
is_component_build = false
is_clang = true
treat_warnings_as_errors = false
use_custom_libcxx = false
use_custom_libcxx_for_host = false
use_sysroot = false
use_lld = false
use_thin_lto = false
symbol_level = 0
angle_build_tests = false
build_angle_deqp_tests = false
angle_enable_swiftshader = false
angle_enable_vulkan = false
angle_enable_wgpu = false
clang_use_chrome_plugins = false
enable_rust = false
visual_studio_version = "2022"
windows_sdk_version = "10.0.22621.0"
clang_version = "19"
use_clang_modules = false
angle_copy_compiler_dll = false
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""Build Avalonia's pinned macOS native sources with Zig on Linux."""
import argparse,subprocess,re,json,os,hashlib
from pathlib import Path
p=argparse.ArgumentParser();p.add_argument('--source',type=Path,required=True);p.add_argument('--rid',choices=['osx-x64','osx-arm64'],required=True);p.add_argument('--sdk',type=Path,required=True);p.add_argument('--zig',default='zig');p.add_argument('--output',type=Path,required=True);p.add_argument('--work',type=Path,required=True);a=p.parse_args()
a.source=a.source.resolve();a.output=a.output.resolve();a.work=a.work.resolve();a.output.mkdir(parents=True,exist_ok=True);a.work.mkdir(parents=True,exist_ok=True)
repo=Path(__file__).resolve().parents[2]
tools=a.work/'tools'
subprocess.run(['python3',str(repo/'build/linux-toolchain/zig-cross.py'),'--zig',a.zig,'--target',a.rid,'--sdk',str(a.sdk),'--output',str(tools)],check=True)
descriptor=tools/'toolchain.json'
config=json.loads(descriptor.read_text());config['target']=config['target'].split('-macos.')[0]+'-macos.12.0.0';descriptor.write_text(json.dumps(config,indent=2)+'\n')
native=a.source/'native/Avalonia.Native';project=native/'src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj'
headers=native/'inc'
if not (headers/'avalonia-native.h').is_file():
header_project=native/'Avalonia.Native.macOS.proj'
if header_project.is_file():
subprocess.run(['dotnet','build',str(header_project),'-t:GenerateMicroComItems'],cwd=a.source,check=True)
else:
subprocess.run(['bash',str(native/'generate-headers.sh')],cwd=a.source,check=True)
# Match the authoritative Xcode Sources build phases, rather than guessing every .mm in the repo.
project_text=project.read_text()
names=set(re.findall(r'/\* (.+?\.(?:mm|m|cpp|cc|c)) in Sources \*/',project_text))
file_flags={}
for name,flags in re.findall(r'/\* (.+?\.(?:mm|m|cpp|cc|c)) in Sources \*/[^\n]*COMPILER_FLAGS = \"([^\"]+)\"',project_text):
import shlex
file_flags[name]=shlex.split(flags)
if not names:raise SystemExit('No authoritative Xcode source list found')
objects=[];source_hashes={}
for name in sorted(names):
matches=list(native.rglob(name))
if len(matches)!=1:raise SystemExit('Ambiguous or missing Xcode source '+name+': '+str(matches))
source=matches[0];obj=a.work/(hashlib.sha256(str(source.relative_to(native)).encode()).hexdigest()+'.o')
command=[str(tools/('cc' if source.suffix in ('.c','.m') else 'cxx')),'-O2','-fPIC','-fblocks','-mmacosx-version-min=12.0','-I'+str(headers),'-I'+str(native/'src/OSX'),'-I'+str(native/'src'),'-c',str(source),'-o',str(obj)]
if source.suffix in ('.mm','.m'):command+=['-fobjc-arc']
if source.suffix in ('.mm','.cpp','.cc'):command+=['-std=c++17']
command+=file_flags.get(name,[])
subprocess.run(command,check=True);objects.append(obj);source_hashes[str(source.relative_to(native))]=hashlib.sha256(source.read_bytes()).hexdigest()
subprocess.run([str(tools/'ar'),'rcs',str(a.output/'libAvaloniaNative.a'),*map(str,objects)],check=True)
# The macOS font pre-initializer is compiled by the same cross compiler.
env=dict(os.environ,RID=a.rid,OUTPUT_DIR=str(a.output),CC=str(tools/'cc'),STATICLINK_ZIG_CROSS='1')
subprocess.run(['bash',str(repo/'scripts/build-macos-font-preinit.sh')],env=env,check=True)
commit=subprocess.check_output(['git','-C',str(a.source),'rev-parse','HEAD'],text=True).strip()
(a.output/'avalonia-native-build.json').write_text(json.dumps({'source_commit':commit,'rid':a.rid,'toolchain':json.loads((tools/'toolchain.json').read_text()),'sources':source_hashes,'validation':'compiled archive; macOS AOT/runtime validation runs separately'},indent=2)+'\n')
+242
View File
@@ -0,0 +1,242 @@
#!/usr/bin/env python3
"""Build pinned, already dependency-synced SkiaSharp/ANGLE sources from Linux."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import platform
import shutil
import subprocess
import sys
from skia_compat import archive, configure, parse_args, repair_xps, repair_skcms
HERE = Path(__file__).resolve().parent
def run(args, cwd=None, env=None):
print('+', ' '.join(map(str, args)), flush=True)
subprocess.run(list(map(str, args)), cwd=cwd, env=env, check=True)
def patch(source, filename):
path = HERE / 'patches' / filename
cmd = ['git', '-C', str(source), 'apply']
if subprocess.run(cmd + ['--reverse', '--check', str(path)], capture_output=True).returncode == 0:
return
run(cmd + ['--check', path])
run(cmd + [path])
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--target', choices=['linux-x64', 'linux-arm64', 'linux-musl-x64', 'linux-musl-arm64', 'win-x64', 'win-x86', 'win-arm64', 'osx-x64', 'osx-arm64'], required=True)
p.add_argument('--sources-manifest', type=Path, help='cross-sources.json from prepare-cross-sources.sh')
p.add_argument('--source-lock', type=Path, help='Exact version profile; defaults to sources.lock.json')
p.add_argument('--skia', type=Path, help='SkiaSharp externals/skia, with its pinned dependencies synced')
p.add_argument('--angle', type=Path, help='ANGLE source with pinned DEPS synced, including tools/win')
p.add_argument('--angle-gn', type=Path, help='GN from the pinned ANGLE DEPS')
p.add_argument('--zig', default='zig')
p.add_argument('--sdk', type=Path, default=Path('/opt/macos-sdk/MacOSX15.5.sdk'))
p.add_argument('--mingw-headers', type=Path, default=Path('/opt/mingw/usr/share/mingw-w64/include'))
p.add_argument('--availability-source', type=Path, default=Path('/opt/staticlink/os_version_check.c'))
p.add_argument('--linux-sysroots', type=Path, default=Path('/opt/sysroots'))
p.add_argument('--linux-headers', type=Path, default=Path('/opt/staticlink/linux-headers'))
p.add_argument('--work', type=Path, required=True)
p.add_argument('--output', type=Path, required=True)
p.add_argument('--jobs', type=int, default=4)
p.add_argument('--configure-only', action='store_true')
p.add_argument('--component', choices=['all', 'skia'], default='all', help='Build Skia alone when validating a new SkiaSharp release')
a = p.parse_args()
if a.sources_manifest:
sources = json.loads(a.sources_manifest.read_text())
if a.source_lock is None and sources.get('source_lock'):
a.source_lock = Path(sources['source_lock'])
for name in ('skia', 'angle', 'angle_gn'):
if getattr(a, name) is None:
setattr(a, name, Path(sources[name]))
if any(getattr(a, name) is None for name in ('skia', 'angle', 'angle_gn')):
p.error('Provide --sources-manifest or all of --skia, --angle and --angle-gn')
if platform.system() != 'Linux' or a.jobs < 1:
p.error('Requires Linux and positive --jobs')
for name in ('skia', 'angle', 'angle_gn', 'sdk', 'work', 'output', 'availability_source', 'mingw_headers'):
setattr(a, name, getattr(a, name).resolve())
lock = json.loads((a.source_lock or HERE / 'sources.lock.json').read_text())
for src, key in ((a.skia, 'skia_revision'), (a.angle, 'angle_revision')):
revision = subprocess.check_output(['git', '-C', str(src), 'rev-parse', 'HEAD'], text=True).strip()
if revision != lock[key]:
raise SystemExit(f'{src}: expected {lock[key]}, got {revision}; update the lock and revalidate first')
if 'skiasharp_build("SkiaSharp")' not in (a.skia / 'BUILD.gn').read_text():
raise SystemExit('--skia must be the SkiaSharp-patched Skia source, not vanilla Google Skia')
a.work.mkdir(parents=True, exist_ok=True)
a.output.mkdir(parents=True, exist_ok=True)
windows = a.target.startswith('win')
mac = a.target.startswith('osx')
linux = a.target.startswith('linux')
cpu = a.target.rsplit('-', 1)[1]
os_name = 'win' if windows else ('mac' if mac else 'linux')
if windows and not (a.mingw_headers / 'windows.ui.composition.h').is_file():
raise SystemExit('Windows ANGLE requires --mingw-headers containing the pinned WinRT headers')
recipe = hashlib.sha256()
for path in sorted(HERE.rglob('*')):
if path.is_file() and (path.suffix in ('.py', '.in', '.patch', '.c') or path.name in ('cross-toolchain.lock.json', 'linux-sysroots.lock.json', 'linux-sysroots-musl.lock.json')):
recipe.update(str(path.relative_to(HERE)).encode())
recipe.update(path.read_bytes())
variant = hashlib.sha256((json.dumps(lock, sort_keys=True) + recipe.hexdigest()).encode()).hexdigest()[:16]
shared = {'angle_revision': lock['angle_revision'], 'recipe': recipe.hexdigest(), 'toolchain': json.loads((HERE / 'cross-toolchain.lock.json').read_text())}
angle_variant = hashlib.sha256(json.dumps(shared, sort_keys=True).encode()).hexdigest()[:16]
tools = a.work / angle_variant / a.target
host_cpu = 'arm64' if platform.machine() == 'aarch64' else 'x64'
host_tools = a.work / angle_variant / ('linux-' + host_cpu)
for target, output in ((a.target, tools), ('linux-' + host_cpu, host_tools)):
command = [sys.executable, HERE / 'zig-cross.py', '--zig', a.zig, '--target', target,
'--output', output, '--sdk', a.sdk, '--mingw-headers', a.mingw_headers]
if target.startswith('linux'):
command += ['--linux-sysroot', a.linux_sysroots / target, '--linux-headers', a.linux_headers]
run(command)
config = json.loads((tools / 'toolchain.json').read_text())
zig = config['zig']
zig_lib = Path(json.loads(subprocess.check_output([zig, 'env'], text=True))['lib_dir'])
print('skcms Clang compatibility:', repair_skcms(a.skia), flush=True)
if windows:
print('XPS compatibility:', repair_xps(a.skia), flush=True)
# First apply the project's static ANGLE target patch if necessary.
if 'angle_static_library("libANGLE_static")' not in (a.angle / 'BUILD.gn').read_text():
patch(a.angle, 'angle-static-targets.patch')
for filename in ('BUILDCONFIG.gn-zig.patch', 'BUILD.gn-zig.patch',
'sdk_info-linux.patch', 'find_sdk-linux.patch', 'gcc-toolchain-objc.patch'):
patch(a.angle, filename)
run([sys.executable, a.angle / 'build/util/lastchange.py', '-o', a.angle / 'build/util/LASTCHANGE'], cwd=a.angle)
# Separate runtime paths prevent architectures from sharing an incompatible cache.
clang = tools / 'clang'
runtime = clang / 'lib/clang/19/lib' / ('windows' if windows else ('darwin' if mac else 'linux')) / (
'clang_rt.builtins-' + {'x64': 'x86_64', 'x86': 'i386', 'arm64': 'aarch64'}[cpu] + '.lib'
if windows else ('libclang_rt.osx.a' if mac else 'libclang_rt.builtins-' + ('x86_64' if cpu == 'x64' else 'aarch64') + '.a'))
runtime.parent.mkdir(parents=True, exist_ok=True)
run([zig, 'build-lib', zig_lib / 'compiler_rt.zig', '-target', config['target'],
'-O', 'ReleaseFast', '-fPIC', '-femit-bin=' + str(runtime)])
if linux:
for runtime_cpu, runtime_target in ((cpu, config['target']), (host_cpu, json.loads((host_tools / 'toolchain.json').read_text())['target'])):
triple = 'x86_64-unknown-linux-gnu' if runtime_cpu == 'x64' else 'aarch64-unknown-linux-gnu'
alias = clang / 'lib/clang/19/lib' / triple / 'libclang_rt.builtins.a'
alias.parent.mkdir(parents=True, exist_ok=True)
run([zig, 'build-lib', zig_lib / 'compiler_rt.zig', '-target', runtime_target,
'-O', 'ReleaseFast', '-fPIC', '-femit-bin=' + str(alias)])
availability = None
if mac:
if hashlib.sha256(a.availability_source.read_bytes()).hexdigest() != 'bc0b9f10e8d1a1ac5905d710f1c9fead23878e31233748d580393edc4e3bfc18':
raise SystemExit('LLVM availability source checksum mismatch')
availability = tools / 'os_version_check.o'
run([tools / 'cc', '-c', a.availability_source, '-o', availability])
run([tools / 'ar', 'rcs', runtime, availability])
env = os.environ.copy()
env['PATH'] = str(tools) + os.pathsep + env['PATH']
env['CCACHE_BASEDIR'] = str(a.skia)
env['CCACHE_NOHASHDIR'] = 'true'
env['CCACHE_COMPILERCHECK'] = 'content'
env['STATICLINK_MACOS_SDK'] = str(a.sdk)
env['STATICLINK_DARWIN_TOOLS'] = str(tools)
skia_out = a.skia / 'out' / ('staticlink-zig-' + a.target + '-' + variant)
angle_out = a.angle / 'out' / ('staticlink-zig-' + a.target + '-' + angle_variant)
for out in (skia_out, angle_out):
out.mkdir(parents=True, exist_ok=True)
skia_args = (HERE / ('skia-' + os_name + '.gn.in')).read_text().replace('@CPU@', cpu)
if mac:
skia_args += f'min_macos_version = "{"11.0" if cpu == "arm64" else "10.13"}"\n'
skia_args += f'xcode_sysroot = {json.dumps(str(a.sdk))}\n'
for name in ('cc', 'cxx', 'ar'):
skia_args += name + ' = ' + json.dumps(str(tools / name)) + '\n'
if linux:
for key in ('cc', 'cxx', 'ar'):
skia_args = skia_args.replace('@' + key.upper() + '@', str(tools / key))
capabilities = configure(a.skia, skia_out, parse_args(skia_args), a.skia / 'bin/gn', env)
for target in ('skia', 'SkiaSharp', 'HarfBuzzSharp'):
description = json.loads(subprocess.check_output([str(a.skia / 'bin/gn'), 'desc', str(skia_out.relative_to(a.skia)),
'//:' + target, '--format=json'], cwd=a.skia, env=env, text=True))
kind = next(iter(description.values()))['type']
if kind != 'static_library':
raise SystemExit(f'{target} is {kind}; expected static_library')
if windows:
# Upstream Skia fixes Windows to the MSVC rules; retain its graph and
# replace compiler/archive commands explicitly with the GNU adapters.
path = skia_out / 'toolchain.ninja'
body = path.read_text()
index = body.index('subninja ')
rules = ''
for name, flags in (('cc', 'cflags_c'), ('cxx', 'cflags_cc')):
rules += f'rule {name}\n command = {tools / name} -MD -MF ${{out}}.d ${{defines}} ${{include_dirs}} ${{cflags}} ${{{flags}}} -c ${{in}} -o ${{out}}\n depfile = ${{out}}.d\n deps = gcc\n'
rules += f'rule alink\n command = {tools / "ar"} rcs ${{out}} @${{out}}.rsp\n rspfile = ${{out}}.rsp\n rspfile_content = ${{in_newline}}\n'
rules += 'rule stamp\n command = touch ${out}\nrule copy\n command = cp ${in} ${out}\nrule asm\n command = false\n'
path.write_text(rules + '\n' + body[index:])
sdk_link = angle_out / 'sdk/xcode_links/MacOSX15.5.sdk'
if mac:
sdk_link.parent.mkdir(parents=True, exist_ok=True)
if sdk_link.is_symlink():
sdk_link.unlink()
sdk_link.symlink_to(a.sdk)
# Each architecture gets an independent toolchain file; concurrent builds
# do not rewrite another target's generated GN toolchain.
toolchain_dir = a.angle / ('staticlink_zig_' + a.target.replace('-', '_') + '_' + angle_variant)
toolchain_dir.mkdir(exist_ok=True)
text = 'import("//build/toolchain/gcc_toolchain.gni")\n'
for name, folder, target_os, target_cpu in (('target', tools, os_name, cpu), ('host', host_tools, 'linux', host_cpu)):
text += f'gcc_toolchain("{name}") {{\n'
for var in ('cc', 'cxx', 'ar'):
text += ' ' + var + ' = ' + json.dumps(str(folder / var)) + '\n'
text += f' ld = cxx\n nm = "nm"\n readelf = "readelf"\n toolchain_args = {{ current_os = "{target_os}" current_cpu = "{target_cpu}" is_clang = true use_remoteexec = false }}\n}}\n'
(toolchain_dir / 'BUILD.gn').write_text(text)
angle_args = (HERE / ('angle-' + os_name + '.gn.in')).read_text().replace('@CPU@', cpu)
angle_args += f'custom_toolchain = "//{toolchain_dir.name}:target"\nhost_toolchain = "//{toolchain_dir.name}:host"\n'
angle_args += 'clang_base_path = ' + json.dumps(str(clang)) + '\n'
if linux:
angle_args += 'pkg_config = ' + json.dumps(str(tools / 'pkg-config')) + '\n'
angle_args += 'host_pkg_config = ' + json.dumps(str(host_tools / 'pkg-config')) + '\n'
if windows:
# These mandatory GN metadata paths refer to the real MinGW headers.
# The SDK/version strings satisfy GN; MSVC/Windows SDK are not used.
for name, path in (('visual_studio_path', Path(zig).parent), ('wdk_path', Path(zig).parent),
('windows_sdk_path', zig_lib / 'libc/include/any-windows-any')):
angle_args += name + ' = ' + json.dumps(str(path)) + '\n'
(angle_out / 'args.gn').write_text(angle_args)
run([a.angle_gn, 'gen', angle_out.relative_to(a.angle)], cwd=a.angle, env=env)
if a.configure_only:
print(f'Configured {lock["skiasharp_version"]} {a.target}: all three Skia targets are static libraries')
return
run(['ninja', '-C', skia_out, '-j', a.jobs, 'skia', 'SkiaSharp', 'HarfBuzzSharp'], env=env)
if a.component == 'all':
run(['ninja', '-C', angle_out, '-j', a.jobs, 'libANGLE_static', 'libGLESv2_static', 'libEGL_static'], env=env)
if availability:
run([tools / 'ar', 'rcs', archive(skia_out, 'skia'), availability])
files = [(archive(skia_out, name), name)
for name in ('skia', 'SkiaSharp', 'HarfBuzzSharp')]
if a.component == 'all':
files += [(angle_out / ('obj/lib' + name + '_static.a'), name) for name in ('ANGLE', 'GLESv2', 'EGL')]
manifest = dict(target=a.target, sources=lock, toolchain=config, archives={})
manifest['build_variant'] = variant
manifest['skia_capabilities'] = capabilities
if linux:
manifest['linux_sdk_packages'] = json.loads((HERE / ('linux-sysroots-musl.lock.json' if 'musl' in a.target else 'linux-sysroots.lock.json')).read_text())[a.target]
manifest['toolchain_inputs'] = json.loads((HERE / 'cross-toolchain.lock.json').read_text())
manifest['gn_sha256'] = {str(path): hashlib.sha256(path.read_bytes()).hexdigest()
for path in (a.skia / 'bin/gn', a.angle_gn)}
manifest['recipe_sha256'] = {str(path.relative_to(HERE)): hashlib.sha256(path.read_bytes()).hexdigest()
for path in sorted(HERE.rglob('*')) if path.is_file() and (path.suffix in ('.py', '.in', '.patch', '.c') or path.name in ('cross-toolchain.lock.json', 'linux-sysroots.lock.json', 'linux-sysroots-musl.lock.json'))}
packages = Path('/opt/staticlink/packages.lock')
if packages.is_file():
shutil.copy2(packages, a.output / 'toolchain-packages.lock')
manifest['packages_sha256'] = hashlib.sha256(packages.read_bytes()).hexdigest()
for source, name in files:
dest = a.output / ('lib' + name + '.a')
shutil.copy2(source, dest)
manifest['archives'][dest.name] = hashlib.sha256(dest.read_bytes()).hexdigest()
run([sys.executable, HERE / 'link-cross.py', '--toolchain', tools, '--skia', a.skia,
'--angle', a.angle, '--libraries', a.output, '--output', a.output / 'link-tests', '--component', a.component])
if a.target == 'win-x86':
run([sys.executable, HERE / 'x86-thunks.py', '--source', a.skia.parent.parent,
'--toolchain', tools, '--libraries', a.output])
manifest['link_tests'] = 'passed; target runtime tests still required'
(a.output / 'cross-build.json').write_text(json.dumps(manifest, indent=2) + '\n')
if __name__ == '__main__':
main()
@@ -0,0 +1,10 @@
{
"zig_version": "0.14.1",
"macos_sdk_version": "15.5",
"macos_sdk_sha256": "c15cf0f3f17d714d1aa5a642da8e118db53d79429eb015771ba816aa7c6c1cbd",
"mingw_headers_version": "11.0.1-3build1",
"mingw_package_sha256": "4a42fd30fbc3838c58c929d8a4882682fc12408bb7a0e6cafa913c0f0a57c187",
"llvm_availability_revision": "llvmorg-19.1.7",
"llvm_availability_sha256": "bc0b9f10e8d1a1ac5905d710f1c9fead23878e31233748d580393edc4e3bfc18",
"angle_gn_revision": "e331ddb6e93389abfc75c611690bb82f5274029e"
}
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""Materialize target SDK libraries from immutable, checksum-locked packages."""
import argparse,json,hashlib,urllib.request,tarfile,subprocess
from pathlib import Path
p=argparse.ArgumentParser();p.add_argument('--output',type=Path,required=True);p.add_argument('--locks',type=Path,nargs='+',required=True);a=p.parse_args()
for lock in a.locks:
for rid,packages in json.loads(lock.read_text()).items():
dest=a.output/rid;dest.mkdir(parents=True,exist_ok=True)
for package in packages:
with urllib.request.urlopen(package['url'],timeout=90) as response:data=response.read()
if hashlib.sha256(data).hexdigest()!=package['sha256']:raise SystemExit('SDK checksum mismatch: '+package['name'])
path=a.output/(rid+'-'+Path(package['url']).name);path.write_bytes(data)
if path.suffix=='.deb':subprocess.run(['dpkg-deb','--extract',str(path),str(dest)],check=True)
else:
with tarfile.open(path,mode='r:gz',ignore_zeros=True) as tar:
members=[m for m in tar.getmembers() if m.name.startswith(('lib/','usr/lib/','usr/include/','etc/fonts/'))]
for m in members:
if m.issym() and m.linkname.startswith('/'):
import posixpath
m.linkname=posixpath.relpath(m.linkname.lstrip('/'),posixpath.dirname(m.name))
# Debian 12's pinned Python 3.11 has no extraction-filter API.
# Validate paths and links explicitly before extracting locked APK files.
root=dest.resolve()
for m in members:
target=(root/m.name).resolve()
if not target.is_relative_to(root):raise SystemExit('Unsafe SDK archive path: '+m.name)
if not (m.isfile() or m.isdir() or m.issym() or m.islnk()):raise SystemExit('Unsupported SDK archive entry: '+m.name)
if m.issym() or m.islnk():
link=(target.parent/m.linkname if m.issym() else root/m.linkname).resolve()
if not link.is_relative_to(root):raise SystemExit('Unsafe SDK archive link: '+m.name)
tar.extractall(dest,members=members)
path.unlink()
print(rid,package['name'],package['version'],flush=True)
# Copy only platform headers; never replace Zig's target libc headers with the host's.
import shutil
headers=a.output.parent/'staticlink/linux-headers';headers.mkdir(parents=True,exist_ok=True)
for name in ('fontconfig','X11','xcb','GL','KHR','EGL','freetype2','libdrm'):
src=a.output/'linux-x64/usr/include'/name
if src.is_dir():shutil.copytree(src,headers/name,dirs_exist_ok=True,symlinks=True)
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""Link raster/HarfBuzz and EGL/GLES executables against cross-built archives."""
import argparse
import json
from pathlib import Path
import subprocess
import re
HERE = Path(__file__).resolve().parent
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--toolchain', type=Path, required=True)
p.add_argument('--skia', type=Path, required=True)
p.add_argument('--angle', type=Path, required=True)
p.add_argument('--libraries', type=Path, required=True)
p.add_argument('--output', type=Path, required=True)
p.add_argument('--component', choices=['all', 'skia'], default='all')
a = p.parse_args()
config = json.loads((a.toolchain / 'toolchain.json').read_text())
windows = 'windows' in config['target']
a.output.mkdir(parents=True, exist_ok=True)
target = config['target']
flags = []
mac = 'macos' in target
linux = 'linux' in target
if mac:
# ANGLE's pinned Chromium build has a macOS 13 deployment minimum.
target = target.split('-macos.')[0] + '-macos.13.0'
sdk = config['sdk']
flags = ['-isysroot', sdk, '-L' + sdk + '/usr/lib', '-F' + sdk + '/System/Library/Frameworks']
for name in (('raster', 'angle') if a.component == 'all' else ('raster',)):
obj = a.output / ('smoke-' + name + '.o')
includes = [a.skia, a.skia / 'third_party/externals/harfbuzz/src', a.angle / 'include']
command = [str(a.toolchain / 'cc')]
if name == 'angle':
command.extend(('-DANGLE_STATIC=1', '-DKHRONOS_STATIC'))
else:
header = (a.skia / 'include/core/SkMilestone.h').read_text()
match = re.search(r'^\s*#define\s+SK_MILESTONE\s+(\d+)', header, re.MULTILINE)
if not match:
raise SystemExit('Cannot discover the Skia milestone; inspect the new source layout')
command.append('-DSTATICLINK_EXPECTED_MILESTONE=' + match[1])
command += ['-I' + str(path) for path in includes]
command += ['-c', str(HERE / ('smoke-' + name + '.c')), '-o', str(obj)]
subprocess.run(command, check=True)
libs = ('SkiaSharp', 'skia', 'HarfBuzzSharp') if name == 'raster' else ('EGL', 'GLESv2', 'ANGLE')
command = [config['zig'], 'c++', '-target', target, '-O2', *flags, str(obj)]
command += [str(a.libraries / ('lib' + library + '.a')) for library in libs]
if windows:
command += ['-l' + lib for lib in ('dwrite', 'gdi32', 'ole32', 'oleaut32', 'user32', 'uuid',
'windowscodecs', 'dxguid', 'dxgi', 'd3d11', 'd3d9', 'dwmapi', 'shlwapi', 'setupapi', 'version', 'advapi32', 'api-ms-win-core-synch-l1-2-0')]
elif mac:
for framework in ('CoreFoundation', 'CoreGraphics', 'CoreText', 'Foundation', 'AppKit',
'Metal', 'QuartzCore', 'OpenGL', 'IOKit', 'IOSurface'):
command += ['-framework', framework]
command += ['-lobjc']
availability = a.toolchain / 'os_version_check.o'
if availability.is_file():
command.append(str(availability))
elif linux:
sysroot = Path(config.get('linux_sysroot') or '/')
for path in (sysroot / 'usr/lib', sysroot / 'lib', sysroot / 'usr/lib' / ('x86_64-linux-gnu' if target.startswith('x86_64') else 'aarch64-linux-gnu')):
if path.is_dir():
command += ['-L' + str(path)]
command += ['-lfontconfig', '-ldl', '-lm', '-lpthread'] if name == 'raster' else ['-lX11', '-lXext', '-lxcb', '-lwayland-client', '-lwayland-egl', '-lffi', '-ldl', '-lm', '-lpthread']
command += ['-v']
command += ['-o', str(a.output / ('smoke-' + name + ('.exe' if windows else '')))]
result = subprocess.run(command, capture_output=True, text=True)
print(result.stdout, end='')
print(result.stderr, end='')
result.check_returncode()
# Keep the exact runtime archives chosen by this Zig target. AOT consumes
# these without substituting the consumer machine's C++ toolchain.
import shlex, shutil
runtimes = {}
project_archives = {path.resolve() for path in a.libraries.glob('lib*.a')}
for line in (result.stdout + '\n' + result.stderr).splitlines():
try:
tokens = shlex.split(line)
except ValueError:
continue
for token in tokens:
path = Path(token)
if path.suffix in ('.a', '.lib') and path.is_file() and path.resolve() not in project_archives and not path.resolve().is_relative_to(a.libraries.resolve()):
dest = a.libraries / 'zig-runtime' / path.name
dest.parent.mkdir(exist_ok=True)
shutil.copy2(path, dest)
runtimes[path.name] = str(path)
descriptor = a.libraries / 'zig-runtime' / 'inputs.json'
if descriptor.exists():
runtimes = dict(json.loads(descriptor.read_text()), **runtimes)
descriptor.parent.mkdir(exist_ok=True)
descriptor.write_text(json.dumps(runtimes, indent=2) + '\n')
print('Executable link tests passed. Run them on the target OS to validate behavior.')
if __name__ == '__main__':
main()
@@ -0,0 +1,474 @@
{
"linux-musl-x64": [
{
"name": "brotli-dev",
"version": "1.2.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/brotli-dev-1.2.0-r0.apk",
"sha256": "f61bd37140c76a22e97ad9d7461becda5d41526bbee1a8bc2523edad5ae6f35e"
},
{
"name": "brotli-libs",
"version": "1.2.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/brotli-libs-1.2.0-r0.apk",
"sha256": "09fbf1ddf0c71c6c4ab7bc12aef3452d8ace5adeab10c4c30214fc70f5e0e087"
},
{
"name": "bzip2-dev",
"version": "1.0.8-r6",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/bzip2-dev-1.0.8-r6.apk",
"sha256": "59a73f8712a9c0c16553a759149ee5ecf87a6160ef021aeb922ddb0c0b3a695b"
},
{
"name": "expat",
"version": "2.8.5-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/expat-2.8.5-r0.apk",
"sha256": "19f3d19d7deb82e357a70cb30b38dd81fa1cbecdb7e0a834071e993d5df18246"
},
{
"name": "expat-dev",
"version": "2.8.5-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/expat-dev-2.8.5-r0.apk",
"sha256": "5a31977673fbdacf195d372114501c06b18eb1f24553c99b92e47e0e24478f9e"
},
{
"name": "fontconfig",
"version": "2.17.1-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/fontconfig-2.17.1-r0.apk",
"sha256": "f44cea53076f18428dad0c27c75ca717ebfc3d2f0f4c8d4428ce70b62e25adf1"
},
{
"name": "fontconfig-dev",
"version": "2.17.1-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/fontconfig-dev-2.17.1-r0.apk",
"sha256": "e819dd0eb3f2a2a4f802a16911959e1232b323c4e2db9ce020c3d52e9bfd5053"
},
{
"name": "freetype",
"version": "2.14.3-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/freetype-2.14.3-r0.apk",
"sha256": "670c9922ab4eb9df65c0e4ad7839bbe3f486016fc1fb637c46d657b74d99a24f"
},
{
"name": "freetype-dev",
"version": "2.14.3-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/freetype-dev-2.14.3-r0.apk",
"sha256": "85a90244c039722c409996e5e5e65d4751664984ed1843a00c5d751577627972"
},
{
"name": "libbsd",
"version": "0.12.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libbsd-0.12.2-r0.apk",
"sha256": "1252092dea853063252bbdaf9fa604819055b004ba3114b88b842fbfe44659e9"
},
{
"name": "libbz2",
"version": "1.0.8-r6",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libbz2-1.0.8-r6.apk",
"sha256": "9995840a76ac97ec006259391b7a81b73b73c5cf2f462bf12bb2250afbf665b9"
},
{
"name": "libexpat",
"version": "2.8.5-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libexpat-2.8.5-r0.apk",
"sha256": "5216046085b92da88a6b107dbbb558f69d49c347a6335291fcf199ebd1670a8f"
},
{
"name": "libffi",
"version": "3.5.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libffi-3.5.2-r0.apk",
"sha256": "0b3473a1468bed5fbb913a1ffa121e253080b21b013fe47e0300bd7e85224e76"
},
{
"name": "libffi-dev",
"version": "3.5.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libffi-dev-3.5.2-r0.apk",
"sha256": "737a9109fc4a763465cc117ca20edd18d4ea3fe9ed6f717251aa744b8ebbaba6"
},
{
"name": "libmd",
"version": "1.1.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libmd-1.1.0-r0.apk",
"sha256": "704446100fb39f2699d848d97f09463dd6fdd35f3a1acb038483712e1e37f766"
},
{
"name": "libpng",
"version": "1.6.59-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libpng-1.6.59-r0.apk",
"sha256": "148a10e3d6419f787b16edbc2d0bb19d12a2a078225baaa416c895c14f47fc1e"
},
{
"name": "libpng-dev",
"version": "1.6.59-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libpng-dev-1.6.59-r0.apk",
"sha256": "01b89d3cea124fed78359b71f77e4d545de6e3163077167947e6adf84bdcb595"
},
{
"name": "libx11",
"version": "1.8.12-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libx11-1.8.12-r1.apk",
"sha256": "cbc478a642226812ab13688bfe9ec69c86cf0e8e037dba7cfd055e8d7741f30c"
},
{
"name": "libx11-dev",
"version": "1.8.12-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libx11-dev-1.8.12-r1.apk",
"sha256": "91eac12015dffbeb06b2755eae52722b1740018d775db33fbe68abc353b951df"
},
{
"name": "libxau",
"version": "1.0.12-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libxau-1.0.12-r0.apk",
"sha256": "61ee983ec9d47821cae9b3e1c8245a5b0b228361839be9b596a42c817b691a9f"
},
{
"name": "libxau-dev",
"version": "1.0.12-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libxau-dev-1.0.12-r0.apk",
"sha256": "2af8a78f1deaf398e96964f17d5640e3448fc5e935392e3550283e8d7c54a6cc"
},
{
"name": "libxcb",
"version": "1.17.0-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libxcb-1.17.0-r1.apk",
"sha256": "f73934506edfdcb6895f04e313481ca48592ac4cec59cdb8fc073a7681bfbd31"
},
{
"name": "libxcb-dev",
"version": "1.17.0-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libxcb-dev-1.17.0-r1.apk",
"sha256": "8735d97a346eae3999a599e49eb533481478d0ef9466e6e5a5453dd1e3ae8f3d"
},
{
"name": "libxdmcp",
"version": "1.1.5-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libxdmcp-1.1.5-r1.apk",
"sha256": "f134d28babb070834066aa716c303768ed0ede32ac086f3dfbd8cbbac62380ee"
},
{
"name": "libxdmcp-dev",
"version": "1.1.5-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libxdmcp-dev-1.1.5-r1.apk",
"sha256": "d8a304762ea4295287ef1adf9d93d64f04bdfc4e845b453698b3276b12e8fc10"
},
{
"name": "libxext",
"version": "1.3.6-r2",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libxext-1.3.6-r2.apk",
"sha256": "404beb855daf09fdb8d15f177a3608da62825a44497a805675a186171c04eb6c"
},
{
"name": "libxext-dev",
"version": "1.3.6-r2",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libxext-dev-1.3.6-r2.apk",
"sha256": "ebb6262372a918a6bee4ff2137dacdb1fa13be78d0e3544a082d6530ce8e05b6"
},
{
"name": "libxml2",
"version": "2.13.9-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/libxml2-2.13.9-r1.apk",
"sha256": "1af363248efb54914993bbd516c89878c2b5b0c53be0c78bb43d5a3e9a8ceda6"
},
{
"name": "mesa-dev",
"version": "25.2.7-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/mesa-dev-25.2.7-r1.apk",
"sha256": "da17f767ae27fc8ff9a958ff82d217ee3a890d888b43e6a62171a402b48156a1"
},
{
"name": "musl",
"version": "1.2.5-r23",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/musl-1.2.5-r23.apk",
"sha256": "4f3c4a7bf9f51d2c91007e333b17459362ffd881b4a343e8da07b6c50c4f4a0d"
},
{
"name": "wayland-dev",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/wayland-dev-1.24.0-r0.apk",
"sha256": "5afcb3e3c414d2c0e602bc9ef1b4868fd7df9e53aec55f3f722ce4897019f014"
},
{
"name": "wayland-libs-client",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/wayland-libs-client-1.24.0-r0.apk",
"sha256": "020313c601f85a9e91d1ae0d01e9f1e9905d1127b78dce9c278a75cfd52416d2"
},
{
"name": "wayland-libs-cursor",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/wayland-libs-cursor-1.24.0-r0.apk",
"sha256": "e87fb03656132b550182b019b7f3218f336ff9637dc574ef0f22a0ad148f8577"
},
{
"name": "wayland-libs-egl",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/wayland-libs-egl-1.24.0-r0.apk",
"sha256": "2d9165a79e7e779b52dd203959f64eb8f7caad460f3e6d76097efc432e115936"
},
{
"name": "wayland-libs-server",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/wayland-libs-server-1.24.0-r0.apk",
"sha256": "c30fd5c6f081732670e7dd69316df73cb3939b8c53237314ca1ec3f991b2a1f7"
},
{
"name": "xorgproto",
"version": "2024.1-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/xorgproto-2024.1-r0.apk",
"sha256": "96e2b99a0227d301170ae840c37693d65c767dd0a15a1243ced45325850ad424"
},
{
"name": "xz-libs",
"version": "5.8.4-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/xz-libs-5.8.4-r0.apk",
"sha256": "3c8cc415e88c2a097543d9ee828081ec1905e7d0f57a319c8896269b0e00a672"
},
{
"name": "zlib",
"version": "1.3.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/zlib-1.3.2-r0.apk",
"sha256": "f56dea63692059bd65854bb179b7551971a441000b0d98c5b031291ca0450b56"
},
{
"name": "zlib-dev",
"version": "1.3.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/zlib-dev-1.3.2-r0.apk",
"sha256": "c811e48ea401e05479a74d93f8ac820bbb6af44b2adf0335769316e5cc2b72c4"
}
],
"linux-musl-arm64": [
{
"name": "brotli-dev",
"version": "1.2.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/brotli-dev-1.2.0-r0.apk",
"sha256": "012c9917e3a3f37efbb255fdff240d4fe0ee83a2e4e5d7511eddcce57a2daa30"
},
{
"name": "brotli-libs",
"version": "1.2.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/brotli-libs-1.2.0-r0.apk",
"sha256": "c8a9113b0007d40291e55cfc196744b64004253707e37b3c07e188166fb2d0a0"
},
{
"name": "bzip2-dev",
"version": "1.0.8-r6",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/bzip2-dev-1.0.8-r6.apk",
"sha256": "69d79bb497c25e81fce8695653f448666c08bc8b28fa51506b74b38946a0a03a"
},
{
"name": "expat",
"version": "2.8.5-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/expat-2.8.5-r0.apk",
"sha256": "b5b520d2bd7a89e8fbad6a59667f70d4656326dd8a4df84c3e5ef4b5c0056243"
},
{
"name": "expat-dev",
"version": "2.8.5-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/expat-dev-2.8.5-r0.apk",
"sha256": "2aad86f9f50aeb5b3c7a0e4022693dd810f51ec9c7a28037f3d2e71dbd9ecb1e"
},
{
"name": "fontconfig",
"version": "2.17.1-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/fontconfig-2.17.1-r0.apk",
"sha256": "38e815b8f898482e3c8111ac158208ba0a6a9aa13dbef49ccc36b36df77c793e"
},
{
"name": "fontconfig-dev",
"version": "2.17.1-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/fontconfig-dev-2.17.1-r0.apk",
"sha256": "5661ba91c9da743151b333a69b05f467f5c4145395f3d54567e7e9999aa8d842"
},
{
"name": "freetype",
"version": "2.14.3-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/freetype-2.14.3-r0.apk",
"sha256": "b1012afa50bdebe872fab255b1c6db8c2c05b0dc4fc274c48d714e1887401b4e"
},
{
"name": "freetype-dev",
"version": "2.14.3-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/freetype-dev-2.14.3-r0.apk",
"sha256": "cd8d91f81c9477f0f76fd8db8158237fa99b1e0f73f6ec6e929bfc46b076b51d"
},
{
"name": "libbsd",
"version": "0.12.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libbsd-0.12.2-r0.apk",
"sha256": "358715f27bfe492dcb11f3589c8f106c37071780f55adafc52b2c5cfd3771e4d"
},
{
"name": "libbz2",
"version": "1.0.8-r6",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libbz2-1.0.8-r6.apk",
"sha256": "ccd478f4d5346a25911d4ee977690e2695e0ca3acf3e6b1a8c1300b2f4311e2f"
},
{
"name": "libexpat",
"version": "2.8.5-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libexpat-2.8.5-r0.apk",
"sha256": "18e5a4f1715dd9fee420d250d14be9e59936352929085939f33da01c3fbde28d"
},
{
"name": "libffi",
"version": "3.5.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libffi-3.5.2-r0.apk",
"sha256": "ebf430b246a6b1167350431e2af59a09f4edbc42dc3e03f3b9d74800ba5f9e7f"
},
{
"name": "libffi-dev",
"version": "3.5.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libffi-dev-3.5.2-r0.apk",
"sha256": "96830dcf534e196258838122fcaae6582ea02c957413a31e53a86ae403db0478"
},
{
"name": "libmd",
"version": "1.1.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libmd-1.1.0-r0.apk",
"sha256": "8818536cdc38dd3c252d6c6915f65ddd49390aa209cc7f70b87c446adedf820c"
},
{
"name": "libpng",
"version": "1.6.59-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libpng-1.6.59-r0.apk",
"sha256": "087102199da459dd8985c118a67d6d6c463de6798f243424feb239d1087b1064"
},
{
"name": "libpng-dev",
"version": "1.6.59-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libpng-dev-1.6.59-r0.apk",
"sha256": "9f7a8ceb60638786dd675ccc526668516a8ca4df4b54daa05f526df12f76c7bd"
},
{
"name": "libx11",
"version": "1.8.12-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libx11-1.8.12-r1.apk",
"sha256": "7f53f6f26293abac468b9563f2a90bc9a34306ec8a677af23d01d7455e3db943"
},
{
"name": "libx11-dev",
"version": "1.8.12-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libx11-dev-1.8.12-r1.apk",
"sha256": "114785b13b6df10d7997e9e8e12bf95fd5f0e6644fd420fa84dcfc0956cdecb9"
},
{
"name": "libxau",
"version": "1.0.12-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libxau-1.0.12-r0.apk",
"sha256": "ddfad479041b53e5f57a2fdfac12454993ae3937f6f893d67cd98cbd63abfcf0"
},
{
"name": "libxau-dev",
"version": "1.0.12-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libxau-dev-1.0.12-r0.apk",
"sha256": "bed89cb8971efccf37f0a77d6cddcd31f122a322d04fc75117cddb48804c2290"
},
{
"name": "libxcb",
"version": "1.17.0-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libxcb-1.17.0-r1.apk",
"sha256": "8609cadcd7af03c1aade3663fdf3bbe27e4180fe0edbe98d3f12f0ae6e95aedc"
},
{
"name": "libxcb-dev",
"version": "1.17.0-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libxcb-dev-1.17.0-r1.apk",
"sha256": "f6d1488df536002017c10a435caa32f9527ec9ad5aa86cbab639cde4258a2b67"
},
{
"name": "libxdmcp",
"version": "1.1.5-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libxdmcp-1.1.5-r1.apk",
"sha256": "f95a668a5b8888aadbee24e8ffb30f60e4d40cdf8ef61b35383e7ecf49182c66"
},
{
"name": "libxdmcp-dev",
"version": "1.1.5-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libxdmcp-dev-1.1.5-r1.apk",
"sha256": "fa1dd28326caf9e3299bf36a94fa5d62c6f448588044fcf95ddcf2bcaabd9aff"
},
{
"name": "libxext",
"version": "1.3.6-r2",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libxext-1.3.6-r2.apk",
"sha256": "bb7e84c1d38e5dcdfdb56e543394469f9c884cb13e09f5e07986863a9bea62e7"
},
{
"name": "libxext-dev",
"version": "1.3.6-r2",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libxext-dev-1.3.6-r2.apk",
"sha256": "9b70dc8e575dbc28feed289a4336bc181cc7327886364aefe4bb3bb81a98c49b"
},
{
"name": "libxml2",
"version": "2.13.9-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/libxml2-2.13.9-r1.apk",
"sha256": "91d3e730edbd5214c501696725e7af9a93e12a9dd6a296e719dca2f3de0697d5"
},
{
"name": "mesa-dev",
"version": "25.2.7-r1",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/mesa-dev-25.2.7-r1.apk",
"sha256": "a9759357562a1365b35b01a2be05cc33ec3ab0f3a1c1bd079a0b68e4c29ef129"
},
{
"name": "musl",
"version": "1.2.5-r23",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/musl-1.2.5-r23.apk",
"sha256": "6a3edd924ead1fad88a69e28c5775809af3026b322f58428001cd02fedc5299e"
},
{
"name": "wayland-dev",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/wayland-dev-1.24.0-r0.apk",
"sha256": "681397cc91e05bb6cc63af4b37a59aa510aaab37539c9e6629d7fbfbcb1f0e57"
},
{
"name": "wayland-libs-client",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/wayland-libs-client-1.24.0-r0.apk",
"sha256": "47634dcc2befcae378f9f60681d2af1864db85e57f65e11a3557a43f77911073"
},
{
"name": "wayland-libs-cursor",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/wayland-libs-cursor-1.24.0-r0.apk",
"sha256": "a6f8e01ec409b183693169af006d1edc8b673af801bd540820fdb7808cb5e27b"
},
{
"name": "wayland-libs-egl",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/wayland-libs-egl-1.24.0-r0.apk",
"sha256": "4f2ae7b94f0b6c95dae885c0c24ac59eea5fd2b7a4680cec43dd8366c86564d2"
},
{
"name": "wayland-libs-server",
"version": "1.24.0-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/wayland-libs-server-1.24.0-r0.apk",
"sha256": "c6bc1243dd337ba638834cd1e7a5ccfd10b6e2315e63af02f441bff581c35c66"
},
{
"name": "xorgproto",
"version": "2024.1-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/xorgproto-2024.1-r0.apk",
"sha256": "8d53e4b89f3067a4d24f7645127b100a4d46a567eccb82428fe5256c17316b0d"
},
{
"name": "xz-libs",
"version": "5.8.4-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/xz-libs-5.8.4-r0.apk",
"sha256": "55cec0880e1d06049147cd295a7c8a2909a668fc58a00c187741f3a16c25c55d"
},
{
"name": "zlib",
"version": "1.3.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/zlib-1.3.2-r0.apk",
"sha256": "ecda4cc94fd18f90182f1d3a615889df5e0db9cf78926d11627dd23e06d2e6e8"
},
{
"name": "zlib-dev",
"version": "1.3.2-r0",
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.23/main/aarch64/zlib-dev-1.3.2-r0.apk",
"sha256": "18a7f594ad7439629cb5889e4f0ad71075dd9f0cd4729a0461ba1501e21bc583"
}
]
}
@@ -0,0 +1,594 @@
{
"linux-x64": [
{
"name": "libbrotli-dev",
"version": "1.0.9-2+b6",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/b/brotli/libbrotli-dev_1.0.9-2+b6_amd64.deb",
"sha256": "932de11d4d9caee3300319b2fdf6ca505ca739706be48737df5e40ee90ccb6ca"
},
{
"name": "libbrotli1",
"version": "1.0.9-2+b6",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/b/brotli/libbrotli1_1.0.9-2+b6_amd64.deb",
"sha256": "563b4caec1aa5e876bd3355b36e7a38e1484baf5a293b48d1e8bd22db786e4d7"
},
{
"name": "libbsd0",
"version": "0.11.7-2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libb/libbsd/libbsd0_0.11.7-2_amd64.deb",
"sha256": "bb31cc8b40f962a85b2cec970f7f79cc704a1ae4bad24257a822055404b2c60b"
},
{
"name": "libbz2-1.0",
"version": "1.0.8-5+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/b/bzip2/libbz2-1.0_1.0.8-5+b1_amd64.deb",
"sha256": "54149da3f44b22d523b26b692033b84503d822cc5122fed606ea69cc83ca5aeb"
},
{
"name": "libbz2-dev",
"version": "1.0.8-5+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/b/bzip2/libbz2-dev_1.0.8-5+b1_amd64.deb",
"sha256": "df7204e188f7514ceae9c6f57c6c8b25ce8a642746fe284c6e89122ef26b7e57"
},
{
"name": "libc6",
"version": "2.36-9+deb12u14",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/g/glibc/libc6_2.36-9+deb12u14_amd64.deb",
"sha256": "ba4f88f73dbc3ae9055f3c20f4523bfdbaf1ad13ff95e258924f77d20b4fbedf"
},
{
"name": "libegl-dev",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libegl-dev_1.6.0-1_amd64.deb",
"sha256": "0ec9bffb024527dfb61eaeae44966e2b5b3114b3dc315b4117b0df95b1c96ef8"
},
{
"name": "libegl1",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libegl1_1.6.0-1_amd64.deb",
"sha256": "fe4d8b39f6e6fe1a32ab1efd85893553eaa9cf3866aa668ccf355f585b37d523"
},
{
"name": "libexpat1",
"version": "2.5.0-1+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/e/expat/libexpat1_2.5.0-1+deb12u2_amd64.deb",
"sha256": "2255e62fc22a86d2c544b8a3f516da9aee19383ad5742722ab4ce7f66a30dbc8"
},
{
"name": "libexpat1-dev",
"version": "2.5.0-1+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/e/expat/libexpat1-dev_2.5.0-1+deb12u2_amd64.deb",
"sha256": "757279c1ce56ca88ca519e4c01cffd94e8480ce936f53fe9b0d61dc1bab20394"
},
{
"name": "libffi-dev",
"version": "3.4.4-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libf/libffi/libffi-dev_3.4.4-1_amd64.deb",
"sha256": "89fb890aee5148f4d308a46cd8980a54fd44135f068f05b38a6ad06800bf6df3"
},
{
"name": "libffi8",
"version": "3.4.4-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libf/libffi/libffi8_3.4.4-1_amd64.deb",
"sha256": "6d9f6c25c30efccce6d4bceaa48ea86c329a3432abb360a141f76ac223a4c34a"
},
{
"name": "libfontconfig-dev",
"version": "2.14.1-4",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/f/fontconfig/libfontconfig-dev_2.14.1-4_amd64.deb",
"sha256": "74f4bbde5795e4fb4c453308bd0eb8ef45d7ade3cf11b3c18394f9e2c15d28df"
},
{
"name": "libfontconfig1",
"version": "2.14.1-4",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/f/fontconfig/libfontconfig1_2.14.1-4_amd64.deb",
"sha256": "16ee38d374e064f534116dc442b086ef26f9831f1c0af7e5fb4fe4512e700649"
},
{
"name": "libfreetype-dev",
"version": "2.12.1+dfsg-5+deb12u4",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/f/freetype/libfreetype-dev_2.12.1+dfsg-5+deb12u4_amd64.deb",
"sha256": "5fa327ae78e645cff5a2aadad92ca3640abc5cee7ad5368d7551bea8930202af"
},
{
"name": "libfreetype6",
"version": "2.12.1+dfsg-5+deb12u4",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/f/freetype/libfreetype6_2.12.1+dfsg-5+deb12u4_amd64.deb",
"sha256": "8043e479f73f29992d652e3f9dfe8b17f9780c7ea6330afe379ec5f9f188ac44"
},
{
"name": "libgl-dev",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libgl-dev_1.6.0-1_amd64.deb",
"sha256": "ebc12df48ae53924e114d9358ef3da4306d7ef8f7179300af52f1faef8b5db3e"
},
{
"name": "libgl1",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libgl1_1.6.0-1_amd64.deb",
"sha256": "6f89b1702c48e9a2437bb3c1ffac8e1ab2d828fc28b3d14b2eecd4cc19b2c790"
},
{
"name": "libglvnd0",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libglvnd0_1.6.0-1_amd64.deb",
"sha256": "b6da5b153dd62d8b5e5fbe25242db1fc05c068707c365db49abda8c2427c75f8"
},
{
"name": "libglx0",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libglx0_1.6.0-1_amd64.deb",
"sha256": "95f568df73dedf43ae66834a75502112e0d4f3ad7124f3dbfa790b739383b896"
},
{
"name": "libmd0",
"version": "1.0.4-2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libm/libmd/libmd0_1.0.4-2_amd64.deb",
"sha256": "03539fd30c509e27101d13a56e52eda9062bdf1aefe337c07ab56def25a13eab"
},
{
"name": "libpng-dev",
"version": "1.6.39-2+deb12u5",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libp/libpng1.6/libpng-dev_1.6.39-2+deb12u5_amd64.deb",
"sha256": "25649c413d02092ae7b1093a3c3ee54cbd2d4fdb0a0494787e434ccf0905903f"
},
{
"name": "libpng16-16",
"version": "1.6.39-2+deb12u5",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libp/libpng1.6/libpng16-16_1.6.39-2+deb12u5_amd64.deb",
"sha256": "a56d64bfaa9da12aafb83347909e62e6fd5fd251e6b34c194065911a30359978"
},
{
"name": "libpthread-stubs0-dev",
"version": "0.4-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.4-1_amd64.deb",
"sha256": "54632f160e1e8a43656a87195a547391038c4ca0f53291b849cd4457ba5dfde9"
},
{
"name": "libuuid1",
"version": "2.38.1-5+deb12u3",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/u/util-linux/libuuid1_2.38.1-5+deb12u3_amd64.deb",
"sha256": "5fff398321da62d28a842bcbabd193d72ae21724962c029dc32b1dc6b2deaeaf"
},
{
"name": "libwayland-client0",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-client0_1.21.0-1_amd64.deb",
"sha256": "1f002d028b8b79eec9847c636d8886d10dfe8c884cc2bebe18086b1391c5a28d"
},
{
"name": "libwayland-cursor0",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-cursor0_1.21.0-1_amd64.deb",
"sha256": "0b684467ec795024fcaae99ea4384959cc4052b2da5adec38c9b76dfed9bcf41"
},
{
"name": "libwayland-dev",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-dev_1.21.0-1_amd64.deb",
"sha256": "063ac40e71069a1a92c2be32799f4a2d234f3d361ba98cea2e3137a7ab6804d1"
},
{
"name": "libwayland-egl1",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-egl1_1.21.0-1_amd64.deb",
"sha256": "cb4225f0e15e00a3570d4cbc288c87ff477d73e741c3f1c5a6d47cf350d45378"
},
{
"name": "libwayland-server0",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-server0_1.21.0-1_amd64.deb",
"sha256": "df0396221d7b794496a687ec61fae82b6465648bc0ab6501ba0a5ed7f56eb8d6"
},
{
"name": "libx11-6",
"version": "2:1.8.4-2+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libx11/libx11-6_1.8.4-2+deb12u2_amd64.deb",
"sha256": "d88c973e79fd9b65838d77624142952757e47a6eb1a58602acf0911cf35989f4"
},
{
"name": "libx11-dev",
"version": "2:1.8.4-2+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libx11/libx11-dev_1.8.4-2+deb12u2_amd64.deb",
"sha256": "8493220d4309af1907a1f2f6eeb204c8103dafcc368394fbc4a0858c28612ff9"
},
{
"name": "libx11-xcb1",
"version": "2:1.8.4-2+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libx11/libx11-xcb1_1.8.4-2+deb12u2_amd64.deb",
"sha256": "f5da45e1d881a793250a96613f28c471a248877f1a0f18a5c90e2a620a76c898"
},
{
"name": "libxau-dev",
"version": "1:1.0.9-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxau/libxau-dev_1.0.9-1_amd64.deb",
"sha256": "d1a7f5d484e0879b3b2e8d512894744505e53d078712ce65903fef2ecfd824bb"
},
{
"name": "libxau6",
"version": "1:1.0.9-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxau/libxau6_1.0.9-1_amd64.deb",
"sha256": "679db1c4579ec7c61079adeaae8528adeb2e4bf5465baa6c56233b995d714750"
},
{
"name": "libxcb1",
"version": "1.15-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxcb/libxcb1_1.15-1_amd64.deb",
"sha256": "fdc61332a3892168f3cc9cfa1fe9cf11a91dc3e0acacbc47cbc50ebaa234cc71"
},
{
"name": "libxcb1-dev",
"version": "1.15-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxcb/libxcb1-dev_1.15-1_amd64.deb",
"sha256": "c078c024114fdada06d3158af1771d7ed8763ab434cfbcbe6a334aa8a9cae358"
},
{
"name": "libxdmcp-dev",
"version": "1:1.1.2-3",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxdmcp/libxdmcp-dev_1.1.2-3_amd64.deb",
"sha256": "c6733e5f6463afd261998e408be6eb37f24ce0a64b63bed50a87ddb18ebc1699"
},
{
"name": "libxdmcp6",
"version": "1:1.1.2-3",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxdmcp/libxdmcp6_1.1.2-3_amd64.deb",
"sha256": "ecb8536f5fb34543b55bb9dc5f5b14c9dbb4150a7bddb3f2287b7cab6e9d25ef"
},
{
"name": "libxext-dev",
"version": "2:1.3.4-1+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxext/libxext-dev_1.3.4-1+b1_amd64.deb",
"sha256": "591456aba90eeed7a1c1b044d469fd7704bf7d83af9dc574bbe2efc4a2fd1dba"
},
{
"name": "libxext6",
"version": "2:1.3.4-1+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxext/libxext6_1.3.4-1+b1_amd64.deb",
"sha256": "504b7be9d7df4f6f4519e8dd4d6f9d03a9fb911a78530fa23a692fba3058cba6"
},
{
"name": "libxfixes3",
"version": "1:6.0.0-2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxfixes/libxfixes3_6.0.0-2_amd64.deb",
"sha256": "1cd616396ff2ecae77e6e8b5b7695d414f0146de2d147837a2a02165f99e1a2c"
},
{
"name": "libxi6",
"version": "2:1.8-1+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxi/libxi6_1.8-1+b1_amd64.deb",
"sha256": "6c2fb5f241b311788990122c6e693915867cd8ed9d6da2e3524aec7ed3cdb4c0"
},
{
"name": "libxrandr2",
"version": "2:1.5.2-2+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxrandr/libxrandr2_1.5.2-2+b1_amd64.deb",
"sha256": "e2e10b672085ddf1d294cd689ed21bde5899325fd13626c2614b7d7b2d49a808"
},
{
"name": "libxrender1",
"version": "1:0.9.10-1.1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxrender/libxrender1_0.9.10-1.1_amd64.deb",
"sha256": "b2a6d160578f359220101947c8d67059b44704a251e688de1d0b744fefedfd1a"
},
{
"name": "x11proto-dev",
"version": "2022.1-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/x/xorgproto/x11proto-dev_2022.1-1_all.deb",
"sha256": "3cab66a6591774188a568f9b54c4e7956f18da76e0e0fc4a779db5f6bcc3f148"
},
{
"name": "xtrans-dev",
"version": "1.4.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/x/xtrans/xtrans-dev_1.4.0-1_all.deb",
"sha256": "9ce1af9464faee0c679348dd11cdf63934c12e734a64e0903692b0cb5af38e06"
},
{
"name": "zlib1g",
"version": "1:1.2.13.dfsg-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/z/zlib/zlib1g_1.2.13.dfsg-1_amd64.deb",
"sha256": "d7dd1d1411fedf27f5e27650a6eff20ef294077b568f4c8c5e51466dc7c08ce4"
},
{
"name": "zlib1g-dev",
"version": "1:1.2.13.dfsg-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/z/zlib/zlib1g-dev_1.2.13.dfsg-1_amd64.deb",
"sha256": "f9ce531f60cbd5df37996af9370e0171be96902a17ec2bdbd8d62038c354094f"
}
],
"linux-arm64": [
{
"name": "libbrotli-dev",
"version": "1.0.9-2+b6",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/b/brotli/libbrotli-dev_1.0.9-2+b6_arm64.deb",
"sha256": "68f334eb61694c2e5ca756174d111109f72193d1e3c14a25a65833c6dfcd98b7"
},
{
"name": "libbrotli1",
"version": "1.0.9-2+b6",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/b/brotli/libbrotli1_1.0.9-2+b6_arm64.deb",
"sha256": "3137de7f71952e710ee9f0df05026b3c6f463be9369a502d57c7696732f1ed22"
},
{
"name": "libbsd0",
"version": "0.11.7-2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libb/libbsd/libbsd0_0.11.7-2_arm64.deb",
"sha256": "ef00f132ddd268ee67d756998723ee18543db27ab34930c0a9f1cff75cf55382"
},
{
"name": "libbz2-1.0",
"version": "1.0.8-5+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/b/bzip2/libbz2-1.0_1.0.8-5+b1_arm64.deb",
"sha256": "d3a96ece03326498b39ff093a76800dfcbcb1d4049d6ae6e9f6fa1aa7a590ad6"
},
{
"name": "libbz2-dev",
"version": "1.0.8-5+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/b/bzip2/libbz2-dev_1.0.8-5+b1_arm64.deb",
"sha256": "c9827ab40420f1259b1a10983978ec8de87c11fc1a6b3c192adea1c4c490754f"
},
{
"name": "libc6",
"version": "2.36-9+deb12u14",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/g/glibc/libc6_2.36-9+deb12u14_arm64.deb",
"sha256": "01f4330719fd4f65580e16ea5a0527f372fca750e8f588d26deaf09f2d3b1cf4"
},
{
"name": "libegl-dev",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libegl-dev_1.6.0-1_arm64.deb",
"sha256": "8a3004f892785a5fa4c9f969c116f567a36d1c8e9b3d5fd0403055151e36c7d3"
},
{
"name": "libegl1",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libegl1_1.6.0-1_arm64.deb",
"sha256": "707097a275155c600e2e9251c4ee7cdfdb2d8f50a678a850a2526a7bc9664166"
},
{
"name": "libexpat1",
"version": "2.5.0-1+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/e/expat/libexpat1_2.5.0-1+deb12u2_arm64.deb",
"sha256": "8dc1976735acf825e3a922d82f171e20feec7394da2b8c616091983e38706275"
},
{
"name": "libexpat1-dev",
"version": "2.5.0-1+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/e/expat/libexpat1-dev_2.5.0-1+deb12u2_arm64.deb",
"sha256": "9987e4aa214b0ec23fc2ba6a99b8f10d2dc68f2de347722215ae3f960802e3d3"
},
{
"name": "libffi-dev",
"version": "3.4.4-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libf/libffi/libffi-dev_3.4.4-1_arm64.deb",
"sha256": "8ff6335f76752ef8659762435d0d88129ef0090a587520853238809bd10ef392"
},
{
"name": "libffi8",
"version": "3.4.4-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libf/libffi/libffi8_3.4.4-1_arm64.deb",
"sha256": "80b5c36177dc0e29d531c7eddbed3cc7355cb490e49f8cfa5959572d161f27b3"
},
{
"name": "libfontconfig-dev",
"version": "2.14.1-4",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/f/fontconfig/libfontconfig-dev_2.14.1-4_arm64.deb",
"sha256": "7da35e69d4f43b337aa9508da8e9089de80e75285141be3d7bec1055a0501aa4"
},
{
"name": "libfontconfig1",
"version": "2.14.1-4",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/f/fontconfig/libfontconfig1_2.14.1-4_arm64.deb",
"sha256": "4836d9deca824883f0de33d4f8f3718ba776c6e7c80cd7c418b5a0f86c009b9e"
},
{
"name": "libfreetype-dev",
"version": "2.12.1+dfsg-5+deb12u4",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/f/freetype/libfreetype-dev_2.12.1+dfsg-5+deb12u4_arm64.deb",
"sha256": "4e5057ac9107c69c0f49f4a8989056a7c37184c94c73ee48ba7023c5fb118db7"
},
{
"name": "libfreetype6",
"version": "2.12.1+dfsg-5+deb12u4",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/f/freetype/libfreetype6_2.12.1+dfsg-5+deb12u4_arm64.deb",
"sha256": "b8d32e54ebc9020637f8fa16b6afa64a5731e40845a6e01880d38d6f632237c9"
},
{
"name": "libgl-dev",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libgl-dev_1.6.0-1_arm64.deb",
"sha256": "56add9e710c072cf56504beed50a639f9f7c64f4e31e66958fa65a566084888c"
},
{
"name": "libgl1",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libgl1_1.6.0-1_arm64.deb",
"sha256": "f4043ab47f52325973d2b608514b3469b055b65c744861c7f378755a32e799d6"
},
{
"name": "libglvnd0",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libglvnd0_1.6.0-1_arm64.deb",
"sha256": "d5063205fab5322ee686f33f87c6315b3fcd1335ea4e7bc8202b48ed4f9c9e83"
},
{
"name": "libglx0",
"version": "1.6.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libg/libglvnd/libglx0_1.6.0-1_arm64.deb",
"sha256": "523f9b71f75e48ae605d8ef298a4b03ade26ade9a808a85329a3bd82a54b9b0d"
},
{
"name": "libmd0",
"version": "1.0.4-2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libm/libmd/libmd0_1.0.4-2_arm64.deb",
"sha256": "99a8c7dd591fae9fb37d8bf8dfdffa850e207fa405b3198c5b24711a5f972381"
},
{
"name": "libpng-dev",
"version": "1.6.39-2+deb12u5",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libp/libpng1.6/libpng-dev_1.6.39-2+deb12u5_arm64.deb",
"sha256": "f02dc017801713dfd11aaa7e72d705f747570e029fd242c29af072d64474ddfc"
},
{
"name": "libpng16-16",
"version": "1.6.39-2+deb12u5",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libp/libpng1.6/libpng16-16_1.6.39-2+deb12u5_arm64.deb",
"sha256": "69aba3963ddfb11b98e5bf4b0d022f76b797436568884b10f2da32e0f4b8c2dd"
},
{
"name": "libpthread-stubs0-dev",
"version": "0.4-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libp/libpthread-stubs/libpthread-stubs0-dev_0.4-1_arm64.deb",
"sha256": "acd58ed5d26d3f4d4b0856ebec6bc43f9c4357601a532eee0d2de839a8b8952e"
},
{
"name": "libuuid1",
"version": "2.38.1-5+deb12u3",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/u/util-linux/libuuid1_2.38.1-5+deb12u3_arm64.deb",
"sha256": "3b2d19df0f3190108cf54228c4cfc822226c06d8f3615a5ab16a7dffb1f58aed"
},
{
"name": "libwayland-client0",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-client0_1.21.0-1_arm64.deb",
"sha256": "ae390cc04c2eb1de90b9a6373505b22c730ada5e72daa50c507b7f99c12faf06"
},
{
"name": "libwayland-cursor0",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-cursor0_1.21.0-1_arm64.deb",
"sha256": "8c8d1a3942bc3fc9dd3ea7c679c1a314e59cdaaa700883d54d1bb3ec89db256b"
},
{
"name": "libwayland-dev",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-dev_1.21.0-1_arm64.deb",
"sha256": "932405c58195d299495230792e374ad304adadf3d3ffdc07eda3466b756adc33"
},
{
"name": "libwayland-egl1",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-egl1_1.21.0-1_arm64.deb",
"sha256": "c08124a4d9af24f058b45c88867a84697882e755ea94965795d152fe26fe57c9"
},
{
"name": "libwayland-server0",
"version": "1.21.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/w/wayland/libwayland-server0_1.21.0-1_arm64.deb",
"sha256": "4d99e7e0503fdaa999e67d47d8612c0465e775eafa47819573da50bdb09064f2"
},
{
"name": "libx11-6",
"version": "2:1.8.4-2+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libx11/libx11-6_1.8.4-2+deb12u2_arm64.deb",
"sha256": "d1d533e983582282a9ea82c87ac5ce715a9b67bd6d1acbd2439a11c63c36549b"
},
{
"name": "libx11-dev",
"version": "2:1.8.4-2+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libx11/libx11-dev_1.8.4-2+deb12u2_arm64.deb",
"sha256": "34bfc833561de983cfb4475523fedc13abb4faee14a876300de659591779add8"
},
{
"name": "libx11-xcb1",
"version": "2:1.8.4-2+deb12u2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libx11/libx11-xcb1_1.8.4-2+deb12u2_arm64.deb",
"sha256": "21c7e6f19909bb1e02bb3c2089789791858c54d37e86cef9d7872782c1cb711e"
},
{
"name": "libxau-dev",
"version": "1:1.0.9-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxau/libxau-dev_1.0.9-1_arm64.deb",
"sha256": "21db3761a8f388fc83707c00fac645d4332737a859dd727571e1e6ede003b048"
},
{
"name": "libxau6",
"version": "1:1.0.9-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxau/libxau6_1.0.9-1_arm64.deb",
"sha256": "36c2bf400641a80521093771dc2562c903df4065f9eb03add50d90564337ea6c"
},
{
"name": "libxcb1",
"version": "1.15-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxcb/libxcb1_1.15-1_arm64.deb",
"sha256": "041d9a68415c3ccf3ce8f4f8b88e4bbfb5dc1f0d97013c6ef8423e620ea50f84"
},
{
"name": "libxcb1-dev",
"version": "1.15-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxcb/libxcb1-dev_1.15-1_arm64.deb",
"sha256": "87b5c3f1bb130727df68295ecfaadc305ee10c150afeb1a183e95b9150e18db5"
},
{
"name": "libxdmcp-dev",
"version": "1:1.1.2-3",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxdmcp/libxdmcp-dev_1.1.2-3_arm64.deb",
"sha256": "6fd0bc51f3a4e6b7f944f24f15524a4c4ba68a4b0aa136c18bdb96a2d36988f2"
},
{
"name": "libxdmcp6",
"version": "1:1.1.2-3",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxdmcp/libxdmcp6_1.1.2-3_arm64.deb",
"sha256": "e92569ac33247261aa09adfadc34ced3994ac301cf8b58de415a2d5dbf15ccfc"
},
{
"name": "libxext-dev",
"version": "2:1.3.4-1+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxext/libxext-dev_1.3.4-1+b1_arm64.deb",
"sha256": "07f5e3efeba9564071885b2d0d2ba277692fa309a9f05ef862688af0022b1fcf"
},
{
"name": "libxext6",
"version": "2:1.3.4-1+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxext/libxext6_1.3.4-1+b1_arm64.deb",
"sha256": "5e9c0ad606eb4674c645fe8e0e64330c47d2729f7a59ed569848610efd5d5b62"
},
{
"name": "libxfixes3",
"version": "1:6.0.0-2",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxfixes/libxfixes3_6.0.0-2_arm64.deb",
"sha256": "d47bda8fed01b19b41d503e2df05d9166c58e30e2376f2f8784ceb7a834befe6"
},
{
"name": "libxi6",
"version": "2:1.8-1+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxi/libxi6_1.8-1+b1_arm64.deb",
"sha256": "b26383c9929d8bfba051eee22301e064d17039ed022bbf31990feb0d7a03b36c"
},
{
"name": "libxrandr2",
"version": "2:1.5.2-2+b1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxrandr/libxrandr2_1.5.2-2+b1_arm64.deb",
"sha256": "f0bc7f79fee182caae176652fdb1bf349d13b9591b10ad4dc1c896521da8e49f"
},
{
"name": "libxrender1",
"version": "1:0.9.10-1.1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/libx/libxrender/libxrender1_0.9.10-1.1_arm64.deb",
"sha256": "f260193deb4eaf0b410a1c61e1f84804366ee3372b8bad9f38005f7dbcd89aae"
},
{
"name": "x11proto-dev",
"version": "2022.1-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/x/xorgproto/x11proto-dev_2022.1-1_all.deb",
"sha256": "3cab66a6591774188a568f9b54c4e7956f18da76e0e0fc4a779db5f6bcc3f148"
},
{
"name": "xtrans-dev",
"version": "1.4.0-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/x/xtrans/xtrans-dev_1.4.0-1_all.deb",
"sha256": "9ce1af9464faee0c679348dd11cdf63934c12e734a64e0903692b0cb5af38e06"
},
{
"name": "zlib1g",
"version": "1:1.2.13.dfsg-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/z/zlib/zlib1g_1.2.13.dfsg-1_arm64.deb",
"sha256": "52b8b8a145bbe1956bba82034f77022cbef0c3d0885c9e32d9817a7932fe1913"
},
{
"name": "zlib1g-dev",
"version": "1:1.2.13.dfsg-1",
"url": "https://snapshot.debian.org/archive/debian/20260901T000000Z/pool/main/z/zlib/zlib1g-dev_1.2.13.dfsg-1_arm64.deb",
"sha256": "afb60f0bae432f2f0edfe2d674e99c1a13a147c9aa2b52f2f23e3df19ece76f5"
}
]
}
@@ -0,0 +1,103 @@
{
"tested_date_utc": "2026-10-04",
"host": "Ubuntu 24.04 Linux x64",
"zig_version": "0.14.1",
"sources": {
"skiasharp_version": "4.150.1",
"skiasharp_revision": "c3e4f4c20e1f23ab74d31a8838a5bd6dc55365f2",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "0aa2d542e833ffd1d4d1b68a5152375e7f65ce11"
},
"target_runtime_tested": false,
"docker_build_tested": false,
"cold_source_preparation_tested": false,
"avalonia_nativeaot_tested": false,
"targets": {
"win-x64": {
"six_archives_compiled": true,
"raster_harfbuzz_link": "passed",
"egl_gles_link": "passed",
"archives_sha256": {
"libANGLE.a": "9b4af0208fef7773dc91f684cdf676b156f48eb40821c99eb55b531f4b262214",
"libEGL.a": "dc1d8568d8d3bab0d5e6956f97db88a1d3c5887a5f2437dfd29fa5abb2a48c97",
"libGLESv2.a": "a78c019c732a356691b849d8f632dab9f1c3254452ef98fc3ed963fd33e7049c",
"libHarfBuzzSharp.a": "71602aa223348bc34f754438bd2f9b90ba025547c8305b0ad45e23fbd4d0a882",
"libSkiaSharp.a": "3aa2b79953dfcd3b8dde7996701c3a29878e61fb589a6398f0cccacd1c33f4f2",
"libskia.a": "40418692d55d26e55f1882f8386ce341410a0712ae1691d47a26b3033bd7e9a8"
},
"executables_sha256": {
"smoke-angle.exe": "bc54570c5f9cdca0a28e2f9ceb2bdb1a118cabfdf6516557d1d31fd777a7d4c5",
"smoke-raster.exe": "289f963e2180bd12cdca298b931451ebc7698bef71930318899adc4505fbccef"
}
},
"win-x86": {
"six_archives_compiled": true,
"raster_harfbuzz_link": "passed",
"egl_gles_link": "passed",
"archives_sha256": {
"libANGLE.a": "27f715956b9625ffca27ebb10e4135d173ed03970745c257fe6437fd8db23daf",
"libEGL.a": "ddab449877aa244333f43c4a79705d3c1347c5cfae778c46ca707cd78e9ed8ed",
"libGLESv2.a": "adf90968b2a8984c9bceefbbce1edacb1a06038fdc9b2bfc962e9227a9c947bd",
"libHarfBuzzSharp.a": "8483f2e5cc92561884163f317f496a2e4b6b61242774176fe8b6370bbc54c695",
"libSkiaSharp.a": "565c25b03b4f574a7db5dca98327b5f7b8eea07c1f0dab5e09d6c731ad71fd29",
"libskia.a": "97e086493216fef2d372f555c62c2fbaf28e4d6a61398ec37334164408bcc31f"
},
"executables_sha256": {
"smoke-angle.exe": "5e3a6a7951f038fa1e1a9889e8f3212de285a9464b4b722700da259b1bb9e7bb",
"smoke-raster.exe": "9b1b4dbc3686caa89ae03cb19c5d29b3cde16ab8c0381f7c1c8b339a8f5169e5"
}
},
"win-arm64": {
"six_archives_compiled": true,
"raster_harfbuzz_link": "passed",
"egl_gles_link": "passed",
"archives_sha256": {
"libANGLE.a": "d496f595573d62e64ad1fad9339f64249ddd3904d37146ad3d1e1c58b032d797",
"libEGL.a": "8f87692e53a3843eb8131e51f0cbc03c7eef02039d22cc25348ecacbc12028d7",
"libGLESv2.a": "cea510c2d0bbed66bd67d0070953408da23a39a0e5a3be959ed4cec6069973cb",
"libHarfBuzzSharp.a": "662f4174d0bc0ee0760fdeacebb7ce8f187118b524239a745058547d4c25ca10",
"libSkiaSharp.a": "17d4ed43351f0b97b6c816de41a3e2469dd42d4df42acc6201fd30a75b8e25df",
"libskia.a": "52d4b8ec673881d4beb729580eac867696ca5fda4ad4c2a498f33cd8b915c76b"
},
"executables_sha256": {
"smoke-angle.exe": "68367ceba8053f75b841d0e2b7ab6bc8f613836b91ebd13e463fe6d261b245a9",
"smoke-raster.exe": "ce758159e371ae94504ae1502dbe26034ea1c3789f33f71a0f65640991578dfb"
}
},
"osx-x64": {
"six_archives_compiled": true,
"raster_harfbuzz_link": "passed",
"egl_gles_link": "passed",
"archives_sha256": {
"libANGLE.a": "353b6ad07961f94cdbde8f7901fc924fbb50600156da8194ec7dd5d9c9d96115",
"libEGL.a": "6ea3c6d73e1f6a3785fdd941c37e50c7d00f3f5c136edf648f27243e5698d517",
"libGLESv2.a": "e3bbb289e2be771c498eae0c4037e3d9058a0a52e100dd5b6622de42f00ecb7c",
"libHarfBuzzSharp.a": "c80b4156937b0a63ff649d7ef9ee520e342ee6f6d556a65a42e37095e91e1157",
"libSkiaSharp.a": "2a1e5d7d7737921cdb6a9679630fb3068ce1d2eecd7300ec7747898bb8132d39",
"libskia.a": "8062a85f7f5ded8f67c19db06b42c6792a526d5856c4bae467fb46d182992315"
},
"executables_sha256": {
"smoke-angle": "1930720daf83d6a63268427b5c1a700a46fa6cf8edd93821133224fb1d456522",
"smoke-raster": "6cb98cb854b52f94339eb225422d511b6e11abc9e5299658b455352f332139c4"
}
},
"osx-arm64": {
"six_archives_compiled": true,
"raster_harfbuzz_link": "passed",
"egl_gles_link": "passed",
"archives_sha256": {
"libANGLE.a": "3459ece2b66c8cbb096bf20016a40d4d463224ff7a0b4e29cab44c578daf1665",
"libEGL.a": "6011625f7ac8bf467e3135588a9a30e472f55fec6de3834b1b472deed4627d15",
"libGLESv2.a": "2c4e990ddc67f98abcc09401fa5541386cbe80458581c6fd621fd379a8541835",
"libHarfBuzzSharp.a": "3802662600cca36985997d24187c3bc06e1a20c258fc260778eecd34a5d791f9",
"libSkiaSharp.a": "e5bf6468713228ab9386d6f31f6556aed98a9cbc307b4be1fbbd61644f97658b",
"libskia.a": "44e3515fdf0f09c8621b35c37c23d4441d34a831d42b88391811ad8fdff710da"
},
"executables_sha256": {
"smoke-angle": "f19386a0d04af9a89f8ec70df6f3a1205747cfc2761efd86c4e781bcf0818018",
"smoke-raster": "954dde21bf79234fa0abc30b6e5307c88558996b7777f0d459693a3588b1cc43"
}
}
}
}
@@ -0,0 +1,56 @@
{
"date_utc": "2026-10-04",
"compiler": "Zig 0.14.1 / Clang 19",
"cross_configuration_checks": {
"2.88.9/win-x64": 0,
"2.88.9/win-x86": 0,
"2.88.9/win-arm64": 0,
"2.88.9/osx-x64": 0,
"2.88.9/osx-arm64": 0,
"3.119.1/win-x64": 0,
"3.119.1/win-x86": 0,
"3.119.1/win-arm64": 0,
"3.119.1/osx-x64": 0,
"3.119.1/osx-arm64": 0,
"4.150.1/win-x64": 0,
"4.150.1/win-x86": 0,
"4.150.1/win-arm64": 0,
"4.150.1/osx-x64": 0,
"4.150.1/osx-arm64": 0
},
"native_linux_configuration_checks": {
"2.88.9/x64": "passed",
"2.88.9/arm64": "passed",
"3.119.1/x64": "passed",
"3.119.1/arm64": "passed",
"4.150.1/x64": "passed",
"4.150.1/arm64": "passed"
},
"new_full_build_checks": {
"2.88.9": {
"target": "win-x64",
"three_archives": "passed",
"raster_harfbuzz_executable_link": "passed",
"sha256": {
"libskia.a": "d972f868e4910cee774d592b09e3e52f65657ec3b10a1c38c26e6625e998196e",
"libSkiaSharp.a": "e7f22c39ce570fa74988e13a288e00646cf1b08e0be82e60a6b589bab149a8fd",
"libHarfBuzzSharp.a": "e7e117c25a16f9770f0953a260441d920b5a1e27b8c52457429f03ddacbe8855",
"smoke-raster.exe": "6a8ab77adc1444933757f702aa4dab10716a60a284ffa4777745493443909f9d"
}
},
"3.119.1": {
"target": "win-x64",
"three_archives": "passed",
"raster_harfbuzz_executable_link": "passed",
"sha256": {
"libskia.a": "d895266910b2fc8a6a72d56715ddd0b50e1e9752b184d3b99a31b8ca6614451a",
"libSkiaSharp.a": "544685511adf5b29eac2df771ef5088ba271fb570614671d0bde68e841ac3b32",
"libHarfBuzzSharp.a": "6ac05af0bb31b42af86f003e4eb70db4c4eeb021b5a438282bbd182db0dba100",
"smoke-raster.exe": "80cfda94712bd1df0671ce96fb27a174053451557d99ab542f148ac45a951532"
}
}
},
"runtime_tests": false,
"docker_cold_build_tested": false,
"4.150.1": "Previous five-target full archive/link checks are in local-cross-validation.json; updated win-x64 link test also passed."
}
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
"""Export verified Zig archives in the layout consumed by the NuGet package."""
import argparse,json,shutil,hashlib
from pathlib import Path
p=argparse.ArgumentParser();p.add_argument('--input',type=Path,required=True);p.add_argument('--output',type=Path,required=True);p.add_argument('--rid',required=True)
a=p.parse_args();manifest=json.loads((a.input/'cross-build.json').read_text())
if manifest['target']!=a.rid or not manifest.get('link_tests','').startswith('passed'):
raise SystemExit('Only successfully linked target archives may be packaged')
names={'libskia.a':'skia.lib','libSkiaSharp.a':'SkiaSharp.lib','libHarfBuzzSharp.a':'libHarfBuzzSharp.lib','libANGLE.a':'libANGLE_static.lib','libGLESv2.a':'libGLESv2_static.lib','libEGL.a':'libEGL_static.lib'} if a.rid.startswith('win') else {'libskia.a':'libskia.a','libSkiaSharp.a':'libSkiaSharp.a','libHarfBuzzSharp.a':'libHarfBuzzSharp.a','libANGLE.a':'libANGLE_static.a','libGLESv2.a':'libGLESv2_static.a','libEGL.a':'libEGL_static.a'}
a.output.mkdir(parents=True,exist_ok=True)
for source,destination in names.items():
path=a.input/source
if hashlib.sha256(path.read_bytes()).hexdigest()!=manifest['archives'][source]:raise SystemExit('Archive checksum mismatch: '+source)
shutil.copy2(path,a.output/destination)
runtime=a.input/'zig-runtime'
if not runtime.is_dir() or not [p for p in runtime.iterdir() if p.suffix in ('.a','.lib')]:raise SystemExit('Zig C++/compiler runtime archives were not captured')
(a.output/'zig-runtime').mkdir(exist_ok=True)
for path in runtime.iterdir():
if path.suffix not in ('.a','.lib'):continue
dest=path.stem+'.lib' if a.rid.startswith('win') else path.name
shutil.copy2(path,a.output/'zig-runtime'/dest)
if a.rid=='win-x86':
path=a.input/'skia_x86_stdcall_thunks.lib'
if not path.is_file():raise SystemExit('x86 stdcall thunks are required')
shutil.copy2(path,a.output/path.name)
shutil.copy2(a.input/'cross-build.json',a.output/'zig-build.json')
print('Packaged',a.rid)
@@ -0,0 +1,2 @@
# Unified diffs contain space-prefixed empty context lines.
*.patch -whitespace
@@ -0,0 +1,28 @@
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -427,7 +427,8 @@
ldflags = [ "-Wl,--build-id" ]
}
-_use_copy_compiler_dll = angle_has_build && is_win
+declare_args() { angle_copy_compiler_dll = true }
+_use_copy_compiler_dll = angle_has_build && is_win && angle_copy_compiler_dll
if (_use_copy_compiler_dll) {
copy("copy_compiler_dll") {
@@ -1534,6 +1535,7 @@
}
angle_static_library("libGLESv2_static") {
+ complete_static_lib = true
sources = libglesv2_sources
public_deps = [ ":libGLESv2_thin_static" ]
@@ -1652,6 +1654,7 @@
}
libEGL_template("libEGL_static") {
+ complete_static_lib = true
target_type = "angle_static_library"
public_configs = [ ":angle_static_config" ]
deps = [ ":libGLESv2_static" ]
@@ -0,0 +1,20 @@
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -584,7 +584,7 @@
}
if (!defined(use_libcxx_modules)) {
- use_libcxx_modules = is_clang
+ use_libcxx_modules = is_clang && !(is_win && host_os == "linux")
}
if (filter_include(
@@ -715,7 +715,7 @@
}
if (!defined(use_libcxx_modules)) {
- use_libcxx_modules = is_clang
+ use_libcxx_modules = is_clang && !(is_win && host_os == "linux")
}
if (filter_include(
@@ -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;
@@ -0,0 +1,21 @@
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1523,7 +1523,18 @@
public_deps = [ ":libANGLE" ]
}
+angle_static_library("libANGLE_static") {
+ complete_static_lib = true
+ public_deps = [ ":libANGLE" ]
+}
+
+angle_static_library("libANGLE_with_capture_static") {
+ complete_static_lib = true
+ public_deps = [ ":libANGLE_with_capture" ]
+}
+
angle_static_library("libGLESv2_static") {
+
sources = libglesv2_sources
public_deps = [ ":libGLESv2_thin_static" ]
}
@@ -0,0 +1,15 @@
--- a/build/mac/find_sdk.py
+++ b/build/mac/find_sdk.py
@@ -1,3 +1,12 @@
+import os,sys,json
+if sys.platform == 'linux' and os.environ.get('STATICLINK_MACOS_SDK'):
+ sdk=os.environ['STATICLINK_MACOS_SDK']
+ if '--print_sdk_path' in sys.argv:print('//'+str(__import__('pathlib').Path.cwd().relative_to(__import__('pathlib').Path(__file__).resolve().parents[2]))+'/sdk/xcode_links/MacOSX15.5.sdk')
+ if '--print_bin_path' in sys.argv:print(os.environ['STATICLINK_DARWIN_TOOLS'])
+ if '--print_sdk_build' in sys.argv:print('24F74')
+ print('15.5')
+ sys.exit(0)
+
#!/usr/bin/env python3
# Copyright 2012 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
@@ -0,0 +1,38 @@
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -369,6 +369,35 @@
outputs = [ output ]
}
+ if (current_os == "mac") {
+ tool("objc") {
+ output = "$object_subdir/{{source_name_part}}.o"
+ depfile = "$output.d"
+ precompiled_header_type = "gcc"
+
+ # {{output}} may contain .dwo file, so use $output instead of {{output}}
+ # in command.
+ command = "$coverage_wrapper$cc $md -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_objc}}${extra_cppflags}${extra_cflags}${module_name_flag} -c {{source}} -o $output"
+ depsformat = "gcc"
+ description = "OBJC {{output}}"
+ outputs = [ output ]
+ }
+
+ tool("objcxx") {
+ output = "$object_subdir/{{source_name_part}}.o"
+ depfile = "$output.d"
+ precompiled_header_type = "gcc"
+
+ # {{output}} may contain .dwo file, so use $output instead of {{output}}
+ # in command.
+ command = "$coverage_wrapper$cxx $md -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_objcc}}${extra_cppflags}${extra_cxxflags} {{module_deps}}${module_name_flag} -c {{source}} -o $output"
+ depsformat = "gcc"
+ description = "OBJCXX {{output}}"
+ outputs = [ output ]
+ }
+
+ }
+
tool("cxx_module") {
depfile = "{{output}}.d"
@@ -0,0 +1,14 @@
--- a/build/config/apple/sdk_info.py
+++ b/build/config/apple/sdk_info.py
@@ -1,3 +1,11 @@
+import os,sys,json
+if sys.platform == 'linux' and os.environ.get('STATICLINK_MACOS_SDK'):
+ sdk=os.environ['STATICLINK_MACOS_SDK']
+ sdk='//'+str(__import__('pathlib').Path.cwd().relative_to(__import__('pathlib').Path(__file__).resolve().parents[3]))+'/sdk/xcode_links/MacOSX15.5.sdk'
+ values={'xcode_version':'1640','xcode_version_int':1640,'xcode_build':'16F6','sdk_path':sdk,'sdk_version':'15.5','sdk_build':'24F74','sdk_platform_path':sdk,'toolchains_path':os.environ['STATICLINK_DARWIN_TOOLS']}
+ for k,v in values.items():print(k+'='+json.dumps(v))
+ sys.exit(0)
+
#!/usr/bin/env python3
# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
@@ -0,0 +1,11 @@
--- a/src/xps/SkXPSDevice.cpp
+++ b/src/xps/SkXPSDevice.cpp
@@ -337,7 +337,7 @@
return true;
}
-static HRESULT subset_typeface(const SkXPSDevice::TypefaceUse& current) {
+HRESULT subset_typeface(const SkXPSDevice::TypefaceUse& current) {
//The CreateFontPackage API is only supported on desktop, not in UWP
#if defined(SK_WINUWP) || defined(SK_BUILD_FOR_WINRT) || defined(SK_BUILD_FOR_NANOSERVER)
return E_NOTIMPL;
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Compile target objects and SDK API probes; this does not build release libraries."""
import argparse
import json
import pathlib
import subprocess
import tempfile
TARGETS = (
"x86-windows-gnu", "x86_64-windows-gnu", "aarch64-windows-gnu",
"x86-windows-msvc", "x86_64-windows-msvc", "aarch64-windows-msvc",
"x86_64-macos", "aarch64-macos",
)
SOURCES = {
"plain": "int answer(void) { return 42; }\n",
"cpp": '#include <string>\nextern "C" int answer(void) { std::string s="test"; return int(s.size()); }\n',
"windows": '''#include <windows.h>
#include <dwrite_3.h>
#include <xpsobjectmodel.h>
// These call shapes are required by Skia 4.150.1's DirectWrite paint backend.
HRESULT paint_child(IDWritePaintReader* reader, DWRITE_PAINT_ELEMENT* element) {
return reader->MoveToFirstChild(element);
}
void paint_color(IDWritePaintReader* reader, DWRITE_COLOR_F color) {
reader->SetTextColor(color);
}
''',
"macos": '''#include <ApplicationServices/ApplicationServices.h>
#import <Metal/Metal.h>
int answer(void) { return sizeof(CFIndex); }
''',
}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--zig", default="zig")
parser.add_argument("--output", type=pathlib.Path, required=True)
parser.add_argument("--target", choices=TARGETS, action="append")
parser.add_argument("--macos-sdk", type=pathlib.Path)
parser.add_argument("--windows-sdk", type=pathlib.Path,
help="An xwin splat containing crt/include and sdk/include")
args = parser.parse_args()
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
version = subprocess.check_output([args.zig, "version"], text=True).strip()
results = {"zig_version": version, "targets": {}}
with tempfile.TemporaryDirectory(prefix="staticlink-probe-") as temporary:
source_dir = pathlib.Path(temporary)
for name, source in SOURCES.items():
suffix = ".mm" if name == "macos" else ".cpp"
(source_dir / (name + suffix)).write_text(source)
for target in args.target or TARGETS:
destination = output / target
destination.mkdir(exist_ok=True)
flags = []
if "macos" in target and args.macos_sdk:
sdk = args.macos_sdk.resolve()
flags += ["-isysroot", str(sdk), "-F", str(sdk / "System/Library/Frameworks")]
if target.endswith("windows-msvc") and args.windows_sdk:
sdk = args.windows_sdk.resolve()
for include in ("crt/include", "sdk/include/ucrt", "sdk/include/shared", "sdk/include/um"):
flags += ["-isystem", str(sdk / include)]
target_results = {}
for name in ("plain", "cpp", "windows" if "windows" in target else "macos"):
suffix = ".mm" if name == "macos" else ".cpp"
command = [args.zig, "c++", "-target", target, "-std=c++20", *flags,
"-c", str(source_dir / (name + suffix)),
"-o", str(destination / (name + ".o"))]
result = subprocess.run(command, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, text=True)
(destination / (name + ".log")).write_text(result.stdout)
target_results[name] = {"exit_code": result.returncode, "command": command}
print(target, name, "PASS" if result.returncode == 0 else "FAIL", flush=True)
results["targets"][target] = target_results
(output / "results.json").write_text(json.dumps(results, indent=2) + "\n")
return int(any(test["exit_code"] for tests in results["targets"].values() for test in tests.values()))
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,68 @@
{
"date": "2026-10-04",
"zig": "0.14.1",
"versions": [
"3.119.2",
"3.119.4",
"4.150.3",
"4.150.5",
"4.153.1"
],
"configuration": {
"passed": 45,
"targets_per_version": 9,
"full_build": false
},
"full_skia_build_and_executable_link": {
"win-x64": [
"3.119.2",
"3.119.4",
"4.150.3",
"4.150.5",
"4.153.1"
],
"win-x86": [
"3.119.4"
],
"linux-x64": [
"3.119.4"
],
"linux-arm64": [
"3.119.4"
],
"osx-arm64": [
"4.153.1"
]
},
"angle": {
"linux-x64": "3.119.4: six archives compiled and both executable links passed",
"win-x86": "3.119.4: fixed ANGLE archives reused; both executable links passed"
},
"execution": {
"linux-x64": "64 raster pixels and 2 UTF-8 codepoints passed",
"linux-arm64": "same raster/UTF-8 test passed under QEMU",
"gpu": "not validated; Linux ANGLE probe lacked a display"
},
"avalonia_native": {
"versions": [
"11.3.14",
"12.1.0"
],
"targets": [
"osx-x64",
"osx-arm64"
],
"compilation": "all four passed",
"executable_link": "all four passed against macOS SDK system libc++",
"execution": "not run"
},
"not_validated": [
"complete Debian Docker image build",
"all five releases full nine-RID build",
"Windows/macOS runtime",
"full NativeAOT application",
"GitHub Actions execution",
"NuGet publication"
],
"environment_note": "Local builds ran on the Linux host with pinned cross tools, not inside Docker. Disk exhaustion required resumptions; it is not recorded as source incompatibility."
}
+30
View File
@@ -0,0 +1,30 @@
target_os = "linux"
target_cpu = "@CPU@"
is_official_build = true
is_static_skiasharp = true
skia_enable_tools = false
skia_enable_ganesh = true
skia_enable_pdf = false
skia_enable_skottie = false
skia_use_dng_sdk = false
skia_use_fontconfig = true
skia_use_freetype = true
skia_use_harfbuzz = false
skia_use_icu = false
skia_use_piex = false
skia_use_sfntly = false
skia_use_system_expat = false
skia_use_system_freetype2 = false
skia_use_system_libjpeg_turbo = false
skia_use_system_libpng = false
skia_use_system_libwebp = false
skia_use_system_zlib = false
skia_use_vulkan = false
skia_use_xps = false
extra_cflags_cc = [ "-frtti", "-pthread" ]
cc = "@CC@"
cxx = "@CXX@"
ar = "@AR@"
extra_cflags = [ "-DSKIA_C_DLL", "-DHAVE_SYSCALL_GETRANDOM", "-DXML_DEV_URANDOM" ]
# Standalone SkiaSharp excludes the Chromium PartitionAlloc dependency.
skia_use_partition_alloc = false
+27
View File
@@ -0,0 +1,27 @@
target_os = "mac"
target_cpu = "@CPU@"
is_official_build = true
is_static_skiasharp = true
skia_enable_tools = false
skia_enable_ganesh = true
skia_use_metal = true
skia_enable_pdf = false
skia_enable_skottie = false
skia_use_dng_sdk = false
skia_use_fontconfig = false
skia_use_freetype = false
skia_use_harfbuzz = false
skia_use_icu = false
skia_use_piex = false
skia_use_sfntly = false
skia_use_system_expat = false
skia_use_system_libjpeg_turbo = false
skia_use_system_libpng = false
skia_use_system_libwebp = false
skia_use_system_zlib = false
skia_use_vulkan = false
skia_use_xps = false
extra_cflags = [ "-DSKIA_C_DLL" ]
extra_cflags_cc = [ "-frtti" ]
# Standalone SkiaSharp excludes the Chromium PartitionAlloc dependency.
skia_use_partition_alloc = false
+28
View File
@@ -0,0 +1,28 @@
target_os = "win"
target_cpu = "@CPU@"
is_official_build = true
is_static_skiasharp = true
is_clang = true
skia_enable_tools = false
skia_enable_ganesh = true
skia_enable_pdf = false
skia_enable_skottie = false
skia_use_dng_sdk = false
skia_use_fontconfig = false
skia_use_freetype = false
skia_use_harfbuzz = false
skia_use_icu = false
skia_use_piex = false
skia_use_sfntly = false
skia_use_system_expat = false
skia_use_system_freetype2 = false
skia_use_system_libjpeg_turbo = false
skia_use_system_libpng = false
skia_use_system_libwebp = false
skia_use_system_zlib = false
skia_use_vulkan = false
skia_use_xps = true
extra_cflags = [ "-DSKIA_C_DLL" ]
extra_cflags_cc = [ "/GR" ]
# Standalone SkiaSharp excludes the Chromium PartitionAlloc dependency.
skia_use_partition_alloc = false
+123
View File
@@ -0,0 +1,123 @@
"""Discover Skia GN capabilities; keep version policy out of the build driver."""
import json
from pathlib import Path
import re
import subprocess
def repair_xps(source):
path = source / 'src/xps/SkXPSDevice.cpp'
header = source / 'src/xps/SkXPSDevice.h'
if not path.exists() or not header.exists():
return 'absent'
text = path.read_text()
if not re.search(r'friend\s+HRESULT\s+subset_typeface\s*\(', header.read_text()):
return 'not-needed'
text, count = re.subn(r'(?m)^static HRESULT (subset_typeface\s*\()', r'HRESULT \1', text)
if count > 1:
raise RuntimeError('Ambiguous XPS helper declarations; inspect the new source layout')
if count:
path.write_text(text)
return 'repaired' if count else 'already-compatible'
def repair_skcms(source):
repaired = []
for relative in ('third_party/skcms/skcms.cc', 'modules/skcms/skcms.cc'):
path = source / relative
if not path.is_file():
continue
text = path.read_text()
def replace(match):
features = match[2]
if 'avx512f' in features.split(',') and 'evex512' not in features.split(','):
return match[1] + features + ',evex512' + match[3]
return match[0]
updated = re.sub(r'(#pragma clang attribute push\(__attribute__\(\(target\(")([^"]+)("\)\)\))', replace, text)
if updated != text:
path.write_text(updated)
repaired.append(relative)
# This changes only the AVX512-dispatched functions' Clang target attribute;
# the baseline CPU path and runtime dispatch remain unchanged.
return repaired
def configure(source, out, desired, gn, env):
# GN itself resolves imports, defaults and conditional declarations. Text
# searches alone can incorrectly report an option from an inactive branch.
# Apply the feature policy during discovery too: Skia's default tools build
# can otherwise demand test-only dependencies before we can list options.
bootstrap = dict(desired)
out.mkdir(parents=True, exist_ok=True)
def write(values):
(out / 'args.gn').write_text(''.join(f'{key} = {value}\n' for key, value in values.items()))
write(bootstrap)
subprocess.run([str(gn), 'gen', str(out.relative_to(source))],
cwd=source, env=env, check=True)
command = [str(gn), 'args', str(out.relative_to(source)), '--list', '--json']
result = subprocess.run(command, cwd=source, env=env, capture_output=True, text=True)
if result.returncode:
raise RuntimeError('Cannot discover Skia build arguments:\n' + result.stdout + result.stderr)
# Older GN emits warnings before the JSON array for unsupported bootstrap args.
match = re.search(r'(?m)^\[\s*\{', result.stdout)
if not match:
raise RuntimeError('GN did not return an argument list:\n' + result.stdout)
arguments = json.loads(result.stdout[match.start():])
available = {entry['name'] for entry in arguments}
values = {key: value for key, value in desired.items() if key in available}
if 'skia_enable_ganesh' in available:
values['skia_enable_ganesh'] = 'true'
elif 'skia_enable_gpu' in available:
values['skia_enable_gpu'] = 'true'
else:
raise RuntimeError('No known Skia GPU switch; refusing to silently change the graphics feature set')
required = {'target_os', 'target_cpu', 'is_static_skiasharp', 'extra_cflags', 'extra_cflags_cc'}
required |= {key for key in ('skia_use_metal', 'skia_use_xps') if desired.get(key) == 'true'}
missing = required - available
if missing:
raise RuntimeError('Skia source lacks required build capabilities: ' + ', '.join(sorted(missing)))
write(values)
subprocess.run([str(gn), 'gen', str(out.relative_to(source)), '--fail-on-unused-args'],
cwd=source, env=env, check=True)
report = {'available_arguments': sorted(available), 'selected_arguments': values,
'omitted_obsolete_arguments': sorted(set(desired) - available)}
(out / 'skia-capabilities.json').write_text(json.dumps(report, indent=2) + '\n')
return report
def parse_args(text):
# Values can include multiline lists, as used by the native Linux recipe.
assignments = list(re.finditer(r'(?m)^[ \t]*([A-Za-z_]\w*)[ \t]*=', text))
if not assignments:
raise ValueError('No GN assignments in build recipe')
return {match[1]: text[match.end():assignments[index + 1].start()
if index + 1 < len(assignments) else len(text)].strip()
for index, match in enumerate(assignments)}
def archive(source, name):
candidates = [source / (name + '.lib'), source / ('lib' + name + '.a'),
source / 'obj' / (name + '.lib'), source / 'obj' / ('lib' + name + '.a')]
matches = [path for path in candidates if path.is_file()]
if len(matches) != 1:
raise RuntimeError(f'Expected one archive for {name}; found {matches}')
return matches[0]
if __name__ == '__main__':
import argparse
import os
p = argparse.ArgumentParser(description='Configure native or cross Skia using its actual GN capabilities')
p.add_argument('--source', type=Path, required=True)
p.add_argument('--out', type=Path, required=True)
a = p.parse_args()
source, out = a.source.resolve(), a.out.resolve()
gn = source / ('bin/gn.exe' if os.name == 'nt' else 'bin/gn')
repair_xps(source)
desired = parse_args((out / 'args.gn').read_text())
compiler = desired.get('cc', 'clang-cl' if os.name == 'nt' else 'clang').strip('"')
version = subprocess.run([compiler, '--version'], capture_output=True, text=True, check=True)
match = re.search(r'clang version (\d+)', version.stdout)
if match and int(match[1]) >= 19:
repair_skcms(source)
configure(source, out, desired, gn, os.environ.copy())
+3
View File
@@ -0,0 +1,3 @@
#include <EGL/egl.h>
#include <GLES2/gl2.h>
int main(void) { EGLDisplay d = eglGetDisplay(EGL_DEFAULT_DISPLAY); EGLint a,b; if (!eglInitialize(d,&a,&b)) return 1; glClear(GL_COLOR_BUFFER_BIT); return eglTerminate(d) ? 0 : 2; }
+31
View File
@@ -0,0 +1,31 @@
#include <stdio.h>
#include <stdint.h>
#include "include/c/sk_general.h"
#include "include/c/sk_surface.h"
#include "include/c/sk_canvas.h"
#include "hb.h"
#ifndef STATICLINK_EXPECTED_MILESTONE
#error "Pass the milestone detected from this source tree"
#endif
int main(void) {
if (sk_version_get_milestone() != STATICLINK_EXPECTED_MILESTONE) return 1;
sk_imageinfo_t info = {NULL, 8, 8, RGBA_8888_SK_COLORTYPE, PREMUL_SK_ALPHATYPE};
sk_surface_t *surface = sk_surface_new_raster(&info, 0, NULL);
if (!surface) return 2;
sk_canvas_clear(sk_surface_get_canvas(surface), 0xff336699u);
uint8_t pixels[8*8*4];
if (!sk_surface_read_pixels(surface, &info, pixels, 8*4, 0, 0)) return 3;
for (int i=0; i<8*8; i++) {
if (pixels[i*4] != 0x33 || pixels[i*4+1] != 0x66 || pixels[i*4+2] != 0x99 || pixels[i*4+3] != 0xff) return 4;
}
sk_surface_unref(surface);
hb_buffer_t *buffer = hb_buffer_create();
hb_buffer_add_utf8(buffer, "A\xe4\xb8\xad", 4, 0, -1);
hb_buffer_guess_segment_properties(buffer);
unsigned length = 0;
hb_glyph_info_t *glyphs = hb_buffer_get_glyph_infos(buffer, &length);
if (length != 2 || glyphs[0].codepoint != 65 || glyphs[1].codepoint != 0x4e2d) return 5;
hb_buffer_destroy(buffer);
printf("PASS Skia=%s HarfBuzz=%s raster=64 pixels UTF8=2 codepoints\n", sk_version_get_string(), hb_version_string());
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
{
"skiasharp_version": "4.150.1",
"skiasharp_revision": "c3e4f4c20e1f23ab74d31a8838a5bd6dc55365f2",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "0aa2d542e833ffd1d4d1b68a5152375e7f65ce11"
}
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""Fetch pinned native cross-build dependencies without cloning Git history."""
import argparse
import ast
import json
import hashlib
from pathlib import Path
import re
import subprocess
import sys
# These dependencies correspond to features explicitly disabled by this recipe.
# Unknown/new dependencies stay enabled; do not infer a skip from their names.
DISABLED = {'dng_sdk', 'icu', 'dawn', 'angle2', 'swiftshader', 'emsdk'}
def evaluate(node, variables):
if isinstance(node, ast.Constant):
return node.value
if isinstance(node, ast.Dict):
return {evaluate(k, variables): evaluate(v, variables) for k, v in zip(node.keys, node.values)}
if isinstance(node, (ast.List, ast.Tuple)):
return [evaluate(n, variables) for n in node.elts]
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add):
return evaluate(node.left, variables) + evaluate(node.right, variables)
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == 'Var' and len(node.args) == 1:
return variables[evaluate(node.args[0], variables)]
raise ValueError('Unsupported DEPS expression: ' + ast.dump(node))
def dependencies(source):
values = {}
for node in ast.parse((source / 'DEPS').read_text()).body:
if isinstance(node, ast.Assign):
for name in node.targets:
if isinstance(name, ast.Name) and name.id in ('vars', 'deps'):
values[name.id] = evaluate(node.value, values.get('vars', {}))
if not values.get('deps'):
raise ValueError('No dependency map in DEPS')
return values['deps']
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--source', type=Path, required=True)
p.add_argument('--cache', type=Path, help='Immutable dependency checkouts shared across version profiles')
p.add_argument('--no-fetch-gn', action='store_true', help='Use an existing verified GN for a local test')
a = p.parse_args()
source = a.source.resolve()
records = {}
for relative, value in dependencies(source).items():
if not isinstance(value, str):
continue # GN/Ninja CIPD packages are handled by upstream fetch-gn.
if Path(relative).name in DISABLED:
continue
if '..' in Path(relative).parts:
raise ValueError('Dependency path escapes source: ' + relative)
link = source / relative
path = link.absolute()
if not path.is_relative_to(source):
raise ValueError('Dependency path escapes source: ' + relative)
url, revision = value.rsplit('@', 1)
if not re.fullmatch(r'[0-9a-f]{40}', revision):
raise ValueError('Dependency is not commit-pinned: ' + value)
if a.cache:
key = hashlib.sha256((url + '@' + revision).encode()).hexdigest()
cached = a.cache.resolve() / key
if link.is_symlink():
link.unlink()
if not link.exists():
link.parent.mkdir(parents=True, exist_ok=True)
link.symlink_to(cached, target_is_directory=True)
path = cached
if not (path / '.git').exists():
subprocess.run(['git', 'init', '-q', str(path)], check=True)
subprocess.run(['git', '-C', str(path), 'remote', 'add', 'origin', url], check=True)
current = subprocess.run(['git', '-C', str(path), 'rev-parse', 'HEAD'], capture_output=True, text=True)
if current.stdout.strip() != revision:
subprocess.run(['git', '-C', str(path), 'remote', 'set-url', 'origin', url], check=True)
subprocess.run(['git', '-C', str(path), 'fetch', '--depth=1', 'origin', revision], check=True)
subprocess.run(['git', '-C', str(path), 'checkout', '--detach', revision], check=True)
records[relative] = revision
if not a.no_fetch_gn:
subprocess.run([sys.executable, str(source / 'bin/fetch-gn')], cwd=source, check=True)
(source / 'native-cross-deps.json').write_text(json.dumps(records, indent=2) + '\n')
if __name__ == '__main__':
main()
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env python3
"""Validate the actual release container before compiling native libraries."""
import argparse
import inspect
import json
from pathlib import Path
import runpy
import shutil
import subprocess
import sys
import tarfile
import tempfile
def run(command):
print("+", " ".join(map(str, command)), flush=True)
return subprocess.check_output(list(map(str, command)), text=True, stderr=subprocess.STDOUT)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--rids", default="[]")
args = parser.parse_args()
args.output.mkdir(parents=True, exist_ok=True)
report = {"python": sys.version, "targets": {}, "passed": False}
try:
if "filter" not in inspect.signature(tarfile.TarFile.extractall).parameters:
raise RuntimeError("depot_tools requires tarfile extraction filters")
for module in ("six", "requests", "httplib2"):
__import__(module)
run([sys.executable, "/opt/depot_tools/gclient.py", "help"])
for tool in ("git", "ninja", "clang", "llvm-ar", "lld", "pkg-config", "cmake", "patch", "file", "dotnet", "zig"):
if not shutil.which(tool):
raise RuntimeError("Missing tool: " + tool)
report["zig"] = run(["zig", "version"]).strip()
if report["zig"] != "0.14.1":
raise RuntimeError("Unexpected Zig version")
report["dotnet_sdks"] = run(["dotnet", "--list-sdks"]).splitlines()
for major in ("8.", "10."):
if not any(version.startswith(major) for version in report["dotnet_sdks"]):
raise RuntimeError("Missing .NET SDK " + major)
adapter = Path(__file__).with_name("zig-cross.py")
targets = runpy.run_path(str(adapter))["TARGETS"]
requested = json.loads(args.rids)
if not isinstance(requested, list) or any(rid not in targets for rid in requested):
raise ValueError("Expected a JSON array of supported RIDs")
if requested:
targets = {rid: targets[rid] for rid in requested}
sdk = Path("/opt/macos-sdk/MacOSX15.5.sdk")
if json.loads((sdk / "SDKSettings.json").read_text())["Version"] != "15.5":
raise RuntimeError("Unexpected macOS SDK version")
with tempfile.TemporaryDirectory(prefix="staticlink-environment-") as temporary:
work = Path(temporary)
plain = work / "main.c"
plain.write_text("int main(void) { return 0; }\n")
cpp = work / "probe.cpp"
for rid in targets:
target_work = work / rid
command = [sys.executable, adapter, "--target", rid, "--output", target_work,
"--sdk", sdk, "--mingw-headers", "/opt/mingw/usr/share/mingw-w64/include"]
source = "#include <string>\nint value() { return int(std::string(\\\"ok\\\").size()); }\n".replace('\\\"', '"')
if rid.startswith("linux"):
sysroot = Path("/opt/sysroots") / rid
if not (sysroot / "usr/include").is_dir():
raise RuntimeError("Missing sysroot: " + rid)
if not (sysroot / "usr/include/wayland-client.h").is_file():
raise RuntimeError("Missing Wayland headers: " + rid)
command += ["--linux-sysroot", sysroot, "--linux-headers", "/opt/staticlink/linux-headers"]
source += "#include <fontconfig/fontconfig.h>\n#include <ffi.h>\n#include <ft2build.h>\n#include FT_FREETYPE_H\n"
elif rid.startswith("win"):
source += "#include <windows.h>\n#include <dwrite_3.h>\n#include <XpsObjectModel.h>\n#include <windows.ui.composition.h>\n"
cpp.write_text(source)
run(command)
run([target_work / "cxx", "-std=c++17", "-c", cpp, "-o", target_work / "cpp.o"])
run([target_work / "cc", "-c", plain, "-o", target_work / "main.o"])
run([target_work / "ar", "rcs", target_work / "probe.a", target_work / "main.o"])
executable = target_work / ("probe.exe" if rid.startswith("win") else "probe")
run([target_work / "cc", plain, "-o", executable])
if rid.startswith("osx"):
objc = work / "probe.mm"
objc.write_text("#import <Cocoa/Cocoa.h>\n#import <Metal/Metal.h>\nint value() { return sizeof(CFIndex); }\n")
run([target_work / "cxx", "-fobjc-arc", "-fblocks", "-c", objc, "-o", target_work / "objc.o"])
report["targets"][rid] = {
"passed": True,
"object": run(["file", target_work / "main.o"]).strip(),
"executable": run(["file", executable]).strip(),
}
print(rid + ": C/C++ compile, archive and executable link PASS", flush=True)
report["passed"] = True
except Exception as error:
report["error"] = str(error)
if isinstance(error, subprocess.CalledProcessError):
report["error_output"] = error.output
print(error.output, flush=True)
raise
finally:
(args.output / "environment-validation.json").write_text(json.dumps(report, indent=2) + "\n")
if __name__ == "__main__":
main()
@@ -0,0 +1,7 @@
{
"skiasharp_version": "2.88.9",
"skiasharp_revision": "9699cdf046a989423a5e5eca68e7fd813486d81c",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "4bed689c9c9eb77a120c6a9d54af6a572c85d1c2"
}
@@ -0,0 +1,7 @@
{
"skiasharp_version": "3.119.1",
"skiasharp_revision": "cc78b5933d23e6383db5d246e70db915770d55d6",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "40f75dc0051d141913c07c20d4c19590c7da0cb7"
}
@@ -0,0 +1,7 @@
{
"skiasharp_version": "3.119.2",
"skiasharp_revision": "2888c737ad016d584c74525e2d35db5097ea8576",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "927041a58f130e0dd0562ba86cb4170989ad39e9"
}
@@ -0,0 +1,7 @@
{
"skiasharp_version": "3.119.4",
"skiasharp_revision": "f568ac94dd768ef9a2f593537cfde2dd0d348ef5",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "7dbfc07dd33181f84e0958afb7ee805c6c769f0b"
}
@@ -0,0 +1,7 @@
{
"skiasharp_version": "4.150.1",
"skiasharp_revision": "c3e4f4c20e1f23ab74d31a8838a5bd6dc55365f2",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "0aa2d542e833ffd1d4d1b68a5152375e7f65ce11"
}
@@ -0,0 +1,7 @@
{
"skiasharp_version": "4.150.2",
"skiasharp_revision": "fd1fdd5239f826514651a4ada5d7bb3dcec2fb43",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "b70519562e2d26d3cf56286d3a13ca25152cfae4"
}
@@ -0,0 +1,7 @@
{
"skiasharp_version": "4.150.3",
"skiasharp_revision": "f8a404ff3375872d042404a29189ceb8e3001a41",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "eb86e4b3ee034085b62d3980b3d94629862441e6"
}
@@ -0,0 +1,7 @@
{
"skiasharp_version": "4.150.5",
"skiasharp_revision": "16f82682f756217654eb1a152cdb639e358b8976",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "579fa81eeed6906732e27b7d95149538584dd20b"
}
@@ -0,0 +1,7 @@
{
"skiasharp_version": "4.153.1",
"skiasharp_revision": "4783f51448f9b070dda4f87b83e941c9599e466e",
"angle_branch": "7922",
"angle_revision": "7e08726e32026a0bbabd6ec8a1a90f5ab533ed66",
"skia_revision": "45afab4f1f0921f3feb97f58cf89b136fffa85e6"
}
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
"""Generate cdecl -> stdcall adapters using the pinned managed P/Invoke ABI."""
import argparse,re,subprocess,json
from pathlib import Path
p=argparse.ArgumentParser();p.add_argument('--source',type=Path,required=True);p.add_argument('--toolchain',type=Path,required=True);p.add_argument('--libraries',type=Path,required=True);a=p.parse_args()
files=[a.source/'binding/SkiaSharp/SkiaApi.generated.cs',a.source/'binding/HarfBuzzSharp/HarfBuzzApi.generated.cs']
text='\n'.join(x.read_text() if x.is_file() else subprocess.check_output(['git','-C',str(a.source),'show','HEAD:'+str(x.relative_to(a.source))],text=True) for x in files)
structs={}
# Generated structs use public fields; omit properties and methods.
for match in re.finditer(r'\bstruct\s+(\w+)\s*\{',text):
pos=match.end();depth=1;end=pos
while depth:
if text[end]=='{':depth+=1
if text[end]=='}':depth-=1
end+=1
block=text[pos:end-1]
structs[match[1]]=[m[1].strip() for m in re.finditer(r'^\s*public\s+(.+?)\s+\w+;\s*$',block,re.M)]
sizes={}
def layout(t):
t=re.sub(r'\b(?:ref|out|in|readonly)\b','',t).strip()
if '*' in t or t.endswith(('[]','Delegate')):return 4,4
if t in sizes:return sizes[t],4
if t in structs:raise KeyError(t)
if t in ['Int64','UInt64','long','ulong','Double','double']:return 8,4
if t in ['Int16','UInt16','short','ushort']:return 2,2
if t in ['Byte','SByte','byte','sbyte','Boolean','bool']:return 1,1
return 4,4 # enums and generated opaque pointer aliases
pending=set(structs)
while pending:
done=set()
for name in sorted(pending):
offset=0;align=1
try:
for t in structs[name]:
n,k=layout(t);offset=(offset+k-1)//k*k+n;align=max(align,k)
except KeyError:continue
sizes[name]=(offset+align-1)//align*align;done.add(name)
if not done:raise SystemExit('Unresolved managed struct layout: '+str(pending))
pending-=done
symbols=set()
for path in a.libraries.glob('lib*.a'):
result=subprocess.run(['nm','--defined-only',str(path)],capture_output=True,text=True,check=True)
symbols.update(re.findall(r'\bT\s+(_(?:sk|gr|hb)_\w+)\s*$',result.stdout,re.M))
thunks={}
def parameters(s):
result=[];depth=0;start=0
for i,c in enumerate(s):
if c in '[<(':depth+=1
elif c in ']> )'.replace(' ',''):depth-=1
elif c==',' and depth==0:result.append(s[start:i]);start=i+1
result.append(s[start:]);return [v.strip() for v in result if v.strip()]
for m in re.finditer(r'internal static (?:extern|partial)\s+.+?\s+((?:sk|gr|hb)_\w+)\s*\((.*?)\);',text,re.S):
symbol='_'+m[1]
if symbol not in symbols:continue
count=0
for param in parameters(m[2]):
param=re.sub(r'/\*.*?\*/|\[[^\]]+\]','',param).strip();typ=param.rsplit(' ',1)[0]
n,_=layout(typ);count+=(n+3)//4*4
thunks[symbol+'@'+str(count)]=(symbol,count)
if not thunks:raise SystemExit('No x86 exported APIs matched pinned managed bindings')
assembly=['.text','.set "@feat.00", 1']
for name,(symbol,count) in sorted(thunks.items()):
assembly+=['.globl "'+name+'"','"'+name+'":']+['pushl '+str(count)+'(%esp)']*(count//4)
assembly+=['calll '+symbol,'addl $'+str(count)+', %esp','retl $'+str(count)]
path=a.libraries/'skia_x86_stdcall_thunks.s';path.write_text('\n'.join(assembly)+'\n');obj=path.with_suffix('.o')
subprocess.run([str(a.toolchain/'cc'),'-c',str(path),'-o',str(obj)],check=True)
subprocess.run([str(a.toolchain/'ar'),'rcs',str(a.libraries/'skia_x86_stdcall_thunks.lib'),str(obj)],check=True)
(a.libraries/'x86-thunks.json').write_text(json.dumps({'count':len(thunks),'layouts':sizes,'thunks':thunks},indent=2)+'\n')
print('Generated',len(thunks),'x86 stdcall thunks')
+293
View File
@@ -0,0 +1,293 @@
#!/usr/bin/env python3
"""Pinned Zig compiler adapters for static-library cross-builds on Linux."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import shlex
import shutil
import subprocess
import sys
import re
TARGETS = {
"linux-x64": "x86_64-linux-gnu.2.36.0", "linux-arm64": "aarch64-linux-gnu.2.36.0",
"linux-musl-x64": "x86_64-linux-musl", "linux-musl-arm64": "aarch64-linux-musl",
"win-x64": "x86_64-windows-gnu", "win-x86": "x86-windows-gnu",
"win-arm64": "aarch64-windows-gnu", "osx-x64": "x86_64-macos.10.13.0",
"osx-arm64": "aarch64-macos.11.0.0",
}
def mingw_overlay(zig_lib, output):
source = zig_lib / "libc/include/any-windows-any/dwrite_3.h"
text = source.read_text()
# Add only non-virtual overloads/default arguments. Keep IID, virtual order,
# virtual parameter types and C vtables exactly as supplied by MinGW.
for name in ("IDWritePaintReader", "IDWriteFontFace4"):
start = text.index(name + " : public")
end = text.index("#ifdef __CRT_UUID_DECL", start)
block = text[start:end]
if name == "IDWritePaintReader":
block = block.replace("UINT32 struct_size) = 0;",
"UINT32 struct_size = sizeof(DWRITE_PAINT_ELEMENT)) = 0;")
extra = """ HRESULT SetCurrentGlyph(UINT32 glyph, DWRITE_PAINT_ELEMENT *element,
D2D_RECT_F *clip, DWRITE_PAINT_ATTRIBUTES *attributes = 0) {
return SetCurrentGlyph(glyph, element, sizeof(DWRITE_PAINT_ELEMENT), clip, attributes);
}
HRESULT SetTextColor(const DWRITE_COLOR_F &color) { return SetTextColor(&color); }
"""
else:
extra = """ HRESULT GetGlyphImageFormats(UINT16 glyph, UINT32 first, UINT32 last,
DWRITE_GLYPH_IMAGE_FORMATS *formats) {
return GetGlyphImageFormats_(glyph, first, last, formats);
}
"""
index = block.rindex("};")
block = block[:index] + extra + block[index:]
text = text[:start] + block + text[end:]
output.mkdir(parents=True, exist_ok=True)
(output / "dwrite_3.h").write_text(text)
for name in ("XpsObjectModel.h", "ObjBase.h", "DispatcherQueue.h", "VersionHelpers.h", "T2EmbApi.h", "FontSub.h"):
alias = output / name
if alias.is_symlink():
alias.unlink()
alias.symlink_to(source.parent / name.lower())
return hashlib.sha256(source.read_bytes()).hexdigest()
def compiler(config, tool, incoming):
target = config["target"]
if "--version" in incoming and "linux-gnu." in target:
target = target.split("linux-gnu.")[0] + "linux-gnu"
windows = "windows" in target
args = ["-I" + config["include"]] if windows else [
"-isysroot", config["sdk"], "-idirafter", config["sdk"] + "/usr/include",
"-F", config["sdk"] + "/System/Library/Frameworks", "-Wno-elaborated-enum-base",
] if "macos" in target else []
if config.get('linux_abi_headers'):
args += ['-I' + config['linux_abi_headers']]
if config.get('linux_headers'):
args += ['-I' + config['linux_headers'], '-I' + config['linux_headers'] + '/freetype2']
if config.get('linux_sysroot') and '-c' not in incoming and '-o' in incoming:
root = Path(config['linux_sysroot'])
cpu = 'x86_64' if target.startswith('x86_64') else 'aarch64'
for path in (root / 'usr/lib' / (cpu + '-linux-gnu'), root / 'lib' / (cpu + '-linux-gnu'), root / 'usr/lib', root / 'lib'):
args += ['-L' + str(path)]
if 'linux' in target and '-c' not in incoming and '-o' in incoming:
args.append('-fuse-ld=lld')
if windows:
# Accept Windows header/Skia spellings such as __forceinline while
# retaining the GNU target ABI and leaving _MSC_VER undefined.
args.append("-fms-extensions")
if windows and config.get("mingw_headers"):
args.extend(("-idirafter", config["mingw_headers"]))
mapping = {
"/O2": "-O2", "/fp:precise": "-ffp-model=precise", "/std:c++20": "-std=c++20",
"/std:c11": "-std=c11", "/GR": "-frtti", "/GR-": "-fno-rtti", "/w": "-w",
"/Ob2": "-finline-functions", "/arch:AVX2": "-mcpu=x86_64_v3",
"/arch:AVX512": "-mcpu=x86_64_v4+evex512", "/TC": "-xc", "/TP": "-xc++",
"/Gy": "-ffunction-sections", "/Gw": "-fdata-sections",
"/Oy-": "-fno-omit-frame-pointer", "/guard:cf": "-mguard=cf",
"/W3": "-Wall",
"-march=x86-64-v3": "-mcpu=x86_64_v3", "-march=x86-64-v4": "-mcpu=x86_64_v4+evex512",
"-march=armv8-a+crc+crypto": "-mcpu=generic+crc+aes+sha2",
}
iterator = iter(incoming)
for flag in iterator:
if flag in ("-Wl,--fatal-warnings", "-Wl,--disable-new-dtags"):
continue
if flag in ("-o", "-MF", "-MT", "-MQ", "-I", "-isystem", "-isysroot", "-F", "-idirafter", "-include"):
args.extend((flag, next(iterator)))
continue
if flag in ("-target", "--target"):
next(iterator)
continue
if flag.startswith("--target="):
continue
if flag.startswith("/clang:"):
flag = flag[7:]
if flag.startswith(("-fdiagnostics-show-inlining-chain", "-fno-lifetime-dse",
"-fsanitize-ignore-for-ubsan-feature=", "-fmsc-version=")):
continue
if flag.startswith("/D"):
flag = "-D" + flag[2:]
if re.fullmatch(r"/std:(c\+\+\d+|c\d+)", flag):
flag = "-std=" + flag[5:]
if flag in ("/arch:SSE", "/arch:SSE2", "/arch:AVX"):
flag = {"/arch:SSE": "-msse", "/arch:SSE2": "-msse2", "/arch:AVX": "-mavx"}[flag]
if flag == "/W4":
args.extend(("-Wall", "-Wextra"))
continue
if flag in ("/bigobj", "/utf-8", "/Zc:inline", "/Zc:lambda", "/Zc:twoPhase", "/FS", "/MT"):
continue
if flag.startswith(("/wd", "/we")):
continue # MSVC diagnostic numbers have no GNU Clang equivalent.
if windows and flag.startswith("/") and flag not in mapping and not Path(flag).exists():
raise SystemExit("Unsupported MSVC option: " + flag)
if flag == '-stdlib=libstdc++':
flag = '-stdlib=libc++'
args.append(mapping.get(flag, flag))
if any(flag.startswith("-mavx512") for flag in args):
args.append("-mevex512")
if windows:
if "-DCRC32_SIMD_SSE42_PCLMUL" in args:
args.extend(("-msse4.2", "-mpclmul"))
if "-DADLER32_SIMD_SSSE3" in args:
args.append("-mssse3")
# Older MSVC GN rules express SSE dispatch through a macro: Clang needs
# matching CPU features on those units, never on ordinary baseline units.
if target.startswith(("x86", "i386")):
levels = {"SSE2": ["-msse2"], "SSE3": ["-msse3"], "SSSE3": ["-mssse3"],
"SSE41": ["-msse4.1"], "SSE42": ["-msse4.2", "-mcrc32"]}
for level, features in levels.items():
if "-DSK_CPU_SSE_LEVEL=SK_CPU_SSE_LEVEL_" + level in args:
args.extend(features)
return [config["zig"], "cc" if tool == "cc" else "c++", "-target", target, *args]
def execute(config, tool, incoming):
if tool in ("cc", "cxx"):
command = compiler(config, tool, incoming)
elif tool == "ar":
flags = [x.replace("rcsT", "rcs") for x in incoming if x not in ("/WX", "/ignore:4221", "-T", "--thin")]
command = [config["zig"], "ar"]
if "macos" in config["target"]:
command.append("--format=darwin")
command += flags
else:
output = None
inputs = []
iterator = iter(incoming)
for flag in iterator:
if flag in ("-static", "-no_warning_for_no_symbols"):
continue
if flag == "-o":
output = next(iterator)
elif flag == "-filelist":
inputs.extend(Path(next(iterator)).read_text().splitlines())
elif flag.startswith("@"):
inputs.extend(shlex.split(Path(flag[1:]).read_text()))
else:
inputs.append(flag)
if not output:
raise SystemExit("Darwin libtool adapter needs -o")
command = [config["zig"], "ar", "--format=darwin", "rcs", output, *inputs]
if tool in ('ar', 'libtool'):
# Never leave a truncated archive behind when disk/network/job failures
# interrupt archiving. GN response files contain a complete member list.
index = next((i for i, arg in enumerate(command[2:], 2)
if not arg.startswith('@') and Path(arg).suffix in ('.a', '.lib')), None)
if index is None:
raise SystemExit('Cannot identify archive output: ' + str(command))
dest = Path(command[index])
temporary = dest.with_name(dest.name + '.tmp-' + str(os.getpid()))
temporary.unlink(missing_ok=True)
objects = [x for x in command[index + 1:] if not x.startswith('-')]
if tool == 'ar' and dest.exists() and len(objects) == 1 and not objects[0].startswith('@'):
shutil.copy2(dest, temporary) # Explicit runtime/availability append.
command[index] = str(temporary)
try:
subprocess.run(command, check=True)
os.replace(temporary, dest)
finally:
temporary.unlink(missing_ok=True)
return
cache = shutil.which('ccache')
if tool in ('cc', 'cxx') and cache and os.environ.get('CCACHE_DIR') and '-c' in incoming:
os.environ['CCACHE_COMPILERTYPE'] = 'clang'
raw = str(Path(config['output']) / ('_cached-' + tool))
os.execv(cache, [cache, raw, *command[4:]])
os.execv(config["zig"], command)
def prepare(args):
zig = str(Path(shutil.which(args.zig) or args.zig).resolve())
version = subprocess.check_output([zig, "version"], text=True).strip()
if version != "0.14.1":
raise SystemExit("This adapter is verified with Zig 0.14.1; got " + version)
lib = Path(json.loads(subprocess.check_output([zig, "env"], text=True))["lib_dir"])
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
config = {"zig": zig, "target": TARGETS[args.target], "version": version}
if args.target.startswith("win"):
include = output / "include"
# Regeneration is idempotent; remove only the generated SDK case alias.
alias = include / "XpsObjectModel.h"
if alias.is_symlink():
alias.unlink()
config["mingw_header_sha256"] = mingw_overlay(lib, include)
config["include"] = str(include)
if args.mingw_headers:
config["mingw_headers"] = str(args.mingw_headers.resolve())
header = args.mingw_headers / "windows.ui.composition.h"
if header.is_file():
original = header.read_text()
text = original
# WIDL 11 omitted namespace qualification on these imported
# types. Use the real declarations already imported by the
# header; preserve interface UUIDs and method/vtable order.
for name in ("DirectXAlphaMode", "DirectXPixelFormat"):
text = text.replace("enum " + name, "enum ABI::Windows::Graphics::DirectX::" + name)
text = text.replace("struct Size", "struct ABI::Windows::Foundation::Size")
for name in ("Vector2", "Vector3", "Vector4", "Matrix3x2", "Matrix4x4", "Quaternion"):
text = text.replace("struct " + name, "struct ABI::Windows::Foundation::Numerics::" + name)
(include / header.name).write_text(text)
config["composition_header_sha256"] = hashlib.sha256(header.read_bytes()).hexdigest()
elif args.target.startswith("osx"):
if not args.sdk or not (args.sdk / "SDKSettings.json").is_file():
raise SystemExit("macOS target requires --sdk pointing to a macOS SDK")
config["sdk"] = str(args.sdk.resolve())
settings = args.sdk / "SDKSettings.json"
if json.loads(settings.read_text()).get("Version") != "15.5":
raise SystemExit("ANGLE metadata adapter is pinned to macOS SDK 15.5")
config["sdk_settings_sha256"] = hashlib.sha256(settings.read_bytes()).hexdigest()
if args.target.startswith('linux'):
config['linux_sysroot'] = str(args.linux_sysroot.resolve()) if args.linux_sysroot else ''
config['linux_headers'] = str(args.linux_headers.resolve()) if args.linux_headers else ''
config['output'] = str(output)
if args.target.startswith('linux'):
root = Path(config['linux_sysroot'])
abi = output / 'include-linux'
abi.mkdir(exist_ok=True)
cpu = 'x86_64' if config['target'].startswith('x86_64') else 'aarch64'
for name in ('ffi.h', 'ffitarget.h'):
for prefix in (root / 'usr/include' / (cpu + '-linux-gnu'), root / 'usr/include'):
src = prefix / name
if src.is_file():
(abi / name).write_bytes(src.read_bytes())
break
config['linux_abi_headers'] = str(abi)
pc = output / 'pkg-config'
dirs = [root / 'usr/lib' / (cpu + '-linux-gnu') / 'pkgconfig', root / 'usr/lib/pkgconfig', root / 'usr/share/pkgconfig']
pc.write_text('#!/bin/sh\nexport PKG_CONFIG_SYSROOT_DIR=' + shlex.quote(str(root)) + '\nexport PKG_CONFIG_LIBDIR=' + shlex.quote(':'.join(map(str, dirs))) + '\nexec /usr/bin/pkg-config "$@"\n')
pc.chmod(0o755)
for kind in ('cc', 'cxx'):
raw = output / ('_cached-' + kind)
mode = 'cc' if kind == 'cc' else 'c++'
raw.write_text('#!/bin/sh\nexec ' + shlex.quote(zig) + ' ' + mode + ' -target ' + shlex.quote(config['target']) + ' "$@"\n')
raw.chmod(0o755)
descriptor = output / "toolchain.json"
descriptor.write_text(json.dumps(config, indent=2) + "\n")
for tool in ("cc", "cxx", "ar", "libtool"):
executable = output / tool
executable.write_text("#!/bin/sh\nexec python3 " + shlex.quote(str(Path(__file__).resolve())) +
" run " + shlex.quote(str(descriptor)) + " " + tool + ' "$@"\n')
executable.chmod(0o755)
print(descriptor)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "run":
execute(json.loads(Path(sys.argv[2]).read_text()), sys.argv[3], sys.argv[4:])
else:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--zig", default="zig")
parser.add_argument("--target", choices=TARGETS, required=True)
parser.add_argument("--sdk", type=Path)
parser.add_argument("--mingw-headers", type=Path, help="Pinned extra WinRT headers, after Zig's headers")
parser.add_argument("--linux-sysroot", type=Path)
parser.add_argument("--linux-headers", type=Path)
parser.add_argument("--output", type=Path, required=True)
prepare(parser.parse_args())
+5
View File
@@ -0,0 +1,5 @@
{
"branches": [
{"major": 4, "ref": "skia4", "angle_branch": "7922", "revision": "2"}
]
}
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env python3
import json, os
from pathlib import Path
import xml.etree.ElementTree as ET
lock = json.loads(os.environ["SOURCE_LOCK_JSON"])
path = Path("build/linux-toolchain/release.lock.json")
path.write_text(json.dumps(lock, indent=2) + "\n")
project = Path("NuGet/StaticGraphics/StaticLink.Avalonia.csproj")
root = ET.parse(project)
root.find(".//Version").text = os.environ["PACKAGE_VERSION"]
for item in root.findall(".//PackageReference"):
if item.attrib.get("Include") == "SkiaSharp":
item.set("Version", "[" + lock["skiasharp_version"] + "]")
root.write(project, encoding="unicode")
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
rid="${RID:-linux-x64}"
case "$rid" in
linux-x64) cpu=x64; platform=linux/amd64 ;;
linux-arm64) cpu=arm64; platform=linux/arm64 ;;
*) echo "Debian container currently supports linux-x64 and linux-arm64: $rid" >&2; exit 2 ;;
esac
source_lock="$(realpath "${SOURCE_LOCK:-$root/build/linux-toolchain/sources.lock.json}")"
case "$source_lock" in
"$root/"*) container_lock="/src/${source_lock#"$root/"}" ;;
*) echo "SOURCE_LOCK must be inside the mounted repository" >&2; exit 2 ;;
esac
lock_key="$(sha256sum "$source_lock" | cut -c1-16)"
eval "$(python3 - "$source_lock" <<'PY'
import json, shlex, sys
lock = json.load(open(sys.argv[1]))
for key, value in lock.items():
print(f'{key.upper()}={shlex.quote(value)}')
PY
)"
image="staticlink-debian12:$(sha256sum "$root/build/linux-toolchain/Dockerfile" | cut -c1-16)"
docker build --platform "$platform" -t "$image" "$root/build/linux-toolchain"
docker run --rm --platform "$platform" \
--user "$(id -u):$(id -g)" \
-v "$root:/src" -w /src \
-e HOME=/tmp/staticlink-home \
-e RID="$rid" -e TARGET_CPU="$cpu" \
-e BUILD_JOBS="${BUILD_JOBS:-4}" \
-e WORK_DIR="/src/External/NativeStatic/.work/native-$lock_key" \
-e SOURCE_LOCK="$container_lock" \
-e SKIASHARP_VERSION="$SKIASHARP_VERSION" -e SKIASHARP_REVISION="$SKIASHARP_REVISION" \
-e ANGLE_BRANCH="$ANGLE_BRANCH" -e ANGLE_REVISION="$ANGLE_REVISION" \
-e DEPOT_TOOLS_DIR=/src/External/NativeStatic/.work/depot_tools \
"$image" bash -c '
set -euo pipefail
mkdir -p "$HOME" "$(dirname "$DEPOT_TOOLS_DIR")"
# The image checkout is read-only for a non-root user; gclient needs a writable copy.
if [[ ! -d "$DEPOT_TOOLS_DIR/.git" ]]; then
cp -a /opt/depot_tools "$DEPOT_TOOLS_DIR"
fi
scripts/build-linux-static-graphics.sh "${1:-all}"
cp /opt/staticlink/packages.lock "External/NativeStatic/$RID/toolchain-packages.lock"
cp "$SOURCE_LOCK" "External/NativeStatic/$RID/sources.lock.json"
' bash "${1:-all}"
+47 -21
View File
@@ -53,10 +53,20 @@ ensure_tools() {
}
ensure_depot_tools() {
local depot_dir="$WORK_DIR/depot_tools"
local depot_dir="${DEPOT_TOOLS_DIR:-$WORK_DIR/depot_tools}"
local python_bin_dir
python_bin_dir="$(dirname "$(command -v python3)")"
if [[ ! -d "$depot_dir/.git" ]]; then
if [[ -n "${DEPOT_TOOLS_REVISION:-}" ]]; then
if [[ ! -d "$depot_dir/.git" ]]; then
git init "$depot_dir"
git -C "$depot_dir" remote add origin https://chromium.googlesource.com/chromium/tools/depot_tools.git
fi
if [[ "$(git -C "$depot_dir" rev-parse HEAD 2>/dev/null || true)" != "$DEPOT_TOOLS_REVISION" ]]; then
git -C "$depot_dir" fetch --depth 1 origin "$DEPOT_TOOLS_REVISION"
git -C "$depot_dir" checkout --detach FETCH_HEAD
fi
export DEPOT_TOOLS_UPDATE=0
elif [[ ! -d "$depot_dir/.git" ]]; then
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git "$depot_dir"
else
git -C "$depot_dir" pull --ff-only
@@ -136,7 +146,14 @@ PY
sync_skiasharp() {
local src="$WORK_DIR/SkiaSharp-$SKIASHARP_VERSION"
if [[ ! -d "$src/.git" ]]; then
if [[ -n "${SKIASHARP_REVISION:-}" ]]; then
if [[ ! -d "$src/.git" ]]; then
git init "$src" >&2
git -C "$src" remote add origin https://github.com/mono/SkiaSharp.git
fi
git -C "$src" fetch --depth 1 origin "$SKIASHARP_REVISION"
git -C "$src" checkout --detach FETCH_HEAD >&2
elif [[ ! -d "$src/.git" ]]; then
git clone --depth 1 --branch "release/$SKIASHARP_VERSION" https://github.com/mono/SkiaSharp.git "$src"
else
git -C "$src" fetch --depth 1 origin "release/$SKIASHARP_VERSION"
@@ -155,7 +172,7 @@ import sys
path = pathlib.Path(sys.argv[1])
text = path.read_text()
deps_path = path.with_name("DEPS")
deps_path = path.parent.parent / "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)
@@ -169,21 +186,21 @@ PY
sync_skia_deps() {
local skia_dir="$1/externals/skia"
if [[ ! -x "$skia_dir/bin/gn" ]]; then
prepare_skia_git_sync_deps "$skia_dir"
# GN can exist after a partial/failed sync; it is not a completion marker.
# git-sync-deps reuses already-correct checkouts and repairs missing dependencies.
prepare_skia_git_sync_deps "$skia_dir"
local attempt
for attempt in $(seq 1 "$SKIA_DEPS_RETRIES"); do
if python3 "$skia_dir/tools/git-sync-deps"; then
return 0
fi
if [[ "$attempt" == "$SKIA_DEPS_RETRIES" ]]; then
return 1
fi
echo "git-sync-deps failed; retrying ($attempt/$SKIA_DEPS_RETRIES)..." >&2
sleep 10
done
fi
local attempt
for attempt in $(seq 1 "$SKIA_DEPS_RETRIES"); do
if GIT_SYNC_DEPS_SKIP_EMSDK=1 python3 "$skia_dir/tools/git-sync-deps"; then
return 0
fi
if [[ "$attempt" == "$SKIA_DEPS_RETRIES" ]]; then
return 1
fi
echo "git-sync-deps failed; retrying ($attempt/$SKIA_DEPS_RETRIES)..." >&2
sleep 10
done
}
build_skia() {
@@ -233,7 +250,7 @@ extra_cflags_cc = [ "-frtti" ]
extra_ldflags = [ "-static-libstdc++", "-static-libgcc" ]
EOF_ARGS
(cd "$skia_dir" && "$skia_dir/bin/gn" gen "$out_dir")
python3 "$ROOT_DIR/build/linux-toolchain/skia_compat.py" --source "$skia_dir" --out "$out_dir"
ninja -C "$out_dir" -j "$BUILD_JOBS" skia SkiaSharp HarfBuzzSharp
copy_first_existing "$OUTPUT_DIR/libskia.a" \
@@ -249,7 +266,14 @@ EOF_ARGS
sync_angle() {
local src="$WORK_DIR/ANGLE-$ANGLE_BRANCH"
if [[ ! -d "$src/.git" ]]; then
if [[ -n "${ANGLE_REVISION:-}" ]]; then
if [[ ! -d "$src/.git" ]]; then
git init "$src" >&2
git -C "$src" remote add origin https://github.com/google/angle.git
fi
git -C "$src" fetch --depth 1 origin "$ANGLE_REVISION"
git -C "$src" checkout --detach FETCH_HEAD >&2
elif [[ ! -d "$src/.git" ]]; then
git clone --depth 1 --branch "chromium/$ANGLE_BRANCH" https://github.com/google/angle.git "$src"
else
git -C "$src" fetch --depth 1 origin "chromium/$ANGLE_BRANCH"
@@ -544,4 +568,6 @@ main() {
esac
}
main "$@"
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
main "$@"
fi
+3 -1
View File
@@ -30,7 +30,9 @@ static void StaticLinkAvaloniaPreinitializeAppKitFonts(void)
}
OBJC
clang -arch "$ARCH" \
compiler=("${CC:-clang}")
if [[ "${STATICLINK_ZIG_CROSS:-0}" != 1 ]]; then compiler+=(-arch "$ARCH"); fi
"${compiler[@]}" \
-mmacosx-version-min=12.0 \
-fobjc-arc \
-c "$src" \
+16 -5
View File
@@ -33,8 +33,11 @@ ensure_depot_tools() {
if [[ ! -d "$depot_dir/.git" ]]; then
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git "$depot_dir"
else
git -C "$depot_dir" pull --ff-only
: # The pinned checkout is refreshed below
fi
git -C "$depot_dir" fetch --depth 1 origin 8a5434051036b32412a2ecb10c213a72e3f3ccb9
git -C "$depot_dir" checkout -q --force FETCH_HEAD
export DEPOT_TOOLS_UPDATE=0
export PATH="$depot_dir:$PATH"
}
@@ -56,11 +59,15 @@ copy_first_existing() {
sync_skiasharp() {
local src="$WORK_DIR/SkiaSharp-$SKIASHARP_VERSION"
if [[ ! -d "$src/.git" ]]; then
git clone --depth 1 --branch "release/$SKIASHARP_VERSION" https://github.com/mono/SkiaSharp.git "$src"
git clone --depth 1 --branch "v$SKIASHARP_VERSION" https://github.com/mono/SkiaSharp.git "$src"
else
git -C "$src" fetch --depth 1 origin "release/$SKIASHARP_VERSION"
git -C "$src" fetch --depth 1 origin "${SKIASHARP_REVISION:-refs/tags/v$SKIASHARP_VERSION}"
git -C "$src" checkout -q FETCH_HEAD
fi
if [[ -n "${SKIASHARP_REVISION:-}" ]]; then
git -C "$src" fetch --depth 1 origin "$SKIASHARP_REVISION"
git -C "$src" checkout -q --force FETCH_HEAD
fi
git -C "$src" submodule update --init --depth 1 externals/skia >&2
echo "$src"
}
@@ -74,7 +81,7 @@ import sys
path = pathlib.Path(sys.argv[1])
text = path.read_text()
deps_path = path.with_name("DEPS")
deps_path = path.parent.parent / "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)
@@ -147,7 +154,7 @@ extra_cflags = [ "-DSKIA_C_DLL" ]
extra_cflags_cc = [ "-frtti" ]
EOF_ARGS
(cd "$skia_dir" && "$skia_dir/bin/gn" gen "$out_dir")
python3 "$ROOT_DIR/build/linux-toolchain/skia_compat.py" --source "$skia_dir" --out "$out_dir"
ninja -C "$out_dir" -j "$BUILD_JOBS" skia SkiaSharp HarfBuzzSharp
copy_first_existing "$OUTPUT_DIR/libskia.a" "$out_dir/libskia.a" "$out_dir/obj/libskia.a"
copy_first_existing "$OUTPUT_DIR/libSkiaSharp.a" "$out_dir/libSkiaSharp.a" "$out_dir/obj/libSkiaSharp.a"
@@ -162,6 +169,10 @@ sync_angle() {
git -C "$src" fetch --depth 1 origin "chromium/$ANGLE_BRANCH"
git -C "$src" checkout -q FETCH_HEAD
fi
if [[ -n "${ANGLE_REVISION:-}" ]]; then
git -C "$src" fetch --depth 1 origin "$ANGLE_REVISION"
git -C "$src" checkout -q --force FETCH_HEAD
fi
echo "$src"
}
+24 -4
View File
@@ -56,8 +56,13 @@ function Ensure-DepotTools {
if (-not (Test-Path (Join-Path $depotDir ".git"))) {
$null = git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git $depotDir
} else {
$null = git -C $depotDir pull --ff-only
# Refresh the pinned checkout below.
}
$null = git -C $depotDir fetch --depth 1 origin 8a5434051036b32412a2ecb10c213a72e3f3ccb9
if ($LASTEXITCODE -ne 0) { throw "Cannot fetch pinned depot_tools" }
$null = git -C $depotDir checkout -q --force FETCH_HEAD
if ($LASTEXITCODE -ne 0) { throw "Cannot checkout pinned depot_tools" }
$env:DEPOT_TOOLS_UPDATE = "0"
$env:PATH = "$depotDir;$env:PATH"
}
@@ -344,11 +349,17 @@ function New-WinX86SkiaStdcallThunks([string[]]$InputLibraries, [string[]]$Bindi
function Sync-SkiaSharp {
$src = Join-Path $WorkDir "SkiaSharp-$SkiaSharpVersion"
if (-not (Test-Path (Join-Path $src ".git"))) {
$null = git -c core.longpaths=true clone --depth 1 --branch "release/$SkiaSharpVersion" https://github.com/mono/SkiaSharp.git $src
$null = git -c core.longpaths=true clone --depth 1 --branch "v$SkiaSharpVersion" https://github.com/mono/SkiaSharp.git $src
} else {
$null = git -C $src fetch --depth 1 origin "release/$SkiaSharpVersion"
$null = git -C $src fetch --depth 1 origin "refs/tags/v$SkiaSharpVersion"
$null = git -C $src checkout -q FETCH_HEAD
}
if ($env:SKIASHARP_REVISION) {
$null = git -C $src fetch --depth 1 origin $env:SKIASHARP_REVISION
if ($LASTEXITCODE -ne 0) { throw "Cannot fetch locked SkiaSharp commit" }
$null = git -C $src checkout -q --force FETCH_HEAD
if ($LASTEXITCODE -ne 0) { throw "Cannot checkout locked SkiaSharp commit" }
}
$null = git -C $src submodule update --init --depth 1 externals/skia
return $src
}
@@ -359,7 +370,9 @@ function Patch-WinX86SkiaLinker($SkiaDir) {
}
$linkerPath = Join-Path $SkiaDir "src\c\sk_linker.cpp"
if (-not (Test-Path $linkerPath)) { return }
$text = Get-Content -Path $linkerPath -Raw
if ($text -notmatch 'skjson::ObjectValue') { return }
$pattern = '(?m)^ skjson::ObjectValue\* a = nullptr;\r?\n auto r = \(\*a\)\["tmp"\]\.getType\(\);\r?$'
$patched = [regex]::Replace($text, $pattern, ' int r = 0;')
if ($patched -eq $text) {
@@ -447,7 +460,8 @@ extra_cflags_cc = [ "/GR" ]
Push-Location $skiaDir
try {
& (Join-Path $skiaDir "bin\gn.exe") gen $outDir
python (Join-Path $RootDir "build\linux-toolchain\skia_compat.py") --source $skiaDir --out $outDir
if ($LASTEXITCODE -ne 0) { throw "Skia capability configuration failed" }
ninja -C $outDir -j $BuildJobs skia SkiaSharp HarfBuzzSharp
} finally {
Pop-Location
@@ -480,6 +494,12 @@ function Sync-Angle {
$null = git -C $src fetch --depth 1 origin "chromium/$AngleBranch"
$null = git -C $src checkout -q FETCH_HEAD
}
if ($env:ANGLE_REVISION) {
$null = git -C $src fetch --depth 1 origin $env:ANGLE_REVISION
if ($LASTEXITCODE -ne 0) { throw "Cannot fetch locked ANGLE commit" }
$null = git -C $src checkout -q --force FETCH_HEAD
if ($LASTEXITCODE -ne 0) { throw "Cannot checkout locked ANGLE commit" }
}
return $src
}
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""Plan stable releases. HTTP/network errors fail closed; only 404 means absent."""
import argparse, json, re, urllib.request, urllib.error
from pathlib import Path
def get(url):
for attempt in range(3):
try:
with urllib.request.urlopen(url, timeout=45) as response:
return json.load(response)
except urllib.error.HTTPError as error:
if error.code == 404:
return None
if error.code < 500 and error.code != 429:
raise
if attempt == 2:
raise
except (OSError, TimeoutError):
if attempt == 2:
raise
raise RuntimeError("Unreachable")
def plan(config, upstream, published, force=False):
result = []
for entry in config["branches"]:
major = entry["major"]
candidates = [v for v in upstream if re.fullmatch(r"\d+\.\d+\.\d+", v)
and int(v.split(".")[0]) == major]
version = entry.get("fixed_version") or max(candidates, key=lambda v: tuple(map(int, v.split("."))))
package = f"{version}-{entry['angle_branch']}.{entry['revision']}"
if entry.get("daily", True) and (force or package.lower() not in {v.lower() for v in published}):
result.append({"ref": entry["ref"], "skiasharp_version": version, "package_version": package})
return result
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--config", default="build/release-tracks.json")
parser.add_argument("--output", default="release-plan.json")
parser.add_argument("--force", action="store_true")
args = parser.parse_args()
upstream = get("https://api.nuget.org/v3-flatcontainer/skiasharp/index.json")
published = get("https://api.nuget.org/v3-flatcontainer/staticlink.avalonia/index.json")
if upstream is None:
raise SystemExit("SkiaSharp package index is missing")
result = plan(json.loads(Path(args.config).read_text()), upstream["versions"], (published or {}).get("versions", []), force=args.force)
Path(args.output).write_text(json.dumps(result, indent=2) + "\n")
print(json.dumps(result))
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SOURCE_LOCK="${SOURCE_LOCK:-$SCRIPT_ROOT/build/linux-toolchain/sources.lock.json}"
SOURCE_LOCK="$(realpath "$SOURCE_LOCK")"
export WORK_DIR="${WORK_DIR:-$SCRIPT_ROOT/External/NativeStatic/.work/zig-sources}"
export LLVM_AR="${LLVM_AR:-llvm-ar}"
mkdir -p "$WORK_DIR"
read -r SKIASHARP_VERSION SKIASHARP_REVISION ANGLE_BRANCH ANGLE_REVISION < <(
python3 - "$SOURCE_LOCK" <<'PY'
import json, sys
lock = json.load(open(sys.argv[1]))
print(lock['skiasharp_version'], lock['skiasharp_revision'], lock['angle_branch'], lock['angle_revision'])
PY
)
export SKIASHARP_VERSION SKIASHARP_REVISION ANGLE_BRANCH ANGLE_REVISION
export WORK_DIR="$WORK_DIR/$SKIASHARP_VERSION-$(sha256sum "$SOURCE_LOCK" | cut -c1-16)"
mkdir -p "$WORK_DIR"
source "$SCRIPT_ROOT/scripts/build-linux-static-graphics.sh"
if [[ ! -d "$WORK_DIR/depot_tools/.git" ]]; then
cp -a /opt/depot_tools "$WORK_DIR/depot_tools"
fi
export DEPOT_TOOLS_DIR="$WORK_DIR/depot_tools"
ensure_tools
ensure_depot_tools
initialize_depot_tools_system_python "$DEPOT_TOOLS_DIR"
patch_depot_tools_python_deps "$DEPOT_TOOLS_DIR"
export VPYTHON_BYPASS="manually managed python not supported by chrome operations"
skia_sharp="$(sync_skiasharp)"
python3 "$SCRIPT_ROOT/build/linux-toolchain/sync-skia-deps.py" --source "$skia_sharp/externals/skia" --cache "$SCRIPT_ROOT/External/NativeStatic/.work/skia-deps"
angle="$(sync_angle)"
project_patch="$SCRIPT_ROOT/External/NativeStatic/patches/angle-chromium-$ANGLE_BRANCH.patch"
if git -C "$angle" apply --reverse --check "$project_patch" >/dev/null 2>&1; then
:
else
git -C "$angle" apply --check "$project_patch"
git -C "$angle" apply "$project_patch"
fi
python3 - "$angle/.gclient" <<'PY'
from pathlib import Path
import sys
Path(sys.argv[1]).write_text('''solutions = [{
"name": ".", "url": "https://chromium.googlesource.com/angle/angle.git",
"deps_file": "DEPS", "managed": False,
"custom_deps": {"third_party/llvm-build/Release+Asserts": None},
"custom_vars": {"checkout_angle_cl_deps": False, "checkout_angle_dawn_deps": False},
}]
target_os = ["linux", "win", "mac"]
''')
PY
# Zig and the image's SDKs supply the compiler/runtime; skip native Xcode/MSVC
# setup hooks while retaining the pinned source dependencies for every target.
(cd "$angle" && gclient sync --nohooks --force)
python3 - "$WORK_DIR/cross-sources.json" "$skia_sharp/externals/skia" "$angle" "$SOURCE_LOCK" <<'PY'
import json, sys
from pathlib import Path
Path(sys.argv[1]).write_text(json.dumps({'skia': sys.argv[2], 'angle': sys.argv[3],
'angle_gn': sys.argv[3] + '/buildtools/linux64/gn', 'source_lock': sys.argv[4]}, indent=2) + '\n')
PY
echo "Prepared $WORK_DIR/cross-sources.json"
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""Resolve a published SkiaSharp tag and its Skia gitlink to immutable commits."""
import argparse, json, re, subprocess, tempfile
from pathlib import Path
import xml.etree.ElementTree as ET
def git(*args):
return subprocess.check_output(["git", *args], text=True).strip()
p = argparse.ArgumentParser()
p.add_argument("--version", default="")
p.add_argument("--output", default="build/linux-toolchain/release.lock.json")
a = p.parse_args()
root = ET.parse("NuGet/StaticGraphics/StaticLink.Avalonia.csproj").getroot()
current = next(x.attrib["Version"] for x in root.findall(".//PackageReference") if x.attrib.get("Include") == "SkiaSharp")
version = a.version or current
if not re.fullmatch(r"\d+\.\d+\.\d+", version) or version.split(".")[0] != current.split(".")[0]:
raise SystemExit("Only stable versions from this branch's SkiaSharp major are allowed")
if current.startswith("2.") and version != "2.88.9":
raise SystemExit("SkiaSharp 2 is frozen at 2.88.9")
base, revision = root.findtext(".//Version").rsplit(".", 1)
angle = base.split("-", 1)[1]
# ANGLE remains fixed: changing SkiaSharp must not silently change its toolchain.
profile = json.loads(Path("build/linux-toolchain/sources.lock.json").read_text())
if profile["angle_branch"] != angle:
raise SystemExit("ANGLE branch does not match the source lock")
remote = "https://github.com/mono/SkiaSharp.git"
refs = git("ls-remote", "--tags", remote, f"refs/tags/v{version}", f"refs/tags/v{version}^{{}}")
if not refs:
raise SystemExit(f"No immutable upstream release tag for {version}; refusing a moving branch")
lines = refs.splitlines()
commit = next((x.split()[0] for x in lines if x.endswith("^{}")), lines[0].split()[0])
with tempfile.TemporaryDirectory() as tmp:
git("init", "-q", tmp)
git("-C", tmp, "fetch", "-q", "--depth=1", remote, commit)
tree = git("-C", tmp, "ls-tree", "FETCH_HEAD", "externals/skia")
mode, kind, skia, name = tree.split()
if mode != "160000" or kind != "commit":
raise SystemExit("Skia source is not a pinned git submodule")
profile.update(skiasharp_version=version, skiasharp_revision=commit, skia_revision=skia)
Path(a.output).write_text(json.dumps(profile, indent=2) + "\n")
package = f"{version}-{angle}.{revision}"
print(json.dumps({"skiasharp_version": version, "package_version": package, "lock": profile}))