diff --git a/.github/workflows/nuget-static-graphics.yml b/.github/workflows/nuget-static-graphics.yml new file mode 100644 index 0000000..70c5074 --- /dev/null +++ b/.github/workflows/nuget-static-graphics.yml @@ -0,0 +1,197 @@ +name: nuget-static-graphics + +on: + workflow_dispatch: + inputs: + publish_to_nuget: + description: Publish package to nuget.org + required: true + default: false + type: boolean + push: + tags: + - "static-graphics-v*" + +env: + SKIASHARP_VERSION: "3.119.2" + ANGLE_BRANCH: "7151" + +jobs: + build-linux: + strategy: + fail-fast: false + matrix: + include: + - rid: linux-x64 + target_cpu: x64 + runner: ubuntu-latest + - rid: linux-arm64 + target_cpu: arm64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v4 + + - name: Install native build dependencies + run: | + sudo apt-get update + sudo apt-get install -y clang lld llvm ninja-build pkg-config libfontconfig1-dev + + - name: Build Linux static graphics libraries + env: + RID: ${{ matrix.rid }} + TARGET_CPU: ${{ matrix.target_cpu }} + LLVM_AR: llvm-ar + 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 + + build-windows: + strategy: + fail-fast: false + matrix: + include: + - rid: win-x64 + target_cpu: x64 + msvc_arch: x64 + - rid: win-arm64 + target_cpu: arm64 + msvc_arch: amd64_arm64 + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup MSVC environment + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ matrix.msvc_arch }} + + - name: Install native build dependencies + shell: pwsh + run: choco install ninja llvm -y + + - name: Build Windows static graphics libraries + shell: pwsh + env: + RID: ${{ matrix.rid }} + TARGET_CPU: ${{ matrix.target_cpu }} + run: .\scripts\build-windows-static-graphics.ps1 all + + - name: Upload Windows static archives + uses: actions/upload-artifact@v4 + with: + name: native-static-${{ matrix.rid }} + path: External/NativeStatic/${{ matrix.rid }}/* + if-no-files-found: error + + build-macos: + strategy: + fail-fast: false + matrix: + include: + - rid: osx-x64 + target_cpu: x64 + - rid: osx-arm64 + target_cpu: arm64 + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Install native build dependencies + run: brew install ninja + + - name: Build macOS static graphics libraries + env: + RID: ${{ matrix.rid }} + TARGET_CPU: ${{ matrix.target_cpu }} + run: 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 + + pack: + runs-on: ubuntu-latest + needs: + - build-linux + - build-windows + - build-macos + 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 archives + uses: actions/download-artifact@v4 + with: + pattern: native-static-* + path: External/NativeStatic + merge-multiple: false + + - name: Move static archives into RID directories + run: | + for rid in win-x64 win-arm64 linux-x64 linux-arm64 osx-x64 osx-arm64; do + mkdir -p "External/NativeStatic/$rid" + mv "External/NativeStatic/native-static-$rid"/* "External/NativeStatic/$rid/" + rmdir "External/NativeStatic/native-static-$rid" + done + + - name: Verify static archives + run: | + for rid in linux-x64 linux-arm64 osx-x64 osx-arm64; do + test -f External/NativeStatic/$rid/libskia.a + test -f External/NativeStatic/$rid/libSkiaSharp.a + test -f External/NativeStatic/$rid/libHarfBuzzSharp.a + test -f External/NativeStatic/$rid/libANGLE_static.a + test -f External/NativeStatic/$rid/libGLESv2_static.a + done + + for rid in win-x64 win-arm64; do + test -f External/NativeStatic/$rid/skia.lib + test -f External/NativeStatic/$rid/SkiaSharp.lib + test -f External/NativeStatic/$rid/libHarfBuzzSharp.lib + test -f External/NativeStatic/$rid/libANGLE_static.lib + test -f External/NativeStatic/$rid/libGLESv2_static.lib + done + + - name: Pack NuGet package + run: dotnet pack NuGet/LinuxStaticGraphics/Greepar.Avalonia.StaticGraphics.csproj -c Release -o artifacts/nuget + + - name: Upload NuGet artifact + uses: actions/upload-artifact@v4 + with: + name: nuget-static-graphics + path: | + artifacts/nuget/*.nupkg + artifacts/nuget/*.snupkg + if-no-files-found: error + + - name: Publish to nuget.org + if: ${{ startsWith(github.ref, 'refs/tags/static-graphics-v') || (github.event_name == 'workflow_dispatch' && inputs.publish_to_nuget == true) }} + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + run: | + if [ -z "$NUGET_API_KEY" ]; then + echo "NUGET_API_KEY secret is not configured. Skipping publish." + exit 0 + fi + + for package in artifacts/nuget/*.nupkg; do + dotnet nuget push "$package" \ + --source "https://api.nuget.org/v3/index.json" \ + --api-key "$NUGET_API_KEY" \ + --skip-duplicate + done diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6f0af6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin/ +obj/ +artifacts/ +*.nupkg +*.snupkg +External/NativeStatic/.work/ diff --git a/External/NativeStatic/linux-x64/libANGLE_static.a b/External/NativeStatic/linux-x64/libANGLE_static.a new file mode 100644 index 0000000..11ab2b5 Binary files /dev/null and b/External/NativeStatic/linux-x64/libANGLE_static.a differ diff --git a/External/NativeStatic/linux-x64/libGLESv2_static.a b/External/NativeStatic/linux-x64/libGLESv2_static.a new file mode 100644 index 0000000..5ac5e06 Binary files /dev/null and b/External/NativeStatic/linux-x64/libGLESv2_static.a differ diff --git a/External/NativeStatic/linux-x64/libHarfBuzzSharp.a b/External/NativeStatic/linux-x64/libHarfBuzzSharp.a new file mode 100644 index 0000000..6289599 Binary files /dev/null and b/External/NativeStatic/linux-x64/libHarfBuzzSharp.a differ diff --git a/External/NativeStatic/linux-x64/libSkiaSharp.a b/External/NativeStatic/linux-x64/libSkiaSharp.a new file mode 100644 index 0000000..e319def Binary files /dev/null and b/External/NativeStatic/linux-x64/libSkiaSharp.a differ diff --git a/External/NativeStatic/linux-x64/libskia.a b/External/NativeStatic/linux-x64/libskia.a new file mode 100644 index 0000000..57d1cba Binary files /dev/null and b/External/NativeStatic/linux-x64/libskia.a differ diff --git a/External/NativeStatic/patches/angle-chromium-7151.patch b/External/NativeStatic/patches/angle-chromium-7151.patch new file mode 100644 index 0000000..e7f8167 --- /dev/null +++ b/External/NativeStatic/patches/angle-chromium-7151.patch @@ -0,0 +1,87 @@ +diff --git a/BUILD.gn b/BUILD.gn +index 07e1268a57..25f7cba544 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -1472,7 +1472,18 @@ if (angle_has_frame_capture) { + } + } + ++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") { ++ complete_static_lib = true + sources = libglesv2_sources + if (angle_enable_cl) { + sources += libglesv2_cl_sources +diff --git a/DEPS b/DEPS +index 9085ba513e..565840a832 100644 +--- a/DEPS ++++ b/DEPS +@@ -696,11 +696,6 @@ deps = { + 'dep_type': 'cipd', + }, + +- 'third_party/catapult': { +- 'url': Var('chromium_git') + '/catapult.git' + '@' + Var('catapult_revision'), +- 'condition': 'not build_with_chromium', +- }, +- + # Cherry is a dEQP/VK-GL-CTS management GUI written in Go. We use it for viewing test results. + 'third_party/cherry': { + 'url': Var('android_git') + '/platform/external/cherry@4f8fb08d33ca5ff05a1c638f04c85bbb8d8b52cc', +@@ -722,12 +717,6 @@ deps = { + 'condition': 'checkout_android and not build_with_chromium', + }, + +- +- 'third_party/dawn': { +- 'url': Var('dawn_git') + '/dawn.git' + '@' + Var('dawn_revision'), +- 'condition': 'not build_with_chromium' +- }, +- + 'third_party/depot_tools': { + 'url': Var('chromium_git') + '/chromium/tools/depot_tools.git@1fcc527019d786502b02f71b8b764ee674a40953', + 'condition': 'not build_with_chromium', +@@ -809,11 +798,6 @@ deps = { + 'condition': 'not build_with_chromium', + }, + +- 'third_party/llvm/src': { +- 'url': Var('chromium_git') + '/external/github.com/llvm/llvm-project@1fd8d3fea53e6e4573cdce55bd38ef0a7813a442', +- 'condition': 'not build_with_chromium', +- }, +- + 'third_party/jdk': { + 'packages': [ + { +@@ -1000,11 +984,6 @@ deps = { + 'condition': 'checkout_android and not build_with_chromium', + }, + +- 'third_party/SwiftShader': { +- 'url': Var('swiftshader_git') + '/SwiftShader@7905fa19e456df5aa8e2233a7ec5832c9c6c287b', +- 'condition': 'not build_with_chromium', +- }, +- + 'third_party/turbine/cipd': { + 'packages': [ + { +@@ -1016,10 +995,6 @@ deps = { + 'dep_type': 'cipd', + }, + +- 'third_party/VK-GL-CTS/src': { +- 'url': Var('chromium_git') + '/external/github.com/KhronosGroup/VK-GL-CTS' + '@' + Var('vk_gl_cts_revision'), +- }, +- + 'third_party/vulkan-deps': { + 'url': Var('chromium_git') + '/vulkan-deps@b9aab6c35f37e6532011ddab976c3ca71819c074', + 'condition': 'not build_with_chromium', diff --git a/NuGet/LinuxStaticGraphics/Greepar.Avalonia.StaticGraphics.csproj b/NuGet/LinuxStaticGraphics/Greepar.Avalonia.StaticGraphics.csproj new file mode 100644 index 0000000..703aeec --- /dev/null +++ b/NuGet/LinuxStaticGraphics/Greepar.Avalonia.StaticGraphics.csproj @@ -0,0 +1,32 @@ + + + net10.0 + true + true + false + true + Greepar.Avalonia.StaticGraphics + 3.119.2-7151.4 + greepar + greepar + Static SkiaSharp, HarfBuzzSharp, and ANGLE libraries for Avalonia NativeAOT single-file publishing. + Avalonia;SkiaSharp;ANGLE;NativeAOT;Windows;Linux;macOS;Static + MIT + README.md + git + https://github.com/greepar/Avalonia.StaticGraphics + https://github.com/greepar/Avalonia.StaticGraphics + + + + + + + + + + + + + + diff --git a/NuGet/LinuxStaticGraphics/README.md b/NuGet/LinuxStaticGraphics/README.md new file mode 100644 index 0000000..43dc51e --- /dev/null +++ b/NuGet/LinuxStaticGraphics/README.md @@ -0,0 +1,163 @@ +# Greepar.Avalonia.StaticGraphics + +Static graphics libraries for Avalonia NativeAOT single-file publishing. + +This package provides static archives for SkiaSharp, HarfBuzzSharp, and ANGLE, then wires them into MSBuild through `buildTransitive` targets. It is intended for Avalonia applications that want NativeAOT single-file publish outputs without shipping Avalonia graphics native libraries next to the executable. + +Repository: + +## Supported RIDs + +- `win-x64` +- `win-arm64` +- `linux-x64` +- `linux-arm64` +- `osx-x64` +- `osx-arm64` + +## Included Libraries + +Windows: + +- `skia.lib` +- `SkiaSharp.lib` +- `libHarfBuzzSharp.lib` +- `libANGLE_static.lib` +- `libGLESv2_static.lib` + +Linux and macOS: + +- `libskia.a` +- `libSkiaSharp.a` +- `libHarfBuzzSharp.a` +- `libANGLE_static.a` +- `libGLESv2_static.a` + +## Install + +```bash +dotnet add package Greepar.Avalonia.StaticGraphics +``` + +## Publish + +Windows x64 or arm64: + +```bash +dotnet publish -c Release -r win-x64 \ + -p:PublishAot=true \ + -p:SelfContained=true \ + -p:PublishSingleFile=true \ + -p:StripSymbols=true +``` + +Linux x64 or arm64: + +```bash +dotnet publish -c Release -r linux-x64 \ + -p:PublishAot=true \ + -p:SelfContained=true \ + -p:PublishSingleFile=true \ + -p:StripSymbols=true \ + -p:LinkerFlavor=lld +``` + +macOS x64 or arm64: + +```bash +dotnet publish -c Release -r osx-arm64 \ + -p:PublishAot=true \ + -p:SelfContained=true \ + -p:PublishSingleFile=true \ + -p:StripSymbols=true +``` + +The package automatically contributes the static archives as `NativeLibrary` items when `PublishAot=true` and the `RuntimeIdentifier` is supported. + +## Verify Output + +After publishing, the publish directory should not contain Avalonia graphics runtime files such as: + +- `libSkiaSharp.so` +- `libHarfBuzzSharp.so` +- `libSkiaSharp.dylib` +- `libHarfBuzzSharp.dylib` +- `libSkiaSharp.dll` +- `libHarfBuzzSharp.dll` +- `av_libglesv2.dll` + +The package stores static archives under `static//native/` inside the NuGet package, so `.a` and `.lib` files are used for linking and are not normal runtime assets. + +## Build From Source + +This repository does not depend on 2ndLAB static packages. The GitHub Actions workflow builds the native libraries from upstream source repositories: + +- SkiaSharp: +- ANGLE: + +Build scripts: + +- `scripts/build-linux-static-graphics.sh` +- `scripts/build-windows-static-graphics.ps1` +- `scripts/build-macos-static-graphics.sh` + +Build locally after the static archives exist: + +```bash +dotnet pack NuGet/LinuxStaticGraphics/Greepar.Avalonia.StaticGraphics.csproj -c Release -o artifacts/nuget +``` + +The package will be written to: + +```text +artifacts/nuget/Greepar.Avalonia.StaticGraphics.*.nupkg +``` + +## GitHub Actions + +The workflow `.github/workflows/nuget-static-graphics.yml` builds all native archives and packs a single NuGet package for all supported RIDs. + +It will: + +- build Linux x64 static archives on `ubuntu-latest` +- build Linux arm64 static archives on `ubuntu-24.04-arm` +- build Windows x64 and arm64 static archives on `windows-latest` +- build macOS x64 and arm64 static archives on `macos-latest` +- download all native artifacts into `External/NativeStatic//` +- run `dotnet pack` +- upload the generated `.nupkg` as a workflow artifact + +To publish automatically to nuget.org, add a repository secret named `NUGET_API_KEY`, then either: + +- run the workflow manually with `publish_to_nuget=true` +- push a tag matching `static-graphics-v*` + +Example tag: + +```bash +git tag static-graphics-v3.119.2-7151.4 +git push origin static-graphics-v3.119.2-7151.4 +``` + +## Known Runtime Dependencies + +Single-file does not always mean dependency-free. Depending on platform and publish settings, the final executable may still depend on operating system libraries and frameworks. + +Linux examples: + +- `libc.so.6` +- `libm.so.6` +- `libstdc++.so.6` +- `libgcc_s.so.1` +- `libfontconfig.so.1` +- `libfreetype.so.6` + +Linux systems also need usable fonts for text rendering and font fallback. + +## Scope + +This package only covers Avalonia graphics-related native libraries. If your application uses other native dependencies, such as SQLite, audio backends, OpenSSL, or custom native libraries, those dependencies may require separate handling. + +## License + +The NuGet package metadata uses the MIT license. The bundled third-party native libraries are built from their upstream projects and remain subject to their respective upstream licenses. diff --git a/NuGet/LinuxStaticGraphics/buildTransitive/Greepar.Avalonia.StaticGraphics.props b/NuGet/LinuxStaticGraphics/buildTransitive/Greepar.Avalonia.StaticGraphics.props new file mode 100644 index 0000000..77443c0 --- /dev/null +++ b/NuGet/LinuxStaticGraphics/buildTransitive/Greepar.Avalonia.StaticGraphics.props @@ -0,0 +1,5 @@ + + + $(MSBuildThisFileDirectory)../static/ + + diff --git a/NuGet/LinuxStaticGraphics/buildTransitive/Greepar.Avalonia.StaticGraphics.targets b/NuGet/LinuxStaticGraphics/buildTransitive/Greepar.Avalonia.StaticGraphics.targets new file mode 100644 index 0000000..70d95b9 --- /dev/null +++ b/NuGet/LinuxStaticGraphics/buildTransitive/Greepar.Avalonia.StaticGraphics.targets @@ -0,0 +1,92 @@ + + + <_GreeparAvaloniaStaticGraphicsRid Condition="'$(RuntimeIdentifier)' == 'win-x64'">win-x64 + <_GreeparAvaloniaStaticGraphicsRid Condition="'$(RuntimeIdentifier)' == 'win-arm64'">win-arm64 + <_GreeparAvaloniaStaticGraphicsRid Condition="'$(RuntimeIdentifier)' == 'linux-x64'">linux-x64 + <_GreeparAvaloniaStaticGraphicsRid Condition="'$(RuntimeIdentifier)' == 'linux-arm64'">linux-arm64 + <_GreeparAvaloniaStaticGraphicsRid Condition="'$(RuntimeIdentifier)' == 'osx-x64'">osx-x64 + <_GreeparAvaloniaStaticGraphicsRid Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">osx-arm64 + <_GreeparAvaloniaStaticGraphicsNativeRoot Condition="'$(_GreeparAvaloniaStaticGraphicsRid)' != ''">$(GreeparAvaloniaStaticGraphicsRoot)$(_GreeparAvaloniaStaticGraphicsRid)/native + lld + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + <_GreeparAvaloniaStaticGraphicsFilesToDelete Include="$(PublishDir)av_libglesv2.dll" /> + <_GreeparAvaloniaStaticGraphicsFilesToDelete Include="$(PublishDir)libHarfBuzzSharp.dll" /> + <_GreeparAvaloniaStaticGraphicsFilesToDelete Include="$(PublishDir)libSkiaSharp.dll" /> + <_GreeparAvaloniaStaticGraphicsFilesToDelete Include="$(PublishDir)libHarfBuzzSharp.so" /> + <_GreeparAvaloniaStaticGraphicsFilesToDelete Include="$(PublishDir)libSkiaSharp.so" /> + <_GreeparAvaloniaStaticGraphicsFilesToDelete Include="$(PublishDir)libHarfBuzzSharp.dylib" /> + <_GreeparAvaloniaStaticGraphicsFilesToDelete Include="$(PublishDir)libSkiaSharp.dylib" /> + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..43dc51e --- /dev/null +++ b/README.md @@ -0,0 +1,163 @@ +# Greepar.Avalonia.StaticGraphics + +Static graphics libraries for Avalonia NativeAOT single-file publishing. + +This package provides static archives for SkiaSharp, HarfBuzzSharp, and ANGLE, then wires them into MSBuild through `buildTransitive` targets. It is intended for Avalonia applications that want NativeAOT single-file publish outputs without shipping Avalonia graphics native libraries next to the executable. + +Repository: + +## Supported RIDs + +- `win-x64` +- `win-arm64` +- `linux-x64` +- `linux-arm64` +- `osx-x64` +- `osx-arm64` + +## Included Libraries + +Windows: + +- `skia.lib` +- `SkiaSharp.lib` +- `libHarfBuzzSharp.lib` +- `libANGLE_static.lib` +- `libGLESv2_static.lib` + +Linux and macOS: + +- `libskia.a` +- `libSkiaSharp.a` +- `libHarfBuzzSharp.a` +- `libANGLE_static.a` +- `libGLESv2_static.a` + +## Install + +```bash +dotnet add package Greepar.Avalonia.StaticGraphics +``` + +## Publish + +Windows x64 or arm64: + +```bash +dotnet publish -c Release -r win-x64 \ + -p:PublishAot=true \ + -p:SelfContained=true \ + -p:PublishSingleFile=true \ + -p:StripSymbols=true +``` + +Linux x64 or arm64: + +```bash +dotnet publish -c Release -r linux-x64 \ + -p:PublishAot=true \ + -p:SelfContained=true \ + -p:PublishSingleFile=true \ + -p:StripSymbols=true \ + -p:LinkerFlavor=lld +``` + +macOS x64 or arm64: + +```bash +dotnet publish -c Release -r osx-arm64 \ + -p:PublishAot=true \ + -p:SelfContained=true \ + -p:PublishSingleFile=true \ + -p:StripSymbols=true +``` + +The package automatically contributes the static archives as `NativeLibrary` items when `PublishAot=true` and the `RuntimeIdentifier` is supported. + +## Verify Output + +After publishing, the publish directory should not contain Avalonia graphics runtime files such as: + +- `libSkiaSharp.so` +- `libHarfBuzzSharp.so` +- `libSkiaSharp.dylib` +- `libHarfBuzzSharp.dylib` +- `libSkiaSharp.dll` +- `libHarfBuzzSharp.dll` +- `av_libglesv2.dll` + +The package stores static archives under `static//native/` inside the NuGet package, so `.a` and `.lib` files are used for linking and are not normal runtime assets. + +## Build From Source + +This repository does not depend on 2ndLAB static packages. The GitHub Actions workflow builds the native libraries from upstream source repositories: + +- SkiaSharp: +- ANGLE: + +Build scripts: + +- `scripts/build-linux-static-graphics.sh` +- `scripts/build-windows-static-graphics.ps1` +- `scripts/build-macos-static-graphics.sh` + +Build locally after the static archives exist: + +```bash +dotnet pack NuGet/LinuxStaticGraphics/Greepar.Avalonia.StaticGraphics.csproj -c Release -o artifacts/nuget +``` + +The package will be written to: + +```text +artifacts/nuget/Greepar.Avalonia.StaticGraphics.*.nupkg +``` + +## GitHub Actions + +The workflow `.github/workflows/nuget-static-graphics.yml` builds all native archives and packs a single NuGet package for all supported RIDs. + +It will: + +- build Linux x64 static archives on `ubuntu-latest` +- build Linux arm64 static archives on `ubuntu-24.04-arm` +- build Windows x64 and arm64 static archives on `windows-latest` +- build macOS x64 and arm64 static archives on `macos-latest` +- download all native artifacts into `External/NativeStatic//` +- run `dotnet pack` +- upload the generated `.nupkg` as a workflow artifact + +To publish automatically to nuget.org, add a repository secret named `NUGET_API_KEY`, then either: + +- run the workflow manually with `publish_to_nuget=true` +- push a tag matching `static-graphics-v*` + +Example tag: + +```bash +git tag static-graphics-v3.119.2-7151.4 +git push origin static-graphics-v3.119.2-7151.4 +``` + +## Known Runtime Dependencies + +Single-file does not always mean dependency-free. Depending on platform and publish settings, the final executable may still depend on operating system libraries and frameworks. + +Linux examples: + +- `libc.so.6` +- `libm.so.6` +- `libstdc++.so.6` +- `libgcc_s.so.1` +- `libfontconfig.so.1` +- `libfreetype.so.6` + +Linux systems also need usable fonts for text rendering and font fallback. + +## Scope + +This package only covers Avalonia graphics-related native libraries. If your application uses other native dependencies, such as SQLite, audio backends, OpenSSL, or custom native libraries, those dependencies may require separate handling. + +## License + +The NuGet package metadata uses the MIT license. The bundled third-party native libraries are built from their upstream projects and remain subject to their respective upstream licenses. diff --git a/scripts/build-linux-static-graphics.sh b/scripts/build-linux-static-graphics.sh new file mode 100755 index 0000000..7ba1876 --- /dev/null +++ b/scripts/build-linux-static-graphics.sh @@ -0,0 +1,273 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +WORK_DIR="${WORK_DIR:-$ROOT_DIR/External/NativeStatic/.work}" +TARGET_CPU="${TARGET_CPU:-x64}" +RID="${RID:-linux-$TARGET_CPU}" +OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}" +SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.2}" +ANGLE_BRANCH="${ANGLE_BRANCH:-7151}" +BUILD_JOBS="${BUILD_JOBS:-$(nproc)}" +ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}" +LLVM_AR="${LLVM_AR:-llvm-ar-19}" + +usage() { + cat <<'USAGE' +Usage: scripts/build-linux-static-graphics.sh [skia|angle|all] + +Environment: + WORK_DIR Source/build cache directory. Default: External/NativeStatic/.work + OUTPUT_DIR Final static library directory. Default: External/NativeStatic/linux-$TARGET_CPU + SKIASHARP_VERSION SkiaSharp release branch version. Default: 3.119.2 + ANGLE_BRANCH ANGLE chromium branch. Default: 7151 + TARGET_CPU GN target_cpu. Default: x64. Supported: x64, arm64 + RID Output RID. Default: linux-$TARGET_CPU + BUILD_JOBS Ninja parallelism. Default: nproc + ANGLE_PATCH_DIR ANGLE patch directory. Default: External/NativeStatic/patches + LLVM_AR llvm-ar command used to expand thin archives. Default: llvm-ar-19 +USAGE +} + +require_cmd() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Missing required command: $1" >&2 + exit 1 + fi +} + +ensure_tools() { + require_cmd git + require_cmd python3 + require_cmd clang + require_cmd clang++ + require_cmd ar + require_cmd "$LLVM_AR" + require_cmd ninja + require_cmd pkg-config +} + +ensure_depot_tools() { + local depot_dir="$WORK_DIR/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 + fi + export PATH="$depot_dir:$PATH" +} + +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" + else + git -C "$src" fetch --depth 1 origin "release/$SKIASHARP_VERSION" + git -C "$src" checkout -q FETCH_HEAD + fi + git -C "$src" submodule update --init --depth 1 externals/skia >&2 + echo "$src" +} + +sync_skia_deps() { + local skia_dir="$1/externals/skia" + if [[ ! -x "$skia_dir/bin/gn" ]]; then + python3 "$skia_dir/tools/git-sync-deps" + fi +} + +build_skia() { + ensure_tools + ensure_depot_tools + local src + src="$(sync_skiasharp)" + sync_skia_deps "$src" + + local skia_dir="$src/externals/skia" + local out_dir="$skia_dir/out/linux-static-$TARGET_CPU" + mkdir -p "$out_dir" "$OUTPUT_DIR" + + cat >"$out_dir/args.gn" <"$out_dir/args.gn" </dev/null 2>&1; then + : + elif [[ -f "$deps_patch" ]]; then + git -C "$src" apply "$deps_patch" + fi +} + +copy_first_existing() { + local dest="$1" + shift + for src in "$@"; do + if [[ -f "$src" ]]; then + cp "$src" "$dest" + echo "Wrote $dest" + return 0 + fi + done + echo "None of the expected files exist for $dest:" >&2 + printf ' %s\n' "$@" >&2 + return 1 +} + +copy_full_archive_from_thin() { + local base_dir="$1" + local dest="$2" + shift 2 + + local src + for src in "$@"; do + if [[ -f "$src" ]]; then + if [[ "$(head -c 7 "$src")" == "!" ]]; then + local tmp="$dest.tmp" + local rel_src="${src#$base_dir/}" + ( + cd "$base_dir" + mapfile -t members < <("$LLVM_AR" t "$rel_src") + "$LLVM_AR" rcs "$tmp" "${members[@]}" + ) + mv "$tmp" "$dest" + else + cp "$src" "$dest" + fi + echo "Wrote $dest" + return 0 + fi + done + + echo "None of the expected files exist for $dest:" >&2 + printf ' %s\n' "$@" >&2 + return 1 +} + +main() { + mkdir -p "$WORK_DIR" "$OUTPUT_DIR" + case "${1:-all}" in + skia) build_skia ;; + angle) build_angle ;; + all) + build_skia + build_angle + ;; + -h|--help|help) + usage + ;; + *) + usage >&2 + exit 1 + ;; + esac +} + +main "$@" diff --git a/scripts/build-macos-static-graphics.sh b/scripts/build-macos-static-graphics.sh new file mode 100755 index 0000000..ea3b2ee --- /dev/null +++ b/scripts/build-macos-static-graphics.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +WORK_DIR="${WORK_DIR:-$ROOT_DIR/External/NativeStatic/.work}" +TARGET_CPU="${TARGET_CPU:-arm64}" +RID="${RID:-osx-$TARGET_CPU}" +OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/External/NativeStatic/$RID}" +SKIASHARP_VERSION="${SKIASHARP_VERSION:-3.119.2}" +ANGLE_BRANCH="${ANGLE_BRANCH:-7151}" +BUILD_JOBS="${BUILD_JOBS:-$(sysctl -n hw.ncpu)}" +ANGLE_PATCH_DIR="${ANGLE_PATCH_DIR:-$ROOT_DIR/External/NativeStatic/patches}" + +require_cmd() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Missing required command: $1" >&2 + exit 1 + fi +} + +ensure_tools() { + require_cmd git + require_cmd python3 + require_cmd clang + require_cmd clang++ + require_cmd ar + require_cmd ninja +} + +ensure_depot_tools() { + local depot_dir="$WORK_DIR/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 + fi + export PATH="$depot_dir:$PATH" +} + +copy_first_existing() { + local dest="$1" + shift + for src in "$@"; do + if [[ -f "$src" ]]; then + cp "$src" "$dest" + echo "Wrote $dest" + return 0 + fi + done + echo "None of the expected files exist for $dest:" >&2 + printf ' %s\n' "$@" >&2 + return 1 +} + +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" + else + git -C "$src" fetch --depth 1 origin "release/$SKIASHARP_VERSION" + git -C "$src" checkout -q FETCH_HEAD + fi + git -C "$src" submodule update --init --depth 1 externals/skia >&2 + echo "$src" +} + +build_skia() { + ensure_tools + ensure_depot_tools + local src + src="$(sync_skiasharp)" + local skia_dir="$src/externals/skia" + if [[ ! -x "$skia_dir/bin/gn" ]]; then + python3 "$skia_dir/tools/git-sync-deps" + fi + + local out_dir="$skia_dir/out/mac-static-$TARGET_CPU" + mkdir -p "$out_dir" "$OUTPUT_DIR" + cat >"$out_dir/args.gn" <"$out_dir/args.gn" <&2; exit 1 ;; + esac +} + +main "$@" diff --git a/scripts/build-windows-static-graphics.ps1 b/scripts/build-windows-static-graphics.ps1 new file mode 100644 index 0000000..3c9c28a --- /dev/null +++ b/scripts/build-windows-static-graphics.ps1 @@ -0,0 +1,170 @@ +param( + [ValidateSet("skia", "angle", "all")] + [string]$Target = "all" +) + +$ErrorActionPreference = "Stop" + +$RootDir = Split-Path -Parent $PSScriptRoot +$WorkDir = if ($env:WORK_DIR) { $env:WORK_DIR } else { Join-Path $RootDir "External\NativeStatic\.work" } +$SkiaSharpVersion = if ($env:SKIASHARP_VERSION) { $env:SKIASHARP_VERSION } else { "3.119.2" } +$AngleBranch = if ($env:ANGLE_BRANCH) { $env:ANGLE_BRANCH } else { "7151" } +$TargetCpu = if ($env:TARGET_CPU) { $env:TARGET_CPU } else { "x64" } +$Rid = if ($env:RID) { $env:RID } else { "win-$TargetCpu" } +$OutputDir = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { Join-Path $RootDir "External\NativeStatic\$Rid" } +$BuildJobs = if ($env:BUILD_JOBS) { $env:BUILD_JOBS } else { [Environment]::ProcessorCount } +$AnglePatchDir = if ($env:ANGLE_PATCH_DIR) { $env:ANGLE_PATCH_DIR } else { Join-Path $RootDir "External\NativeStatic\patches" } + +function Require-Command($Name) { + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Missing required command: $Name" + } +} + +function Ensure-Tools { + Require-Command git + Require-Command python + Require-Command ninja +} + +function Ensure-DepotTools { + $depotDir = Join-Path $WorkDir "depot_tools" + if (-not (Test-Path (Join-Path $depotDir ".git"))) { + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git $depotDir + } else { + git -C $depotDir pull --ff-only + } + $env:PATH = "$depotDir;$env:PATH" +} + +function Copy-FirstExisting($Destination, [string[]]$Candidates) { + foreach ($candidate in $Candidates) { + if (Test-Path $candidate) { + Copy-Item $candidate $Destination -Force + Write-Host "Wrote $Destination" + return + } + } + throw "None of the expected files exist for $Destination: $($Candidates -join ', ')" +} + +function Sync-SkiaSharp { + $src = Join-Path $WorkDir "SkiaSharp-$SkiaSharpVersion" + if (-not (Test-Path (Join-Path $src ".git"))) { + git clone --depth 1 --branch "release/$SkiaSharpVersion" https://github.com/mono/SkiaSharp.git $src + } else { + git -C $src fetch --depth 1 origin "release/$SkiaSharpVersion" + git -C $src checkout -q FETCH_HEAD + } + git -C $src submodule update --init --depth 1 externals/skia + return $src +} + +function Build-Skia { + Ensure-Tools + Ensure-DepotTools + $src = Sync-SkiaSharp + $skiaDir = Join-Path $src "externals\skia" + if (-not (Test-Path (Join-Path $skiaDir "bin\gn.exe"))) { + python (Join-Path $skiaDir "tools\git-sync-deps") + } + + $outDir = Join-Path $skiaDir "out\win-static-$TargetCpu" + New-Item -ItemType Directory -Path $outDir, $OutputDir -Force | Out-Null + @" +target_os = "win" +target_cpu = "$TargetCpu" +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 = 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 = [ "-DSKIA_C_DLL" ] +extra_cflags_cc = [ "/GR" ] +"@ | Set-Content -Path (Join-Path $outDir "args.gn") -Encoding ASCII + + & (Join-Path $skiaDir "bin\gn.exe") gen $outDir + ninja -C $outDir -j $BuildJobs skia SkiaSharp HarfBuzzSharp + + Copy-FirstExisting (Join-Path $OutputDir "skia.lib") @((Join-Path $outDir "skia.lib"), (Join-Path $outDir "obj\skia.lib")) + Copy-FirstExisting (Join-Path $OutputDir "SkiaSharp.lib") @((Join-Path $outDir "SkiaSharp.lib"), (Join-Path $outDir "obj\SkiaSharp.lib")) + Copy-FirstExisting (Join-Path $OutputDir "libHarfBuzzSharp.lib") @((Join-Path $outDir "libHarfBuzzSharp.lib"), (Join-Path $outDir "obj\libHarfBuzzSharp.lib")) +} + +function Sync-Angle { + $src = Join-Path $WorkDir "ANGLE-$AngleBranch" + if (-not (Test-Path (Join-Path $src ".git"))) { + git clone --depth 1 --branch "chromium/$AngleBranch" https://github.com/google/angle.git $src + } else { + git -C $src fetch --depth 1 origin "chromium/$AngleBranch" + git -C $src checkout -q FETCH_HEAD + } + return $src +} + +function Apply-AnglePatches($Src) { + $patch = Join-Path $AnglePatchDir "angle-chromium-$AngleBranch.patch" + if ((Test-Path $patch) -and -not (Select-String -Path (Join-Path $Src "BUILD.gn") -Pattern 'angle_static_library\("libANGLE_static"\)' -Quiet)) { + git -C $Src apply $patch + } +} + +function Build-Angle { + Ensure-Tools + Ensure-DepotTools + $src = Sync-Angle + New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null + Apply-AnglePatches $src + Push-Location $src + try { + python scripts/bootstrap.py + gclient sync -f -D -R + $outDir = Join-Path $src "out\win-static-$TargetCpu" + New-Item -ItemType Directory -Path $outDir -Force | Out-Null + @" +target_os = "win" +target_cpu = "$TargetCpu" +is_debug = false +is_component_build = false +is_clang = true +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 +"@ | Set-Content -Path (Join-Path $outDir "args.gn") -Encoding ASCII + gn gen $outDir + ninja -C $outDir -j $BuildJobs libANGLE_static libGLESv2_static + Copy-FirstExisting (Join-Path $OutputDir "libANGLE_static.lib") @((Join-Path $outDir "libANGLE_static.lib"), (Join-Path $outDir "obj\libANGLE_static.lib"), (Join-Path $outDir "obj\libANGLE_static\libANGLE_static.lib")) + Copy-FirstExisting (Join-Path $OutputDir "libGLESv2_static.lib") @((Join-Path $outDir "libGLESv2_static.lib"), (Join-Path $outDir "obj\libGLESv2_static.lib"), (Join-Path $outDir "obj\libGLESv2_static\libGLESv2_static.lib")) + } finally { + Pop-Location + } +} + +New-Item -ItemType Directory -Path $WorkDir, $OutputDir -Force | Out-Null +switch ($Target) { + "skia" { Build-Skia } + "angle" { Build-Angle } + "all" { Build-Skia; Build-Angle } +}