use std::{env, path::Path};
type Version = Option<String>;
#[allow(clippy::upper_case_acronyms)]
enum AppleRuntime {
MacOS(Version),
IOS(Version),
TvOS(Version),
WatchOS(Version),
}
use AppleRuntime::*;
enum Runtime {
Apple(AppleRuntime),
GNUStep(u8, u8),
WinObjC,
#[allow(dead_code)]
ObjFW(Option<String>),
}
use Runtime::*;
fn get_env(env: &str) -> Option<String> {
println!("cargo:rerun-if-env-changed={}", env);
match env::var(env) {
Ok(var) => Some(var),
Err(env::VarError::NotPresent) => None,
Err(env::VarError::NotUnicode(var)) => panic!("Invalid unicode for {}: {:?}", env, var),
}
}
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if env::var("TARGET").unwrap().ends_with("macabi") {
println!("cargo:rustc-cfg=target_abi_macabi");
}
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let mut apple = env::var_os("CARGO_FEATURE_APPLE").is_some();
let mut gnustep = env::var_os("CARGO_FEATURE_GNUSTEP_1_7").is_some();
let objfw = env::var_os("CARGO_FEATURE_UNSTABLE_OBJFW").is_some();
if cfg!(feature = "unstable-docsrs") {
if let "macos" | "ios" | "tvos" | "watchos" = &*target_os {
apple = true;
} else {
gnustep = true; }
}
let runtime = match (apple, gnustep, objfw) {
(true, false, false) => {
Apple(match &*target_os {
"macos" => MacOS(Some(
get_env("MACOSX_DEPLOYMENT_TARGET").unwrap_or_else(|| "10.7".into()),
)),
"ios" => IOS(Some(
get_env("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or_else(|| "7.0".into()),
)),
"tvos" => TvOS(get_env("TVOS_DEPLOYMENT_TARGET")),
"watchos" => WatchOS(get_env("WATCHOS_DEPLOYMENT_TARGET")),
_ => MacOS(None),
})
}
(false, true, false) => {
if cfg!(feature = "unstable-docsrs") {
if "windows" == target_os {
WinObjC
} else {
GNUStep(1, 7)
}
} else if env::var_os("CARGO_FEATURE_UNSTABLE_WINOBJC").is_some() {
WinObjC
} else if env::var_os("CARGO_FEATURE_GNUSTEP_2_1").is_some() {
GNUStep(2, 1)
} else if env::var_os("CARGO_FEATURE_GNUSTEP_2_0").is_some() {
GNUStep(2, 0)
} else if env::var_os("CARGO_FEATURE_GNUSTEP_1_9").is_some() {
GNUStep(1, 9)
} else if env::var_os("CARGO_FEATURE_GNUSTEP_1_8").is_some() {
GNUStep(1, 8)
} else {
GNUStep(1, 7)
}
}
(false, false, true) => {
unimplemented!("ObjFW is not yet supported")
}
(false, false, false) => panic!("Must specify the desired runtime (using cargo features)."),
_ => panic!("Invalid feature combination; only one runtime may be selected!"),
};
let runtime_cfg = match runtime {
Apple(_) => "apple",
GNUStep(_, _) | WinObjC => "gnustep",
ObjFW(_) => "objfw",
};
println!("cargo:rustc-cfg={}", runtime_cfg);
if let Apple(runtime) = &runtime {
if let (MacOS(_), "x86") = (runtime, &*target_arch) {
println!("cargo:rustc-cfg=apple_old");
} else {
println!("cargo:rustc-cfg=apple_new");
}
}
let clang_runtime = match &runtime {
Apple(runtime) => {
let clang_runtime_str = match (runtime, &*target_arch) {
(MacOS(_), "x86") => "macosx-fragile",
(MacOS(_), _) => "macosx",
(IOS(_), _) => "ios",
(WatchOS(_), _) => "watchos",
(TvOS(_), _) => "ios",
};
match runtime {
MacOS(version) | IOS(version) | WatchOS(version) | TvOS(version) => {
if let Some(version) = version {
format!("{}-{}", clang_runtime_str, version)
} else {
clang_runtime_str.into()
}
}
}
}
GNUStep(major, minor) => format!("gnustep-{}.{}", major, minor),
WinObjC => "gnustep-1.8".into(),
ObjFW(version) => {
let version = version.as_deref().unwrap_or("0.8");
format!("objfw-{}", version)
}
};
let mut cc_args = format!(
"-fobjc-arc -fobjc-arc-exceptions -fobjc-exceptions -fobjc-runtime={}",
clang_runtime
);
if let Runtime::ObjFW(_) = &runtime {
let compat_headers = Path::new(env!("CARGO_MANIFEST_DIR")).join("compat-headers-objfw");
cc_args.push_str(" -I");
cc_args.push_str(compat_headers.to_str().unwrap());
}
println!("cargo:cc_args={}", cc_args);
if let Runtime::ObjFW(_) = &runtime {
println!("cargo:rustc-link-lib=dylib=objfw-rt");
} else {
println!("cargo:rustc-link-lib=dylib=objc");
}
#[cfg(feature = "unstable-exception")]
{
if std::env::var("DOCS_RS").is_ok() {
return;
}
println!("cargo:rerun-if-changed=extern/exception.m");
let mut builder = cc::Build::new();
builder.file("extern/exception.m");
for flag in cc_args.split(' ') {
builder.flag(flag);
}
builder.compile("librust_objc_sys_0_2_try_catch_exception.a");
}
}