| 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 | |