Library / SDK
tpoechtrager/osxcross avatar
tpoechtrager/osxcross

osxcross recommends its llvm flavor, and that flavor drops two of the four target architectures

MacOS Cross-Toolchain for Linux and *BSD

3,420 stars371 forksC++GPL-2.0

At a glance

What is it?
A macOS cross-compilation toolchain for Linux and BSD hosts, GPL-2.0 licensed, that combines a host compiler, a macOS SDK you generate yourself, Darwin binary utilities and a wrapper. The interesting decisions are all in the configuration. Build flavor selection has three mechanisms with a fallback, deployment-target defaults are derived from whichever SDK you supply so two people can get different minimum OS versions from the same script, and the flavor recommended for new projects is the one with the smallest target set.
Who is it for?
osxcross suits someone who needs to build macOS binaries from a Linux or BSD builder, particularly in CI, because the wrapper plus an emulated xcrun means macOS build recipes transfer without rewriting. Check four things first.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • tpoechtrager/osxcross3,420 ★ · +19
Star history
Repository2026-09-242026-11-14
tpoechtrager/osxcross3.4k3.4k

19 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Three flavors, and the recommended one supports the fewest targets

Flavor selection is the first decision the toolchain forces on you, and the three options are pinned to specific Apple tool versions.

`stable` is the default. It uses cctools 986 and ld64 711, and is described as known to be stable and well-working. `latest` uses cctools 1030.6.3 and ld64 956.6, and is called out as taking the longest to build because the newer tool versions require additional dependencies. `llvm` uses LLVM tools and `ld64.lld` instead of cctools and ld64, is described as the easiest flavor to build, and is the one recommended for new projects.

Here is the tension. The recommended flavor supports arm64, arm64e and x86_64, and explicitly does not support i386 or x86_64h. So the advice for people starting something new points at the flavor with the narrower target list, and if your project needs i386 you are being steered toward `stable` or `latest` instead.

The flavor also decides which binary utilities you get. The two cctools-based flavors build a port of Apple's tools, including `ar`, `lipo` and `otool`, together with the `ld64` linker. The `llvm` flavor uses the corresponding LLVM tools and `ld64.lld`, and can optionally build `cctools lipo` as a drop-in replacement for `llvm-lipo` where the LLVM version causes compatibility problems.

The two extra versions are the interesting numbers: cctools moves from 986 to 1030.6.3 and ld64 from 711 to 956.6, and the cost of taking the newer pair is stated outright as extra dependencies and a longer build.

Three ways to pick a flavor, ending in stable

Flavor selection has three mechanisms and a documented fallback order, which is unusual and useful.

Run `./build.sh` with nothing set and it prompts you to select a flavor; pressing Enter takes the default, which is `stable`. Set `BUILD_FLAVOR` to `stable`, `latest` or `llvm` and it selects without prompting. Set `UNATTENDED=1` and the specified flavor is used without any interaction, with the rule that if no flavor is specified then `stable` is selected automatically.

So there is a chain: an interactive prompt, then an environment variable, then an unattended mode with its own default, and every path terminates at `stable`.

The examples are given as four lines in one block:

sh
./build.sh
BUILD_FLAVOR=llvm ./build.sh
UNATTENDED=1 BUILD_FLAVOR=latest ./build.sh
TARGET_DIR=/usr/local/osxcross OSX_VERSION_MIN=XX.X ENABLE_ARCHS="<ARCHS>" ./build.sh

Two details in those four lines are worth pulling out. The unattended example selects `latest`, which the flavor table says takes the longest to build, so the documented non-interactive path is also the slow one. And the last line shows the other two knobs in one invocation: `TARGET_DIR` to change where the toolchain lands, and `ENABLE_ARCHS` to restrict the build to a subset, given as a space-separated list inside one quoted value such as `"arm64 x86_64"`.

By default the toolchain installs into `target` inside the current directory, and you are told to add `<target>/bin` to your `PATH` afterwards. So the install location follows the directory you happen to run the script in, which is what `TARGET_DIR` exists to override.

The deployment target is computed from your SDK, so builds are not comparable by default

This is the setting that surprises people, because it is derived rather than declared.

There are three defaults, keyed on the SDK you supplied. An SDK at or below 10.13 produces a deployment target of macOS 10.6. An SDK at or above 10.14 produces macOS 10.9. An SDK at or above 14.0 produces macOS 10.13.

So the minimum macOS version your binary will run on is a function of which SDK you extracted, not of anything you asked for. Two people running the identical command with different SDKs get binaries with different deployment targets, and nothing in the output distinguishes them.

There are three documented overrides. Set `OSX_VERSION_MIN=XX.X` when running the build script. Pass `-mmacos-version-min=XX.X` to the compiler. Or set the `MACOSX_DEPLOYMENT_TARGET=XX.X` environment variable.

The C++ standard library follows a related rule. A deployment target of 10.9 or above defaults to `libc++`, and it can be switched explicitly to `libstdc++` by passing `-stdlib=libstdc++`.

That switch matters more than it looks for a cross toolchain, because the two libraries are not interchangeable at the ABI level. A build that silently picks one while your build system assumes the other produces link errors or worse. Both this note and the deployment target section contain the same misspelling of the word overridden, which suggests they were edited in the same pass.

You supply Apple's SDK, so the toolchain cannot bootstrap itself

The first prerequisite is not a package. It is an SDK you generate, and the resulting archive goes into the `tarballs/` directory in the repository.

The rest of the prerequisites are ordinary: install the build dependencies, with a separate document covering required and optional ones, the Debian and Ubuntu installation commands, and the systems supported by a `tools/get_dependencies.sh` helper. Then optionally build a recent Clang and LLD, which is the subject of another document.

The SDK step is the one that shapes everything else. The file states that SDKs can be extracted either from the full Xcode or from the Xcode Command Line Tools, and points to a document with step-by-step instructions on macOS and on Linux. So the supported route is: take Apple's SDK from an Apple product you have access to, extract it yourself, and hand the archive to the build.

Nothing about that is the project's fault, and it is the only arrangement that works, since the SDK is Apple's and not something an open source toolchain can redistribute. But it means two consequences worth stating plainly. The toolchain's terms and Apple's SDK terms are separate, and this repository grants nothing about the SDK. And your build is not reproducible from the repository alone, because the SDK is the largest and least version-pinned input in the whole system, which is what makes the deployment-target defaults above vary.

The other consequence is that you cannot build this offline from a clean checkout. There is a gap you have to fill yourself.

build.sh does not build a compiler at all

It is worth being clear about what the base build produces, because the name suggests more than it does.

The description of what happens during `./build.sh` is three things: the SDK is installed into the target directory, the tools and dependencies required by the selected flavor are prepared, and the OSXCross wrapper is compiled. No compiler is built from source.

So the toolchain is a composition of four parts: a host compiler, a packaged macOS SDK, Darwin-compatible binary utilities, and a compiler wrapper. The host compiler is whatever clang or gcc you already have.

Compilers are available, but as five separate optional scripts rather than as part of the main build. One builds current upstream LLVM, Clang and LLD. One builds Apple Clang. One builds vanilla GCC for x86_64, with i386 multilib where that is supported. One builds an experimental Darwin GCC fork for ARM64 and x86_64. And one builds the compiler-rt runtime library.

The naming is the useful signal. The script for the experimental fork says so in its own description, and the one for vanilla GCC qualifies its multilib support with where supported. Neither presents itself as the default path.

What the wrapper contributes is described precisely: the installed target-prefixed commands configure the target triple, the SDK sysroot, the deployment target and the linker for macOS automatically. Four things injected, which is why you do not pass any of them on the command line.

The top-level entries match. There are the six build scripts, plus a `wrapper/` directory holding the wrapper, `patches/`, `tools/`, `oclang/` and the `tarballs/` directory the SDK archive goes into.

xcrun is emulated, which is why macOS build recipes transfer

The compatibility trick is the xcrun shim, and it is the reason this kind of toolchain is useful rather than merely possible.

Every usage example is offered twice. Once with the target-prefixed command directly, such as `x86_64-apple-darwinXX-clang++ test.cpp -O3 -o test`, and once through xcrun with an architecture flag, such as `xcrun clang++ -arch x86_64 test.cpp -O3 -o test`. Four architectures are shown each way: i386, x86_64, arm64 and arm64e, with a note that replacing clang++ with g++ works where you need GCC.

So a build system that already calls xcrun works on Linux without modification. That covers a large amount of real tooling, because xcrun is how macOS build scripts locate the compiler in the first place.

The Makefile example shows the mechanism from the other side:

sh
make CC=$(xcrun -f clang) CXX=$(xcrun -f clang++)

`xcrun -f clang` prints the path to clang, so the cross compiler is discovered at build time and handed to make as the compiler, rather than hardcoded. That is the general escape hatch for any project with CC and CXX baked into its build system.

There is also a check for the version the toolchain was built for. `osxcross-conf` prints it, under a `TARGET` key. You need that because of the other naming detail: the target-prefixed commands embed the target OS version, which is why the examples read `x86_64-apple-darwinXX-clang++` with the version as a placeholder rather than a fixed number.

Beyond that there are worked examples for autotools, for libc++ with a stated macOS 10.7 minimum, for link-time optimisation across two translation units, and for universal binaries built either as one clang invocation with two `-arch` flags or as two separate object builds joined with `xcrun lipo -create`.

The top-level readme is an index for ten sibling documents

The document you are reading is the shortest one in the repository, and the substance is elsewhere.

There are ten other readme files at the top level, each with a scoped name: one for SDK generation, one for build dependencies, one for building Clang, one for building GCC, one for CMake, one for the compiler-rt runtime, one for debugging, one for the bundled MacPorts, and one for pkg-config. Alongside them sit `CHANGELOG`, `COPYING`, `AUTHORS` and a file named `KNOWN_BUGS.md`.

Two of the sibling topics tell you what the project expects to be used with. The CMake document covers setup, compiler selection, universal binaries, package discovery and complete examples, which matches the claim that OSXCross provides architecture- and compiler-specific launchers plus an installed toolchain file. The MacPorts document covers what is described as a minimal MacPorts package manager included in the toolchain, which is how you obtain target libraries once you are cross compiling.

Two real projects are named as using it. One is a multi-architecture cross-compiler collection that runs in Docker and lists macOS among its systems and ppc and mips among its architectures, with OSXCross powering the Darwin builds specifically. The other is smartmontools.

The last section of the main document is cut short. It is headed Legacy branches, and the visible text begins describing a branch whose name starts with `ppc-te`, which is consistent with a PowerPC target, and then stops. So the branch list for older architectures is present but incomplete in the main file, which is consistent with the pattern elsewhere in this repository of the summary pointing at detail rather than containing it.

For the supported-architecture question specifically, the answer is in the flavor table rather than the legacy section: arm64, arm64e, x86_64 and conditionally i386.

Editorial conclusion

osxcross suits someone who needs to build macOS binaries from a Linux or BSD builder, particularly in CI, because the wrapper plus an emulated xcrun means macOS build recipes transfer without rewriting. Check four things first. That you can produce the SDK, since the toolchain does not ship one and the documented route is extracting it from Xcode or the Command Line Tools, which means Apple's own licence terms apply to what you build with and that is not something this repository grants you. Which flavor to use, knowing the one recommended for new projects supports three targets rather than four. What deployment target you end up with, since the default is computed from your SDK and you may need to override it in one of three places. And what your real command names are, because the target version is embedded in them. Read the sibling documents rather than the top-level readme, which is an index.

Frequently asked questions

How can I cross-compile macOS code on Linux?

With OSXCross. Generate the macOS SDK and place the archive in the `tarballs/` directory, install the build dependencies, run `./build.sh` to install into `target` under the current directory, then add `<target>/bin` to your `PATH`. Compile with the target-prefixed commands such as `x86_64-apple-darwinXX-clang++` or through the emulated xcrun, and check the target version with `osxcross-conf`. Host operating systems are Linux and the BSDs.

Which osxcross build flavor should I use for a new project?

The file recommends `llvm` for new projects and describes it as the easiest to build, using LLVM tools and `ld64.lld` instead of cctools and ld64. It supports arm64, arm64e and x86_64, and does not support i386 or x86_64h. It can optionally use `cctools lipo` in place of `llvm-lipo` for better compatibility.

What differs between the osxcross build flavors?

The default `stable` uses cctools 986 with ld64 711 and is described as known to be stable. `latest` uses cctools 1030.6.3 with ld64 956.6 and takes the longest to build because the newer tool versions need additional dependencies. `llvm` uses LLVM tools with ld64.lld. The two cctools flavors build a port of Apple's tools including `ar`, `lipo` and `otool`.

Why does my osxcross command name contain a version?

The target-prefixed commands embed the target OS version in the name, which is why the examples read `x86_64-apple-darwinXX-clang++` with the version as a placeholder rather than a number. The file says to check your target version with `osxcross-conf` and look at its `TARGET` value. Four target architectures are shown, i386, x86_64, arm64 and arm64e.

How do I set the macOS deployment target in osxcross?

It defaults from your SDK: an SDK at or below 10.13 gives macOS 10.6, at or above 10.14 gives 10.9, and at or above 14.0 gives 10.13. Override it with `OSX_VERSION_MIN=XX.X ./build.sh`, with `-mmacos-version-min=XX.X` passed to the compiler, or with the `MACOSX_DEPLOYMENT_TARGET=XX.X` environment variable. A target of 10.9 or above defaults to libc++ and can be switched to libstdc++ with `-stdlib=libstdc++`.

Official sources

  1. Issues
  2. License: GPL-2.0
  3. README
  4. tpoechtrager/osxcross on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tpoechtrager-osxcross.svg)](https://hysenlabs.com/projects/tpoechtrager-osxcross)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/tpoechtrager-osxcross.svg)](https://hysenlabs.com/projects/tpoechtrager-osxcross)

Was this useful?