Merge remote-tracking branch 'origin/trunk' into cli-max-threads

This commit is contained in:
Richard Feldman 2022-05-07 16:23:41 -04:00
commit 51ee101bd1
No known key found for this signature in database
GPG key ID: 7E4127D1E4241798
17 changed files with 905 additions and 784 deletions

1130
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -85,6 +85,8 @@ indoc = "1.0.3"
serial_test = "0.5.1" serial_test = "0.5.1"
criterion = { git = "https://github.com/Anton-4/criterion.rs"} criterion = { git = "https://github.com/Anton-4/criterion.rs"}
cli_utils = { path = "../cli_utils" } cli_utils = { path = "../cli_utils" }
strum = "0.24.0"
strum_macros = "0.24"
# Wasmer singlepass compiler only works on x86_64. # Wasmer singlepass compiler only works on x86_64.
[target.'cfg(target_arch = "x86_64")'.dev-dependencies] [target.'cfg(target_arch = "x86_64")'.dev-dependencies]

View file

@ -3,16 +3,15 @@ extern crate const_format;
use build::BuiltFile; use build::BuiltFile;
use bumpalo::Bump; use bumpalo::Bump;
use clap::Command; use clap::{Arg, ArgMatches, Command};
use clap::{Arg, ArgMatches};
use roc_build::link::LinkType; use roc_build::link::LinkType;
use roc_error_macros::user_error; use roc_error_macros::user_error;
use roc_load::{LoadingProblem, Threading}; use roc_load::{LoadingProblem, Threading};
use roc_mono::ir::OptLevel; use roc_mono::ir::OptLevel;
use std::env; use std::env;
use std::ffi::OsStr;
use std::io; use std::io;
use std::path::Path; use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::process; use std::process;
use target_lexicon::BinaryFormat; use target_lexicon::BinaryFormat;
use target_lexicon::{ use target_lexicon::{
@ -105,6 +104,16 @@ pub fn build_app<'a>() -> Command<'a> {
.possible_values(["true", "false"]) .possible_values(["true", "false"])
.required(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") let app = Command::new("roc")
.version(concatcp!(VERSION, "\n")) .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!") .about("Runs the given .roc file, if there are no compilation errors.\nUse one of the SUBCOMMANDS below to do something else!")
@ -142,6 +151,7 @@ pub fn build_app<'a>() -> Command<'a> {
.arg( .arg(
Arg::new(ROC_FILE) Arg::new(ROC_FILE)
.help("The .roc file to build") .help("The .roc file to build")
.allow_invalid_utf8(true)
.required(true), .required(true),
) )
) )
@ -159,11 +169,8 @@ pub fn build_app<'a>() -> Command<'a> {
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_precompiled.clone()) .arg(flag_precompiled.clone())
.arg(flag_valgrind.clone()) .arg(flag_valgrind.clone())
.arg( .arg(roc_file_to_run.clone().required(true))
Arg::new(ROC_FILE) .arg(args_for_app.clone())
.help("The .roc file of an app to run")
.required(true),
)
) )
.subcommand(Command::new(CMD_FORMAT) .subcommand(Command::new(CMD_FORMAT)
.about("Format a .roc file using standard Roc formatting") .about("Format a .roc file using standard Roc formatting")
@ -189,6 +196,7 @@ pub fn build_app<'a>() -> Command<'a> {
.arg( .arg(
Arg::new(ROC_FILE) Arg::new(ROC_FILE)
.help("The .roc file of an app to check") .help("The .roc file of an app to check")
.allow_invalid_utf8(true)
.required(true), .required(true),
) )
) )
@ -213,17 +221,8 @@ pub fn build_app<'a>() -> Command<'a> {
.arg(flag_linker) .arg(flag_linker)
.arg(flag_precompiled) .arg(flag_precompiled)
.arg(flag_valgrind) .arg(flag_valgrind)
.arg( .arg(roc_file_to_run.required(false))
Arg::new(ROC_FILE) .arg(args_for_app);
.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),
);
if cfg!(feature = "editor") { if cfg!(feature = "editor") {
app.subcommand( app.subcommand(
@ -249,8 +248,8 @@ pub fn docs(files: Vec<PathBuf>) {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum BuildConfig { pub enum BuildConfig {
BuildOnly, BuildOnly,
BuildAndRun { roc_file_arg_index: usize }, BuildAndRun,
BuildAndRunIfNoErrors { roc_file_arg_index: usize }, BuildAndRunIfNoErrors,
} }
pub enum FormatMode { pub enum FormatMode {
@ -268,7 +267,7 @@ pub fn build(
use BuildConfig::*; use BuildConfig::*;
let arena = Bump::new(); 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 original_cwd = std::env::current_dir()?;
let opt_level = match ( let opt_level = match (
@ -395,7 +394,7 @@ pub fn build(
// Return a nonzero exit code if there were problems // Return a nonzero exit code if there were problems
Ok(problems.exit_code()) Ok(problems.exit_code())
} }
BuildAndRun { roc_file_arg_index } => { BuildAndRun => {
if problems.errors > 0 || problems.warnings > 0 { if problems.errors > 0 || problems.warnings > 0 {
println!( println!(
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nRunning program anyway…\n\n\x1B[36m{}\x1B[39m", "\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nRunning program anyway…\n\n\x1B[36m{}\x1B[39m",
@ -426,15 +425,11 @@ pub fn build(
); );
} }
roc_run( let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
arena,
&original_cwd, roc_run(arena, &original_cwd, triple, args, &binary_path)
triple,
roc_file_arg_index,
&binary_path,
)
} }
BuildAndRunIfNoErrors { roc_file_arg_index } => { BuildAndRunIfNoErrors => {
if problems.errors == 0 { if problems.errors == 0 {
if problems.warnings > 0 { if problems.warnings > 0 {
println!( println!(
@ -450,13 +445,9 @@ pub fn build(
); );
} }
roc_run( let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
arena,
&original_cwd, roc_run(arena, &original_cwd, triple, args, &binary_path)
triple,
roc_file_arg_index,
&binary_path,
)
} else { } else {
println!( 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", "\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",
@ -483,7 +474,7 @@ pub fn build(
"warnings" "warnings"
}, },
total_time.as_millis(), total_time.as_millis(),
filename filename.to_string_lossy()
); );
Ok(problems.exit_code()) Ok(problems.exit_code())
@ -502,17 +493,14 @@ pub fn build(
} }
} }
#[cfg(target_family = "unix")] fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
fn roc_run(
arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it! arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
cwd: &Path, cwd: &Path,
triple: Triple, triple: Triple,
roc_file_arg_index: usize, args: I,
binary_path: &Path, binary_path: &Path,
) -> io::Result<i32> { ) -> io::Result<i32> {
use std::os::unix::process::CommandExt; match triple.architecture {
let mut cmd = match triple.architecture {
Architecture::Wasm32 => { Architecture::Wasm32 => {
// If possible, report the generated executable name relative to the current dir. // If possible, report the generated executable name relative to the current dir.
let generated_filename = binary_path let generated_filename = binary_path
@ -523,19 +511,44 @@ fn roc_run(
// since the process is about to exit anyway. // since the process is about to exit anyway.
std::mem::forget(arena); std::mem::forget(arena);
let args = std::env::args() if cfg!(target_family = "unix") {
.skip(roc_file_arg_index) use std::os::unix::ffi::OsStrExt;
.collect::<Vec<_>>();
run_with_wasmer(generated_filename, &args); run_with_wasmer(
return Ok(0); 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.",
)
}),
);
} }
_ => std::process::Command::new(&binary_path),
};
if let Architecture::Wasm32 = triple.architecture { Ok(0)
cmd.arg(binary_path);
} }
_ => {
if cfg!(target_family = "unix") {
roc_run_unix(cwd, args, binary_path)
} else {
roc_run_non_unix(arena, cwd, args, 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 // Forward all the arguments after the .roc file argument
// to the new process. This way, you can do things like: // to the new process. This way, you can do things like:
@ -544,11 +557,9 @@ fn roc_run(
// //
// ...and have it so that app.roc will receive only `foo`, // ...and have it so that app.roc will receive only `foo`,
// `bar`, and `baz` as its arguments. // `bar`, and `baz` as its arguments.
for (index, arg) in std::env::args().enumerate() { for arg in args {
if index > roc_file_arg_index {
cmd.arg(arg); cmd.arg(arg);
} }
}
// This is much faster than spawning a subprocess if we're on a UNIX system! // This is much faster than spawning a subprocess if we're on a UNIX system!
let err = cmd.current_dir(cwd).exec(); let err = cmd.current_dir(cwd).exec();
@ -559,29 +570,36 @@ fn roc_run(
Err(err) Err(err)
} }
#[cfg(not(target_family = "unix"))] fn roc_run_non_unix<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
fn roc_run(cmd: &mut Command) -> io::Result<i32> { _arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
// Run the compiled app _cwd: &Path,
let exit_status = cmd _args: I,
.spawn() _binary_path: &Path,
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err)) ) -> io::Result<i32> {
.wait() todo!("TODO support running roc programs on non-UNIX targets");
.expect("TODO gracefully handle block_on failing when `roc` spawns a subprocess for the compiled app"); // let mut cmd = std::process::Command::new(&binary_path);
// `roc [FILE]` exits with the same status code as the app it ran. // // Run the compiled app
// // let exit_status = cmd
// If you want to know whether there were compilation problems // .spawn()
// via status code, use either `roc build` or `roc check` instead! // .unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
match exit_status.code() { // .wait()
Some(code) => Ok(code), // .expect("TODO gracefully handle block_on failing when `roc` spawns a subprocess for the compiled app");
None => {
todo!("TODO gracefully handle the `roc [FILE]` subprocess terminating with a signal."); // // `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")] #[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}; use wasmer::{Instance, Module, Store};
let store = Store::default(); let store = Store::default();
@ -612,8 +630,8 @@ fn run_with_wasmer(wasm_path: &std::path::Path, args: &[String]) {
} }
#[cfg(not(feature = "run-wasm32"))] #[cfg(not(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) {
println!("Running wasm files not support"); println!("Running wasm files is not supported on this target.");
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]

View file

@ -27,45 +27,33 @@ fn main() -> io::Result<()> {
let exit_code = match matches.subcommand() { let exit_code = match matches.subcommand() {
None => { None => {
match matches.index_of(ROC_FILE) { if matches.is_present(ROC_FILE) {
Some(arg_index) => {
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
build( build(
&matches, &matches,
BuildConfig::BuildAndRunIfNoErrors { roc_file_arg_index }, BuildConfig::BuildAndRunIfNoErrors,
Triple::host(), Triple::host(),
LinkType::Executable, LinkType::Executable,
) )
} } else {
None => {
launch_editor(None)?; launch_editor(None)?;
Ok(0) Ok(0)
} }
} }
}
Some((CMD_RUN, matches)) => { Some((CMD_RUN, matches)) => {
match matches.index_of(ROC_FILE) { if matches.is_present(ROC_FILE) {
Some(arg_index) => {
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
build( build(
matches, matches,
BuildConfig::BuildAndRun { roc_file_arg_index }, BuildConfig::BuildAndRun,
Triple::host(), Triple::host(),
LinkType::Executable, LinkType::Executable,
) )
} } else {
None => {
eprintln!("What .roc file do you want to run? Specify it at the end of the `roc run` command."); 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)) => { Some((CMD_BUILD, matches)) => {
let target: Target = matches.value_of_t(FLAG_TARGET).unwrap_or_default(); let target: Target = matches.value_of_t(FLAG_TARGET).unwrap_or_default();
@ -90,7 +78,7 @@ fn main() -> io::Result<()> {
let arena = bumpalo::Bump::new(); let arena = bumpalo::Bump::new();
let emit_timings = matches.is_present(FLAG_TIME); 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 roc_file_path = PathBuf::from(filename);
let src_dir = roc_file_path.parent().unwrap().to_owned(); let src_dir = roc_file_path.parent().unwrap().to_owned();

View file

@ -14,10 +14,29 @@ mod cli_run {
known_bad_file, run_cmd, run_roc, run_with_valgrind, Out, ValgrindError, known_bad_file, run_cmd, run_roc, run_with_valgrind, Out, ValgrindError,
ValgrindErrorXWhat, ValgrindErrorXWhat,
}; };
use const_format::concatcp;
use indoc::indoc; use indoc::indoc;
use roc_cli::{CMD_BUILD, CMD_CHECK, CMD_FORMAT, CMD_RUN};
use roc_test_utils::assert_multiline_str_eq; use roc_test_utils::assert_multiline_str_eq;
use serial_test::serial; use serial_test::serial;
use std::iter;
use std::path::{Path, PathBuf}; 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))] #[cfg(not(debug_assertions))]
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
@ -65,7 +84,7 @@ mod cli_run {
} }
fn check_compile_error(file: &Path, flags: &[&str], expected: &str) { 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 = compile_out.stdout.trim();
let err = strip_colors(err); 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) { fn check_format_check_as_expected(file: &Path, expects_success_exit_code: bool) {
let flags = &["--check"]; let out = run_roc([CMD_FORMAT, file.to_str().unwrap(), CHECK_FLAG], &[]);
let out = run_roc(&[&["format", file.to_str().unwrap()], &flags[..]].concat());
if expects_success_exit_code { assert_eq!(out.status.success(), expects_success_exit_code);
assert!(out.status.success());
} else {
assert!(!out.status.success());
}
} }
fn build_example(file: &Path, flags: &[&str]) -> Out { fn run_roc_on<'a, I: IntoIterator<Item = &'a str>>(
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat()); file: &'a Path,
if !compile_out.stderr.is_empty() { args: I,
panic!("roc build had stderr: {}", compile_out.stderr); 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); assert!(compile_out.status.success(), "bad status {:?}", compile_out);
@ -106,27 +147,33 @@ mod cli_run {
expected_ending: &str, expected_ending: &str,
use_valgrind: bool, use_valgrind: bool,
) { ) {
let mut all_flags = vec![]; for cli_mode in CliMode::iter() {
all_flags.extend_from_slice(flags); let flags = {
let mut vec = flags.to_vec();
if use_valgrind { if use_valgrind {
all_flags.extend_from_slice(&["--valgrind"]); vec.push(VALGRIND_FLAG);
} }
build_example(file, &all_flags[..]); vec.into_iter()
};
let out = if use_valgrind && ALLOW_VALGRIND { let out = match cli_mode {
let (valgrind_out, raw_xml) = if let Some(input_file) = input_file { CliMode::RocBuild => {
run_roc_on(file, iter::once(CMD_BUILD).chain(flags.clone()), &[], None);
if use_valgrind && ALLOW_VALGRIND {
let (valgrind_out, raw_xml) = if let Some(ref input_file) = input_file {
run_with_valgrind( run_with_valgrind(
stdin, stdin.clone().iter().copied(),
&[ &[
file.with_file_name(executable_filename).to_str().unwrap(), file.with_file_name(executable_filename).to_str().unwrap(),
input_file.to_str().unwrap(), input_file.clone().to_str().unwrap(),
], ],
) )
} else { } else {
run_with_valgrind( run_with_valgrind(
stdin, stdin.clone().iter().copied(),
&[file.with_file_name(executable_filename).to_str().unwrap()], &[file.with_file_name(executable_filename).to_str().unwrap()],
) )
}; };
@ -166,27 +213,39 @@ mod cli_run {
} }
valgrind_out valgrind_out
} else if let Some(input_file) = input_file { } else if let Some(ref input_file) = input_file {
run_cmd( run_cmd(
file.with_file_name(executable_filename).to_str().unwrap(), file.with_file_name(executable_filename).to_str().unwrap(),
stdin, stdin.iter().copied(),
&[input_file.to_str().unwrap()], &[input_file.to_str().unwrap()],
) )
} else { } else {
run_cmd( run_cmd(
file.with_file_name(executable_filename).to_str().unwrap(), file.with_file_name(executable_filename).to_str().unwrap(),
stdin, 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) { if !&out.stdout.ends_with(expected_ending) {
panic!( panic!(
"expected output to end with {:?} but instead got {:#?} - stderr was: {:#?}", "expected output to end with {:?} but instead got {:#?} - stderr was: {:#?}",
expected_ending, out.stdout, out.stderr expected_ending, out.stdout, out.stderr
); );
} }
assert!(out.status.success()); assert!(out.status.success());
} }
}
#[cfg(feature = "wasm32-cli-run")] #[cfg(feature = "wasm32-cli-run")]
fn check_wasm_output_with_stdin( fn check_wasm_output_with_stdin(
@ -199,9 +258,13 @@ mod cli_run {
) { ) {
assert_eq!(input_file, None, "Wasm does not support input files"); assert_eq!(input_file, None, "Wasm does not support input files");
let mut flags = flags.to_vec(); 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() { if !compile_out.stderr.is_empty() {
panic!("{}", compile_out.stderr); panic!("{}", compile_out.stderr);
} }
@ -258,7 +321,7 @@ mod cli_run {
} }
"hello-gui" | "breakout" => { "hello-gui" | "breakout" => {
// Since these require opening a window, we do `roc build` on them but don't run them. // 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; return;
} }
@ -283,7 +346,7 @@ mod cli_run {
&file_name, &file_name,
example.stdin, example.stdin,
example.executable_filename, example.executable_filename,
&["--optimize"], &[OPTIMIZE_FLAG],
example.input_file.and_then(|file| Some(example_file(dir_name, file))), example.input_file.and_then(|file| Some(example_file(dir_name, file))),
example.expected_ending, example.expected_ending,
example.use_valgrind, example.use_valgrind,
@ -296,7 +359,7 @@ mod cli_run {
&file_name, &file_name,
example.stdin, example.stdin,
example.executable_filename, example.executable_filename,
&["--linker", "legacy"], &[LINKER_FLAG, "legacy"],
example.input_file.and_then(|file| Some(example_file(dir_name, file))), example.input_file.and_then(|file| Some(example_file(dir_name, file))),
example.expected_ending, example.expected_ending,
example.use_valgrind, example.use_valgrind,
@ -513,7 +576,7 @@ mod cli_run {
&file_name, &file_name,
benchmark.stdin, benchmark.stdin,
benchmark.executable_filename, benchmark.executable_filename,
&["--optimize"], &[OPTIMIZE_FLAG],
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))), benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
benchmark.expected_ending, benchmark.expected_ending,
benchmark.use_valgrind, benchmark.use_valgrind,
@ -555,7 +618,7 @@ mod cli_run {
&file_name, &file_name,
benchmark.stdin, benchmark.stdin,
benchmark.executable_filename, benchmark.executable_filename,
&["--optimize"], &[OPTIMIZE_FLAG],
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))), benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
benchmark.expected_ending, benchmark.expected_ending,
); );
@ -587,7 +650,7 @@ mod cli_run {
&file_name, &file_name,
benchmark.stdin, benchmark.stdin,
benchmark.executable_filename, benchmark.executable_filename,
&["--target=x86_32"], [concatcp!(TARGET_FLAG, "=x86_32")],
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))), benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
benchmark.expected_ending, benchmark.expected_ending,
benchmark.use_valgrind, benchmark.use_valgrind,
@ -597,7 +660,7 @@ mod cli_run {
&file_name, &file_name,
benchmark.stdin, benchmark.stdin,
benchmark.executable_filename, 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.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
benchmark.expected_ending, benchmark.expected_ending,
benchmark.use_valgrind, benchmark.use_valgrind,
@ -816,7 +879,7 @@ mod cli_run {
&fixture_file("multi-dep-str", "Main.roc"), &fixture_file("multi-dep-str", "Main.roc"),
&[], &[],
"multi-dep-str", "multi-dep-str",
&["--optimize"], &[OPTIMIZE_FLAG],
None, None,
"I am Dep2.str2\n", "I am Dep2.str2\n",
true, true,
@ -844,7 +907,7 @@ mod cli_run {
&fixture_file("multi-dep-thunk", "Main.roc"), &fixture_file("multi-dep-thunk", "Main.roc"),
&[], &[],
"multi-dep-thunk", "multi-dep-thunk",
&["--optimize"], &[OPTIMIZE_FLAG],
None, None,
"I am Dep2.value2\n", "I am Dep2.value2\n",
true, true,

View file

@ -20,6 +20,7 @@ serde = { version = "1.0.130", features = ["derive"] }
serde-xml-rs = "0.5.1" serde-xml-rs = "0.5.1"
strip-ansi-escapes = "0.1.1" strip-ansi-escapes = "0.1.1"
tempfile = "3.2.0" tempfile = "3.2.0"
const_format = "0.2.22"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
rlimit = "0.6.2" rlimit = "0.6.2"

View file

@ -1,9 +1,13 @@
use crate::helpers::{example_file, run_cmd, run_roc}; use crate::helpers::{example_file, run_cmd, run_roc};
use const_format::concatcp;
use criterion::{black_box, measurement::Measurement, BenchmarkGroup}; use criterion::{black_box, measurement::Measurement, BenchmarkGroup};
use roc_cli::CMD_BUILD;
use std::{path::Path, thread}; use std::{path::Path, thread};
const CFOLD_STACK_SIZE: usize = 8192 * 100000; const CFOLD_STACK_SIZE: usize = 8192 * 100000;
const OPTIMIZE_FLAG: &str = concatcp!("--", roc_cli::FLAG_OPTIMIZE);
fn exec_bench_w_input<T: Measurement>( fn exec_bench_w_input<T: Measurement>(
file: &Path, file: &Path,
stdin_str: &'static str, stdin_str: &'static str,
@ -11,9 +15,10 @@ fn exec_bench_w_input<T: Measurement>(
expected_ending: &str, expected_ending: &str,
bench_group_opt: Option<&mut BenchmarkGroup<T>>, bench_group_opt: Option<&mut BenchmarkGroup<T>>,
) { ) {
let flags: &[&str] = &["--optimize"]; let compile_out = run_roc(
[CMD_BUILD, OPTIMIZE_FLAG, file.to_str().unwrap()],
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat()); &[stdin_str],
);
if !compile_out.stderr.is_empty() { if !compile_out.stderr.is_empty() {
panic!("{}", compile_out.stderr); panic!("{}", compile_out.stderr);
@ -45,12 +50,12 @@ fn check_cmd_output(
let out = if cmd_str.contains("cfold") { let out = if cmd_str.contains("cfold") {
let child = thread::Builder::new() let child = thread::Builder::new()
.stack_size(CFOLD_STACK_SIZE) .stack_size(CFOLD_STACK_SIZE)
.spawn(move || run_cmd(&cmd_str, &[stdin_str], &[])) .spawn(move || run_cmd(&cmd_str, [stdin_str], &[]))
.unwrap(); .unwrap();
child.join().unwrap() child.join().unwrap()
} else { } else {
run_cmd(&cmd_str, &[stdin_str], &[]) run_cmd(&cmd_str, [stdin_str], &[])
}; };
if !&out.stdout.ends_with(expected_ending) { if !&out.stdout.ends_with(expected_ending) {
@ -93,12 +98,12 @@ fn bench_cmd<T: Measurement>(
if let Some(bench_group) = bench_group_opt { if let Some(bench_group) = bench_group_opt {
bench_group.bench_function(&format!("Benchmarking {:?}", executable_filename), |b| { bench_group.bench_function(&format!("Benchmarking {:?}", executable_filename), |b| {
b.iter(|| run_cmd(black_box(&cmd_str), black_box(&[stdin_str]), &[])) b.iter(|| run_cmd(black_box(&cmd_str), black_box([stdin_str]), &[]))
}); });
} else { } else {
run_cmd( run_cmd(
black_box(file.with_file_name(executable_filename).to_str().unwrap()), black_box(file.with_file_name(executable_filename).to_str().unwrap()),
black_box(&[stdin_str]), black_box([stdin_str]),
&[], &[],
); );
} }

View file

@ -7,6 +7,7 @@ extern crate tempfile;
use serde::Deserialize; use serde::Deserialize;
use serde_xml_rs::from_str; use serde_xml_rs::from_str;
use std::env; use std::env;
use std::ffi::OsStr;
use std::io::Read; use std::io::Read;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
@ -44,18 +45,34 @@ pub fn path_to_roc_binary() -> PathBuf {
path path
} }
#[allow(dead_code)] pub fn run_roc<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(args: I, stdin_vals: &[&str]) -> Out {
pub fn run_roc(args: &[&str]) -> Out {
let mut cmd = Command::new(path_to_roc_binary()); let mut cmd = Command::new(path_to_roc_binary());
for arg in args { for arg in args {
cmd.arg(arg); cmd.arg(arg);
} }
let output = cmd let mut child = cmd
.output() .stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("failed to execute compiled `roc` binary in CLI test"); .expect("failed to execute compiled `roc` binary in CLI test");
{
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
for stdin_str in stdin_vals.iter() {
stdin
.write_all(stdin_str.as_bytes())
.expect("Failed to write to stdin");
}
}
let output = child
.wait_with_output()
.expect("failed to get output for compiled `roc` binary in CLI test");
Out { Out {
stdout: String::from_utf8(output.stdout).unwrap(), stdout: String::from_utf8(output.stdout).unwrap(),
stderr: String::from_utf8(output.stderr).unwrap(), stderr: String::from_utf8(output.stderr).unwrap(),
@ -63,8 +80,11 @@ pub fn run_roc(args: &[&str]) -> Out {
} }
} }
#[allow(dead_code)] pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>>(
pub fn run_cmd(cmd_name: &str, stdin_vals: &[&str], args: &[&str]) -> Out { cmd_name: &str,
stdin_vals: I,
args: &[&str],
) -> Out {
let mut cmd = Command::new(cmd_name); let mut cmd = Command::new(cmd_name);
for arg in args { for arg in args {
@ -99,8 +119,10 @@ pub fn run_cmd(cmd_name: &str, stdin_vals: &[&str], args: &[&str]) -> Out {
} }
} }
#[allow(dead_code)] pub fn run_with_valgrind<'a, I: IntoIterator<Item = &'a str>>(
pub fn run_with_valgrind(stdin_vals: &[&str], args: &[&str]) -> (Out, String) { stdin_vals: I,
args: &[&str],
) -> (Out, String) {
//TODO: figure out if there is a better way to get the valgrind executable. //TODO: figure out if there is a better way to get the valgrind executable.
let mut cmd = Command::new("valgrind"); let mut cmd = Command::new("valgrind");
let named_tempfile = let named_tempfile =

View file

@ -14,8 +14,6 @@ roc_module = { path = "../module" }
roc_parse = { path = "../parse" } roc_parse = { path = "../parse" }
roc_problem = { path = "../problem" } roc_problem = { path = "../problem" }
roc_types = { path = "../types" } roc_types = { path = "../types" }
roc_builtins = { path = "../builtins" }
ven_graph = { path = "../../vendor/pathfinding" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
static_assertions = "1.1.0" static_assertions = "1.1.0"
bitvec = "1" bitvec = "1"

View file

@ -4,6 +4,3 @@ version = "0.1.0"
authors = ["The Roc Contributors"] authors = ["The Roc Contributors"]
license = "UPL-1.0" license = "UPL-1.0"
edition = "2018" edition = "2018"
[dependencies]
arrayvec = "0.7.2"

View file

@ -22,14 +22,12 @@ roc_mono = { path = "../mono" }
roc_target = { path = "../roc_target" } roc_target = { path = "../roc_target" }
roc_reporting = { path = "../../reporting" } roc_reporting = { path = "../../reporting" }
roc_debug_flags = { path = "../debug_flags" } roc_debug_flags = { path = "../debug_flags" }
morphic_lib = { path = "../../vendor/morphic_lib" }
ven_pretty = { path = "../../vendor/pretty" } ven_pretty = { path = "../../vendor/pretty" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
parking_lot = "0.12" parking_lot = "0.12"
crossbeam = "0.8.1" crossbeam = "0.8.1"
[dev-dependencies] [dev-dependencies]
tempfile = "3.2.0"
pretty_assertions = "1.0.0" pretty_assertions = "1.0.0"
maplit = "1.0.2" maplit = "1.0.2"
indoc = "1.0.3" indoc = "1.0.3"

View file

@ -14,4 +14,3 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
static_assertions = "1.1.0" static_assertions = "1.1.0"
snafu = { version = "0.6.10", features = ["backtraces"] } snafu = { version = "0.6.10", features = ["backtraces"] }
arrayvec = "0.7.2"

View file

@ -21,7 +21,6 @@ roc_target = { path = "../roc_target" }
roc_error_macros = {path="../../error_macros"} roc_error_macros = {path="../../error_macros"}
roc_debug_flags = {path="../debug_flags"} roc_debug_flags = {path="../debug_flags"}
ven_pretty = { path = "../../vendor/pretty" } ven_pretty = { path = "../../vendor/pretty" }
morphic_lib = { path = "../../vendor/morphic_lib" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
hashbrown = { version = "0.11.2", features = [ "bumpalo" ] } hashbrown = { version = "0.11.2", features = [ "bumpalo" ] }
ven_graph = { path = "../../vendor/pathfinding" } ven_graph = { path = "../../vendor/pathfinding" }

View file

@ -19,6 +19,5 @@ roc_mono = { path = "../mono" }
roc_target = { path = "../roc_target" } roc_target = { path = "../roc_target" }
roc_reporting = { path = "../../reporting" } roc_reporting = { path = "../../reporting" }
test_mono_macros = { path = "../test_mono_macros" } test_mono_macros = { path = "../test_mono_macros" }
pretty_assertions = "1.0.0"
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
indoc = "1.0.3" indoc = "1.0.3"

View file

@ -28,6 +28,3 @@ peg = "0.8.0"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.0.0" pretty_assertions = "1.0.0"
tempfile = "3.2.0"
uuid = { version = "0.8.2", features = ["v4"] }
indoc = "1.0.3"

View file

@ -74,7 +74,9 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_main() -> i32 { pub extern "C" fn rust_main() -> i32 {
let arg = env::args().nth(1).unwrap(); let arg = env::args()
.nth(1)
.expect("Please pass a .false file as a command-line argument to the false interpreter!");
let arg = RocStr::from(arg.as_str()); let arg = RocStr::from(arg.as_str());
let size = unsafe { roc_main_size() } as usize; let size = unsafe { roc_main_size() } as usize;

View file

@ -29,4 +29,3 @@ roc_target = { path = "../compiler/roc_target" }
roc_test_utils = { path = "../test_utils" } roc_test_utils = { path = "../test_utils" }
pretty_assertions = "1.0.0" pretty_assertions = "1.0.0"
indoc = "1.0.3" indoc = "1.0.3"
tempfile = "3.2.0"