|
1 | 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | 2 |
|
| 3 | +use std::io::Write; |
| 4 | + |
3 | 5 | fn main() { |
4 | 6 | println!("cargo:rustc-env=WRAPPER_EXECUTABLE_PATH=/usr/libexec/bear/wrapper"); |
5 | 7 | println!("cargo:rustc-env=PRELOAD_LIBRARY_PATH=/usr/libexec/bear/$LIB/libexec.so"); |
| 8 | + |
| 9 | + // Re-run build script if env changes |
| 10 | + println!("cargo:rerun-if-env-changed=PATH"); |
| 11 | + |
| 12 | + // check things for the libexec.so |
| 13 | + check_include_file("dlfcn.h", "dlfcn_h"); |
| 14 | + check_symbol_exists("dlopen", "dlfcn.h"); |
| 15 | + check_symbol_exists("dlsym", "dlfcn.h"); |
| 16 | + check_symbol_exists("dlerror", "dlfcn.h"); |
| 17 | + check_symbol_exists("dlclose", "dlfcn.h"); |
| 18 | + check_symbol_exists("RTLD_NEXT", "dlfcn.h"); |
| 19 | + |
| 20 | + check_include_file("errno.h", "errno_h"); |
| 21 | + check_symbol_exists("EACCES", "errno.h"); |
| 22 | + check_symbol_exists("ENOENT", "errno.h"); |
| 23 | + |
| 24 | + // check things for the integration tests |
| 25 | + check_include_file("unistd.h", "unistd_h"); |
| 26 | + check_symbol_exists("execve", "unistd.h"); |
| 27 | + check_symbol_exists("execv", "unistd.h"); |
| 28 | + check_symbol_exists("execvpe", "unistd.h"); |
| 29 | + check_symbol_exists("execvp", "unistd.h"); |
| 30 | + check_symbol_exists("execvP", "unistd.h"); |
| 31 | + check_symbol_exists("exect", "unistd.h"); |
| 32 | + check_symbol_exists("execl", "unistd.h"); |
| 33 | + check_symbol_exists("execlp", "unistd.h"); |
| 34 | + check_symbol_exists("execle", "unistd.h"); |
| 35 | + check_symbol_exists("execveat", "unistd.h"); |
| 36 | + check_symbol_exists("fexecve", "unistd.h"); |
| 37 | + |
| 38 | + check_include_file("spawn.h", "spawn_h"); |
| 39 | + check_symbol_exists("posix_spawn", "spawn.h"); |
| 40 | + check_symbol_exists("posix_spawnp", "spawn.h"); |
| 41 | + |
| 42 | + check_include_file("stdio.h", "stdio_h"); |
| 43 | + check_symbol_exists("popen", "stdio.h"); |
| 44 | + |
| 45 | + check_include_file("stdlib.h", "stdlib_h"); |
| 46 | + check_symbol_exists("system", "stdlib.h"); |
| 47 | + |
| 48 | + check_executable_exists("true"); |
| 49 | + check_executable_exists("false"); |
| 50 | + check_executable_exists("echo"); |
| 51 | + check_executable_exists("sleep"); |
| 52 | + check_one_executable_exists("shell", &["sh", "zsh", "bash"]); |
| 53 | + check_one_executable_exists("make", &["make", "gmake", "mingw32-make"]); |
| 54 | + check_one_executable_exists("compiler_c", &["cc", "gcc", "clang"]); |
| 55 | + check_one_executable_exists("compiler_cxx", &["c++", "g++", "clang++"]); |
| 56 | + check_one_executable_exists("compiler_fortran", &["gfortran", "flang"]); |
| 57 | + check_one_executable_exists("compiler_cuda", &["nvcc"]); |
| 58 | + check_executable_exists("libtool"); |
| 59 | + check_executable_exists("fakeroot"); |
| 60 | + check_executable_exists("valgrind"); |
| 61 | + check_executable_exists("ar"); |
| 62 | +} |
| 63 | + |
| 64 | +fn check_include_file(header: &str, define: &str) { |
| 65 | + let result = cc::Build::new() |
| 66 | + .cargo_metadata(false) |
| 67 | + .cargo_output(false) |
| 68 | + .cargo_warnings(false) |
| 69 | + .inherit_rustflags(true) |
| 70 | + .file( |
| 71 | + tempfile::Builder::new() |
| 72 | + .prefix("check_include_") |
| 73 | + .suffix(".c") |
| 74 | + .tempfile_in(std::env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string())) |
| 75 | + .expect("Failed to create temp file for include check") |
| 76 | + .keep() // Keep the file for cc to compile |
| 77 | + .expect("Failed to keep temp file") |
| 78 | + .1, // Get the PathBuf |
| 79 | + ) |
| 80 | + .include(header) |
| 81 | + .try_compile(define); |
| 82 | + |
| 83 | + match result { |
| 84 | + Ok(_) => { |
| 85 | + println!("cargo:rustc-cfg=has_header_{}", define); |
| 86 | + println!("cargo:rustc-check-cfg=cfg(has_header_{})", define); |
| 87 | + println!( |
| 88 | + "cargo:warning=Checking for include file: {} ... found", |
| 89 | + header |
| 90 | + ); |
| 91 | + } |
| 92 | + Err(_) => { |
| 93 | + println!( |
| 94 | + "cargo:warning=Checking for include file: {} ... missing", |
| 95 | + header |
| 96 | + ); |
| 97 | + } |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +fn check_symbol_exists(symbol: &str, header: &str) { |
| 102 | + let check_code = format!( |
| 103 | + r#" |
| 104 | + #include <stddef.h> |
| 105 | + #include <{header}> |
| 106 | +
|
| 107 | + // Use a function pointer to avoid unused function warnings, |
| 108 | + // and ensure the linker must find the symbol. |
| 109 | + int main() {{ |
| 110 | + void *ptr = (void*){symbol}; |
| 111 | + (void)ptr; // Suppress unused variable warning |
| 112 | + return 0; |
| 113 | + }} |
| 114 | + "#, |
| 115 | + symbol = symbol, |
| 116 | + header = header |
| 117 | + ); |
| 118 | + |
| 119 | + let (mut file, path) = tempfile::Builder::new() |
| 120 | + .prefix(&format!("check_{}", symbol)) |
| 121 | + .suffix(".c") |
| 122 | + .tempfile_in(std::env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string())) |
| 123 | + .expect("Failed to create temp file for symbol check") |
| 124 | + .keep() |
| 125 | + .expect("Failed to keep temp file"); |
| 126 | + |
| 127 | + file.write_all(check_code.as_bytes()) |
| 128 | + .expect("Failed to write to temp file"); |
| 129 | + file.flush().expect("Failed to flush temp file"); |
| 130 | + |
| 131 | + let result = cc::Build::new() |
| 132 | + .cargo_metadata(false) |
| 133 | + .cargo_output(false) |
| 134 | + .cargo_warnings(false) |
| 135 | + .inherit_rustflags(true) |
| 136 | + .file(path) |
| 137 | + .try_compile(&format!("check_{}", symbol)); |
| 138 | + |
| 139 | + match result { |
| 140 | + Ok(_) => { |
| 141 | + println!("cargo:rustc-cfg=has_symbol_{}", symbol); |
| 142 | + println!("cargo:rustc-check-cfg=cfg(has_symbol_{})", symbol); |
| 143 | + println!("cargo:warning=Checking for symbol: {} ... found", symbol); |
| 144 | + } |
| 145 | + Err(_) => { |
| 146 | + println!("cargo:warning=Checking for symbol: {} ... missing", symbol); |
| 147 | + } |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +fn check_executable_exists(executable: &str) { |
| 152 | + match which::which(executable) { |
| 153 | + Ok(path) => { |
| 154 | + println!("cargo:rustc-cfg=has_executable_{}", executable); |
| 155 | + println!("cargo:rustc-check-cfg=cfg(has_executable_{})", executable); |
| 156 | + println!( |
| 157 | + "cargo:rustc-env={}_PATH={}", |
| 158 | + executable.to_uppercase(), |
| 159 | + path.display() |
| 160 | + ); |
| 161 | + println!( |
| 162 | + "cargo:warning=Checking for executable: {} ... {}", |
| 163 | + executable, |
| 164 | + path.display() |
| 165 | + ); |
| 166 | + } |
| 167 | + Err(_) => { |
| 168 | + println!( |
| 169 | + "cargo:warning=Checking for executable: {} ... missing", |
| 170 | + executable |
| 171 | + ); |
| 172 | + } |
| 173 | + } |
| 174 | +} |
| 175 | + |
| 176 | +fn check_one_executable_exists(define: &str, executables: &[&str]) { |
| 177 | + for executable in executables { |
| 178 | + if let Ok(path) = which::which(executable) { |
| 179 | + println!("cargo:rustc-cfg=has_executable_{}", define); |
| 180 | + println!("cargo:rustc-check-cfg=cfg(has_executable_{})", define); |
| 181 | + println!( |
| 182 | + "cargo:rustc-env={}_PATH={}", |
| 183 | + define.to_uppercase(), |
| 184 | + path.display() |
| 185 | + ); |
| 186 | + println!( |
| 187 | + "cargo:warning=Checking for executable: {} ... {}", |
| 188 | + define, |
| 189 | + path.display() |
| 190 | + ); |
| 191 | + return; |
| 192 | + } |
| 193 | + } |
| 194 | + println!( |
| 195 | + "cargo:warning=Checking for executable: {} ... missing", |
| 196 | + define |
| 197 | + ); |
6 | 198 | } |
0 commit comments