1 | use std::env::var; |
2 | use std::io::Write; |
3 | |
4 | /// The directory for inline asm. |
5 | const ASM_PATH: &str = "src/backend/linux_raw/arch" ; |
6 | |
7 | fn main() { |
8 | // Don't rerun this on changes other than build.rs, as we only depend on |
9 | // the rustc version. |
10 | println!("cargo:rerun-if-changed=build.rs" ); |
11 | |
12 | // Gather target information. |
13 | let arch = var("CARGO_CFG_TARGET_ARCH" ).unwrap(); |
14 | let env = var("CARGO_CFG_TARGET_ENV" ).unwrap(); |
15 | let inline_asm_name = format!("{}/{}.rs" , ASM_PATH, arch); |
16 | let inline_asm_name_present = std::fs::metadata(inline_asm_name).is_ok(); |
17 | let os = var("CARGO_CFG_TARGET_OS" ).unwrap(); |
18 | let pointer_width = var("CARGO_CFG_TARGET_POINTER_WIDTH" ).unwrap(); |
19 | let endian = var("CARGO_CFG_TARGET_ENDIAN" ).unwrap(); |
20 | |
21 | // Check for special target variants. |
22 | let is_x32 = arch == "x86_64" && pointer_width == "32" ; |
23 | let is_arm64_ilp32 = arch == "aarch64" && pointer_width == "32" ; |
24 | let is_powerpc64be = arch == "powerpc64" && endian == "big" ; |
25 | let is_mipseb = (arch == "mips" || arch == "mips32r6" ) && endian == "big" ; |
26 | let is_mips64eb = arch.contains("mips64" ) && endian == "big" ; |
27 | let is_unsupported_abi = is_x32 || is_arm64_ilp32 || is_powerpc64be || is_mipseb || is_mips64eb; |
28 | |
29 | // Check for `--features=use-libc`. This allows crate users to enable the |
30 | // libc backend. |
31 | let feature_use_libc = var("CARGO_FEATURE_USE_LIBC" ).is_ok(); |
32 | |
33 | // Check for `RUSTFLAGS=--cfg=rustix_use_libc`. This allows end users to |
34 | // enable the libc backend even if rustix is depended on transitively. |
35 | let cfg_use_libc = var("CARGO_CFG_RUSTIX_USE_LIBC" ).is_ok(); |
36 | |
37 | // Check for `--features=rustc-dep-of-std`. |
38 | let rustc_dep_of_std = var("CARGO_FEATURE_RUSTC_DEP_OF_STD" ).is_ok(); |
39 | |
40 | // Check for eg. `RUSTFLAGS=--cfg=rustix_use_experimental_features`. This |
41 | // is a rustc flag rather than a cargo feature flag because it's |
42 | // experimental and not something we want accidentally enabled via |
43 | // `--all-features`. |
44 | let rustix_use_experimental_features = |
45 | var("CARGO_CFG_RUSTIX_USE_EXPERIMENTAL_FEATURES" ).is_ok(); |
46 | |
47 | // Check for eg. `RUSTFLAGS=--cfg=rustix_use_experimental_asm`. This is a |
48 | // rustc flag rather than a cargo feature flag because it's experimental |
49 | // and not something we want accidentally enabled via `--all-features`. |
50 | let rustix_use_experimental_asm = var("CARGO_CFG_RUSTIX_USE_EXPERIMENTAL_ASM" ).is_ok(); |
51 | |
52 | // Miri doesn't support inline asm, and has builtin support for recognizing |
53 | // libc FFI calls, so if we're running under miri, use the libc backend. |
54 | let miri = var("CARGO_CFG_MIRI" ).is_ok(); |
55 | |
56 | // If experimental features are enabled, auto-detect and use available |
57 | // features. |
58 | if rustc_dep_of_std { |
59 | use_feature("rustc_attrs" ); |
60 | use_feature("core_intrinsics" ); |
61 | } else if rustix_use_experimental_features { |
62 | use_feature_or_nothing("rustc_attrs" ); |
63 | use_feature_or_nothing("core_intrinsics" ); |
64 | } |
65 | |
66 | // Features needed only in no-std configurations. |
67 | #[cfg (not(feature = "std" ))] |
68 | { |
69 | use_feature_or_nothing("core_c_str" ); |
70 | use_feature_or_nothing("core_ffi_c" ); |
71 | use_feature_or_nothing("alloc_c_string" ); |
72 | use_feature_or_nothing("alloc_ffi" ); |
73 | } |
74 | |
75 | // Feature needed for testing. |
76 | if use_static_assertions() { |
77 | use_feature("static_assertions" ); |
78 | } |
79 | |
80 | // WASI support can utilize wasi_ext if present. |
81 | if os == "wasi" { |
82 | use_feature_or_nothing("wasi_ext" ); |
83 | } |
84 | |
85 | // If the libc backend is requested, or if we're not on a platform for |
86 | // which we have linux_raw support, use the libc backend. |
87 | // |
88 | // For now Android uses the libc backend; in theory it could use the |
89 | // linux_raw backend, but to do that we'll need to figure out how to |
90 | // install the toolchain for it. |
91 | let libc = feature_use_libc |
92 | || cfg_use_libc |
93 | || os != "linux" |
94 | || !inline_asm_name_present |
95 | || is_unsupported_abi |
96 | || miri |
97 | || ((arch == "powerpc64" || arch.starts_with("mips" )) && !rustix_use_experimental_asm); |
98 | if libc { |
99 | // Use the libc backend. |
100 | use_feature("libc" ); |
101 | } else { |
102 | // Use the linux_raw backend. |
103 | use_feature("linux_raw" ); |
104 | if rustix_use_experimental_asm { |
105 | use_feature("asm_experimental_arch" ); |
106 | } |
107 | } |
108 | |
109 | // Detect whether the compiler requires us to use thumb mode on ARM. |
110 | if arch == "arm" && use_thumb_mode() { |
111 | use_feature("thumb_mode" ); |
112 | } |
113 | |
114 | // Rust's libc crate groups some OS's together which have similar APIs; |
115 | // create similarly-named features to make `cfg` tests more concise. |
116 | let freebsdlike = os == "freebsd" || os == "dragonfly" ; |
117 | if freebsdlike { |
118 | use_feature("freebsdlike" ); |
119 | } |
120 | let netbsdlike = os == "openbsd" || os == "netbsd" ; |
121 | if netbsdlike { |
122 | use_feature("netbsdlike" ); |
123 | } |
124 | let apple = os == "macos" || os == "ios" || os == "tvos" || os == "watchos" ; |
125 | if apple { |
126 | use_feature("apple" ); |
127 | } |
128 | if os == "linux" || os == "l4re" || os == "android" || os == "emscripten" { |
129 | use_feature("linux_like" ); |
130 | } |
131 | if os == "solaris" || os == "illumos" { |
132 | use_feature("solarish" ); |
133 | } |
134 | if apple || freebsdlike || netbsdlike { |
135 | use_feature("bsd" ); |
136 | } |
137 | |
138 | // Add some additional common target combinations. |
139 | |
140 | // Android and "regular" Linux both use the Linux kernel. |
141 | if os == "android" || os == "linux" { |
142 | use_feature("linux_kernel" ); |
143 | } |
144 | |
145 | // These platforms have a 32-bit `time_t`. |
146 | if libc |
147 | && (arch == "arm" |
148 | || arch == "mips" |
149 | || arch == "sparc" |
150 | || arch == "x86" |
151 | || (arch == "wasm32" && os == "emscripten" )) |
152 | && (apple |
153 | || os == "android" |
154 | || os == "emscripten" |
155 | || os == "haiku" |
156 | || env == "gnu" |
157 | || (env == "musl" && arch == "x86" )) |
158 | { |
159 | use_feature("fix_y2038" ); |
160 | } |
161 | |
162 | println!("cargo:rerun-if-env-changed=CARGO_CFG_RUSTIX_USE_EXPERIMENTAL_ASM" ); |
163 | println!("cargo:rerun-if-env-changed=CARGO_CFG_RUSTIX_USE_LIBC" ); |
164 | |
165 | // Rerun this script if any of our features or configuration flags change, |
166 | // or if the toolchain we used for feature detection changes. |
167 | println!("cargo:rerun-if-env-changed=CARGO_FEATURE_USE_LIBC" ); |
168 | println!("cargo:rerun-if-env-changed=CARGO_FEATURE_RUSTC_DEP_OF_STD" ); |
169 | println!("cargo:rerun-if-env-changed=CARGO_CFG_MIRI" ); |
170 | } |
171 | |
172 | fn use_static_assertions() -> bool { |
173 | // `offset_from` was made const in Rust 1.65. |
174 | can_compile("const unsafe fn foo(p: *const u8) -> isize { p.offset_from(p) }" ) |
175 | } |
176 | |
177 | fn use_thumb_mode() -> bool { |
178 | // In thumb mode, r7 is reserved. |
179 | !can_compile("pub unsafe fn f() { core::arch::asm!( \"udf #16 \", in( \"r7 \") 0); }" ) |
180 | } |
181 | |
182 | fn use_feature_or_nothing(feature: &str) { |
183 | if has_feature(feature) { |
184 | use_feature(feature); |
185 | } |
186 | } |
187 | |
188 | fn use_feature(feature: &str) { |
189 | println!("cargo:rustc-cfg={}" , feature); |
190 | } |
191 | |
192 | /// Test whether the rustc at `var("RUSTC")` supports the given feature. |
193 | fn has_feature(feature: &str) -> bool { |
194 | can_compile(format!( |
195 | "#![allow(stable_features)] \n#![feature({})]" , |
196 | feature |
197 | )) |
198 | } |
199 | |
200 | /// Test whether the rustc at `var("RUSTC")` can compile the given code. |
201 | fn can_compile<T: AsRef<str>>(test: T) -> bool { |
202 | use std::process::Stdio; |
203 | |
204 | let out_dir = var("OUT_DIR" ).unwrap(); |
205 | let rustc = var("RUSTC" ).unwrap(); |
206 | let target = var("TARGET" ).unwrap(); |
207 | |
208 | // Use `RUSTC_WRAPPER` if it's set, unless it's set to an empty string, as |
209 | // documented [here]. |
210 | // [here]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads |
211 | let wrapper = var("RUSTC_WRAPPER" ) |
212 | .ok() |
213 | .and_then(|w| if w.is_empty() { None } else { Some(w) }); |
214 | |
215 | let mut cmd = if let Some(wrapper) = wrapper { |
216 | let mut cmd = std::process::Command::new(wrapper); |
217 | // The wrapper's first argument is supposed to be the path to rustc. |
218 | cmd.arg(rustc); |
219 | cmd |
220 | } else { |
221 | std::process::Command::new(rustc) |
222 | }; |
223 | |
224 | cmd.arg("--crate-type=rlib" ) // Don't require `main`. |
225 | .arg("--emit=metadata" ) // Do as little as possible but still parse. |
226 | .arg("--target" ) |
227 | .arg(target) |
228 | .arg("--out-dir" ) |
229 | .arg(out_dir); // Put the output somewhere inconsequential. |
230 | |
231 | // If Cargo wants to set RUSTFLAGS, use that. |
232 | if let Ok(rustflags) = var("CARGO_ENCODED_RUSTFLAGS" ) { |
233 | if !rustflags.is_empty() { |
234 | for arg in rustflags.split(' \x1f' ) { |
235 | cmd.arg(arg); |
236 | } |
237 | } |
238 | } |
239 | |
240 | let mut child = cmd |
241 | .arg("-" ) // Read from stdin. |
242 | .stdin(Stdio::piped()) // Stdin is a pipe. |
243 | .stderr(Stdio::null()) // Errors from feature detection aren't interesting and can be confusing. |
244 | .spawn() |
245 | .unwrap(); |
246 | |
247 | writeln!(child.stdin.take().unwrap(), "{}" , test.as_ref()).unwrap(); |
248 | |
249 | child.wait().unwrap().success() |
250 | } |
251 | |