Skip to content

Commit 00a2381

Browse files
committed
Add support for cross-compiling Apple targets with cmake 3.14
1 parent 08624b8 commit 00a2381

File tree

2 files changed

+180
-0
lines changed

2 files changed

+180
-0
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ categories = ["development-tools::build-utils"]
1515

1616
[dependencies]
1717
cc = "1.0.41"
18+
lazy_static = "1.0"
19+
regex = "1.0"

src/lib.rs

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,13 @@
4444
4545
#![deny(missing_docs)]
4646

47+
#[macro_use]
48+
extern crate lazy_static;
49+
4750
extern crate cc;
51+
extern crate regex;
4852

53+
use regex::Regex;
4954
use std::env;
5055
use std::ffi::{OsStr, OsString};
5156
use std::fs::{self, File};
@@ -854,6 +859,8 @@ trait Target {
854859

855860
fn get_target(target_triple: &str) -> Box<dyn Target> {
856861
let target: Option<Box<dyn Target>>;
862+
target = AppleTarget::new(target_triple)
863+
.map(|apple_target| Box::new(apple_target) as Box<dyn Target>);
857864
target.unwrap_or_else(|| Box::new(GenericTarget::new(target_triple)))
858865
}
859866

@@ -867,6 +874,177 @@ impl GenericTarget {
867874

868875
impl Target for GenericTarget {}
869876

877+
struct AppleTarget {
878+
rust_target: String,
879+
rust_target_arch: String,
880+
rust_target_platform: String,
881+
}
882+
883+
impl AppleTarget {
884+
fn new(target_triple: &str) -> Option<AppleTarget> {
885+
let parts: Vec<&str> = target_triple.split('-').collect();
886+
if parts.len() < 3 {
887+
return None;
888+
}
889+
890+
let rust_target_arch = parts[0];
891+
let rust_target_vendor = parts[1];
892+
let rust_target_platform = parts[2];
893+
894+
if rust_target_vendor != "apple" {
895+
return None;
896+
}
897+
if rust_target_platform != "ios" && rust_target_platform != "darwin" {
898+
eprintln!(
899+
"Warning: unknown Apple platform ({}) for target: {}",
900+
rust_target_platform, target_triple
901+
);
902+
return None;
903+
}
904+
905+
Some(AppleTarget {
906+
rust_target: target_triple.to_owned(),
907+
rust_target_arch: rust_target_arch.to_owned(),
908+
rust_target_platform: rust_target_platform.to_owned(),
909+
})
910+
}
911+
912+
fn is_ios_target(&self) -> bool {
913+
self.rust_target_platform == "ios"
914+
}
915+
916+
fn is_osx_target(&self) -> bool {
917+
self.rust_target_platform == "darwin"
918+
}
919+
920+
fn cmake_target_arch(&self) -> Option<String> {
921+
match self.rust_target_arch.as_str() {
922+
"aarch64" => Some("arm64".to_owned()),
923+
"armv7" => Some("armv7".to_owned()),
924+
"armv7s" => Some("armv7s".to_owned()),
925+
"i386" => Some("i386".to_owned()),
926+
"x86_64" => Some("x86_64".to_owned()),
927+
_ => {
928+
eprintln!(
929+
"Warning: unknown architecture for target: {}",
930+
self.rust_target_arch
931+
);
932+
None
933+
}
934+
}
935+
}
936+
937+
fn sdk_name(&self) -> Option<String> {
938+
if self.is_ios_target() {
939+
match self.rust_target_arch.as_str() {
940+
"aarch64" | "armv7" | "armv7s" => Some("iphoneos".to_owned()),
941+
"i386" | "x86_64" => Some("iphonesimulator".to_owned()),
942+
_ => {
943+
eprintln!(
944+
"Warning: unknown architecture for Apple target: {}",
945+
self.rust_target_arch
946+
);
947+
None
948+
}
949+
}
950+
} else if self.is_osx_target() {
951+
Some("macosx".to_owned())
952+
} else {
953+
eprintln!(
954+
"Warning: could not determine sdk name for Apple target: {}",
955+
self.rust_target
956+
);
957+
None
958+
}
959+
}
960+
961+
fn deployment_target(&self) -> Option<String> {
962+
if self.is_ios_target() {
963+
println!("cargo:rerun-if-env-changed=IPHONEOS_DEPLOYMENT_TARGET");
964+
Some(std::env::var("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or_else(|_| "7.0".into()))
965+
} else if self.is_osx_target() {
966+
println!("cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET");
967+
Some(std::env::var("MACOSX_DEPLOYMENT_TARGET").unwrap_or_else(|_| "".into()))
968+
} else {
969+
eprintln!(
970+
"Warning: could not determine deployment target for Apple target: {}",
971+
self.rust_target
972+
);
973+
None
974+
}
975+
}
976+
}
977+
978+
impl Target for AppleTarget {
979+
fn add_cmake_defines(&self, cmd: &mut Command, config: &Config) {
980+
// These 3 CMAKE_OSX_* variables apply to all Apple platforms
981+
982+
if !config.defined("CMAKE_OSX_ARCHITECTURES") {
983+
if let Some(cmake_target_arch) = self.cmake_target_arch() {
984+
cmd.arg(format!("-DCMAKE_OSX_ARCHITECTURES={}", cmake_target_arch));
985+
}
986+
}
987+
988+
if !config.defined("CMAKE_OSX_SYSROOT") {
989+
if let Some(sdk_name) = self.sdk_name() {
990+
cmd.arg(format!("-DCMAKE_OSX_SYSROOT={}", sdk_name));
991+
}
992+
}
993+
994+
if !config.defined("CMAKE_OSX_DEPLOYMENT_TARGET") {
995+
if let Some(deployment_target) = self.deployment_target() {
996+
cmd.arg(format!(
997+
"-DCMAKE_OSX_DEPLOYMENT_TARGET={}",
998+
deployment_target
999+
));
1000+
}
1001+
}
1002+
1003+
// CMAKE_SYSTEM_NAME is used to tell cmake we're cross-compiling
1004+
if self.is_ios_target() && !config.defined("CMAKE_SYSTEM_NAME") {
1005+
cmd.arg("-DCMAKE_SYSTEM_NAME=iOS");
1006+
}
1007+
}
1008+
1009+
fn should_exclude_env_var(&self, key: &OsStr, _value: &OsStr) -> bool {
1010+
key.to_str().map_or(false, |key| {
1011+
// These cause issues with llvm if an env var for a different Apple platform
1012+
// is present. Since cmake handles communicating these values to llvm, and
1013+
// we use cmake defines to tell cmake what the value is, the env vars themselves
1014+
// are filtered out.
1015+
key.ends_with("DEPLOYMENT_TARGET") || key.starts_with("SDK")
1016+
})
1017+
}
1018+
1019+
fn filter_compiler_args(&self, flags: &mut OsString) {
1020+
if let Some(flags_str) = flags.to_str() {
1021+
lazy_static! {
1022+
static ref ARCH_REGEX: Regex = Regex::new("-arch [^ ]+ ").unwrap();
1023+
static ref DEPLOYMENT_TARGET_REGEX: Regex =
1024+
Regex::new("-m[\\w-]+-version-min=[\\d.]+ ").unwrap();
1025+
static ref SYSROOT_REGEX: Regex = Regex::new("-isysroot [^ ]+ ").unwrap();
1026+
}
1027+
1028+
let mut flags_string = flags_str.to_owned();
1029+
flags_string.push(' ');
1030+
1031+
// These are set by cmake
1032+
flags_string = ARCH_REGEX.replace(&flags_string, "").into_owned();
1033+
flags_string = DEPLOYMENT_TARGET_REGEX
1034+
.replace(&flags_string, "")
1035+
.into_owned();
1036+
flags_string = SYSROOT_REGEX.replace(&flags_string, "").into_owned();
1037+
1038+
if flags_string.ends_with(' ') {
1039+
flags_string.pop();
1040+
}
1041+
1042+
flags.clear();
1043+
flags.push(OsString::from(flags_string));
1044+
}
1045+
}
1046+
}
1047+
8701048
fn run(cmd: &mut Command, program: &str) {
8711049
println!("running: {:?}", cmd);
8721050
let status = match cmd.status() {

0 commit comments

Comments
 (0)