cargo_test_support/
cross_compile.rs1use crate::{basic_manifest, main_file, project};
13use cargo_util::ProcessError;
14use std::env;
15use std::fmt::Write;
16use std::process::{Command, Output};
17use std::sync::atomic::{AtomicBool, Ordering};
18use std::sync::Once;
19
20static CAN_RUN_ON_HOST: AtomicBool = AtomicBool::new(false);
22
23pub fn disabled() -> bool {
24 match env::var("CFG_DISABLE_CROSS_TESTS") {
26 Ok(ref s) if *s == "1" => return true,
27 _ => {}
28 }
29
30 if !(cfg!(target_os = "macos") || cfg!(target_os = "linux") || cfg!(target_env = "msvc")) {
32 return true;
33 }
34
35 static CAN_BUILD_CROSS_TESTS: AtomicBool = AtomicBool::new(false);
39 static CHECK: Once = Once::new();
40
41 let cross_target = alternate();
42
43 let run_cross_test = || -> anyhow::Result<Output> {
44 let p = project()
45 .at("cross_test")
46 .file("Cargo.toml", &basic_manifest("cross_test", "1.0.0"))
47 .file("src/main.rs", &main_file(r#""testing!""#, &[]))
48 .build();
49
50 let build_result = p
51 .cargo("build --target")
52 .arg(&cross_target)
53 .exec_with_output();
54
55 if build_result.is_ok() {
56 CAN_BUILD_CROSS_TESTS.store(true, Ordering::SeqCst);
57 }
58
59 let result = p
60 .cargo("run --target")
61 .arg(&cross_target)
62 .exec_with_output();
63
64 if result.is_ok() {
65 CAN_RUN_ON_HOST.store(true, Ordering::SeqCst);
66 }
67 build_result
68 };
69
70 CHECK.call_once(|| {
71 drop(run_cross_test());
72 });
73
74 if CAN_BUILD_CROSS_TESTS.load(Ordering::SeqCst) {
75 return false;
79 }
80
81 static HAVE_WARNED: AtomicBool = AtomicBool::new(false);
87
88 if HAVE_WARNED.swap(true, Ordering::SeqCst) {
89 return true;
92 }
93
94 let mut message = format!(
96 "
97Cannot cross compile to {}.
98
99This failure can be safely ignored. If you would prefer to not see this
100failure, you can set the environment variable CFG_DISABLE_CROSS_TESTS to \"1\".
101
102Alternatively, you can install the necessary libraries to enable cross
103compilation tests. Cross compilation tests depend on your host platform.
104",
105 cross_target
106 );
107
108 if cfg!(target_os = "linux") {
109 message.push_str(
110 "
111Linux cross tests target i686-unknown-linux-gnu, which requires the ability to
112build and run 32-bit targets. This requires the 32-bit libraries to be
113installed. For example, on Ubuntu, run `sudo apt install gcc-multilib` to
114install the necessary libraries.
115",
116 );
117 } else if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
118 message.push_str(
119 "
120macOS on aarch64 cross tests to target x86_64-apple-darwin.
121This should be natively supported via Xcode, nothing additional besides the
122rustup target should be needed.
123",
124 );
125 } else if cfg!(target_os = "macos") {
126 message.push_str(
127 "
128macOS on x86_64 cross tests to target x86_64-apple-ios, which requires the iOS
129SDK to be installed. This should be included with Xcode automatically. If you
130are using the Xcode command line tools, you'll need to install the full Xcode
131app (from the Apple App Store), and switch to it with this command:
132
133 sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
134
135Some cross-tests want to *run* the executables on the host. These tests will
136be ignored if this is not possible. On macOS, this means you need an iOS
137simulator installed to run these tests. To install a simulator, open Xcode, go
138to preferences > Components, and download the latest iOS simulator.
139",
140 );
141 } else if cfg!(target_os = "windows") {
142 message.push_str(
143 "
144Windows cross tests target i686-pc-windows-msvc, which requires the ability
145to build and run 32-bit targets. This should work automatically if you have
146properly installed Visual Studio build tools.
147",
148 );
149 } else {
150 panic!("platform should have been skipped");
152 }
153
154 let rustup_available = Command::new("rustup").output().is_ok();
155 if rustup_available {
156 write!(
157 message,
158 "
159Make sure that the appropriate `rustc` target is installed with rustup:
160
161 rustup target add {}
162",
163 cross_target
164 )
165 .unwrap();
166 } else {
167 write!(
168 message,
169 "
170rustup does not appear to be installed. Make sure that the appropriate
171`rustc` target is installed for the target `{}`.
172",
173 cross_target
174 )
175 .unwrap();
176 }
177
178 match run_cross_test() {
180 Ok(_) => message.push_str("\nUh oh, second run succeeded?\n"),
181 Err(err) => match err.downcast_ref::<ProcessError>() {
182 Some(proc_err) => write!(message, "\nTest error: {}\n", proc_err).unwrap(),
183 None => write!(message, "\nUnexpected non-process error: {}\n", err).unwrap(),
184 },
185 }
186
187 panic!("{}", message);
188}
189
190pub fn native() -> &'static str {
192 env!("NATIVE_ARCH")
193}
194
195pub fn native_arch() -> &'static str {
196 match native()
197 .split("-")
198 .next()
199 .expect("Target triple has unexpected format")
200 {
201 "x86_64" => "x86_64",
202 "aarch64" => "aarch64",
203 "i686" => "x86",
204 _ => panic!("This test should be gated on cross_compile::disabled."),
205 }
206}
207
208pub fn alternate() -> &'static str {
212 try_alternate().expect("This test should be gated on cross_compile::disabled.")
213}
214
215pub(crate) fn try_alternate() -> Option<&'static str> {
217 if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
218 Some("x86_64-apple-darwin")
219 } else if cfg!(target_os = "macos") {
220 Some("x86_64-apple-ios")
221 } else if cfg!(target_os = "linux") {
222 Some("i686-unknown-linux-gnu")
223 } else if cfg!(all(target_os = "windows", target_env = "msvc")) {
224 Some("i686-pc-windows-msvc")
225 } else if cfg!(all(target_os = "windows", target_env = "gnu")) {
226 Some("i686-pc-windows-gnu")
227 } else {
228 None
229 }
230}
231
232pub fn alternate_arch() -> &'static str {
233 if cfg!(target_os = "macos") {
234 "x86_64"
235 } else {
236 "x86"
237 }
238}
239
240pub fn unused() -> &'static str {
246 "wasm32-unknown-unknown"
247}
248
249pub fn can_run_on_host() -> bool {
251 if disabled() {
252 return false;
253 }
254 if cfg!(target_os = "macos") {
259 if CAN_RUN_ON_HOST.load(Ordering::SeqCst) {
260 return true;
261 } else {
262 println!("Note: Cannot run on host, skipping.");
263 return false;
264 }
265 } else {
266 assert!(CAN_RUN_ON_HOST.load(Ordering::SeqCst));
267 return true;
268 }
269}
270
271pub fn requires_target_installed(target: &str) -> bool {
279 let has_target = std::process::Command::new("rustup")
280 .args(["target", "list", "--installed"])
281 .output()
282 .ok()
283 .map(|output| {
284 String::from_utf8(output.stdout)
285 .map(|stdout| stdout.contains(target))
286 .unwrap_or_default()
287 })
288 .unwrap_or_default();
289 if !has_target {
290 let msg =
291 format!("to run this test, run `rustup target add {target} --toolchain <toolchain>`",);
292 if cargo_util::is_ci() {
293 panic!("{msg}");
294 } else {
295 eprintln!("{msg}");
296 }
297 }
298 has_target
299}