mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
Merge remote-tracking branch 'origin/trunk' into cli-max-threads
This commit is contained in:
commit
51ee101bd1
17 changed files with 905 additions and 784 deletions
1130
Cargo.lock
generated
1130
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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]
|
||||||
|
|
180
cli/src/lib.rs
180
cli/src/lib.rs
|
@ -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.",
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// 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,10 +557,8 @@ 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!
|
||||||
|
@ -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)]
|
||||||
|
|
|
@ -27,43 +27,31 @@ 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) => {
|
build(
|
||||||
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
&matches,
|
||||||
|
BuildConfig::BuildAndRunIfNoErrors,
|
||||||
|
Triple::host(),
|
||||||
|
LinkType::Executable,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
launch_editor(None)?;
|
||||||
|
|
||||||
build(
|
Ok(0)
|
||||||
&matches,
|
|
||||||
BuildConfig::BuildAndRunIfNoErrors { roc_file_arg_index },
|
|
||||||
Triple::host(),
|
|
||||||
LinkType::Executable,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
None => {
|
|
||||||
launch_editor(None)?;
|
|
||||||
|
|
||||||
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) => {
|
build(
|
||||||
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
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(
|
Ok(1)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some((CMD_BUILD, matches)) => {
|
Some((CMD_BUILD, matches)) => {
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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,86 +147,104 @@ 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 (valgrind_out, raw_xml) = if let Some(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()],
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if valgrind_out.status.success() {
|
let out = match cli_mode {
|
||||||
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
|
CliMode::RocBuild => {
|
||||||
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);
|
run_roc_on(file, iter::once(CMD_BUILD).chain(flags.clone()), &[], None);
|
||||||
});
|
|
||||||
|
|
||||||
if !memory_errors.is_empty() {
|
if use_valgrind && ALLOW_VALGRIND {
|
||||||
for error in memory_errors {
|
let (valgrind_out, raw_xml) = if let Some(ref input_file) = input_file {
|
||||||
let ValgrindError {
|
run_with_valgrind(
|
||||||
kind,
|
stdin.clone().iter().copied(),
|
||||||
what: _,
|
&[
|
||||||
xwhat,
|
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||||
} = error;
|
input_file.clone().to_str().unwrap(),
|
||||||
println!("Valgrind Error: {}\n", kind);
|
],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
run_with_valgrind(
|
||||||
|
stdin.clone().iter().copied(),
|
||||||
|
&[file.with_file_name(executable_filename).to_str().unwrap()],
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(ValgrindErrorXWhat {
|
if valgrind_out.status.success() {
|
||||||
text,
|
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
|
||||||
leakedbytes: _,
|
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);
|
||||||
leakedblocks: _,
|
});
|
||||||
}) = xwhat
|
|
||||||
{
|
if !memory_errors.is_empty() {
|
||||||
println!(" {}", text);
|
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");
|
|
||||||
}
|
|
||||||
} 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
|
assert!(out.status.success());
|
||||||
} 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wasm32-cli-run")]
|
#[cfg(feature = "wasm32-cli-run")]
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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]),
|
||||||
&[],
|
&[],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue