diff --git a/Cargo.lock b/Cargo.lock index d944fc5ec3..1280f0ff95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -496,6 +496,7 @@ dependencies = [ "roc_load", "roc_module", "roc_reporting", + "roc_utils", "serde", "serde-xml-rs", "strip-ansi-escapes", @@ -3572,6 +3573,7 @@ dependencies = [ "roc_solve", "roc_types", "roc_unify", + "roc_utils", "rodio", "serde", "snafu", @@ -3991,6 +3993,7 @@ dependencies = [ "roc_reporting", "roc_target", "roc_types", + "roc_utils", "wasi_libc_sys", "wasm-bindgen", "wasm-bindgen-futures", @@ -4858,6 +4861,7 @@ dependencies = [ "roc_target", "roc_types", "roc_unify", + "roc_utils", "target-lexicon", "tempfile", "wasi_libc_sys", @@ -5260,6 +5264,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi_libc_sys" version = "0.0.1" +dependencies = [ + "roc_utils", +] [[package]] name = "wasm-bindgen" diff --git a/crates/cli/src/build.rs b/crates/cli/src/build.rs index 475317ff1c..5161f6d00a 100644 --- a/crates/cli/src/build.rs +++ b/crates/cli/src/build.rs @@ -252,7 +252,9 @@ pub fn build_file<'a>( } let rebuild_timing = if linking_strategy == LinkingStrategy::Additive { - let rebuild_duration = rebuild_thread.join().unwrap(); + let rebuild_duration = rebuild_thread + .join() + .expect("Failed to (re)build platform."); if emit_timings && !prebuilt { println!( "Finished rebuilding the platform in {} ms\n", @@ -304,7 +306,7 @@ pub fn build_file<'a>( } if let HostRebuildTiming::ConcurrentWithApp(thread) = rebuild_timing { - let rebuild_duration = thread.join().unwrap(); + let rebuild_duration = thread.join().expect("Failed to (re)build platform."); if emit_timings && !prebuilt { println!( "Finished rebuilding the platform in {} ms\n", diff --git a/crates/cli_utils/Cargo.toml b/crates/cli_utils/Cargo.toml index ffa860fc02..5a19d43b6b 100644 --- a/crates/cli_utils/Cargo.toml +++ b/crates/cli_utils/Cargo.toml @@ -14,6 +14,7 @@ roc_collections = { path = "../compiler/collections" } roc_reporting = { path = "../reporting" } roc_load = { path = "../compiler/load" } roc_module = { path = "../compiler/module" } +roc_utils = { path = "../utils" } bumpalo = { version = "3.8.0", features = ["collections"] } criterion = { git = "https://github.com/Anton-4/criterion.rs"} serde = { version = "1.0.130", features = ["derive"] } diff --git a/crates/cli_utils/src/helpers.rs b/crates/cli_utils/src/helpers.rs index 26071d7c20..0ae0157d66 100644 --- a/crates/cli_utils/src/helpers.rs +++ b/crates/cli_utils/src/helpers.rs @@ -4,6 +4,7 @@ extern crate roc_load; extern crate roc_module; extern crate tempfile; +use roc_utils::cargo; use serde::Deserialize; use serde_xml_rs::from_str; use std::env; @@ -48,7 +49,7 @@ where vec!["build", "--release", "--bin", "roc"] }; - let output = Command::new("cargo") + let output = cargo() .current_dir(root_project_dir) .args(args) .output() diff --git a/crates/compiler/build/src/link.rs b/crates/compiler/build/src/link.rs index 9a9a4985e1..32cff2afd6 100644 --- a/crates/compiler/build/src/link.rs +++ b/crates/compiler/build/src/link.rs @@ -4,21 +4,15 @@ use roc_builtins::bitcode; use roc_error_macros::internal_error; use roc_mono::ir::OptLevel; use roc_utils::get_lib_path; +use roc_utils::{cargo, clang, zig}; use std::collections::HashMap; use std::env; use std::io; use std::path::{Path, PathBuf}; -use std::process::{self, Child, Command, Output}; +use std::process::{self, Child, Command}; use target_lexicon::{Architecture, OperatingSystem, Triple}; use wasi_libc_sys::{WASI_COMPILER_RT_PATH, WASI_LIBC_PATH}; -fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum LinkType { // These numbers correspond to the --lib and --no-link flags @@ -113,9 +107,9 @@ pub fn build_zig_host_native( target: &str, opt_level: OptLevel, shared_lib_path: Option<&Path>, -) -> Output { - let mut command = Command::new(&zig_executable()); - command +) -> Command { + let mut zig_cmd = zig(); + zig_cmd .env_clear() .env("PATH", env_path) .env("HOME", env_home); @@ -130,7 +124,7 @@ pub fn build_zig_host_native( bitcode::get_builtins_host_obj_path() }; - command.args(&[ + zig_cmd.args(&[ "build-exe", "-fPIE", "-rdynamic", // make sure roc_alloc and friends are exposed @@ -138,10 +132,10 @@ pub fn build_zig_host_native( &builtins_obj, ]); } else { - command.args(&["build-obj", "-fPIC"]); + zig_cmd.args(&["build-obj", "-fPIC"]); } - command.args(&[ + zig_cmd.args(&[ zig_host_src, emit_bin, "--pkg-begin", @@ -160,7 +154,7 @@ pub fn build_zig_host_native( // when we use zig 0.9. It looks like zig 0.10 is going to fix // this problem for us, so this is a temporary workaround if !target.contains("windows") { - command.args(&[ + zig_cmd.args(&[ // include the zig runtime "-fcompiler-rt", ]); @@ -168,16 +162,16 @@ pub fn build_zig_host_native( // valgrind does not yet support avx512 instructions, see #1963. if env::var("NO_AVX512").is_ok() { - command.args(&["-mcpu", "x86_64"]); + zig_cmd.args(&["-mcpu", "x86_64"]); } if matches!(opt_level, OptLevel::Optimize) { - command.args(&["-O", "ReleaseSafe"]); + zig_cmd.args(&["-O", "ReleaseSafe"]); } else if matches!(opt_level, OptLevel::Size) { - command.args(&["-O", "ReleaseSmall"]); + zig_cmd.args(&["-O", "ReleaseSmall"]); } - command.output().unwrap() + zig_cmd } #[cfg(windows)] @@ -191,25 +185,25 @@ pub fn build_zig_host_native( target: &str, opt_level: OptLevel, shared_lib_path: Option<&Path>, -) -> Output { - let mut command = Command::new(&zig_executable()); - command +) -> Command { + let mut zig_cmd = zig(); + zig_cmd .env_clear() .env("PATH", env_path) .env("HOME", env_home); if let Some(shared_lib_path) = shared_lib_path { - command.args(&[ + zig_cmd.args(&[ "build-exe", // "-fPIE", PIE seems to fail on windows shared_lib_path.to_str().unwrap(), &bitcode::get_builtins_windows_obj_path(), ]); } else { - command.args(&["build-obj", "-fPIC"]); + zig_cmd.args(&["build-obj", "-fPIC"]); } - command.args(&[ + zig_cmd.args(&[ zig_host_src, emit_bin, "--pkg-begin", @@ -227,12 +221,12 @@ pub fn build_zig_host_native( ]); if matches!(opt_level, OptLevel::Optimize) { - command.args(&["-O", "ReleaseSafe"]); + zig_cmd.args(&["-O", "ReleaseSafe"]); } else if matches!(opt_level, OptLevel::Size) { - command.args(&["-O", "ReleaseSmall"]); + zig_cmd.args(&["-O", "ReleaseSmall"]); } - command.output().unwrap() + zig_cmd } #[cfg(target_os = "macos")] @@ -247,14 +241,11 @@ pub fn build_zig_host_native( opt_level: OptLevel, shared_lib_path: Option<&Path>, // For compatibility with the non-macOS def above. Keep these in sync. -) -> Output { +) -> Command { use serde_json::Value; // Run `zig env` to find the location of zig's std/ directory - let zig_env_output = Command::new(&zig_executable()) - .args(&["env"]) - .output() - .unwrap(); + let zig_env_output = zig().args(&["env"]).output().unwrap(); let zig_env_json = if zig_env_output.status.success() { std::str::from_utf8(&zig_env_output.stdout).unwrap_or_else(|utf8_err| { @@ -291,22 +282,22 @@ pub fn build_zig_host_native( zig_compiler_rt_path.push("special"); zig_compiler_rt_path.push("compiler_rt.zig"); - let mut command = Command::new(&zig_executable()); - command + let mut zig_cmd = zig(); + zig_cmd .env_clear() .env("PATH", &env_path) .env("HOME", &env_home); if let Some(shared_lib_path) = shared_lib_path { - command.args(&[ + zig_cmd.args(&[ "build-exe", "-fPIE", shared_lib_path.to_str().unwrap(), &bitcode::get_builtins_host_obj_path(), ]); } else { - command.args(&["build-obj", "-fPIC"]); + zig_cmd.args(&["build-obj", "-fPIC"]); } - command.args(&[ + zig_cmd.args(&[ zig_host_src, emit_bin, "--pkg-begin", @@ -323,11 +314,12 @@ pub fn build_zig_host_native( "c", ]); if matches!(opt_level, OptLevel::Optimize) { - command.args(&["-O", "ReleaseSafe"]); + zig_cmd.args(&["-O", "ReleaseSafe"]); } else if matches!(opt_level, OptLevel::Size) { - command.args(&["-O", "ReleaseSmall"]); + zig_cmd.args(&["-O", "ReleaseSmall"]); } - command.output().unwrap() + + zig_cmd } pub fn build_zig_host_wasm32( @@ -338,7 +330,7 @@ pub fn build_zig_host_wasm32( zig_str_path: &str, opt_level: OptLevel, shared_lib_path: Option<&Path>, -) -> Output { +) -> Command { if shared_lib_path.is_some() { unimplemented!("Linking a shared library to wasm not yet implemented"); } @@ -358,7 +350,7 @@ pub fn build_zig_host_wasm32( // we'd like to compile with `-target wasm32-wasi` but that is blocked on // // https://github.com/ziglang/zig/issues/9414 - let mut command = Command::new(&zig_executable()); + let mut zig_cmd = zig(); let args = &[ "build-obj", zig_host_src, @@ -379,18 +371,19 @@ pub fn build_zig_host_wasm32( "--strip", ]; - command + zig_cmd .env_clear() .env("PATH", env_path) .env("HOME", env_home) .args(args); if matches!(opt_level, OptLevel::Optimize) { - command.args(&["-O", "ReleaseSafe"]); + zig_cmd.args(&["-O", "ReleaseSafe"]); } else if matches!(opt_level, OptLevel::Size) { - command.args(&["-O", "ReleaseSmall"]); + zig_cmd.args(&["-O", "ReleaseSmall"]); } - command.output().unwrap() + + zig_cmd } #[allow(clippy::too_many_arguments)] @@ -403,9 +396,9 @@ pub fn build_c_host_native( sources: &[&str], opt_level: OptLevel, shared_lib_path: Option<&Path>, -) -> Output { - let mut command = Command::new("clang"); - command +) -> Command { + let mut clang_cmd = clang(); + clang_cmd .env_clear() .env("PATH", &env_path) .env("CPATH", &env_cpath) @@ -432,7 +425,7 @@ pub fn build_c_host_native( ); } _ => { - command.args(&[ + clang_cmd.args(&[ shared_lib_path.to_str().unwrap(), // This line is commented out because // @bhansconnect: With the addition of Str.graphemes, always @@ -451,14 +444,15 @@ pub fn build_c_host_native( } } } else { - command.args(&["-fPIC", "-c"]); + clang_cmd.args(&["-fPIC", "-c"]); } if matches!(opt_level, OptLevel::Optimize) { - command.arg("-O3"); + clang_cmd.arg("-O3"); } else if matches!(opt_level, OptLevel::Size) { - command.arg("-Os"); + clang_cmd.arg("-Os"); } - command.output().unwrap() + + clang_cmd } #[allow(clippy::too_many_arguments)] @@ -471,7 +465,7 @@ pub fn build_swift_host_native( shared_lib_path: Option<&Path>, objc_header_path: Option<&str>, arch: Architecture, -) -> Output { +) -> Command { if shared_lib_path.is_some() { unimplemented!("Linking a shared library to Swift not yet implemented"); } @@ -505,7 +499,7 @@ pub fn build_swift_host_native( command.arg("-Osize"); } - command.output().unwrap() + command } pub fn rebuild_host( @@ -567,7 +561,7 @@ pub fn rebuild_host( &zig_str_path ); - let output = match target.architecture { + let zig_cmd = match target.architecture { Architecture::Wasm32 => { let emit_bin = if matches!(opt_level, OptLevel::Development) { format!("-femit-bin={}", host_dest.to_str().unwrap()) @@ -633,7 +627,7 @@ pub fn rebuild_host( _ => internal_error!("Unsupported architecture {:?}", target.architecture), }; - validate_output("host.zig", &zig_executable(), output) + run_build_command(zig_cmd, "host.zig") } else if cargo_host_src.exists() { // Compile and link Cargo.toml, if it exists let cargo_dir = host_input_path.parent().unwrap(); @@ -646,25 +640,23 @@ pub fn rebuild_host( }, ); - let mut command = Command::new("cargo"); - command.arg("build").current_dir(cargo_dir); + let mut cargo_cmd = cargo(); + cargo_cmd.arg("build").current_dir(cargo_dir); // Rust doesn't expose size without editing the cargo.toml. Instead just use release. if matches!(opt_level, OptLevel::Optimize | OptLevel::Size) { - command.arg("--release"); + cargo_cmd.arg("--release"); } let source_file = if shared_lib_path.is_some() { - command.env("RUSTFLAGS", "-C link-dead-code"); - command.args(&["--bin", "host"]); + cargo_cmd.env("RUSTFLAGS", "-C link-dead-code"); + cargo_cmd.args(&["--bin", "host"]); "src/main.rs" } else { - command.arg("--lib"); + cargo_cmd.arg("--lib"); "src/lib.rs" }; - let output = command.output().unwrap(); - - validate_output(source_file, "cargo build", output); + run_build_command(cargo_cmd, source_file); if shared_lib_path.is_some() { // For surgical linking, just copy the dynamically linked rust app. @@ -674,7 +666,7 @@ pub fn rebuild_host( } else { // Cargo hosts depend on a c wrapper for the api. Compile host.c as well. - let output = build_c_host_native( + let clang_cmd = build_c_host_native( target, &env_path, &env_home, @@ -684,23 +676,22 @@ pub fn rebuild_host( opt_level, shared_lib_path, ); - validate_output("host.c", "clang", output); - let output = Command::new("ld") - .env_clear() - .env("PATH", &env_path) - .args(&[ - "-r", - "-L", - cargo_out_dir.to_str().unwrap(), - c_host_dest.to_str().unwrap(), - "-lhost", - "-o", - host_dest.to_str().unwrap(), - ]) - .output() - .unwrap(); - validate_output("c_host.o", "ld", output); + run_build_command(clang_cmd, "host.c"); + + let mut ld_cmd = Command::new("ld"); + + ld_cmd.env_clear().env("PATH", &env_path).args(&[ + "-r", + "-L", + cargo_out_dir.to_str().unwrap(), + c_host_dest.to_str().unwrap(), + "-lhost", + "-o", + host_dest.to_str().unwrap(), + ]); + + run_build_command(ld_cmd, "c_host.o"); // Clean up c_host.o if c_host_dest.exists() { @@ -709,25 +700,24 @@ pub fn rebuild_host( } } else if rust_host_src.exists() { // Compile and link host.rs, if it exists - let mut command = Command::new("rustc"); - command.args(&[ + let mut rustc_cmd = Command::new("rustc"); + rustc_cmd.args(&[ rust_host_src.to_str().unwrap(), "-o", rust_host_dest.to_str().unwrap(), ]); if matches!(opt_level, OptLevel::Optimize) { - command.arg("-O"); + rustc_cmd.arg("-O"); } else if matches!(opt_level, OptLevel::Size) { - command.arg("-C opt-level=s"); + rustc_cmd.arg("-C opt-level=s"); } - let output = command.output().unwrap(); - validate_output("host.rs", "rustc", output); + run_build_command(rustc_cmd, "host.rs"); // Rust hosts depend on a c wrapper for the api. Compile host.c as well. if shared_lib_path.is_some() { // If compiling to executable, let c deal with linking as well. - let output = build_c_host_native( + let clang_cmd = build_c_host_native( target, &env_path, &env_home, @@ -740,9 +730,9 @@ pub fn rebuild_host( opt_level, shared_lib_path, ); - validate_output("host.c", "clang", output); + run_build_command(clang_cmd, "host.c"); } else { - let output = build_c_host_native( + let clang_cmd = build_c_host_native( target, &env_path, &env_home, @@ -753,21 +743,19 @@ pub fn rebuild_host( shared_lib_path, ); - validate_output("host.c", "clang", output); - let output = Command::new("ld") - .env_clear() - .env("PATH", &env_path) - .args(&[ - "-r", - c_host_dest.to_str().unwrap(), - rust_host_dest.to_str().unwrap(), - "-o", - host_dest.to_str().unwrap(), - ]) - .output() - .unwrap(); + run_build_command(clang_cmd, "host.c"); - validate_output("rust_host.o", "ld", output); + let mut ld_cmd = Command::new("ld"); + + ld_cmd.env_clear().env("PATH", &env_path).args(&[ + "-r", + c_host_dest.to_str().unwrap(), + rust_host_dest.to_str().unwrap(), + "-o", + host_dest.to_str().unwrap(), + ]); + + run_build_command(ld_cmd, "rust_host.o"); } // Clean up rust_host.o and c_host.o @@ -779,7 +767,7 @@ pub fn rebuild_host( } } else if c_host_src.exists() { // Compile host.c, if it exists - let output = build_c_host_native( + let clang_cmd = build_c_host_native( target, &env_path, &env_home, @@ -789,10 +777,11 @@ pub fn rebuild_host( opt_level, shared_lib_path, ); - validate_output("host.c", "clang", output); + + run_build_command(clang_cmd, "host.c"); } else if swift_host_src.exists() { // Compile host.swift, if it exists - let output = build_swift_host_native( + let swiftc_cmd = build_swift_host_native( &env_path, &env_home, host_dest.to_str().unwrap(), @@ -804,7 +793,8 @@ pub fn rebuild_host( .then(|| swift_host_header_src.to_str().unwrap()), target.architecture, ); - validate_output("host.swift", "swiftc", output); + + run_build_command(swiftc_cmd, "host.swift"); } host_dest @@ -873,7 +863,7 @@ fn link_linux( if let Architecture::X86_32(_) = target.architecture { return Ok(( - Command::new(&zig_executable()) + zig() .args(&["build-exe"]) .args(input_paths) .args(&[ @@ -1202,7 +1192,7 @@ fn link_wasm32( let zig_str_path = find_zig_str_path(); let wasi_libc_path = find_wasi_libc_path(); - let child = Command::new(&zig_executable()) + let child = zig() // .env_clear() // .env("PATH", &env_path) .args(&["build-exe"]) @@ -1239,7 +1229,7 @@ fn link_windows( match link_type { LinkType::Dylib => { - let child = Command::new(&zig_executable()) + let child = zig() .args(&["build-lib"]) .args(input_paths) .args([ @@ -1261,7 +1251,7 @@ fn link_windows( Ok((child, output_path)) } LinkType::Executable => { - let child = Command::new(&zig_executable()) + let child = zig() .args(&["build-exe"]) .args(input_paths) .args([ @@ -1349,7 +1339,7 @@ pub fn preprocess_host_wasm32(host_input_path: &Path, preprocessed_host_path: &P (but seems to be an unofficial API) */ - let mut command = Command::new(&zig_executable()); + let mut zig_cmd = zig(); let args = &[ "wasm-ld", &bitcode::get_builtins_wasm32_obj_path(), @@ -1364,28 +1354,30 @@ pub fn preprocess_host_wasm32(host_input_path: &Path, preprocessed_host_path: &P "--relocatable", ]; - command.args(args); + zig_cmd.args(args); // println!("\npreprocess_host_wasm32"); // println!("zig {}\n", args.join(" ")); - let output = command.output().unwrap(); - validate_output(output_file, "zig", output) + run_build_command(zig_cmd, output_file) } -fn validate_output(file_name: &str, cmd_name: &str, output: Output) { - if !output.status.success() { - match std::str::from_utf8(&output.stderr) { +fn run_build_command(mut command: Command, file_to_build: &str) { + let cmd_str = format!("{:?}", &command); + let cmd_output = command.output().unwrap(); + + if !cmd_output.status.success() { + match std::str::from_utf8(&cmd_output.stderr) { Ok(stderr) => internal_error!( - "Failed to rebuild {} - stderr of the `{}` command was:\n{}", - file_name, - cmd_name, + "Error:\n Failed to rebuild {}:\n The executed command was:\n {}\n stderr of that command:\n {}", + file_to_build, + cmd_str, stderr ), Err(utf8_err) => internal_error!( - "Failed to rebuild {} - stderr of the `{}` command was invalid utf8 ({:?})", - file_name, - cmd_name, + "Error:\n Failed to rebuild {}:\n The executed command was:\n {}\n stderr of that command could not be parsed as valid utf8:\n {}", + file_to_build, + cmd_str, utf8_err ), } diff --git a/crates/compiler/builtins/Cargo.toml b/crates/compiler/builtins/Cargo.toml index 840cce6f60..1c63f517bf 100644 --- a/crates/compiler/builtins/Cargo.toml +++ b/crates/compiler/builtins/Cargo.toml @@ -16,6 +16,7 @@ lazy_static = "1.4.0" [build-dependencies] # dunce can be removed once ziglang/zig#5109 is fixed dunce = "1.0.3" +roc_utils = { path = "../../utils" } [target.'cfg(target_os = "macos")'.build-dependencies] tempfile = "3.2.0" diff --git a/crates/compiler/builtins/build.rs b/crates/compiler/builtins/build.rs index 63b8ceb4cf..bcdc7f9c49 100644 --- a/crates/compiler/builtins/build.rs +++ b/crates/compiler/builtins/build.rs @@ -1,6 +1,5 @@ -use std::convert::AsRef; +use roc_utils::zig; use std::env; -use std::ffi::OsStr; use std::fs; use std::io; use std::path::Path; @@ -14,13 +13,6 @@ use tempfile::tempdir; /// To debug the zig code with debug prints, we need to disable the wasm code gen const DEBUG: bool = false; -fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -95,12 +87,13 @@ fn generate_object_file(bitcode_path: &Path, zig_object: &str, object_file_name: println!("Compiling zig object `{}` to: {}", zig_object, src_obj); if !DEBUG { - run_command( - &bitcode_path, - &zig_executable(), - &["build", zig_object, "-Drelease=true"], - 0, - ); + let mut zig_cmd = zig(); + + zig_cmd + .current_dir(&bitcode_path) + .args(["build", zig_object, "-Drelease=true"]); + + run_command(zig_cmd, 0); println!("Moving zig object `{}` to: {}", zig_object, dest_obj); @@ -130,12 +123,13 @@ fn generate_bc_file(bitcode_path: &Path, zig_object: &str, file_name: &str) { #[cfg(target_os = "macos")] let _ = fs::remove_dir_all("./bitcode/zig-cache"); - run_command( - &bitcode_path, - &zig_executable(), - &["build", zig_object, "-Drelease=true"], - 0, - ); + let mut zig_cmd = zig(); + + zig_cmd + .current_dir(&bitcode_path) + .args(["build", zig_object, "-Drelease=true"]); + + run_command(zig_cmd, 0); } pub fn get_lib_dir() -> PathBuf { @@ -204,19 +198,10 @@ fn cp_unless_zig_cache(src_dir: &Path, target_dir: &Path) -> io::Result<()> { Ok(()) } -fn run_command + Copy>( - path: P, - command_str: &str, - args: I, - flaky_fail_counter: usize, -) where - I: IntoIterator, - S: AsRef, -{ - let output_result = Command::new(OsStr::new(&command_str)) - .current_dir(path) - .args(args) - .output(); +fn run_command(mut command: Command, flaky_fail_counter: usize) { + let command_str = format!("{:?}", &command); + + let output_result = command.output(); match output_result { Ok(output) => match output.status.success() { @@ -234,7 +219,7 @@ fn run_command + Copy>( if flaky_fail_counter == 10 { panic!("{} failed 10 times in a row. The following error is unlikely to be a flaky error: {}", command_str, error_str); } else { - run_command(path, command_str, args, flaky_fail_counter + 1) + run_command(command, flaky_fail_counter + 1) } } else { panic!("{} failed: {}", command_str, error_str); diff --git a/crates/compiler/test_gen/Cargo.toml b/crates/compiler/test_gen/Cargo.toml index 4d16e8f947..db12829684 100644 --- a/crates/compiler/test_gen/Cargo.toml +++ b/crates/compiler/test_gen/Cargo.toml @@ -11,6 +11,7 @@ path = "src/tests.rs" [build-dependencies] roc_builtins = { path = "../builtins" } +roc_utils = { path = "../../utils" } wasi_libc_sys = { path = "../../wasi-libc-sys" } [dev-dependencies] @@ -25,6 +26,7 @@ roc_types = { path = "../types" } roc_builtins = { path = "../builtins" } roc_constrain = { path = "../constrain" } roc_unify = { path = "../unify" } +roc_utils = { path = "../../utils" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } roc_reporting = { path = "../../reporting" } diff --git a/crates/compiler/test_gen/build.rs b/crates/compiler/test_gen/build.rs index 94e1577d15..74aca5c32f 100644 --- a/crates/compiler/test_gen/build.rs +++ b/crates/compiler/test_gen/build.rs @@ -1,9 +1,9 @@ use roc_builtins::bitcode; +use roc_utils::zig; use std::env; use std::fs; use std::path::Path; use std::path::PathBuf; -use std::process::Command; use wasi_libc_sys::{WASI_COMPILER_RT_PATH, WASI_LIBC_PATH}; @@ -113,13 +113,6 @@ fn build_wasm_test_host() { ]); } -fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - fn build_wasm_platform(out_dir: &str, source_path: &str) -> PathBuf { let mut outfile = PathBuf::from(out_dir).join(PLATFORM_FILENAME); outfile.set_extension("o"); @@ -146,16 +139,25 @@ fn feature_is_enabled(feature_name: &str) -> bool { // Run cargo with -vv to see commands printed out fn run_zig(args: &[&str]) { - let zig = zig_executable(); - println!("{} {}", zig, args.join(" ")); - let output = Command::new(&zig).args(args).output().unwrap(); + let mut zig_cmd = zig(); - if !output.status.success() { - eprintln!("stdout:\n{}", String::from_utf8_lossy(&output.stdout)); - eprintln!("stderr:\n{}", String::from_utf8_lossy(&output.stderr)); - panic!("zig call failed with status {:?}", output.status); + let full_zig_cmd = zig_cmd.args(args); + println!("{:?}", full_zig_cmd); + + let zig_cmd_output = full_zig_cmd.output().unwrap(); + + if !zig_cmd_output.status.success() { + eprintln!( + "stdout:\n{}", + String::from_utf8_lossy(&zig_cmd_output.stdout) + ); + eprintln!( + "stderr:\n{}", + String::from_utf8_lossy(&zig_cmd_output.stderr) + ); + panic!("zig call failed with status {:?}", zig_cmd_output.status); } - assert!(output.stdout.is_empty(), "{:#?}", output); - assert!(output.stderr.is_empty(), "{:#?}", output); + assert!(zig_cmd_output.stdout.is_empty(), "{:#?}", zig_cmd_output); + assert!(zig_cmd_output.stderr.is_empty(), "{:#?}", zig_cmd_output); } diff --git a/crates/compiler/test_gen/src/helpers/llvm.rs b/crates/compiler/test_gen/src/helpers/llvm.rs index 707584fc47..896c050d72 100644 --- a/crates/compiler/test_gen/src/helpers/llvm.rs +++ b/crates/compiler/test_gen/src/helpers/llvm.rs @@ -12,6 +12,7 @@ use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading}; use roc_mono::ir::OptLevel; use roc_region::all::LineInfo; use roc_reporting::report::RenderTarget; +use roc_utils::zig; use target_lexicon::Triple; #[cfg(feature = "gen-llvm-wasm")] @@ -456,9 +457,7 @@ fn llvm_module_to_wasm_file( .write_to_file(llvm_module, file_type, &test_a_path) .unwrap(); - use std::process::Command; - - let output = Command::new(&crate::helpers::zig_executable()) + let output = zig() .current_dir(dir_path) .args(&[ "wasm-ld", diff --git a/crates/compiler/test_gen/src/helpers/mod.rs b/crates/compiler/test_gen/src/helpers/mod.rs index 577cf25d01..ed49fa04ed 100644 --- a/crates/compiler/test_gen/src/helpers/mod.rs +++ b/crates/compiler/test_gen/src/helpers/mod.rs @@ -10,14 +10,6 @@ pub mod llvm; #[cfg(any(feature = "gen-wasm", feature = "gen-llvm-wasm"))] pub mod wasm; -#[allow(dead_code)] -pub fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - #[allow(dead_code)] pub(crate) fn src_hash(src: &str) -> u64 { use std::collections::hash_map::DefaultHasher; diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index d4777885be..32cc2376fc 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -30,6 +30,7 @@ roc_problem = { path = "../compiler/problem" } roc_types = { path = "../compiler/types" } roc_unify = { path = "../compiler/unify" } roc_reporting = { path = "../reporting" } +roc_utils = { path = "../utils" } roc_solve = { path = "../compiler/solve" } ven_graph = { path = "../vendor/pathfinding" } bumpalo = { version = "3.11.0", features = ["collections"] } diff --git a/crates/editor/src/editor/mvc/ed_update.rs b/crates/editor/src/editor/mvc/ed_update.rs index 6bae9d3a07..6e4fc667b3 100644 --- a/crates/editor/src/editor/mvc/ed_update.rs +++ b/crates/editor/src/editor/mvc/ed_update.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] -use std::process::Command; use std::process::Stdio; use crate::editor::code_lines::CodeLines; @@ -63,6 +62,7 @@ use roc_solve::module::Solved; use roc_types::pretty_print::name_and_print_var; use roc_types::pretty_print::DebugPrint; use roc_types::subs::{Subs, VarStore, Variable}; +use roc_utils::cargo; use snafu::OptionExt; use threadpool::ThreadPool; use winit::event::VirtualKeyCode; @@ -622,7 +622,7 @@ impl<'a> EdModel<'a> { let roc_file_str = path_to_string(self.file_path); - let cmd_out = Command::new("cargo") + let cmd_out = cargo() .arg("run") .arg("check") .arg(roc_file_str) @@ -641,7 +641,7 @@ impl<'a> EdModel<'a> { let roc_file_str = path_to_string(self.file_path); - Command::new("cargo") + cargo() .arg("run") .arg(roc_file_str) .stdout(Stdio::inherit()) diff --git a/crates/repl_wasm/Cargo.toml b/crates/repl_wasm/Cargo.toml index 469c95acde..560a314f21 100644 --- a/crates/repl_wasm/Cargo.toml +++ b/crates/repl_wasm/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib"] [build-dependencies] roc_builtins = {path = "../compiler/builtins"} +roc_utils = {path = "../utils"} wasi_libc_sys = { path = "../wasi-libc-sys" } [dependencies] diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index cdc5d3fb0e..8214e28e64 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,5 +1,5 @@ use snafu::OptionExt; -use std::{collections::HashMap, path::PathBuf, slice::SliceIndex}; +use std::{collections::HashMap, path::PathBuf, process::Command, slice::SliceIndex}; use util_error::{IndexOfFailedSnafu, KeyNotFoundSnafu, OutOfBoundsSnafu, UtilResult}; pub mod util_error; @@ -120,3 +120,81 @@ pub fn get_lib_path() -> Option { None } + +/// 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 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 mut cmd = Command::new("which"); + + 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 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 + } +} diff --git a/crates/wasi-libc-sys/Cargo.toml b/crates/wasi-libc-sys/Cargo.toml index 1406303e29..f7faff97fd 100644 --- a/crates/wasi-libc-sys/Cargo.toml +++ b/crates/wasi-libc-sys/Cargo.toml @@ -6,3 +6,6 @@ license = "UPL-1.0" name = "wasi_libc_sys" repository = "https://github.com/roc-lang/roc" version = "0.0.1" + +[build-dependencies] +roc_utils = {path = "../utils"} \ No newline at end of file diff --git a/crates/wasi-libc-sys/build.rs b/crates/wasi-libc-sys/build.rs index 4474290304..2aca3eadd2 100644 --- a/crates/wasi-libc-sys/build.rs +++ b/crates/wasi-libc-sys/build.rs @@ -1,8 +1,8 @@ +use roc_utils::zig; use std::env; use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; -use std::process::Command; fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -13,7 +13,7 @@ fn main() { let out_file = PathBuf::from(&out_dir).join("wasi-libc.a"); // Compile a dummy C program with Zig, with our own private cache directory - Command::new(&zig_executable()) + zig() .args([ "build-exe", "-target", @@ -51,13 +51,6 @@ fn main() { ); } -fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - fn find(dir: &Path, filename: &OsString) -> std::io::Result> { for entry in fs::read_dir(dir)? { let entry = entry?;