diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 614482482ae..f9f53aff1cc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -252,7 +252,7 @@ jobs: - name: Build with cargo run: python x.py build --all --jobs 1 - name: Run cargo tests - run: python x.py test --all + run: python x.py test --all --jobs 1 - name: Run a subset of tests with Carbon run: | python x.py test pass/no-annotation --all --verbose diff --git a/Cargo.lock b/Cargo.lock index e070c4b73aa..902545fd6e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,7 @@ dependencies = [ "glob", "log", "prusti-rustc-interface", + "prusti-utils", "serde", "serde_json", "syn 1.0.109", diff --git a/analysis/Cargo.toml b/analysis/Cargo.toml index b1ef5e53f44..279fe66828d 100644 --- a/analysis/Cargo.toml +++ b/analysis/Cargo.toml @@ -13,6 +13,7 @@ syn = { version = "1.0", features = [ "full", "parsing" ] } derive_more = "0.99" prusti-rustc-interface = { path = "../prusti-rustc-interface" } tracing = { path = "../tracing" } +prusti-utils = { path = "../prusti-utils" } [dev-dependencies] compiletest_rs = "0.10" diff --git a/analysis/tests/test_accessibility.rs b/analysis/tests/test_accessibility.rs index 04a888141f2..1684bde35f6 100644 --- a/analysis/tests/test_accessibility.rs +++ b/analysis/tests/test_accessibility.rs @@ -1,6 +1,7 @@ mod utils; use glob::glob; +use prusti_utils::utils::find_compiled_executable; use std::{ env, ffi::OsStr, diff --git a/analysis/tests/test_analysis.rs b/analysis/tests/test_analysis.rs index fe5284bf05e..5b24fe3e5eb 100644 --- a/analysis/tests/test_analysis.rs +++ b/analysis/tests/test_analysis.rs @@ -5,6 +5,7 @@ mod utils; /// Source: https://github.com/rust-lang/miri/blob/master/tests/compiletest.rs use compiletest_rs as compiletest; +use prusti_utils::utils::find_compiled_executable; use std::{env, path::PathBuf}; use utils::*; diff --git a/analysis/tests/utils.rs b/analysis/tests/utils.rs index 397f53aeaa6..930a73299e0 100644 --- a/analysis/tests/utils.rs +++ b/analysis/tests/utils.rs @@ -1,35 +1,3 @@ -use std::path::PathBuf; - -pub fn find_compiled_executable(name: &str) -> PathBuf { - let target_directory = if cfg!(debug_assertions) { - "debug" - } else { - "release" - }; - let executable_name = if cfg!(windows) { - format!("{name}.exe") - } else { - name.to_string() - }; - let local_driver_path: PathBuf = ["target", target_directory, executable_name.as_str()] - .iter() - .collect(); - if local_driver_path.exists() { - return local_driver_path; - } - let workspace_driver_path: PathBuf = - ["..", "target", target_directory, executable_name.as_str()] - .iter() - .collect(); - if workspace_driver_path.exists() { - return workspace_driver_path; - } - panic!( - "Could not find the {target_directory:?} {executable_name:?} binary to be used in tests. \ - It might be that the project has not been compiled correctly." - ); -} - pub fn find_sysroot() -> String { // Taken from https://github.com/Manishearth/rust-clippy/pull/911. let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); diff --git a/prusti-contracts-build/build.rs b/prusti-contracts-build/build.rs index d67b796e9e0..71fdac4fa7c 100644 --- a/prusti-contracts-build/build.rs +++ b/prusti-contracts-build/build.rs @@ -1,5 +1,17 @@ use std::{path::PathBuf, process::Command}; +use prusti_utils::launch::get_prusti_contracts_build_target_dir; + +fn executable_name(name: &str) -> String { + #[cfg(windows)] + let name = format!("{}.exe", name); + + #[cfg(not(windows))] + let name = name.to_string(); + + name +} + fn main() { // Rerun if running with e.g. cargo clippy println!("cargo:rerun-if-env-changed=RUSTC_WORKSPACE_WRAPPER"); @@ -14,32 +26,26 @@ fn main() { let target: PathBuf = ["..", "target"].iter().collect(); force_reexport_specs(target.join("verify").as_path()); - // Copy just-built binaries to `target/dir` dir - let bin_dir = if cfg!(debug_assertions) { - target.join("debug") - } else { - target.join("release") - }; + let target = get_prusti_contracts_build_target_dir(&target); + let bin_dir = target.join("bin"); + std::fs::create_dir_all(&bin_dir).unwrap(); + + // Copies all files into `bin_dir`. `cargo_prusti` cannot be run directly + // from std::env::var("CARGO_BIN_FILE_PRUSTI_LAUNCH_cargo-prusti") + // because it requires `prusti-rustc` and `prusti-driver` to be in the same + // path. for (krate, file) in [ ("PRUSTI_LAUNCH", "cargo-prusti"), ("PRUSTI_LAUNCH", "prusti-rustc"), ("PRUSTI", "prusti-driver"), ] { let file_from = std::env::var(format!("CARGO_BIN_FILE_{krate}_{file}")).unwrap(); - let file_to = &format!("{file}{}", if cfg!(windows) { ".exe" } else { "" }); + let file_to = executable_name(file); let file_to = bin_dir.join(file_to); std::fs::copy(file_from, file_to).unwrap(); } - // Run `cargo-prusti` - let cargo_prusti = format!("cargo-prusti{}", if cfg!(windows) { ".exe" } else { "" }); - let cargo_prusti = bin_dir.join(cargo_prusti); - - // In theory we should build to here (i.e. set `CARGO_TARGET_DIR` to this), - // but this is hard to find for linking. So instead build to the `prusti-contracts` dir. - // let out_dir = std::env::var("OUT_DIR").unwrap(); - // println!("cargo:warning=out_dir: {}", out_dir); - + let cargo_prusti = bin_dir.join(executable_name("cargo-prusti")); let mut cmd = Command::new(cargo_prusti); cmd.env("CARGO_TARGET_DIR", target.as_os_str()); cmd.current_dir(&prusti_contracts); diff --git a/prusti-launch/tests/test_binaries.rs b/prusti-launch/tests/test_binaries.rs index 3adb94105fa..6bc0307e9f5 100644 --- a/prusti-launch/tests/test_binaries.rs +++ b/prusti-launch/tests/test_binaries.rs @@ -5,41 +5,13 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use glob::glob; +use prusti_utils::utils::find_compiled_executable; use std::{ io::{BufRead, BufReader}, path::PathBuf, process::{Child, Command, Stdio}, }; -fn find_executable_path(base_name: &str) -> PathBuf { - let target_directory = if cfg!(debug_assertions) { - "debug" - } else { - "release" - }; - let executable_name = if cfg!(windows) { - format!("{base_name}.exe") - } else { - base_name.to_string() - }; - let local_prusti_rustc_path: PathBuf = ["target", target_directory, &executable_name] - .iter() - .collect(); - if local_prusti_rustc_path.exists() { - return local_prusti_rustc_path; - } - let workspace_prusti_rustc_path: PathBuf = ["..", "target", target_directory, &executable_name] - .iter() - .collect(); - if workspace_prusti_rustc_path.exists() { - return workspace_prusti_rustc_path; - } - panic!( - "Could not find the {target_directory:?} prusti-rustc binary to be used in tests. \ - It might be that Prusti has not been compiled correctly." - ); -} - fn run_on_test_files Command>(run: F) { let mut num_pass_tests = 0; let pass_entries = glob("tests/pass/**/*.rs").expect("failed to read glob pattern"); @@ -107,7 +79,7 @@ impl Drop for ChildGuard { #[test] fn test_prusti_rustc() { - let prusti_rustc = find_executable_path("prusti-rustc"); + let prusti_rustc = find_compiled_executable("prusti-rustc"); run_on_test_files(|program: &PathBuf| { let mut cmd = Command::new(&prusti_rustc); @@ -121,7 +93,7 @@ fn test_prusti_rustc() { #[test] fn test_prusti_rustc_dump() { - let prusti_rustc = find_executable_path("prusti-rustc"); + let prusti_rustc = find_compiled_executable("prusti-rustc"); run_on_test_files(|program: &PathBuf| { let mut cmd = Command::new(&prusti_rustc); @@ -146,7 +118,7 @@ fn test_prusti_rustc_dump() { // so this test fails. #[test] fn test_prusti_be_rustc() { - let prusti_rustc = find_executable_path("prusti-rustc"); + let prusti_rustc = find_compiled_executable("prusti-rustc"); run_on_test_files(|program: &PathBuf| { let mut cmd = Command::new(&prusti_rustc); @@ -162,8 +134,8 @@ fn test_prusti_be_rustc() { #[test] fn test_prusti_rustc_with_server() { - let prusti_rustc = find_executable_path("prusti-rustc"); - let prusti_server = find_executable_path("prusti-server"); + let prusti_rustc = find_compiled_executable("prusti-rustc"); + let prusti_server = find_compiled_executable("prusti-server"); // Preserve SYSTEMROOT on Windows. // See: https://travis-ci.community/t/socket-the-requested-service-provider-could-not-be-loaded-or-initialized/1127 diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index b137f929c6e..fe1528d1e0c 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -350,7 +350,7 @@ pub fn viper_home() -> String { if let Some(path) = find_viper_home(¤t_executable_dir) { path.to_str().unwrap().to_owned() } else { - panic!("Failed to detect Vipe home, please set viper_home configuration flag") + panic!("Failed to detect Viper home, please set viper_home configuration flag") } } } diff --git a/prusti-utils/src/launch/mod.rs b/prusti-utils/src/launch/mod.rs index c546e107c2c..51ed671b713 100644 --- a/prusti-utils/src/launch/mod.rs +++ b/prusti-utils/src/launch/mod.rs @@ -18,6 +18,12 @@ pub mod job; /// for `prusti-rustc`. pub const PRUSTI_LIBS: [&str; 2] = ["prusti-contracts", "prusti-std"]; +#[cfg(debug_assertions)] +pub const BUILD_MODE: &str = "debug"; + +#[cfg(not(debug_assertions))] +pub const BUILD_MODE: &str = "release"; + pub fn get_current_executable_dir() -> PathBuf { env::current_exe() .expect("current executable path invalid") @@ -26,26 +32,32 @@ pub fn get_current_executable_dir() -> PathBuf { .to_path_buf() } +/// Finds the closest `target` directory in the current path. +/// This should be the target directory at the root of the repository, +/// i.e. `prusti-dev/target`. +pub fn get_target_dir(exe_dir: &Path) -> PathBuf { + let mut root_dir = exe_dir; + while root_dir.file_name().unwrap() != "target" { + root_dir = root_dir.parent().unwrap(); + } + root_dir.to_path_buf() +} + +pub fn get_prusti_contracts_build_target_dir(target_dir: &Path) -> PathBuf { + target_dir.join("prusti-contracts").join(BUILD_MODE) +} + pub fn get_prusti_contracts_dir(exe_dir: &Path) -> Option { let a_prusti_contracts_file = format!("lib{}.rlib", PRUSTI_LIBS[0].replace('-', "_")); - let target_dir = if cfg!(debug_assertions) { - "debug" - } else { - "release" - }; + + let target_dir = get_target_dir(exe_dir); let candidates = [ // Libraries in the Prusti artifact will show up here - exe_dir.to_path_buf(), + get_prusti_contracts_build_target_dir(&target_dir), // Libraries when building Prusti will show up here - exe_dir - .parent() - .unwrap() - .parent() - .unwrap() - .join("target") - .join("verify") - .join(target_dir), - ]; + target_dir, + ] + .map(|path| path.join("verify").join(BUILD_MODE)); candidates .into_iter() .find(|candidate| candidate.join(&a_prusti_contracts_file).exists()) @@ -178,28 +190,24 @@ fn get_sysroot_from_rustup() -> Option { /// Find Viper home pub fn find_viper_home(base_dir: &Path) -> Option { - let candidates = vec![ - base_dir.join("viper_tools").join("server"), - base_dir - .join("..") - .join("..") - .join("viper_tools") - .join("server"), - base_dir.join("viper_tools").join("backends"), - base_dir - .join("..") - .join("..") - .join("viper_tools") - .join("backends"), - base_dir - .join("..") - .join("..") - .join("..") - .join("viper_tools") - .join("backends"), - ]; - - candidates.into_iter().find(|candidate| candidate.is_dir()) + let mut dir = base_dir; + loop { + if dir.join("viper_tools").is_dir() { + let viper_tools_dir = dir.join("viper_tools"); + let backends_dir = viper_tools_dir.join("backends"); + if backends_dir.is_dir() { + return Some(backends_dir); + } + let server_dir = viper_tools_dir.join("server"); + if server_dir.is_dir() { + return Some(server_dir); + } + } + match dir.parent() { + Some(parent) => dir = parent, + None => return None, + } + } } /// Find Z3 executable diff --git a/prusti-utils/src/utils/mod.rs b/prusti-utils/src/utils/mod.rs index d64afd98f06..0a44cb22f70 100644 --- a/prusti-utils/src/utils/mod.rs +++ b/prusti-utils/src/utils/mod.rs @@ -6,3 +6,55 @@ pub mod identifiers; pub mod to_string; +use std::{env, path::PathBuf}; + +pub fn find_compiled_executable(name: &str) -> PathBuf { + let target_directory = if cfg!(debug_assertions) { + "debug" + } else { + "release" + }; + + let mut target_path = PathBuf::from("target"); + + // If prusti was compiled for a custom target, e.g. via x.py build --target + // , then the executables will be placed in /target//debug + // rather than /target/debug. + + // The environment variable COMPILATION_TARGET_PRUSTI should be set to the + // appropriate triple when running the tests, so that executable for that + // target is used. + if let Ok(triple) = env::var("COMPILATION_TARGET_PRUSTI") { + if !triple.is_empty() { + target_path.push(triple); + } + } + + target_path.push(target_directory); + + let executable_name = if cfg!(windows) { + format!("{name}.exe") + } else { + name.to_string() + }; + + let mut local_driver_path = target_path.clone(); + local_driver_path.push(&executable_name); + + if local_driver_path.exists() { + return local_driver_path; + } + + let mut workspace_driver_path = PathBuf::from(".."); + workspace_driver_path.push(target_path); + workspace_driver_path.push(&executable_name); + + if workspace_driver_path.exists() { + return workspace_driver_path; + } + + panic!( + "Could not find the {target_directory:?} {executable_name:?} binary to be used in tests. \ + It might be that the project has not been compiled correctly." + ); +}