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