use crate::{basic_manifest, main_file, project};
use cargo_util::ProcessError;
use std::env;
use std::fmt::Write;
use std::process::{Command, Output};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;
static CAN_RUN_ON_HOST: AtomicBool = AtomicBool::new(false);
pub fn disabled() -> bool {
match env::var("CFG_DISABLE_CROSS_TESTS") {
Ok(ref s) if *s == "1" => return true,
_ => {}
}
if !(cfg!(target_os = "macos") || cfg!(target_os = "linux") || cfg!(target_env = "msvc")) {
return true;
}
static CAN_BUILD_CROSS_TESTS: AtomicBool = AtomicBool::new(false);
static CHECK: Once = Once::new();
let cross_target = alternate();
let run_cross_test = || -> anyhow::Result<Output> {
let p = project()
.at("cross_test")
.file("Cargo.toml", &basic_manifest("cross_test", "1.0.0"))
.file("src/main.rs", &main_file(r#""testing!""#, &[]))
.build();
let build_result = p
.cargo("build --target")
.arg(&cross_target)
.exec_with_output();
if build_result.is_ok() {
CAN_BUILD_CROSS_TESTS.store(true, Ordering::SeqCst);
}
let result = p
.cargo("run --target")
.arg(&cross_target)
.exec_with_output();
if result.is_ok() {
CAN_RUN_ON_HOST.store(true, Ordering::SeqCst);
}
build_result
};
CHECK.call_once(|| {
drop(run_cross_test());
});
if CAN_BUILD_CROSS_TESTS.load(Ordering::SeqCst) {
return false;
}
static HAVE_WARNED: AtomicBool = AtomicBool::new(false);
if HAVE_WARNED.swap(true, Ordering::SeqCst) {
return true;
}
let mut message = format!(
"
Cannot cross compile to {}.
This failure can be safely ignored. If you would prefer to not see this
failure, you can set the environment variable CFG_DISABLE_CROSS_TESTS to \"1\".
Alternatively, you can install the necessary libraries to enable cross
compilation tests. Cross compilation tests depend on your host platform.
",
cross_target
);
if cfg!(target_os = "linux") {
message.push_str(
"
Linux cross tests target i686-unknown-linux-gnu, which requires the ability to
build and run 32-bit targets. This requires the 32-bit libraries to be
installed. For example, on Ubuntu, run `sudo apt install gcc-multilib` to
install the necessary libraries.
",
);
} else if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
message.push_str(
"
macOS on aarch64 cross tests to target x86_64-apple-darwin.
This should be natively supported via Xcode, nothing additional besides the
rustup target should be needed.
",
);
} else if cfg!(target_os = "macos") {
message.push_str(
"
macOS on x86_64 cross tests to target x86_64-apple-ios, which requires the iOS
SDK to be installed. This should be included with Xcode automatically. If you
are using the Xcode command line tools, you'll need to install the full Xcode
app (from the Apple App Store), and switch to it with this command:
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
Some cross-tests want to *run* the executables on the host. These tests will
be ignored if this is not possible. On macOS, this means you need an iOS
simulator installed to run these tests. To install a simulator, open Xcode, go
to preferences > Components, and download the latest iOS simulator.
",
);
} else if cfg!(target_os = "windows") {
message.push_str(
"
Windows cross tests target i686-pc-windows-msvc, which requires the ability
to build and run 32-bit targets. This should work automatically if you have
properly installed Visual Studio build tools.
",
);
} else {
panic!("platform should have been skipped");
}
let rustup_available = Command::new("rustup").output().is_ok();
if rustup_available {
write!(
message,
"
Make sure that the appropriate `rustc` target is installed with rustup:
rustup target add {}
",
cross_target
)
.unwrap();
} else {
write!(
message,
"
rustup does not appear to be installed. Make sure that the appropriate
`rustc` target is installed for the target `{}`.
",
cross_target
)
.unwrap();
}
match run_cross_test() {
Ok(_) => message.push_str("\nUh oh, second run succeeded?\n"),
Err(err) => match err.downcast_ref::<ProcessError>() {
Some(proc_err) => write!(message, "\nTest error: {}\n", proc_err).unwrap(),
None => write!(message, "\nUnexpected non-process error: {}\n", err).unwrap(),
},
}
panic!("{}", message);
}
pub fn native() -> &'static str {
env!("NATIVE_ARCH")
}
pub fn native_arch() -> &'static str {
match native()
.split("-")
.next()
.expect("Target triple has unexpected format")
{
"x86_64" => "x86_64",
"aarch64" => "aarch64",
"i686" => "x86",
_ => panic!("This test should be gated on cross_compile::disabled."),
}
}
pub fn alternate() -> &'static str {
if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
"x86_64-apple-darwin"
} else if cfg!(target_os = "macos") {
"x86_64-apple-ios"
} else if cfg!(target_os = "linux") {
"i686-unknown-linux-gnu"
} else if cfg!(all(target_os = "windows", target_env = "msvc")) {
"i686-pc-windows-msvc"
} else if cfg!(all(target_os = "windows", target_env = "gnu")) {
"i686-pc-windows-gnu"
} else {
panic!("This test should be gated on cross_compile::disabled.");
}
}
pub fn alternate_arch() -> &'static str {
if cfg!(target_os = "macos") {
"x86_64"
} else {
"x86"
}
}
pub fn unused() -> &'static str {
"wasm32-unknown-unknown"
}
pub fn can_run_on_host() -> bool {
if disabled() {
return false;
}
if cfg!(target_os = "macos") {
if CAN_RUN_ON_HOST.load(Ordering::SeqCst) {
return true;
} else {
println!("Note: Cannot run on host, skipping.");
return false;
}
} else {
assert!(CAN_RUN_ON_HOST.load(Ordering::SeqCst));
return true;
}
}