1mod cmd;
16mod os_hacks;
17mod setup;
18
19use std::ffi::OsString;
20use std::os::unix::prelude::CommandExt;
21use std::path::{Path, PathBuf};
22use std::process::Command;
23use std::{env, fs};
24
25use anyhow::{Context, Result, bail};
26
27pub fn proxy(bin: &str) -> Result<()> {
30 match parse_args(env::args_os().collect()) {
31 ArgsResult::ExplicitSetup { use_local_bundle, use_local_toolchain } => {
32 setup::setup(use_local_bundle, use_local_toolchain)
33 }
34 ArgsResult::Default => {
35 fail_if_in_dev_environment()?;
36 if !setup::appears_setup() {
37 setup::setup(None, None)?;
38 } else {
39 if let Some(path_to_bundle) = setup::appears_incomplete() {
42 setup::setup(Some(path_to_bundle.clone().into_os_string()), None)?;
43 let _ = fs::remove_file(path_to_bundle);
46 }
47 }
48 exec(bin)
49 }
50 }
51}
52
53#[derive(PartialEq, Eq, Debug)]
55enum ArgsResult {
56 ExplicitSetup { use_local_bundle: Option<OsString>, use_local_toolchain: Option<OsString> },
57 Default,
58}
59
60fn parse_args(args: Vec<OsString>) -> ArgsResult {
62 let args_ez: Vec<Option<&str>> = args.iter().map(|x| x.to_str()).collect();
65 match &args_ez[..] {
68 &[_, Some("setup"), Some("--use-local-bundle"), _, Some("--use-local-toolchain"), _] => {
69 ArgsResult::ExplicitSetup {
70 use_local_bundle: Some(args[3].clone()),
71 use_local_toolchain: Some(args[5].clone()),
72 }
73 }
74 &[
75 _,
76 Some("kani"),
77 Some("setup"),
78 Some("--use-local-bundle"),
79 _,
80 Some("--use-local-toolchain"),
81 _,
82 ] => ArgsResult::ExplicitSetup {
83 use_local_bundle: Some(args[4].clone()),
84 use_local_toolchain: Some(args[6].clone()),
85 },
86 &[_, Some("setup"), Some("--use-local-bundle"), _] => ArgsResult::ExplicitSetup {
87 use_local_bundle: Some(args[3].clone()),
88 use_local_toolchain: None,
89 },
90 &[_, Some("kani"), Some("setup"), Some("--use-local-bundle"), _] => {
91 ArgsResult::ExplicitSetup {
92 use_local_bundle: Some(args[4].clone()),
93 use_local_toolchain: None,
94 }
95 }
96 &[_, Some("setup"), Some("--use-local-toolchain"), _] => ArgsResult::ExplicitSetup {
97 use_local_bundle: None,
98 use_local_toolchain: Some(args[3].clone()),
99 },
100 &[_, Some("kani"), Some("setup"), Some("--use-local-toolchain"), _] => {
101 ArgsResult::ExplicitSetup {
102 use_local_bundle: None,
103 use_local_toolchain: Some(args[4].clone()),
104 }
105 }
106 &[_, Some("setup")] | &[_, Some("kani"), Some("setup")] => {
107 ArgsResult::ExplicitSetup { use_local_bundle: None, use_local_toolchain: None }
108 }
109 _ => ArgsResult::Default,
110 }
111}
112
113fn fail_if_in_dev_environment() -> Result<()> {
118 #[allow(clippy::collapsible_if)]
121 if let Ok(exe) = std::env::current_exe() {
122 if let Some(path) = exe.parent() {
123 if path.ends_with("target/debug") || path.ends_with("target/release") {
124 bail!(
125 "Running a release-only executable, {}, from a development environment. This is usually caused by PATH including 'target/release' erroneously.",
126 exe.file_name().unwrap().to_string_lossy()
127 )
128 }
129 }
130 }
131
132 Ok(())
133}
134
135fn exec(bin: &str) -> Result<()> {
138 let kani_dir = setup::kani_dir()?;
139 let program = kani_dir.join("bin").join("kani-driver");
140 let pyroot = kani_dir.join("pyroot");
141 let bin_kani = kani_dir.join("bin");
142 let bin_pyroot = pyroot.join("bin");
143
144 let pythonpath = prepend_search_path(&[pyroot], env::var_os("PYTHONPATH"))?;
146 let path = prepend_search_path(&[bin_kani, bin_pyroot], env::var_os("PATH"))?;
148
149 fixup_dynamic_linking_environment();
151 set_kani_rust_toolchain(&kani_dir)?;
153
154 let mut cmd = Command::new(program);
155 cmd.args(env::args_os().skip(1)).env("PYTHONPATH", pythonpath).env("PATH", path).arg0(bin);
156
157 let result = cmd.status().context("Failed to invoke kani-driver")?;
158
159 std::process::exit(result.code().expect("No exit code?"));
160}
161
162fn prepend_search_path(paths: &[PathBuf], original: Option<OsString>) -> Result<OsString> {
164 match original {
165 None => Ok(env::join_paths(paths)?),
166 Some(original) => {
167 let orig = env::split_paths(&original);
168 let new_iter = paths.iter().cloned().chain(orig);
169 Ok(env::join_paths(new_iter)?)
170 }
171 }
172}
173
174fn fixup_dynamic_linking_environment() {
191 #[cfg(not(target_os = "macos"))]
192 const LOADER_PATH: &str = "LD_LIBRARY_PATH";
193 #[cfg(target_os = "macos")]
194 const LOADER_PATH: &str = "DYLD_FALLBACK_LIBRARY_PATH";
195
196 if let Some(paths) = env::var_os(LOADER_PATH) {
197 let new_val =
199 env::join_paths(env::split_paths(&paths).filter(unlike_toolchain_path)).unwrap();
200 unsafe {
201 env::set_var(LOADER_PATH, new_val);
202 }
203 }
204}
205
206#[allow(clippy::ptr_arg)]
211fn unlike_toolchain_path(path: &PathBuf) -> bool {
212 let mut components = path.iter().rev();
213
214 !(components.next() == Some(std::ffi::OsStr::new("lib"))
216 && components.next().is_some()
217 && components.next() == Some(std::ffi::OsStr::new("toolchains")))
218}
219
220fn set_kani_rust_toolchain(kani_dir: &Path) -> Result<()> {
224 let toolchain_verison = setup::get_rust_toolchain_version(kani_dir)?;
225 unsafe {
226 env::set_var("RUSTUP_TOOLCHAIN", toolchain_verison);
227 }
228 Ok(())
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn check_unlike_toolchain_path() {
237 fn trial(s: &str) -> bool {
238 unlike_toolchain_path(&PathBuf::from(s))
239 }
240 assert!(!trial("/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib"));
242 assert!(!trial("/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/"));
243 assert!(!trial("/home/user/.rustup/toolchains/nightly/lib"));
244 assert!(!trial("/home/user/.rustup/toolchains/stable/lib"));
245 assert!(!trial("toolchains/nightly/lib"));
247 assert!(trial("/home/user/.rustup/toolchains"));
249 assert!(trial("/usr/lib"));
250 assert!(trial("/home/user/lib/toolchains"));
251 assert!(trial(""));
253 assert!(trial("/"));
254 }
255
256 #[test]
257 fn check_arg_parsing() {
258 fn trial(args: &[&str]) -> ArgsResult {
259 parse_args(args.iter().map(OsString::from).collect())
260 }
261 {
262 let e = ArgsResult::Default;
263 assert_eq!(e, trial(&["cargo-kani", "kani"]));
264 assert_eq!(e, trial(&[]));
265 }
266 {
267 let e = ArgsResult::ExplicitSetup { use_local_bundle: None, use_local_toolchain: None };
268 assert_eq!(e, trial(&["cargo-kani", "kani", "setup"]));
269 assert_eq!(e, trial(&["cargo", "kani", "setup"]));
270 assert_eq!(e, trial(&["cargo-kani", "setup"]));
271 }
272 {
273 let e = ArgsResult::ExplicitSetup {
274 use_local_bundle: Some(OsString::from("FILE")),
275 use_local_toolchain: None,
276 };
277 assert_eq!(e, trial(&["cargo-kani", "kani", "setup", "--use-local-bundle", "FILE"]));
278 assert_eq!(e, trial(&["cargo", "kani", "setup", "--use-local-bundle", "FILE"]));
279 assert_eq!(e, trial(&["cargo-kani", "setup", "--use-local-bundle", "FILE"]));
280 }
281 {
282 let e = ArgsResult::ExplicitSetup {
283 use_local_bundle: None,
284 use_local_toolchain: Some(OsString::from("TOOLCHAIN")),
285 };
286 assert_eq!(
287 e,
288 trial(&["cargo-kani", "kani", "setup", "--use-local-toolchain", "TOOLCHAIN"])
289 );
290 assert_eq!(e, trial(&["cargo", "kani", "setup", "--use-local-toolchain", "TOOLCHAIN"]));
291 assert_eq!(e, trial(&["cargo-kani", "setup", "--use-local-toolchain", "TOOLCHAIN"]));
292 }
293 {
294 let e = ArgsResult::ExplicitSetup {
295 use_local_bundle: Some(OsString::from("FILE")),
296 use_local_toolchain: Some(OsString::from("TOOLCHAIN")),
297 };
298 assert_eq!(
299 e,
300 trial(&[
301 "cargo-kani",
302 "kani",
303 "setup",
304 "--use-local-bundle",
305 "FILE",
306 "--use-local-toolchain",
307 "TOOLCHAIN"
308 ])
309 );
310 assert_eq!(
311 e,
312 trial(&[
313 "cargo",
314 "kani",
315 "setup",
316 "--use-local-bundle",
317 "FILE",
318 "--use-local-toolchain",
319 "TOOLCHAIN"
320 ])
321 );
322 assert_eq!(
323 e,
324 trial(&[
325 "cargo-kani",
326 "setup",
327 "--use-local-bundle",
328 "FILE",
329 "--use-local-toolchain",
330 "TOOLCHAIN"
331 ])
332 );
333 }
334 }
335}