Merge remote-tracking branch 'origin/main' into expect-fx-codegen

This commit is contained in:
Folkert 2022-08-23 16:28:21 +02:00
commit a22e04361c
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
222 changed files with 10039 additions and 1945 deletions

View file

@ -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,
);
}
}

View file

@ -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)

View file

@ -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());