Cross-compile to macOS from anywhere.
Downloads and extracts macOS SDKs straight from Apple's public CDN β no Apple ID, no Mac, no Xcode, and nothing redistributed.
$ xmac splat --accept-license
Selected Command Line Tools for Xcode 26.5: MacOSX26.5.sdk
MacOSX26.5.sdk ββββββββββββββββββββββββββββ 100% 58.8 MiB / 58.8 MiB
SDK: MacOSX26.5.sdk (macOS 26.5)
32350 files, 9930 directories, 7451 symlinks, 730 MiB
$ ./xmac-sdk/bin/arm64-apple-darwin-cc -framework CoreFoundation -o hello hello.c
$ file hello
hello: Mach-O 64-bit arm64 executableA complete, linkable macOS SDK on a Linux box in about four seconds.
xwin made it possible to target
Windows from Linux CI by downloading the MSVC CRT + Windows SDK directly from
Microsoft. xmac is the same idea for macOS: Apple serves the Command Line
Tools for Xcode packages β which contain MacOSX*.sdk β from its
software-update CDN with no authentication. xmac reads the public catalog,
downloads only the ~55 MiB SDK sub-package, and extracts the .sdk
directory plus optional toolchain glue for clang + ld64.lld.
| download | needs Apple ID | needs a Mac | |
|---|---|---|---|
Xcode .xip |
3 β 8 GiB | β yes | β yes |
Command Line Tools .dmg |
~ 1 GiB | β yes | β yes |
| xmac (CLT SDK sub-package) | **~ 55 MiB** | β no | β no |
Because every machine downloads the SDK from Apple itself, you never
redistribute Apple's SDK β the same legal posture that makes xwin usable in
CI. You still have to accept Apple's license terms (--accept-license).
Note
This entire project is AI-generated β the code, the tests, and this README were written by an AI (Claude), directed and reviewed by a human. It parses Apple's xar/pbzx/cpio container formats from scratch and is used in Bun's CI to cross-compile macOS binaries from Linux, but you should review it with the same skepticism you'd apply to any new dependency before trusting it in your own build.
Prebuilt binary (no runtime needed) β every commit to main is published
to Releases, tagged with its
commit SHA, and the newest build is always marked Latest:
# pick one of: xmac-linux-x64, xmac-linux-x64-musl, xmac-linux-arm64, xmac-linux-arm64-musl
# (musl builds are for Alpine and also need: apk add libstdc++)
curl -fsSL https://github.com/jarred-sumner/xmac/releases/latest/download/xmac-linux-x64 -o xmac
chmod +x xmacSingle-file JS bundle (~66 KB) if you already have Bun:
curl -fsSL https://github.com/jarred-sumner/xmac/releases/latest/download/xmac.js -o xmac.js
bun xmac.js --helpTo pin an exact build, replace latest/download with download/<commit sha>.
Or run from source with Bun β₯ 1.1 β zero runtime
dependencies, nothing to bun install:
bun xmac.ts --helpEither way you also need on PATH:
xzandbzip2for extraction (apt-get install xz-utils bzip2β already present on most images)clangandlld(forld64.lld) to actually cross-compile. C and Objective-C work with any clang β₯ 13; C++ against the newest SDKs needs clang β₯ 19 because Apple's libc++ headers track Apple's own clang (the macOS 26 SDK uses__builtin_ctzg, added in clang 19). Ubuntu 24.04's default clang is 18 β get a newer one from apt.llvm.org.
# See every Command Line Tools release Apple is currently serving,
# and which SDKs each one contains
xmac list
# CI one-liner: newest SDK + toolchain files into ./xmac-sdk
xmac splat --accept-license
# A specific SDK version
xmac splat --accept-license --sdk 14 --output /opt/mac
# Both SDKs from a specific Command Line Tools release
xmac splat --accept-license --release 16.4 --sdk all
# Just the .sdk directory, no toolchain files
xmac splat --accept-license --sdk-only
# Read Apple's license terms before accepting them
xmac licensedownload β unpack β splat are progressive: each implies the previous.
Downloads are cached in ./.xmac-cache (override with --cache-dir or
XMAC_CACHE_DIR) and verified against the SHA-1 checksums embedded in the
package's signed table of contents.
xmac list β what's available right now
$ xmac list
CLT RELEASED PRODUCT SDKS
26.5 2026-05-11 047-91568 MacOSX26.5 (58.8 MiB), MacOSX15.4 (53.2 MiB)
16.4 2025-05-28 082-41241 MacOSX15 (55.8 MiB), MacOSX14 (56.7 MiB)
16.2 2025-04-14 072-44426 MacOSX15 (54.5 MiB), MacOSX14 (56.7 MiB)
15.3 2024-03-05 052-59890 MacOSX14 (56.6 MiB), MacOSX13 (48.7 MiB)
...
12.4 2021-04-27 001-89745 MacOSX11.1 (50.2 MiB), MacOSX10.15 (41.3 MiB)Coverage is whatever Apple currently serves β a rolling window, today spanning
macOS 10.15 β 26.5. Older SDKs (back to ~10.12) are reachable by pointing
--catalog at one of Apple's older sucatalog URLs. xmac figures out which
MacOSX*.sdk is inside each package without downloading it, using two HTTP
range requests per package.
xmac-sdk/
βββ SDKs/
β βββ MacOSX26.5.sdk/ # the real SDK: headers, frameworks, .tbd stubs
β βββ MacOSX26.sdk β MacOSX26.5.sdk
β βββ MacOSX.sdk β MacOSX26.5.sdk
βββ bin/
β βββ arm64-apple-darwin-cc # clang wrappers, one per arch
β βββ arm64-apple-darwin-c++
β βββ x86_64-apple-darwin-cc
β βββ x86_64-apple-darwin-c++
βββ arm64-apple-darwin.toolchain.cmake # CMake toolchain files, one per
βββ x86_64-apple-darwin.toolchain.cmake # target triple (+ universal-β¦
βββ env.sh # exports CC/CXX/SDKROOT/β¦ for everything else
cmake -B build -G Ninja --toolchain /path/to/xmac-sdk/arm64-apple-darwin.toolchain.cmake
cmake --build buildThe toolchain file sets CMAKE_SYSTEM_NAME=Darwin (so if(APPLE) works),
CMAKE_OSX_SYSROOT, the compiler target triple, and the lld linker β and
nothing else. Everything it touches is an *_INIT variable, so large projects
that manage their own flags (WebKit, LLVM, Qt, β¦) keep full control.
| cache variable | meaning |
|---|---|
XMAC_CLANG / XMAC_CLANGXX |
compiler to use (default: clang on PATH) |
XMAC_LLD |
path to ld64.lld |
CMAKE_OSX_ARCHITECTURES |
arm64, x86_64, or arm64;x86_64 for universal |
CMAKE_OSX_DEPLOYMENT_TARGET |
minimum macOS version |
source /path/to/xmac-sdk/env.sh
$CC -framework CoreFoundation -o hello hello.c
# or fully explicit:
clang --target=arm64-apple-macosx11.0 -isysroot "$SDKROOT" -fuse-ld=lld ...env.sh exports CC, CXX, SDKROOT, MACOSX_DEPLOYMENT_TARGET, plus
XMAC_CFLAGS / XMAC_CXXFLAGS / XMAC_LDFLAGS if you'd rather compose the
flags yourself.
rustup target add aarch64-apple-darwin
source /path/to/xmac-sdk/env.sh # sets CARGO_TARGET_*_LINKER and CC_*/CXX_* for cc-rs
cargo build --target aarch64-apple-darwinzig cc ships its own headers but still needs the real SDK for frameworks:
zig cc -target aarch64-macos --sysroot "$SDKROOT" -framework CoreFoundation ...swscan.apple.com βββΊ software-update catalog (public XML plist)
β which Command Line Tools releases exist?
βΌ
swcdn.apple.com βββΊ CLTools_macOS*_SDK.pkg (~55 MiB, no auth)
β
βΌ
xar archive βββΊ pbzx payload βββΊ cpio archive
β (chunked XZ) β
βΌ βΌ
signed SHA-1 TOC Library/Developer/β¦/SDKs/
(verified) MacOSX*.sdk βββΊ disk
xmac parses all three container formats itself (delegating raw XZ/bzip2
decompression to the system xz/bzip2 binaries), streams the payload
straight to disk, and keeps only β¦/SDKs/**. There is no hardcoded version
list anywhere β when Apple publishes a new SDK, xmac list shows it the same
day with no update to the tool.
The macOS SDK is headers, module maps and .tbd text stubs (linker import
libraries) β no executable code. That is why it is enough for building: the
real dylibs live on the end user's Mac and are bound at load time. You can
compile and link complete Mach-O executables, dylibs and bundles for arm64
and x86_64 with nothing but this SDK, clang and lld.
Not included (not in the SDK package, and macOS-only binaries anyway):
codesign, actool, ibtool, xcodebuild, the Swift compiler. For code
signing from Linux use rcodesign;
note ld64.lld already writes the ad-hoc signature arm64 macOS requires, so
plain executables run as-is.
The xmac tool is dual-licensed under MIT or Apache-2.0, at your option.
The SDK it downloads is Apple's, licensed under the macOS SDK and Xcode
Agreement (run xmac license to read it). In short: you may use it to
develop software for Apple platforms; you may not redistribute the SDK itself.
Practically, that means run xmac in your CI job (the cache makes repeat
runs cheap) rather than committing the extracted SDK to a repository or
publishing it inside a container image. xmac never uploads anything and only
talks to swscan.apple.com / swdist.apple.com / swcdn.apple.com.
This project is not affiliated with or endorsed by Apple Inc.