Split utils into error and command utils

Error utils pulls in snafu which requires procedural macros.
This means it can't start compiling until syn and macro related crates are compiled.
This deals compiling all crates that depend on commands like running zig for the bitcode.
The split enables those crates to run sooner.
This commit is contained in:
Brendan Hansknecht 2023-03-10 09:32:21 -08:00
parent d5e191d083
commit cb191d3788
No known key found for this signature in database
GPG key ID: A199D0660F95F948
30 changed files with 186 additions and 333 deletions

View file

@ -0,0 +1,172 @@
use std::{
env::{self, VarError},
path::PathBuf,
process::Command,
};
// get the path of the lib folder
// runtime dependencies like zig files, Windows dylib builds, are put in the lib folder
pub fn get_lib_path() -> Option<PathBuf> {
let exe_relative_str_path_opt = std::env::current_exe().ok();
if let Some(exe_relative_str_path) = exe_relative_str_path_opt {
#[cfg(windows)]
let exe_relative_str_path = strip_windows_prefix(&exe_relative_str_path);
let mut curr_parent_opt = exe_relative_str_path.parent();
// this differs for regular build and nix releases, so we check in multiple spots.
for _ in 0..3 {
if let Some(curr_parent) = curr_parent_opt {
let lib_path = curr_parent.join("lib");
if std::path::Path::exists(&lib_path) {
return Some(lib_path);
} else {
curr_parent_opt = curr_parent.parent();
}
} else {
break;
}
}
}
None
}
#[cfg(windows)]
use std::path::Path;
/// On windows, the path is prefixed with `\\?\`, the "verbatim" prefix.
/// Such a path does not works as an argument to `zig` and other command line tools,
/// and there seems to be no good way to strip it. So we resort to some string manipulation.
#[cfg(windows)]
pub fn strip_windows_prefix(path_buf: &Path) -> PathBuf {
let path_str = path_buf.display().to_string();
Path::new(path_str.trim_start_matches(r"\\?\")).to_path_buf()
}
/// get the Path of the root of the repository
pub fn root_dir() -> PathBuf {
let mut path = env::current_exe().ok().unwrap();
// Get rid of the filename in target/debug/deps/cli_run-99c65e4e9a1fbd06
path.pop();
// If we're in deps/ get rid of deps/ in target/debug/deps/
if path.ends_with("deps") {
path.pop();
}
// Get rid of target/debug/ so we're back at the project root
path.pop();
path.pop();
// running cargo with --target will put us in the target dir
if path.ends_with("target") {
path.pop();
}
path
}
/// Gives a friendly error if cargo is not installed.
/// Also makes it easy to track where we use cargo in the codebase.
pub fn cargo() -> Command {
let command_str = "cargo";
if check_command_available(command_str) {
Command::new(command_str)
} else {
panic!("I could not find the cargo command.\nVisit https://rustup.rs/ to install cargo.",)
}
}
/// Gives a friendly error if rustup is not installed.
/// Also makes it easy to track where we use rustup in the codebase.
pub fn rustup() -> Command {
// on windows, we need the version of cargo installed by rustup. The meaning of `cargo` is
// different within a process that runs rust. So we need to explicitly refer to where
// rustup put the binary
let command_str = "rustup";
if check_command_available(command_str) {
Command::new(command_str)
} else {
panic!("I could not find the rustup command.\nVisit https://rustup.rs/ to install rustup.",)
}
}
/// Gives a friendly error if clang is not installed.
/// Also makes it easy to track where we use clang in the codebase.
pub fn clang() -> Command {
let command_str = "clang";
if check_command_available(command_str) {
Command::new(command_str)
} else {
panic!("I could not find the clang command.\nPlease install clang.",)
//TODO detect OS and provide detailed install instructions
}
}
/// Gives a friendly error if zig is not installed.
/// Also makes it easy to track where we use zig in the codebase.
pub fn zig() -> Command {
let command_str = match std::env::var("ROC_ZIG") {
Ok(path) => path,
Err(_) => "zig".into(),
};
if check_command_available(&command_str) {
Command::new(command_str)
} else {
panic!("I could not find the zig command.\nPlease install zig, see instructions at https://ziglang.org/learn/getting-started/.",)
}
}
fn check_command_available(command_name: &str) -> bool {
if cfg!(target_family = "unix") {
let unparsed_path = match std::env::var("PATH") {
Ok(var) => var,
Err(VarError::NotPresent) => return false,
Err(VarError::NotUnicode(_)) => {
panic!("found PATH, but it included invalid unicode data!")
}
};
std::env::split_paths(&unparsed_path).any(|dir| dir.join(command_name).exists())
} else if cfg!(target = "windows") {
let mut cmd = Command::new("Get-Command");
cmd.args([command_name]);
let cmd_str = format!("{:?}", cmd);
let cmd_out = cmd.output().unwrap_or_else(|err| {
panic!(
"Failed to execute `{}` to check if {} is available:\n {}",
cmd_str, command_name, err
)
});
cmd_out.status.success()
} else {
// We're in uncharted waters, best not to panic if
// things may end up working out down the line.
true
}
}
pub fn pretty_command_string(command: &Command) -> std::ffi::OsString {
let mut command_string = std::ffi::OsString::new();
command_string.push(command.get_program());
for arg in command.get_args() {
command_string.push(" ");
command_string.push(arg);
}
command_string
}