mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-13 07:16:18 +00:00
Merge remote-tracking branch 'origin/main' into expect-fx-codegen
This commit is contained in:
commit
a22e04361c
222 changed files with 10039 additions and 1945 deletions
|
@ -3,7 +3,7 @@ name = "roc_cli"
|
|||
version = "0.1.0"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
repository = "https://github.com/rtfeldman/roc"
|
||||
repository = "https://github.com/roc-lang/roc"
|
||||
edition = "2021"
|
||||
description = "A CLI for Roc"
|
||||
default-run = "roc"
|
||||
|
@ -88,8 +88,8 @@ wasmer = { version = "2.2.1", optional = true, default-features = false, feature
|
|||
wasmer-wasi = "2.2.1"
|
||||
pretty_assertions = "1.0.0"
|
||||
roc_test_utils = { path = "../test_utils" }
|
||||
indoc = "1.0.3"
|
||||
serial_test = "0.8.0"
|
||||
indoc = "1.0.7"
|
||||
serial_test = "0.9.0"
|
||||
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
||||
cli_utils = { path = "../cli_utils" }
|
||||
strum = "0.24.0"
|
||||
|
|
|
@ -5,7 +5,10 @@ use roc_build::{
|
|||
};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_collections::VecMap;
|
||||
use roc_load::{EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadingProblem, Threading};
|
||||
use roc_load::{
|
||||
EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadMonomorphizedError, LoadedModule,
|
||||
LoadingProblem, Threading,
|
||||
};
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_reporting::report::RenderTarget;
|
||||
|
@ -35,6 +38,23 @@ pub struct BuiltFile {
|
|||
pub interns: Interns,
|
||||
}
|
||||
|
||||
pub enum BuildOrdering {
|
||||
/// Run up through typechecking first; continue building iff that is successful.
|
||||
BuildIfChecks,
|
||||
/// Always build the Roc binary, even if there are type errors.
|
||||
AlwaysBuild,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum BuildFileError<'a> {
|
||||
LoadingProblem(LoadingProblem<'a>),
|
||||
ErrorModule {
|
||||
module: LoadedModule,
|
||||
total_time: Duration,
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_file<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -46,29 +66,46 @@ pub fn build_file<'a>(
|
|||
link_type: LinkType,
|
||||
linking_strategy: LinkingStrategy,
|
||||
precompiled: bool,
|
||||
target_valgrind: bool,
|
||||
threading: Threading,
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
) -> Result<BuiltFile, LoadingProblem<'a>> {
|
||||
order: BuildOrdering,
|
||||
) -> Result<BuiltFile, BuildFileError<'a>> {
|
||||
let compilation_start = Instant::now();
|
||||
let target_info = TargetInfo::from(target);
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let subs_by_module = Default::default();
|
||||
|
||||
let exec_mode = match order {
|
||||
BuildOrdering::BuildIfChecks => ExecutionMode::ExecutableIfCheck,
|
||||
BuildOrdering::AlwaysBuild => ExecutionMode::Executable,
|
||||
};
|
||||
|
||||
let load_config = LoadConfig {
|
||||
target_info,
|
||||
// TODO: expose this from CLI?
|
||||
render: RenderTarget::ColorTerminal,
|
||||
threading,
|
||||
exec_mode: ExecutionMode::Executable,
|
||||
exec_mode,
|
||||
};
|
||||
let loaded = roc_load::load_and_monomorphize(
|
||||
let load_result = roc_load::load_and_monomorphize(
|
||||
arena,
|
||||
app_module_path.clone(),
|
||||
subs_by_module,
|
||||
load_config,
|
||||
)?;
|
||||
);
|
||||
let loaded = match load_result {
|
||||
Ok(loaded) => loaded,
|
||||
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
|
||||
return Err(BuildFileError::LoadingProblem(problem))
|
||||
}
|
||||
Err(LoadMonomorphizedError::ErrorModule(module)) => {
|
||||
return Err(BuildFileError::ErrorModule {
|
||||
module,
|
||||
total_time: compilation_start.elapsed(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
use target_lexicon::Architecture;
|
||||
let emit_wasm = matches!(target.architecture, Architecture::Wasm32);
|
||||
|
@ -152,7 +189,6 @@ pub fn build_file<'a>(
|
|||
target,
|
||||
exposed_values,
|
||||
exposed_closure_types,
|
||||
target_valgrind,
|
||||
);
|
||||
|
||||
// TODO try to move as much of this linking as possible to the precompiled
|
||||
|
@ -161,9 +197,7 @@ pub fn build_file<'a>(
|
|||
.prefix("roc_app")
|
||||
.suffix(&format!(".{}", app_extension))
|
||||
.tempfile()
|
||||
.map_err(|err| {
|
||||
todo!("TODO Gracefully handle tempfile creation error {:?}", err);
|
||||
})?;
|
||||
.map_err(|err| todo!("TODO Gracefully handle tempfile creation error {:?}", err))?;
|
||||
let app_o_file = app_o_file.path();
|
||||
let buf = &mut String::with_capacity(1024);
|
||||
|
||||
|
@ -329,12 +363,12 @@ pub fn build_file<'a>(
|
|||
link_type
|
||||
)
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle `ld` failing to spawn.");
|
||||
todo!("gracefully handle `ld` failing to spawn.")
|
||||
})?;
|
||||
|
||||
let exit_status = child.wait().map_err(|_| {
|
||||
todo!("gracefully handle error after `ld` spawned");
|
||||
})?;
|
||||
let exit_status = child
|
||||
.wait()
|
||||
.map_err(|_| todo!("gracefully handle error after `ld` spawned"))?;
|
||||
|
||||
if exit_status.success() {
|
||||
problems
|
||||
|
@ -377,7 +411,6 @@ fn spawn_rebuild_thread(
|
|||
target: &Triple,
|
||||
exported_symbols: Vec<String>,
|
||||
exported_closure_types: Vec<String>,
|
||||
target_valgrind: bool,
|
||||
) -> std::thread::JoinHandle<u128> {
|
||||
let thread_local_target = target.clone();
|
||||
std::thread::spawn(move || {
|
||||
|
@ -395,7 +428,6 @@ fn spawn_rebuild_thread(
|
|||
&thread_local_target,
|
||||
host_input_path.as_path(),
|
||||
None,
|
||||
target_valgrind,
|
||||
);
|
||||
|
||||
preprocess_host_wasm32(host_dest.as_path(), &preprocessed_host_path);
|
||||
|
@ -408,7 +440,6 @@ fn spawn_rebuild_thread(
|
|||
preprocessed_host_path.as_path(),
|
||||
exported_symbols,
|
||||
exported_closure_types,
|
||||
target_valgrind,
|
||||
);
|
||||
}
|
||||
LinkingStrategy::Legacy => {
|
||||
|
@ -417,7 +448,6 @@ fn spawn_rebuild_thread(
|
|||
&thread_local_target,
|
||||
host_input_path.as_path(),
|
||||
None,
|
||||
target_valgrind,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,10 +31,13 @@ pub mod build;
|
|||
mod format;
|
||||
pub use format::format;
|
||||
|
||||
use crate::build::{BuildFileError, BuildOrdering};
|
||||
|
||||
const DEFAULT_ROC_FILENAME: &str = "main.roc";
|
||||
|
||||
pub const CMD_BUILD: &str = "build";
|
||||
pub const CMD_RUN: &str = "run";
|
||||
pub const CMD_DEV: &str = "dev";
|
||||
pub const CMD_REPL: &str = "repl";
|
||||
pub const CMD_EDIT: &str = "edit";
|
||||
pub const CMD_DOCS: &str = "docs";
|
||||
|
@ -55,7 +58,6 @@ pub const FLAG_TARGET: &str = "target";
|
|||
pub const FLAG_TIME: &str = "time";
|
||||
pub const FLAG_LINKER: &str = "linker";
|
||||
pub const FLAG_PRECOMPILED: &str = "precompiled-host";
|
||||
pub const FLAG_VALGRIND: &str = "valgrind";
|
||||
pub const FLAG_CHECK: &str = "check";
|
||||
pub const FLAG_WASM_STACK_SIZE_KB: &str = "wasm-stack-size-kb";
|
||||
pub const ROC_FILE: &str = "ROC_FILE";
|
||||
|
@ -94,11 +96,6 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.help("Store LLVM debug information in the generated program.")
|
||||
.required(false);
|
||||
|
||||
let flag_valgrind = Arg::new(FLAG_VALGRIND)
|
||||
.long(FLAG_VALGRIND)
|
||||
.help("Some assembly instructions are not supported by valgrind, this flag prevents those from being output when building the host.")
|
||||
.required(false);
|
||||
|
||||
let flag_time = Arg::new(FLAG_TIME)
|
||||
.long(FLAG_TIME)
|
||||
.help("Prints detailed compilation time information.")
|
||||
|
@ -150,7 +147,6 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.arg(flag_time.clone())
|
||||
.arg(flag_linker.clone())
|
||||
.arg(flag_precompiled.clone())
|
||||
.arg(flag_valgrind.clone())
|
||||
.arg(flag_wasm_stack_size_kb.clone())
|
||||
.arg(
|
||||
Arg::new(FLAG_TARGET)
|
||||
|
@ -190,7 +186,6 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.arg(flag_time.clone())
|
||||
.arg(flag_linker.clone())
|
||||
.arg(flag_precompiled.clone())
|
||||
.arg(flag_valgrind.clone())
|
||||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file for the main module")
|
||||
|
@ -213,7 +208,19 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.arg(flag_time.clone())
|
||||
.arg(flag_linker.clone())
|
||||
.arg(flag_precompiled.clone())
|
||||
.arg(flag_valgrind.clone())
|
||||
.arg(roc_file_to_run.clone())
|
||||
.arg(args_for_app.clone())
|
||||
)
|
||||
.subcommand(Command::new(CMD_DEV)
|
||||
.about("`check` a .roc file, and then run it if there were no errors.")
|
||||
.arg(flag_optimize.clone())
|
||||
.arg(flag_max_threads.clone())
|
||||
.arg(flag_opt_size.clone())
|
||||
.arg(flag_dev.clone())
|
||||
.arg(flag_debug.clone())
|
||||
.arg(flag_time.clone())
|
||||
.arg(flag_linker.clone())
|
||||
.arg(flag_precompiled.clone())
|
||||
.arg(roc_file_to_run.clone())
|
||||
.arg(args_for_app.clone())
|
||||
)
|
||||
|
@ -280,7 +287,6 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.arg(flag_time)
|
||||
.arg(flag_linker)
|
||||
.arg(flag_precompiled)
|
||||
.arg(flag_valgrind)
|
||||
.arg(roc_file_to_run.required(false))
|
||||
.arg(args_for_app);
|
||||
|
||||
|
@ -519,7 +525,10 @@ pub fn build(
|
|||
.and_then(|s| s.parse::<u32>().ok())
|
||||
.map(|x| x * 1024);
|
||||
|
||||
let target_valgrind = matches.is_present(FLAG_VALGRIND);
|
||||
let build_ordering = match config {
|
||||
BuildAndRunIfNoErrors => BuildOrdering::BuildIfChecks,
|
||||
_ => BuildOrdering::AlwaysBuild,
|
||||
};
|
||||
let res_binary_path = build_file(
|
||||
&arena,
|
||||
&triple,
|
||||
|
@ -530,9 +539,9 @@ pub fn build(
|
|||
link_type,
|
||||
linking_strategy,
|
||||
precompiled,
|
||||
target_valgrind,
|
||||
threading,
|
||||
wasm_dev_stack_bytes,
|
||||
build_ordering,
|
||||
);
|
||||
|
||||
match res_binary_path {
|
||||
|
@ -633,55 +642,13 @@ pub fn build(
|
|||
x
|
||||
}
|
||||
BuildAndRunIfNoErrors => {
|
||||
if problems.errors == 0 {
|
||||
if problems.warnings > 0 {
|
||||
println!(
|
||||
"\x1B[32m0\x1B[39m errors and \x1B[33m{}\x1B[39m {} found in {} ms.\n\nRunning program…\n\n\x1B[36m{}\x1B[39m",
|
||||
problems.warnings,
|
||||
if problems.warnings == 1 {
|
||||
"warning"
|
||||
} else {
|
||||
"warnings"
|
||||
},
|
||||
total_time.as_millis(),
|
||||
"─".repeat(80)
|
||||
);
|
||||
}
|
||||
|
||||
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
|
||||
|
||||
let mut bytes = std::fs::read(&binary_path).unwrap();
|
||||
|
||||
let x = roc_run(
|
||||
arena,
|
||||
opt_level,
|
||||
triple,
|
||||
args,
|
||||
&mut bytes,
|
||||
expectations,
|
||||
interns,
|
||||
);
|
||||
std::mem::forget(bytes);
|
||||
x
|
||||
} else {
|
||||
let mut output = format!(
|
||||
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with \x1B[32mroc run",
|
||||
if problems.errors == 0 {
|
||||
32 // green
|
||||
} else {
|
||||
33 // yellow
|
||||
},
|
||||
problems.errors,
|
||||
if problems.errors == 1 {
|
||||
"error"
|
||||
} else {
|
||||
"errors"
|
||||
},
|
||||
if problems.warnings == 0 {
|
||||
32 // green
|
||||
} else {
|
||||
33 // yellow
|
||||
},
|
||||
debug_assert!(
|
||||
problems.errors == 0,
|
||||
"if there are errors, they should have been returned as an error variant"
|
||||
);
|
||||
if problems.warnings > 0 {
|
||||
println!(
|
||||
"\x1B[32m0\x1B[39m errors and \x1B[33m{}\x1B[39m {} found in {} ms.\n\nRunning program…\n\n\x1B[36m{}\x1B[39m",
|
||||
problems.warnings,
|
||||
if problems.warnings == 1 {
|
||||
"warning"
|
||||
|
@ -689,22 +656,74 @@ pub fn build(
|
|||
"warnings"
|
||||
},
|
||||
total_time.as_millis(),
|
||||
"─".repeat(80)
|
||||
);
|
||||
// If you're running "main.roc" then you can just do `roc run`
|
||||
// to re-run the program.
|
||||
if filename != DEFAULT_ROC_FILENAME {
|
||||
output.push(' ');
|
||||
output.push_str(&filename.to_string_lossy());
|
||||
}
|
||||
|
||||
println!("{}\x1B[39m", output);
|
||||
|
||||
Ok(problems.exit_code())
|
||||
}
|
||||
|
||||
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
|
||||
|
||||
let mut bytes = std::fs::read(&binary_path).unwrap();
|
||||
|
||||
let x = roc_run(
|
||||
arena,
|
||||
opt_level,
|
||||
triple,
|
||||
args,
|
||||
&mut bytes,
|
||||
expectations,
|
||||
interns,
|
||||
);
|
||||
std::mem::forget(bytes);
|
||||
x
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(LoadingProblem::FormattedReport(report)) => {
|
||||
Err(BuildFileError::ErrorModule {
|
||||
mut module,
|
||||
total_time,
|
||||
}) => {
|
||||
debug_assert!(module.total_problems() > 0);
|
||||
|
||||
let problems = roc_build::program::report_problems_typechecked(&mut module);
|
||||
|
||||
let mut output = format!(
|
||||
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with \x1B[32mroc run",
|
||||
if problems.errors == 0 {
|
||||
32 // green
|
||||
} else {
|
||||
33 // yellow
|
||||
},
|
||||
problems.errors,
|
||||
if problems.errors == 1 {
|
||||
"error"
|
||||
} else {
|
||||
"errors"
|
||||
},
|
||||
if problems.warnings == 0 {
|
||||
32 // green
|
||||
} else {
|
||||
33 // yellow
|
||||
},
|
||||
problems.warnings,
|
||||
if problems.warnings == 1 {
|
||||
"warning"
|
||||
} else {
|
||||
"warnings"
|
||||
},
|
||||
total_time.as_millis(),
|
||||
);
|
||||
// If you're running "main.roc" then you can just do `roc run`
|
||||
// to re-run the program.
|
||||
if filename != DEFAULT_ROC_FILENAME {
|
||||
output.push(' ');
|
||||
output.push_str(&filename.to_string_lossy());
|
||||
}
|
||||
|
||||
println!("{}\x1B[39m", output);
|
||||
|
||||
Ok(problems.exit_code())
|
||||
}
|
||||
Err(BuildFileError::LoadingProblem(LoadingProblem::FormattedReport(report))) => {
|
||||
print!("{}", report);
|
||||
|
||||
Ok(1)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use roc_build::link::LinkType;
|
||||
use roc_cli::build::check_file;
|
||||
use roc_cli::{
|
||||
build_app, format, test, BuildConfig, FormatMode, Target, CMD_BUILD, CMD_CHECK, CMD_DOCS,
|
||||
CMD_EDIT, CMD_FORMAT, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES,
|
||||
FLAG_CHECK, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, GLUE_FILE, ROC_FILE,
|
||||
build_app, format, test, BuildConfig, FormatMode, Target, CMD_BUILD, CMD_CHECK, CMD_DEV,
|
||||
CMD_DOCS, CMD_EDIT, CMD_FORMAT, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION,
|
||||
DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, GLUE_FILE,
|
||||
ROC_FILE,
|
||||
};
|
||||
use roc_docs::generate_docs_html;
|
||||
use roc_error_macros::user_error;
|
||||
|
@ -64,6 +65,20 @@ fn main() -> io::Result<()> {
|
|||
Ok(1)
|
||||
}
|
||||
}
|
||||
Some((CMD_DEV, matches)) => {
|
||||
if matches.is_present(ROC_FILE) {
|
||||
build(
|
||||
matches,
|
||||
BuildConfig::BuildAndRunIfNoErrors,
|
||||
Triple::host(),
|
||||
LinkType::Executable,
|
||||
)
|
||||
} else {
|
||||
eprintln!("What .roc file do you want to build? Specify it at the end of the `roc run` command.");
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
Some((CMD_GLUE, matches)) => {
|
||||
let input_path = Path::new(matches.value_of_os(ROC_FILE).unwrap());
|
||||
let output_path = Path::new(matches.value_of_os(GLUE_FILE).unwrap());
|
||||
|
|
|
@ -25,7 +25,6 @@ mod cli_run {
|
|||
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);
|
||||
const PRECOMPILED_HOST: &str = concatcp!("--", roc_cli::FLAG_PRECOMPILED, "=true");
|
||||
|
@ -137,14 +136,17 @@ mod cli_run {
|
|||
expected_ending: &str,
|
||||
use_valgrind: bool,
|
||||
) {
|
||||
// valgrind does not yet support avx512 instructions, see #1963.
|
||||
// we can't enable this only when testing with valgrind because of host re-use between tests
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
if is_x86_feature_detected!("avx512f") {
|
||||
std::env::set_var("NO_AVX512", "1");
|
||||
}
|
||||
|
||||
for cli_mode in CliMode::iter() {
|
||||
let flags = {
|
||||
let mut vec = flags.to_vec();
|
||||
|
||||
if use_valgrind {
|
||||
vec.push(VALGRIND_FLAG);
|
||||
}
|
||||
|
||||
vec.push("--max-threads=1");
|
||||
|
||||
vec.into_iter()
|
||||
|
@ -295,13 +297,10 @@ mod cli_run {
|
|||
let file_name = example_file(dir_name, example.filename);
|
||||
|
||||
match example.executable_filename {
|
||||
"form" => {
|
||||
// test is skipped until we upgrate to zig 0.9 / llvm 13
|
||||
eprintln!("WARNING: skipping testing example {} because the test is broken right now!", example.filename);
|
||||
return;
|
||||
}
|
||||
"hello-gui" | "breakout" => {
|
||||
// Since these require opening a window, we do `roc build` on them but don't run them.
|
||||
"form" | "hello-gui" | "breakout" | "ruby" => {
|
||||
// Since these require things the build system often doesn't have
|
||||
// (e.g. GUIs open a window, Ruby needs ruby installed, WASM needs a browser)
|
||||
// we do `roc build` on them but don't run them.
|
||||
run_roc_on(&file_name, [CMD_BUILD, OPTIMIZE_FLAG], &[], None);
|
||||
return;
|
||||
}
|
||||
|
@ -443,6 +442,14 @@ mod cli_run {
|
|||
expected_ending:"Roc <3 Zig!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
ruby:"ruby-interop" => Example {
|
||||
filename: "main.roc",
|
||||
executable_filename: "libhello",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
expected_ending:"",
|
||||
use_valgrind: true,
|
||||
},
|
||||
fib:"algorithms" => Example {
|
||||
filename: "fibonacci.roc",
|
||||
executable_filename: "fibonacci",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue