mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
Merge remote-tracking branch 'origin/trunk' into update_zig_09
This commit is contained in:
commit
3e141967bd
9 changed files with 351 additions and 225 deletions
|
@ -85,6 +85,8 @@ indoc = "1.0.3"
|
|||
serial_test = "0.5.1"
|
||||
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
||||
cli_utils = { path = "../cli_utils" }
|
||||
strum = "0.24.0"
|
||||
strum_macros = "0.24"
|
||||
|
||||
# Wasmer singlepass compiler only works on x86_64.
|
||||
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
|
||||
|
|
180
cli/src/lib.rs
180
cli/src/lib.rs
|
@ -3,16 +3,15 @@ extern crate const_format;
|
|||
|
||||
use build::BuiltFile;
|
||||
use bumpalo::Bump;
|
||||
use clap::Command;
|
||||
use clap::{Arg, ArgMatches};
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use roc_build::link::LinkType;
|
||||
use roc_error_macros::user_error;
|
||||
use roc_load::{LoadingProblem, Threading};
|
||||
use roc_mono::ir::OptLevel;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use target_lexicon::BinaryFormat;
|
||||
use target_lexicon::{
|
||||
|
@ -96,6 +95,16 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.possible_values(["true", "false"])
|
||||
.required(false);
|
||||
|
||||
let roc_file_to_run = Arg::new(ROC_FILE)
|
||||
.help("The .roc file of an app to run")
|
||||
.allow_invalid_utf8(true);
|
||||
|
||||
let args_for_app = Arg::new(ARGS_FOR_APP)
|
||||
.help("Arguments to pass into the app being run")
|
||||
.requires(ROC_FILE)
|
||||
.allow_invalid_utf8(true)
|
||||
.multiple_values(true);
|
||||
|
||||
let app = Command::new("roc")
|
||||
.version(concatcp!(VERSION, "\n"))
|
||||
.about("Runs the given .roc file, if there are no compilation errors.\nUse one of the SUBCOMMANDS below to do something else!")
|
||||
|
@ -132,6 +141,7 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file to build")
|
||||
.allow_invalid_utf8(true)
|
||||
.required(true),
|
||||
)
|
||||
)
|
||||
|
@ -148,11 +158,8 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.arg(flag_linker.clone())
|
||||
.arg(flag_precompiled.clone())
|
||||
.arg(flag_valgrind.clone())
|
||||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file of an app to run")
|
||||
.required(true),
|
||||
)
|
||||
.arg(roc_file_to_run.clone().required(true))
|
||||
.arg(args_for_app.clone())
|
||||
)
|
||||
.subcommand(Command::new(CMD_FORMAT)
|
||||
.about("Format a .roc file using standard Roc formatting")
|
||||
|
@ -177,6 +184,7 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file of an app to check")
|
||||
.allow_invalid_utf8(true)
|
||||
.required(true),
|
||||
)
|
||||
)
|
||||
|
@ -200,17 +208,8 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.arg(flag_linker)
|
||||
.arg(flag_precompiled)
|
||||
.arg(flag_valgrind)
|
||||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file of an app to build and run")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(ARGS_FOR_APP)
|
||||
.help("Arguments to pass into the app being run")
|
||||
.requires(ROC_FILE)
|
||||
.multiple_values(true),
|
||||
);
|
||||
.arg(roc_file_to_run.required(false))
|
||||
.arg(args_for_app);
|
||||
|
||||
if cfg!(feature = "editor") {
|
||||
app.subcommand(
|
||||
|
@ -236,8 +235,8 @@ pub fn docs(files: Vec<PathBuf>) {
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum BuildConfig {
|
||||
BuildOnly,
|
||||
BuildAndRun { roc_file_arg_index: usize },
|
||||
BuildAndRunIfNoErrors { roc_file_arg_index: usize },
|
||||
BuildAndRun,
|
||||
BuildAndRunIfNoErrors,
|
||||
}
|
||||
|
||||
pub enum FormatMode {
|
||||
|
@ -255,7 +254,7 @@ pub fn build(
|
|||
use BuildConfig::*;
|
||||
|
||||
let arena = Bump::new();
|
||||
let filename = matches.value_of(ROC_FILE).unwrap();
|
||||
let filename = matches.value_of_os(ROC_FILE).unwrap();
|
||||
|
||||
let original_cwd = std::env::current_dir()?;
|
||||
let opt_level = match (
|
||||
|
@ -372,7 +371,7 @@ pub fn build(
|
|||
// Return a nonzero exit code if there were problems
|
||||
Ok(problems.exit_code())
|
||||
}
|
||||
BuildAndRun { roc_file_arg_index } => {
|
||||
BuildAndRun => {
|
||||
if problems.errors > 0 || problems.warnings > 0 {
|
||||
println!(
|
||||
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nRunning program anyway…\n\n\x1B[36m{}\x1B[39m",
|
||||
|
@ -403,15 +402,11 @@ pub fn build(
|
|||
);
|
||||
}
|
||||
|
||||
roc_run(
|
||||
arena,
|
||||
&original_cwd,
|
||||
triple,
|
||||
roc_file_arg_index,
|
||||
&binary_path,
|
||||
)
|
||||
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
|
||||
|
||||
roc_run(arena, &original_cwd, triple, args, &binary_path)
|
||||
}
|
||||
BuildAndRunIfNoErrors { roc_file_arg_index } => {
|
||||
BuildAndRunIfNoErrors => {
|
||||
if problems.errors == 0 {
|
||||
if problems.warnings > 0 {
|
||||
println!(
|
||||
|
@ -427,13 +422,9 @@ pub fn build(
|
|||
);
|
||||
}
|
||||
|
||||
roc_run(
|
||||
arena,
|
||||
&original_cwd,
|
||||
triple,
|
||||
roc_file_arg_index,
|
||||
&binary_path,
|
||||
)
|
||||
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
|
||||
|
||||
roc_run(arena, &original_cwd, triple, args, &binary_path)
|
||||
} else {
|
||||
println!(
|
||||
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with: \x1B[32mroc run {}\x1B[39m",
|
||||
|
@ -460,7 +451,7 @@ pub fn build(
|
|||
"warnings"
|
||||
},
|
||||
total_time.as_millis(),
|
||||
filename
|
||||
filename.to_string_lossy()
|
||||
);
|
||||
|
||||
Ok(problems.exit_code())
|
||||
|
@ -479,17 +470,14 @@ pub fn build(
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
fn roc_run(
|
||||
fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
||||
arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
|
||||
cwd: &Path,
|
||||
triple: Triple,
|
||||
roc_file_arg_index: usize,
|
||||
args: I,
|
||||
binary_path: &Path,
|
||||
) -> io::Result<i32> {
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
let mut cmd = match triple.architecture {
|
||||
match triple.architecture {
|
||||
Architecture::Wasm32 => {
|
||||
// If possible, report the generated executable name relative to the current dir.
|
||||
let generated_filename = binary_path
|
||||
|
@ -500,19 +488,44 @@ fn roc_run(
|
|||
// since the process is about to exit anyway.
|
||||
std::mem::forget(arena);
|
||||
|
||||
let args = std::env::args()
|
||||
.skip(roc_file_arg_index)
|
||||
.collect::<Vec<_>>();
|
||||
if cfg!(target_family = "unix") {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
run_with_wasmer(generated_filename, &args);
|
||||
return Ok(0);
|
||||
run_with_wasmer(
|
||||
generated_filename,
|
||||
args.into_iter().map(|os_str| os_str.as_bytes()),
|
||||
);
|
||||
} else {
|
||||
run_with_wasmer(
|
||||
generated_filename,
|
||||
args.into_iter().map(|os_str| {
|
||||
os_str.to_str().expect(
|
||||
"Roc does not currently support passing non-UTF8 arguments to Wasmer.",
|
||||
)
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
_ => {
|
||||
if cfg!(target_family = "unix") {
|
||||
roc_run_unix(cwd, args, binary_path)
|
||||
} else {
|
||||
roc_run_non_unix(arena, cwd, args, binary_path)
|
||||
}
|
||||
}
|
||||
_ => std::process::Command::new(&binary_path),
|
||||
};
|
||||
|
||||
if let Architecture::Wasm32 = triple.architecture {
|
||||
cmd.arg(binary_path);
|
||||
}
|
||||
}
|
||||
|
||||
fn roc_run_unix<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
cwd: &Path,
|
||||
args: I,
|
||||
binary_path: &Path,
|
||||
) -> io::Result<i32> {
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
let mut cmd = std::process::Command::new(&binary_path);
|
||||
|
||||
// Forward all the arguments after the .roc file argument
|
||||
// to the new process. This way, you can do things like:
|
||||
|
@ -521,10 +534,8 @@ fn roc_run(
|
|||
//
|
||||
// ...and have it so that app.roc will receive only `foo`,
|
||||
// `bar`, and `baz` as its arguments.
|
||||
for (index, arg) in std::env::args().enumerate() {
|
||||
if index > roc_file_arg_index {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
for arg in args {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
|
||||
// This is much faster than spawning a subprocess if we're on a UNIX system!
|
||||
|
@ -536,29 +547,36 @@ fn roc_run(
|
|||
Err(err)
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
fn roc_run(cmd: &mut Command) -> io::Result<i32> {
|
||||
// Run the compiled app
|
||||
let exit_status = cmd
|
||||
.spawn()
|
||||
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
|
||||
.wait()
|
||||
.expect("TODO gracefully handle block_on failing when `roc` spawns a subprocess for the compiled app");
|
||||
fn roc_run_non_unix<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
_arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
|
||||
_cwd: &Path,
|
||||
_args: I,
|
||||
_binary_path: &Path,
|
||||
) -> io::Result<i32> {
|
||||
todo!("TODO support running roc programs on non-UNIX targets");
|
||||
// let mut cmd = std::process::Command::new(&binary_path);
|
||||
|
||||
// `roc [FILE]` exits with the same status code as the app it ran.
|
||||
//
|
||||
// If you want to know whether there were compilation problems
|
||||
// via status code, use either `roc build` or `roc check` instead!
|
||||
match exit_status.code() {
|
||||
Some(code) => Ok(code),
|
||||
None => {
|
||||
todo!("TODO gracefully handle the `roc [FILE]` subprocess terminating with a signal.");
|
||||
}
|
||||
}
|
||||
// // Run the compiled app
|
||||
// let exit_status = cmd
|
||||
// .spawn()
|
||||
// .unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
|
||||
// .wait()
|
||||
// .expect("TODO gracefully handle block_on failing when `roc` spawns a subprocess for the compiled app");
|
||||
|
||||
// // `roc [FILE]` exits with the same status code as the app it ran.
|
||||
// //
|
||||
// // If you want to know whether there were compilation problems
|
||||
// // via status code, use either `roc build` or `roc check` instead!
|
||||
// match exit_status.code() {
|
||||
// Some(code) => Ok(code),
|
||||
// None => {
|
||||
// todo!("TODO gracefully handle the `roc [FILE]` subprocess terminating with a signal.");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(feature = "run-wasm32")]
|
||||
fn run_with_wasmer(wasm_path: &std::path::Path, args: &[String]) {
|
||||
fn run_with_wasmer<I: Iterator<Item = S>, S: AsRef<[u8]>>(wasm_path: &std::path::Path, args: I) {
|
||||
use wasmer::{Instance, Module, Store};
|
||||
|
||||
let store = Store::default();
|
||||
|
@ -589,8 +607,8 @@ fn run_with_wasmer(wasm_path: &std::path::Path, args: &[String]) {
|
|||
}
|
||||
|
||||
#[cfg(not(feature = "run-wasm32"))]
|
||||
fn run_with_wasmer(_wasm_path: &std::path::Path, _args: &[String]) {
|
||||
println!("Running wasm files not support");
|
||||
fn run_with_wasmer<I: Iterator<Item = S>, S: AsRef<[u8]>>(_wasm_path: &std::path::Path, _args: I) {
|
||||
println!("Running wasm files is not supported on this target.");
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -27,43 +27,31 @@ fn main() -> io::Result<()> {
|
|||
|
||||
let exit_code = match matches.subcommand() {
|
||||
None => {
|
||||
match matches.index_of(ROC_FILE) {
|
||||
Some(arg_index) => {
|
||||
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
||||
if matches.is_present(ROC_FILE) {
|
||||
build(
|
||||
&matches,
|
||||
BuildConfig::BuildAndRunIfNoErrors,
|
||||
Triple::host(),
|
||||
LinkType::Executable,
|
||||
)
|
||||
} else {
|
||||
launch_editor(None)?;
|
||||
|
||||
build(
|
||||
&matches,
|
||||
BuildConfig::BuildAndRunIfNoErrors { roc_file_arg_index },
|
||||
Triple::host(),
|
||||
LinkType::Executable,
|
||||
)
|
||||
}
|
||||
|
||||
None => {
|
||||
launch_editor(None)?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
Some((CMD_RUN, matches)) => {
|
||||
match matches.index_of(ROC_FILE) {
|
||||
Some(arg_index) => {
|
||||
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
||||
if matches.is_present(ROC_FILE) {
|
||||
build(
|
||||
matches,
|
||||
BuildConfig::BuildAndRun,
|
||||
Triple::host(),
|
||||
LinkType::Executable,
|
||||
)
|
||||
} else {
|
||||
eprintln!("What .roc file do you want to run? Specify it at the end of the `roc run` command.");
|
||||
|
||||
build(
|
||||
matches,
|
||||
BuildConfig::BuildAndRun { roc_file_arg_index },
|
||||
Triple::host(),
|
||||
LinkType::Executable,
|
||||
)
|
||||
}
|
||||
|
||||
None => {
|
||||
eprintln!("What .roc file do you want to run? Specify it at the end of the `roc run` command.");
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
Some((CMD_BUILD, matches)) => {
|
||||
|
@ -90,7 +78,7 @@ fn main() -> io::Result<()> {
|
|||
let arena = bumpalo::Bump::new();
|
||||
|
||||
let emit_timings = matches.is_present(FLAG_TIME);
|
||||
let filename = matches.value_of(ROC_FILE).unwrap();
|
||||
let filename = matches.value_of_os(ROC_FILE).unwrap();
|
||||
let roc_file_path = PathBuf::from(filename);
|
||||
let src_dir = roc_file_path.parent().unwrap().to_owned();
|
||||
|
||||
|
|
|
@ -14,10 +14,29 @@ mod cli_run {
|
|||
known_bad_file, run_cmd, run_roc, run_with_valgrind, Out, ValgrindError,
|
||||
ValgrindErrorXWhat,
|
||||
};
|
||||
use const_format::concatcp;
|
||||
use indoc::indoc;
|
||||
use roc_cli::{CMD_BUILD, CMD_CHECK, CMD_FORMAT, CMD_RUN};
|
||||
use roc_test_utils::assert_multiline_str_eq;
|
||||
use serial_test::serial;
|
||||
use std::iter;
|
||||
use std::path::{Path, PathBuf};
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
const OPTIMIZE_FLAG: &str = concatcp!("--", roc_cli::FLAG_OPTIMIZE);
|
||||
const VALGRIND_FLAG: &str = concatcp!("--", roc_cli::FLAG_VALGRIND);
|
||||
const LINKER_FLAG: &str = concatcp!("--", roc_cli::FLAG_LINKER);
|
||||
const CHECK_FLAG: &str = concatcp!("--", roc_cli::FLAG_CHECK);
|
||||
#[allow(dead_code)]
|
||||
const TARGET_FLAG: &str = concatcp!("--", roc_cli::FLAG_TARGET);
|
||||
|
||||
#[derive(Debug, EnumIter)]
|
||||
enum CliMode {
|
||||
RocBuild,
|
||||
RocRun,
|
||||
Roc,
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
use roc_collections::all::MutMap;
|
||||
|
@ -65,7 +84,7 @@ mod cli_run {
|
|||
}
|
||||
|
||||
fn check_compile_error(file: &Path, flags: &[&str], expected: &str) {
|
||||
let compile_out = run_roc(&[&["check", file.to_str().unwrap()], flags].concat());
|
||||
let compile_out = run_roc([CMD_CHECK, file.to_str().unwrap()].iter().chain(flags), &[]);
|
||||
let err = compile_out.stdout.trim();
|
||||
let err = strip_colors(err);
|
||||
|
||||
|
@ -77,19 +96,41 @@ mod cli_run {
|
|||
}
|
||||
|
||||
fn check_format_check_as_expected(file: &Path, expects_success_exit_code: bool) {
|
||||
let flags = &["--check"];
|
||||
let out = run_roc(&[&["format", file.to_str().unwrap()], &flags[..]].concat());
|
||||
if expects_success_exit_code {
|
||||
assert!(out.status.success());
|
||||
} else {
|
||||
assert!(!out.status.success());
|
||||
}
|
||||
let out = run_roc([CMD_FORMAT, file.to_str().unwrap(), CHECK_FLAG], &[]);
|
||||
|
||||
assert_eq!(out.status.success(), expects_success_exit_code);
|
||||
}
|
||||
|
||||
fn build_example(file: &Path, flags: &[&str]) -> Out {
|
||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat());
|
||||
if !compile_out.stderr.is_empty() {
|
||||
panic!("roc build had stderr: {}", compile_out.stderr);
|
||||
fn run_roc_on<'a, I: IntoIterator<Item = &'a str>>(
|
||||
file: &'a Path,
|
||||
args: I,
|
||||
stdin: &[&str],
|
||||
input_file: Option<PathBuf>,
|
||||
) -> Out {
|
||||
let compile_out = match input_file {
|
||||
Some(input_file) => run_roc(
|
||||
// converting these all to String avoids lifetime issues
|
||||
args.into_iter().map(|arg| arg.to_string()).chain([
|
||||
file.to_str().unwrap().to_string(),
|
||||
input_file.to_str().unwrap().to_string(),
|
||||
]),
|
||||
stdin,
|
||||
),
|
||||
None => run_roc(
|
||||
args.into_iter().chain(iter::once(file.to_str().unwrap())),
|
||||
stdin,
|
||||
),
|
||||
};
|
||||
|
||||
if !compile_out.stderr.is_empty() &&
|
||||
// If there is any stderr, it should be reporting the runtime and that's it!
|
||||
!(compile_out.stderr.starts_with("runtime: ")
|
||||
&& compile_out.stderr.ends_with("ms\n"))
|
||||
{
|
||||
panic!(
|
||||
"`roc` command had unexpected stderr: {}",
|
||||
compile_out.stderr
|
||||
);
|
||||
}
|
||||
|
||||
assert!(compile_out.status.success(), "bad status {:?}", compile_out);
|
||||
|
@ -106,89 +147,104 @@ mod cli_run {
|
|||
expected_ending: &str,
|
||||
use_valgrind: bool,
|
||||
) {
|
||||
let mut all_flags = vec![];
|
||||
all_flags.extend_from_slice(flags);
|
||||
for cli_mode in CliMode::iter() {
|
||||
let flags = {
|
||||
let mut vec = flags.to_vec();
|
||||
|
||||
if use_valgrind {
|
||||
all_flags.extend_from_slice(&["--valgrind"]);
|
||||
}
|
||||
if use_valgrind {
|
||||
vec.push(VALGRIND_FLAG);
|
||||
}
|
||||
|
||||
build_example(file, &all_flags[..]);
|
||||
|
||||
let out = if use_valgrind && ALLOW_VALGRIND {
|
||||
let (valgrind_out, raw_xml) = if let Some(ref input_file) = input_file {
|
||||
run_with_valgrind(
|
||||
stdin,
|
||||
&[
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
input_file.to_str().unwrap(),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
run_with_valgrind(
|
||||
stdin,
|
||||
&[file.with_file_name(executable_filename).to_str().unwrap()],
|
||||
)
|
||||
vec.into_iter()
|
||||
};
|
||||
|
||||
if valgrind_out.status.success() {
|
||||
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
|
||||
panic!("failed to parse the `valgrind` xml output. Error was:\n\n{:?}\n\nvalgrind xml was: \"{}\"\n\nvalgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", err, raw_xml, valgrind_out.stdout, valgrind_out.stderr);
|
||||
});
|
||||
let out = match cli_mode {
|
||||
CliMode::RocBuild => {
|
||||
run_roc_on(file, iter::once(CMD_BUILD).chain(flags.clone()), &[], None);
|
||||
|
||||
if !memory_errors.is_empty() {
|
||||
for error in memory_errors {
|
||||
let ValgrindError {
|
||||
kind,
|
||||
what: _,
|
||||
xwhat,
|
||||
} = error;
|
||||
println!("Valgrind Error: {}\n", kind);
|
||||
if use_valgrind && ALLOW_VALGRIND {
|
||||
let (valgrind_out, raw_xml) = if let Some(ref input_file) = input_file {
|
||||
run_with_valgrind(
|
||||
stdin.clone().iter().copied(),
|
||||
&[
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
input_file.clone().to_str().unwrap(),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
run_with_valgrind(
|
||||
stdin.clone().iter().copied(),
|
||||
&[file.with_file_name(executable_filename).to_str().unwrap()],
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(ValgrindErrorXWhat {
|
||||
text,
|
||||
leakedbytes: _,
|
||||
leakedblocks: _,
|
||||
}) = xwhat
|
||||
{
|
||||
println!(" {}", text);
|
||||
if valgrind_out.status.success() {
|
||||
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
|
||||
panic!("failed to parse the `valgrind` xml output. Error was:\n\n{:?}\n\nvalgrind xml was: \"{}\"\n\nvalgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", err, raw_xml, valgrind_out.stdout, valgrind_out.stderr);
|
||||
});
|
||||
|
||||
if !memory_errors.is_empty() {
|
||||
for error in memory_errors {
|
||||
let ValgrindError {
|
||||
kind,
|
||||
what: _,
|
||||
xwhat,
|
||||
} = error;
|
||||
println!("Valgrind Error: {}\n", kind);
|
||||
|
||||
if let Some(ValgrindErrorXWhat {
|
||||
text,
|
||||
leakedbytes: _,
|
||||
leakedblocks: _,
|
||||
}) = xwhat
|
||||
{
|
||||
println!(" {}", text);
|
||||
}
|
||||
}
|
||||
panic!("Valgrind reported memory errors");
|
||||
}
|
||||
} else {
|
||||
let exit_code = match valgrind_out.status.code() {
|
||||
Some(code) => format!("exit code {}", code),
|
||||
None => "no exit code".to_string(),
|
||||
};
|
||||
|
||||
panic!("`valgrind` exited with {}. valgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", exit_code, valgrind_out.stdout, valgrind_out.stderr);
|
||||
}
|
||||
}
|
||||
panic!(
|
||||
"Valgrind reported memory errors in {:?} with flags {:?}",
|
||||
file, flags
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let exit_code = match valgrind_out.status.code() {
|
||||
Some(code) => format!("exit code {}", code),
|
||||
None => "no exit code".to_string(),
|
||||
};
|
||||
|
||||
panic!("`valgrind` exited with {}. valgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", exit_code, valgrind_out.stdout, valgrind_out.stderr);
|
||||
valgrind_out
|
||||
} else if let Some(ref input_file) = input_file {
|
||||
run_cmd(
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
stdin.iter().copied(),
|
||||
&[input_file.to_str().unwrap()],
|
||||
)
|
||||
} else {
|
||||
run_cmd(
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
stdin.iter().copied(),
|
||||
&[],
|
||||
)
|
||||
}
|
||||
}
|
||||
CliMode::Roc => run_roc_on(file, flags.clone(), stdin, input_file.clone()),
|
||||
CliMode::RocRun => run_roc_on(
|
||||
file,
|
||||
iter::once(CMD_RUN).chain(flags.clone()),
|
||||
stdin,
|
||||
input_file.clone(),
|
||||
),
|
||||
};
|
||||
|
||||
if !&out.stdout.ends_with(expected_ending) {
|
||||
panic!(
|
||||
"expected output to end with {:?} but instead got {:#?} - stderr was: {:#?}",
|
||||
expected_ending, out.stdout, out.stderr
|
||||
);
|
||||
}
|
||||
|
||||
valgrind_out
|
||||
} else if let Some(input_file) = input_file {
|
||||
run_cmd(
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
stdin,
|
||||
&[input_file.to_str().unwrap()],
|
||||
)
|
||||
} else {
|
||||
run_cmd(
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
stdin,
|
||||
&[],
|
||||
)
|
||||
};
|
||||
if !&out.stdout.ends_with(expected_ending) {
|
||||
panic!(
|
||||
"expected output to end with {:?} but instead got {:#?} - stderr was: {:#?}",
|
||||
expected_ending, out.stdout, out.stderr
|
||||
);
|
||||
assert!(out.status.success());
|
||||
}
|
||||
assert!(out.status.success());
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm32-cli-run")]
|
||||
|
@ -202,9 +258,13 @@ mod cli_run {
|
|||
) {
|
||||
assert_eq!(input_file, None, "Wasm does not support input files");
|
||||
let mut flags = flags.to_vec();
|
||||
flags.push("--target=wasm32");
|
||||
flags.push(concatcp!(TARGET_FLAG, "=wasm32"));
|
||||
|
||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat());
|
||||
let compile_out = run_roc(
|
||||
[CMD_BUILD, file.to_str().unwrap()]
|
||||
.iter()
|
||||
.chain(flags.as_slice()),
|
||||
);
|
||||
if !compile_out.stderr.is_empty() {
|
||||
panic!("{}", compile_out.stderr);
|
||||
}
|
||||
|
@ -261,7 +321,7 @@ mod cli_run {
|
|||
}
|
||||
"hello-gui" | "breakout" => {
|
||||
// Since these require opening a window, we do `roc build` on them but don't run them.
|
||||
build_example(&file_name, &["--optimize"]);
|
||||
run_roc_on(&file_name, [CMD_BUILD, OPTIMIZE_FLAG], &[], None);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -286,7 +346,7 @@ mod cli_run {
|
|||
&file_name,
|
||||
example.stdin,
|
||||
example.executable_filename,
|
||||
&["--optimize"],
|
||||
&[OPTIMIZE_FLAG],
|
||||
example.input_file.and_then(|file| Some(example_file(dir_name, file))),
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
|
@ -299,7 +359,7 @@ mod cli_run {
|
|||
&file_name,
|
||||
example.stdin,
|
||||
example.executable_filename,
|
||||
&["--linker", "legacy"],
|
||||
&[LINKER_FLAG, "legacy"],
|
||||
example.input_file.and_then(|file| Some(example_file(dir_name, file))),
|
||||
example.expected_ending,
|
||||
example.use_valgrind,
|
||||
|
@ -516,7 +576,7 @@ mod cli_run {
|
|||
&file_name,
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&["--optimize"],
|
||||
&[OPTIMIZE_FLAG],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
|
@ -558,7 +618,7 @@ mod cli_run {
|
|||
&file_name,
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&["--optimize"],
|
||||
&[OPTIMIZE_FLAG],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.expected_ending,
|
||||
);
|
||||
|
@ -590,7 +650,7 @@ mod cli_run {
|
|||
&file_name,
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&["--target=x86_32"],
|
||||
[concatcp!(TARGET_FLAG, "=x86_32")],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
|
@ -600,7 +660,7 @@ mod cli_run {
|
|||
&file_name,
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&["--target=x86_32", "--optimize"],
|
||||
[concatcp!(TARGET_FLAG, "=x86_32"), OPTIMIZE_FLAG],
|
||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||
benchmark.expected_ending,
|
||||
benchmark.use_valgrind,
|
||||
|
@ -819,7 +879,7 @@ mod cli_run {
|
|||
&fixture_file("multi-dep-str", "Main.roc"),
|
||||
&[],
|
||||
"multi-dep-str",
|
||||
&["--optimize"],
|
||||
&[OPTIMIZE_FLAG],
|
||||
None,
|
||||
"I am Dep2.str2\n",
|
||||
true,
|
||||
|
@ -847,7 +907,7 @@ mod cli_run {
|
|||
&fixture_file("multi-dep-thunk", "Main.roc"),
|
||||
&[],
|
||||
"multi-dep-thunk",
|
||||
&["--optimize"],
|
||||
&[OPTIMIZE_FLAG],
|
||||
None,
|
||||
"I am Dep2.value2\n",
|
||||
true,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue