Skip to content

Jarred-Sumner/xmac

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

9 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🍎 xmac

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.

ci release license: MIT OR Apache-2.0 runtime: bun


$ 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 executable

A 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.

πŸ“¦ Install

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 xmac

Single-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 --help

To 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 --help

Either way you also need on PATH:

  • xz and bzip2 for extraction (apt-get install xz-utils bzip2 β€” already present on most images)
  • clang and lld (for ld64.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.

πŸš€ Usage

# 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 license

download β†’ 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.

Output layout

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

πŸ›  Cross-compiling

CMake (any generator β€” Ninja, Makefiles, …)

cmake -B build -G Ninja --toolchain /path/to/xmac-sdk/arm64-apple-darwin.toolchain.cmake
cmake --build build

The 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

Ninja / Make / autotools / shell

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.

Cargo / Rust

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-darwin

Zig

zig cc ships its own headers but still needs the real SDK for frameworks:

zig cc -target aarch64-macos --sysroot "$SDKROOT" -framework CoreFoundation ...

βš™οΈ How it works

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.

πŸ“š What's in the SDK (and what isn't)

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.

βš–οΈ License & legal

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.

About

Download and extract macOS SDKs from Apple's public CDN for cross-compilation. Like xwin, but for macOS.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

 
 
 

Contributors