Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions crates/apollo_compilation_utils/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@
// It must not contain functionality that is available in only in one of these modes. Specifically,
// it must avoid relying on env variables such as 'CARGO_*' or 'OUT_DIR'.

use std::path::PathBuf;
use std::path::{Path, PathBuf};

#[cfg(test)]
#[path = "paths_test.rs"]
pub mod test;

/// Returns `<cargo_tools_root>/<binary>-<version>/bin/<binary>`, where
/// `cargo_tools_root` resolves to `$CARGO_TOOLS_ROOT`, else `$CARGO_HOME/tools`,
/// `cargo_tools_root` resolves to `$CARGO_TOOLS_ROOT`, else a `tools` directory
/// next to the running executable (if one exists), else `$CARGO_HOME/tools`,
/// else `$HOME/.cargo/tools`.
///
/// The path is constructed deterministically; there is no `$PATH` lookup. The
Expand All @@ -22,8 +27,24 @@ fn cargo_tools_root() -> PathBuf {
if let Ok(p) = std::env::var("CARGO_TOOLS_ROOT") {
return PathBuf::from(p);
}
// A `tools` directory next to the running executable takes precedence over the
// cargo-home default: distributed packages ship the compiler tree alongside the
// binary, so prebuilt binaries work on machines with no cargo installation and
// no environment setup.
if let Some(tools_root) =
std::env::current_exe().ok().and_then(|exe_path| exe_relative_tools_root(&exe_path))
{
return tools_root;
}
let cargo_home = std::env::var("CARGO_HOME").map(PathBuf::from).unwrap_or_else(|_| {
PathBuf::from(std::env::var("HOME").expect("HOME must be set")).join(".cargo")
});
cargo_home.join("tools")
}

/// Returns the `tools` directory next to `exe_path`, or `None` if there is no such
/// directory (e.g. for executables under `target/`, which have no bundled tools).
fn exe_relative_tools_root(exe_path: &Path) -> Option<PathBuf> {
let tools_dir = exe_path.parent()?.join("tools");
tools_dir.is_dir().then_some(tools_dir)
}
32 changes: 32 additions & 0 deletions crates/apollo_compilation_utils/src/paths_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::fs;

use tempfile::TempDir;

use crate::paths::exe_relative_tools_root;

#[test]
fn tools_directory_next_to_executable_is_resolved() {
let package_dir = TempDir::new().unwrap();
let tools_dir = package_dir.path().join("tools");
fs::create_dir(&tools_dir).unwrap();
let exe_path = package_dir.path().join("prover_binary");

assert_eq!(exe_relative_tools_root(&exe_path), Some(tools_dir));
}

#[test]
fn missing_tools_directory_yields_none() {
let package_dir = TempDir::new().unwrap();
let exe_path = package_dir.path().join("prover_binary");

assert_eq!(exe_relative_tools_root(&exe_path), None);
}

#[test]
fn tools_path_that_is_a_file_yields_none() {
let package_dir = TempDir::new().unwrap();
fs::write(package_dir.path().join("tools"), "not a directory").unwrap();
let exe_path = package_dir.path().join("prover_binary");

assert_eq!(exe_relative_tools_root(&exe_path), None);
}
Loading