Skip to content

ci: add macOS arm64 (Metal) build - #5

Merged
Geramy merged 6 commits into
lemonade-sdk:masterfrom
Geramy:geramy/macos-arm64-ci
May 5, 2026
Merged

ci: add macOS arm64 (Metal) build#5
Geramy merged 6 commits into
lemonade-sdk:masterfrom
Geramy:geramy/macos-arm64-ci

Conversation

@Geramy

@Geramy Geramy commented Apr 30, 2026

Copy link
Copy Markdown
Member

Adds a darwin-arm64-metal job covering [macos-14, macos-latest] that builds upstream whisper.cpp with Metal enabled and produces whisper-bin-darwin-arm64-metal.zip (plus a …-latest.zip variant). Wired into release.needs: so the new .zip lands in the same draft release as the existing Linux/Windows artifacts.

Mirrors the flag set used by ggml-org/llama.cpp's release.yml:

  • -DGGML_METAL=ON -DGGML_METAL_USE_BF16=ON -DGGML_METAL_EMBED_LIBRARY=ON
  • -DCMAKE_INSTALL_RPATH='@loader_path' -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON so the produced binaries find their *.dylib siblings via @loader_path rather than the GitHub runner's absolute build directory (without this, downloaded archives crash with dyld: Library not loaded: @rpath/libwhisper.…dylib).

End-to-end verification (Apple Silicon)

```
$ otool -l whisper-cli | grep -A2 LC_RPATH
cmd LC_RPATH
path @loader_path

$ ./whisper-cli -m ggml-tiny.en.bin -f samples/jfk.wav -np
[00:00:00.000 --> 00:00:07.960] And so my fellow Americans ask not what your country can do for you
[00:00:07.960 --> 00:00:10.760] ask what you can do for your country.
```

Real Metal-accelerated inference works. Round-trip with Kokoros TTS also passes (TTS-generated WAV → whisper-cli transcribes back the original text).

Release artifacts: https://github.com/Geramy/whisper.cpp-builds/releases/tag/b4078

Companion PRs to lemonade-sdk/stable-diffusion.cpp and lemonade-sdk/Kokoros add the same macOS arm64 coverage to the other two backends.

Geramy added 2 commits April 28, 2026 07:07
Adds a darwin-arm64-metal job covering [macos-14, macos-latest] that builds
upstream whisper.cpp with Metal enabled and produces
whisper-bin-darwin-arm64-metal.zip plus -latest variant. Wired into
release.needs so the .zip lands in the same draft release as the existing
Linux/Windows artifacts.
Same rationale as the sd.cpp fix: without this, binaries that link to
shared libs would carry the runner's absolute build path in rpath and
fail at runtime when the .zip is unpacked elsewhere.
superm1
superm1 previously approved these changes May 1, 2026
Geramy added a commit to Geramy/lemonade that referenced this pull request May 1, 2026
Aligns the macOS Metal download path with the established
whisper-{tag}-{plat}-{backend}-{arch}.{ext} convention used by the
linux-cpu, linux-vulkan, and windows-npu pins. Pairs with
lemonade-sdk/whisper.cpp-builds#5 which renames the published
artifact to match.
Aligns the macOS arm64 Metal artifact's filename with the established
versioned pattern that v1.8.2 already uses for linux-cpu, linux-vulkan,
windows-npu (e.g. whisper-v1.8.2-linux-cpu-x86_64.tar.gz). The macOS
artifact now ships as:

  whisper-{tag}-darwin-metal-arm64.tar.gz
  whisper-{tag}-darwin-metal-arm64-latest.tar.gz  (macos-latest)

Switched packaging from zip to tar.gz to match the linux artifacts'
extension (better for preserving Unix file modes and dylib symlinks
during extraction). The release-job's 'Move artifacts' and
'Upload release' steps now also accept *.tar.gz alongside *.zip so the
new files actually make it into the published release.
@Geramy

Geramy commented May 1, 2026

Copy link
Copy Markdown
Member Author

@superm1 I had renamed the output file to the correct formatting.

@superm1

superm1 commented May 1, 2026

Copy link
Copy Markdown
Member

@superm1 I had renamed the output file to the correct formatting.

Check the ci failure

@Geramy

Geramy commented May 1, 2026

Copy link
Copy Markdown
Member Author

@superm1 I had renamed the output file to the correct formatting.

Check the ci failure

@superm1 I had renamed the output file to the correct formatting.

Check the ci failure

looks like a transient issue, due to qemu I might re-arrange the jobs or adjust them to no longer use emu for linux on arm64 since GitHub has arm64 linux runners, there is no need to use qemu in fact it is just Suboptimal.

… jobs

This fork has no self-hosted runners, so the upstream ggml-ci-* jobs that
target [self-hosted, ...] runner labels queued indefinitely on PRs.

- Retarget ggml-ci-mac-metal to macos-14. Move output paths from ~/results
  and ~/mnt to ./tmp/ (home dir is wiped per run on hosted) and add
  GG_BUILD_LOW_PERF=1 so the model set fits in macos-14's ~14GB free disk.
- Retarget ggml-ci-mac-vulkan to macos-14 with brew-installed MoltenVK,
  vulkan-loader, vulkan-headers, vulkan-tools, glslang, shaderc. Set
  VK_ICD_FILENAMES to MoltenVK's ICD JSON and CMAKE_PREFIX_PATH so CMake
  finds the brew Vulkan. Same ./tmp/ + GG_BUILD_LOW_PERF=1 as mac-metal.
- Comment out (with re-enablement notes) the four jobs that need hardware
  no GitHub-hosted runner provides:
    - ggml-ci-x64-nvidia-cuda            (NVIDIA GPU)
    - ggml-ci-x64-nvidia-vulkan-cm       (NVIDIA GPU)
    - ggml-ci-x64-nvidia-vulkan-cm2      (Ada/Hopper NV w/ COOPMAT2)
    - ggml-ci-x64-cpu-amx                (Sapphire Rapids+ Xeon)
Comment thread .github/workflows/build.yml Outdated
superm1
superm1 previously approved these changes May 4, 2026

@superm1 superm1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a question on adding pointless commented out jobs. decide on that and then fine

First run failed at vulkaninfo with:
  loader_get_json: Failed to open JSON file
    /opt/homebrew/opt/molten-vk/share/vulkan/icd.d/MoltenVK_icd.json

Brew's molten-vk 1.4.1 no longer ships the ICD JSON at that path.
Find it dynamically under $(brew --prefix molten-vk); if absent, write
a minimal ICD JSON pointing at libMoltenVK.dylib (same approach
vulkan-tools uses for its mock ICD). set -euo pipefail to fail loudly
on future env changes.
None of these jobs are runnable on free GitHub-hosted runners and this
fork has no self-hosted runners. Removing entirely (previously commented
out, but now deleting the dead config too):

- ggml-ci-x64-nvidia-cuda            (no NVIDIA GPU on hosted runners)
- ggml-ci-x64-nvidia-vulkan-cm       (no NVIDIA GPU on hosted runners)
- ggml-ci-x64-nvidia-vulkan-cm2      (Ada/Hopper + COOPMAT2 unavailable)
- ggml-ci-x64-cpu-amx                (Sapphire Rapids+ unavailable)
- ggml-ci-mac-vulkan                 (MoltenVK crashes on AppleParavirt-
                                      Device - hosted Macs are virtualized
                                      and lack the Metal API surface
                                      MoltenVK requires)

ggml-ci-mac-metal stays - Metal works on the paravirt device.
@Geramy
Geramy requested a review from superm1 May 4, 2026 20:52
@Geramy
Geramy merged commit 6909ede into lemonade-sdk:master May 5, 2026
62 checks passed
meghsat pushed a commit to lemonade-sdk/lemonade that referenced this pull request Jul 10, 2026
…-cpp (#1777)

* feat(macos): add macOS arm64 (Metal) backends for whisper, kokoros, sd-cpp

Introduces a 'metal' backend identifier for whispercpp, kokoro, and
sd-cpp that mirrors the existing llamacpp/metal pattern: macOS arm64
gets its own row in the recipe matrix, its own arm in each backend's
get_install_params, and its own version pin in backend_versions.json.
No more conflating macOS arm64 with the existing windows/linux cpu
backends.

Source repos for the new artifacts:
  - whispercpp/metal -> lemonade-sdk/whisper.cpp-builds
                       (whisper-bin-darwin-arm64-metal.zip)
  - kokoro/metal     -> lemonade-sdk/Kokoros
                       (kokoros-darwin-arm64-metal.tar.gz)
  - sd-cpp/metal     -> lemonade-sdk/stable-diffusion.cpp
                       (sd-{ver}-bin-Darwin-arm64-metal.zip)

The previous code paths for macOS were dead in two of three cases:
  - whisper_server.cpp pointed at ggml-org/whisper.cpp's
    'whisper-bin-arm64.zip', which has never existed in any of the
    upstream's 32 releases.
  - kokoro_server.cpp threw 'Unsupported platform for kokoros'.
  - sd_server.cpp pointed at a hardcoded 'Darwin-macOS-15.7.2-arm64.zip'
    name that did exist in lemonade-sdk releases master-494..master-555
    but stopped being published around master-585.

Cleanup:
  - whisper_server.cpp: drop dead __APPLE__ arm in cpu branch (which
    referenced the never-published upstream artifact). cpu now only
    targets windows + linux as the matrix says.
  - sd_server.cpp: drop dead __APPLE__ arm in cpu branch (matched a
    moving runner-version filename that hasn't shipped recently).
    cpu now only targets windows + linux.
  - kokoro_server.cpp: switch from hardcoded 'cpu' to a platform-resolved
    backend so the load() flow picks 'metal' on Apple Silicon and 'cpu'
    elsewhere. kokoro doesn't have user-selectable backends through
    RuntimeConfig so this stays implicit.
  - whisper_server.cpp / sd_server.cpp: include 'metal' in the
    DEVICE_GPU branch of the device-type detection.

Blocked on three companion build-farm PRs that publish the artifacts
this code references:
  - lemonade-sdk/stable-diffusion.cpp#2
  - lemonade-sdk/Kokoros#4
  - lemonade-sdk/whisper.cpp-builds#5

backend_versions.json placeholder pins reuse the current cpu/etc.
versions; they need bumping to whatever new release tags are cut after
the build-farm PRs merge, before this PR can be marked ready for
review and tested end-to-end on macOS.

* fix(whisper): use whisper-{ver}-darwin-metal-arm64.tar.gz on macOS

Aligns the macOS Metal download path with the established
whisper-{tag}-{plat}-{backend}-{arch}.{ext} convention used by the
linux-cpu, linux-vulkan, and windows-npu pins. Pairs with
lemonade-sdk/whisper.cpp-builds#5 which renames the published
artifact to match.

* fix(sd-cpp): pin metal to master-593-7f65f2a (Darwin-arm64 asset)

The previously pinned master-569-ab6afe8 release does not publish a
sd-*-bin-Darwin-arm64-metal.zip asset, so macOS install fails.
master-593-7f65f2a includes the Darwin-arm64-metal build.

* fix(kokoro): pin metal to b17 (Darwin-arm64 asset)

* Resolved review comments, removing redundant checks and dummy code.

* ci: capture server logs from Linux test-cli-endpoints jobs

The Test ollama (ubuntu-latest) failure on this branch returns 404 from
/api/generate but no server log is uploaded for diagnosis. Add the same
capture-server-logs step the Windows/macOS jobs and the deb test jobs
already use, so the next run produces a downloadable artifact.

* deps: bump sd-cpp pins to master-596-b6f38cd (portable AVX2 baseline)

The master-593 release of sd-cpp was built with -march=native on a runner
with AVX-512, baking 5,396 zmm + 1,582 k-mask AVX-512 instructions into
libstable-diffusion.so. That binary SIGILLed on AVX-512-less GitHub-hosted
ubuntu-latest runners — surfaced as Test ollama (ubuntu-latest) failing
test_022_generate_image_output with sd-server "exit code: -1" within 200ms.

master-596 is the first release after lemonade-sdk/stable-diffusion.cpp#3,
which pins the Linux x86_64 cpu and rocm builds to GGML_NATIVE=OFF +
AVX2/FMA/F16C. Verified: 0 zmm refs, 0 k-mask refs, 12,720 ymm refs.

* This code was filtering out ALL sdcpp, kokoro, whispercpp, flm, and other non-llamacpp models from `get_supported_models()` on macOS. Since the Model Manager UI fetches models via `/models?show_all=true` which calls `get_supported_models()`, the categories appeared empty even after downloading backends.

* re-enable macos CI

* added macos guards to tests.

* add ci tests to macos

* running lemonade as non system service for whisper ci process, since a non signed binary cant run as a service.

* additional test.

* bump whisper.cpp backend versions to v1.8.3

* fix: run macOS unsigned builds directly from build/ with artifact sharing

- Replace /usr/local/bin/ system install with direct build/ execution for unsigned paths
- Upload built binaries as GitHub Actions artifact from build-lemonade-macos-dmg job
- Download artifacts in test-dmg-inference (fresh runner) and verify binaries
- Start lemond once before matrix tests, keep running for all inference tests
- Avoids race conditions from per-test server restarts

* fix: restore execute permissions on downloaded macOS binaries

GitHub Actions artifacts strip executable permissions on download.
Add chmod +x both before upload and after download to fix
'Permission denied' errors when running binaries.

* bump whisper.cpp backend versions to v1.8.4

Picks up the fix-release-archive-format release on
lemonade-sdk/whisper.cpp-builds, which produces correctly formatted
Linux tarballs and Windows zips. v1.8.3 archives failed to extract
in CI (tar: Error is not recoverable / executable not found).

* fix(ci): include build/resources/ in unsigned macOS test artifact

The unsigned-path test-dmg-inference job downloads the
lemonade-unsigned-binaries artifact into build/ on a fresh runner,
but the artifact only contained the three binaries. lemond then
failed to start with 'Failed to open build/resources/defaults.json'
because get_resource_path resolves relative to the executable.

Add build/resources/ to the upload so defaults.json,
backend_versions.json, server_models.json and the static/web-app
trees are available alongside the binaries.

* test(sd-cpp): use smaller GGUF variant on macos-latest CI

The sd-cpp-metal matrix test on GitHub-hosted macos-latest (M1, 14 GB
RAM) consistently has sd-server die mid-load with SD-Turbo's 2.7 GB
safetensors checkpoint. Replace it with a quantized GGUF variant for
this matrix entry to reduce peak memory during load.

- Register SD-Turbo-GGUF (Green-Sky/SD-Turbo-GGUF, Q8_0, ~2 GB) with
  the same image_defaults so test_007's assertions still hold.
- Let SD_MODEL be overridden via LEMONADE_TEST_SD_MODEL so the rest
  of the test surface (local runs, other CI) keeps using SD-Turbo.
- Wire the override only into the sd-cpp-metal matrix entry via a new
  matrix.sd_model field.

* Disable sdcpp metal ci test

---------

Co-authored-by: Jeremy Fowers <80718789+jeremyfowers@users.noreply.github.com>
Co-authored-by: Jeremy Fowers <jeremy.fowers@amd.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants