diff --git a/.github/workflows/macos-static-avalonia-native.yml b/.github/workflows/macos-static-avalonia-native.yml index 00a1364..4d34cc5 100644 --- a/.github/workflows/macos-static-avalonia-native.yml +++ b/.github/workflows/macos-static-avalonia-native.yml @@ -45,6 +45,9 @@ jobs: - name: Build macOS static graphics libraries run: bash ./scripts/build-macos-static-graphics.sh all + - name: Build macOS font preinit object + run: bash ./scripts/build-macos-font-preinit.sh + - name: List native outputs run: find "External/NativeStatic/${{ matrix.rid }}/native" -maxdepth 1 -type f -print diff --git a/.github/workflows/nuget-static-graphics.yml b/.github/workflows/nuget-static-graphics.yml index 4398728..bd26cf9 100644 --- a/.github/workflows/nuget-static-graphics.yml +++ b/.github/workflows/nuget-static-graphics.yml @@ -136,6 +136,11 @@ jobs: TARGET_CPU: ${{ matrix.target_cpu }} run: scripts/build-macos-static-graphics.sh all + - name: Build macOS font preinit object + env: + RID: ${{ matrix.rid }} + run: bash scripts/build-macos-font-preinit.sh + - name: Upload macOS static archives uses: actions/upload-artifact@v4 with: diff --git a/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj b/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj index 31b8b03..fa75f67 100644 --- a/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj +++ b/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj @@ -6,7 +6,7 @@ false false Shiroka.Avalonia.StaticLink - 3.119.2-7151.7 + 3.119.2-7151.9 greepar greepar Static SkiaSharp, HarfBuzzSharp, and ANGLE libraries for Avalonia NativeAOT single-file publishing. @@ -27,6 +27,7 @@ + diff --git a/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.MacOSFontWarmup.cs b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.MacOSFontWarmup.cs new file mode 100644 index 0000000..fa4c29e --- /dev/null +++ b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.MacOSFontWarmup.cs @@ -0,0 +1,50 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Shiroka.Avalonia.StaticLink; + +internal static class MacOSFontWarmup +{ + [ModuleInitializer] + internal static void Initialize() + { + if (!OperatingSystem.IsMacOS()) + { + return; + } + + try + { + var nsApplication = objc_getClass("NSApplication"); + var sharedApplication = sel_registerName("sharedApplication"); + _ = objc_msgSend(nsApplication, sharedApplication); + + var nsFont = objc_getClass("NSFont"); + var systemFontOfSize = sel_registerName("systemFontOfSize:"); + var systemFontOfSizeWidth = sel_registerName("systemFontOfSize:width:"); + + _ = objc_msgSend(nsFont, systemFontOfSize, 13.0); + _ = objc_msgSend(nsFont, systemFontOfSizeWidth, 13.0, 0.0); + } + catch + { + // Never fail application startup from the defensive macOS font-cache warmup. + } + } + + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_getClass")] + private static extern IntPtr objc_getClass([MarshalAs(UnmanagedType.LPUTF8Str)] string name); + + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "sel_registerName")] + private static extern IntPtr sel_registerName([MarshalAs(UnmanagedType.LPUTF8Str)] string name); + + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_msgSend")] + private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector); + + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_msgSend")] + private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, double value); + + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_msgSend")] + private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, double value, double width); +} diff --git a/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets index 847690a..59e8fbb 100644 --- a/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets +++ b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets @@ -49,9 +49,11 @@ + + diff --git a/scripts/build-macos-font-preinit.sh b/scripts/build-macos-font-preinit.sh new file mode 100644 index 0000000..e386ad2 --- /dev/null +++ b/scripts/build-macos-font-preinit.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +RID="${RID:-osx-arm64}" +OUTPUT_DIR="${OUTPUT_DIR:-$PWD/External/NativeStatic/$RID/native}" + +case "$RID" in + osx-arm64) ARCH="arm64" ;; + osx-x64) ARCH="x86_64" ;; + *) echo "Unsupported RID: $RID" >&2; exit 2 ;; +esac + +mkdir -p "$OUTPUT_DIR" + +src="$OUTPUT_DIR/macos_font_preinit.m" +cat > "$src" <<'OBJC' +#import + +__attribute__((constructor(101))) +static void ShirokaAvaloniaStaticLinkPreinitializeAppKitFonts(void) +{ + @autoreleasepool + { + [NSApplication sharedApplication]; + [NSFont systemFontOfSize:13.0]; + [NSFont systemFontOfSize:13.0 width:0.0]; + } +} +OBJC + +clang -arch "$ARCH" \ + -mmacosx-version-min=12.0 \ + -fobjc-arc \ + -c "$src" \ + -o "$OUTPUT_DIR/macos_font_preinit.o" + +rm -f "$src" +echo "Wrote $OUTPUT_DIR/macos_font_preinit.o"