Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 125 additions & 1 deletion crates/joltc-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,101 @@ fn build_joltc() {
// features just based on opt-level.
config.profile("Release");

// Read the actual cross-compile target rather than the build host so
// the rest of this fn can dispatch correctly. `cfg!(...)` would
// evaluate at build-script-compile time on the HOST.
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();

// Jolt fails to compile via the cmake crate without specifying exception
// handling behavior under MSVC. I'm not sure that this is the correct
// exception handling mode.
if cfg!(windows) {
//
// The original `cfg!(windows)` is a HOST-OS check (build.rs runs on
// the host, so it is always true on Windows). When cross-compiling
// to Android from a Windows host, NDK clang receives `/EHsc` as a
// path argument and errors. Switch to the target check so the flag
// only applies to actual Windows targets.
if target_os == "windows" {
config.cxxflag("/EHsc");
}

// Native Android cross-compile setup. Without this, building
// joltc-sys for Android via the standard
// `cargo ndk --target <android-triple> check` flow fails because
// (a) cmake-rs defaults to MSBuild on Windows hosts which can't
// target Android; (b) the NDK's `android.toolchain.cmake` requires
// `ANDROID_ABI` to be set as a CMake variable (env var is
// ignored); (c) `ANDROID_NDK_HOME` is the canonical env name set
// by `nttld/setup-ndk` GitHub Action + cargo-ndk's local
// workflow.
//
// We translate cargo's target arch to NDK's ABI string and route
// CMake through the NDK toolchain file. `ANDROID_PLATFORM=android-21`
// is the same baseline most cargo-ndk users target.
if target_os == "android" {
let android_ndk_home = env::var("ANDROID_NDK_HOME")
.or_else(|_| env::var("ANDROID_NDK_ROOT"))
.or_else(|_| env::var("ANDROID_NDK"))
.expect(
"Android cross-compile requires ANDROID_NDK_HOME (or ANDROID_NDK_ROOT / \
ANDROID_NDK) to point at the NDK install. Install via \
`nttld/setup-ndk@v1` in CI or the Android SDK manager locally.",
);
let toolchain_file = format!("{android_ndk_home}/build/cmake/android.toolchain.cmake");
config.define("CMAKE_TOOLCHAIN_FILE", &toolchain_file);
let android_abi = match target_arch.as_str() {
"aarch64" => "arm64-v8a",
"arm" => "armeabi-v7a",
"x86" => "x86",
"x86_64" => "x86_64",
_ => panic!("unsupported Android target arch: {target_arch}"),
};
config.define("ANDROID_ABI", android_abi);
config.define("ANDROID_PLATFORM", "android-21");
// cmake-rs defaults to the host-OS generator (MSBuild on Win);
// the NDK toolchain only supports Ninja / Makefiles. Force
// Ninja explicitly.
config.generator("Ninja");
}

// Native iOS cross-compile setup. cargo-side targets are
// `aarch64-apple-ios` (device) and `aarch64-apple-ios-sim`
// (simulator on Apple Silicon hosts); `x86_64-apple-ios` is the
// legacy Intel-Mac simulator path. CMake distinguishes device vs
// simulator via `CMAKE_OSX_SYSROOT` (`iphoneos` vs
// `iphonesimulator`); cargo-side the simulator is identified by
// `CARGO_CFG_TARGET_ABI=sim`. Without this branch, CMake builds
// against the macOS SDK and the resulting static libs fail at
// link time with `undefined symbol` for every iOS-specific
// runtime symbol cargo expects to find.
if target_os == "ios" {
config.define("CMAKE_SYSTEM_NAME", "iOS");
let osx_arch = match target_arch.as_str() {
"aarch64" => "arm64",
"x86_64" => "x86_64",
_ => panic!("unsupported iOS target arch: {target_arch}"),
};
config.define("CMAKE_OSX_ARCHITECTURES", osx_arch);
let target_abi = env::var("CARGO_CFG_TARGET_ABI").unwrap_or_default();
let sysroot = if target_abi == "sim" || target_arch == "x86_64" {
"iphonesimulator"
} else {
"iphoneos"
};
config.define("CMAKE_OSX_SYSROOT", sysroot);
// iOS 12 is the floor for arm64-only devices (post iPhone 5s)
// and aligns with Apple's current minimum-supported deployment
// target tier; consumers can override via cmake's standard
// env var if they need a different floor.
config.define("CMAKE_OSX_DEPLOYMENT_TARGET", "12.0");
// Force Ninja so the build is generator-agnostic on macos
// CI runners (which have both Xcode and Ninja). Xcode generator
// works too but is slower and pulls in Xcode-specific scheme
// noise we don't need for a static lib.
config.generator("Ninja");
}

// Having IPO/LTO turned on breaks lld on Windows.
config.define("INTERPROCEDURAL_OPTIMIZATION", "OFF");

Expand Down Expand Up @@ -110,6 +198,10 @@ fn build_flags() -> Vec<(&'static str, &'static str)> {
}

fn generate_bindings(flags: &[(&'static str, &'static str)]) -> anyhow::Result<()> {
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
let target_abi = env::var("CARGO_CFG_TARGET_ABI").unwrap_or_default();

let mut builder = bindgen::Builder::default()
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.header("JoltC/JoltC/JoltC.h")
Expand All @@ -118,6 +210,38 @@ fn generate_bindings(flags: &[(&'static str, &'static str)]) -> anyhow::Result<(
.default_enum_style(bindgen::EnumVariation::Consts)
.prepend_enum_name(false);

// bindgen's default behaviour passes `TARGET` (rustc's triple) to
// clang. rustc's iOS-simulator triple `aarch64-apple-ios-sim` is
// invalid clang syntax — clang errors with `version 'sim' in
// target triple is invalid`. Translate to clang's recognised form
// (`arm64-apple-ios-simulator` for sim, `arm64-apple-ios` for
// device) and supply the matching SDK sysroot via `xcrun` so iOS
// system headers (stdint.h, etc.) resolve correctly. macOS host
// is the only supported iOS-host today (Apple SDKs are gated to
// that platform).
if target_os == "ios" {
let clang_target = match (target_arch.as_str(), target_abi.as_str()) {
("aarch64", "sim") => "arm64-apple-ios-simulator",
("aarch64", _) => "arm64-apple-ios",
("x86_64", _) => "x86_64-apple-ios-simulator",
_ => panic!("unsupported iOS arch/abi: {target_arch}/{target_abi}"),
};
builder = builder.clang_arg(format!("--target={clang_target}"));

let sdk_name = if target_abi == "sim" || target_arch == "x86_64" {
"iphonesimulator"
} else {
"iphoneos"
};
let sdk_output = std::process::Command::new("xcrun")
.args(["--sdk", sdk_name, "--show-sdk-path"])
.output()
.with_context(|| format!("failed to invoke `xcrun --sdk {sdk_name}`"))?;
let sdk_path = String::from_utf8(sdk_output.stdout).context("xcrun output is not utf-8")?;
let sdk_path = sdk_path.trim();
builder = builder.clang_arg(format!("-isysroot{sdk_path}"));
}

for (key, value) in flags {
builder = builder.clang_arg(format!("-D{key}={value}"));
}
Expand Down