mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
Add roc run
to run even if there are build errors.
This commit is contained in:
parent
a47b3be9c0
commit
62484d3890
4 changed files with 141 additions and 91 deletions
|
@ -1,7 +1,7 @@
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_build::{
|
use roc_build::{
|
||||||
link::{link, rebuild_host, LinkType},
|
link::{link, rebuild_host, LinkType},
|
||||||
program,
|
program::{self, Problems},
|
||||||
};
|
};
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
use roc_load::LoadingProblem;
|
use roc_load::LoadingProblem;
|
||||||
|
@ -21,25 +21,9 @@ fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BuildOutcome {
|
|
||||||
NoProblems,
|
|
||||||
OnlyWarnings,
|
|
||||||
Errors,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuildOutcome {
|
|
||||||
pub fn status_code(&self) -> i32 {
|
|
||||||
match self {
|
|
||||||
Self::NoProblems => 0,
|
|
||||||
Self::OnlyWarnings => 1,
|
|
||||||
Self::Errors => 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BuiltFile {
|
pub struct BuiltFile {
|
||||||
pub binary_path: PathBuf,
|
pub binary_path: PathBuf,
|
||||||
pub outcome: BuildOutcome,
|
pub problems: Problems,
|
||||||
pub total_time: Duration,
|
pub total_time: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +168,7 @@ pub fn build_file<'a>(
|
||||||
// This only needs to be mutable for report_problems. This can't be done
|
// This only needs to be mutable for report_problems. This can't be done
|
||||||
// inside a nested scope without causing a borrow error!
|
// inside a nested scope without causing a borrow error!
|
||||||
let mut loaded = loaded;
|
let mut loaded = loaded;
|
||||||
program::report_problems_monomorphized(&mut loaded);
|
let problems = program::report_problems_monomorphized(&mut loaded);
|
||||||
let loaded = loaded;
|
let loaded = loaded;
|
||||||
|
|
||||||
let code_gen_timing = program::gen_from_mono_module(
|
let code_gen_timing = program::gen_from_mono_module(
|
||||||
|
@ -243,7 +227,7 @@ pub fn build_file<'a>(
|
||||||
|
|
||||||
// Step 2: link the precompiled host and compiled app
|
// Step 2: link the precompiled host and compiled app
|
||||||
let link_start = SystemTime::now();
|
let link_start = SystemTime::now();
|
||||||
let outcome = if surgically_link {
|
let problems = if surgically_link {
|
||||||
roc_linker::link_preprocessed_host(target, &host_input_path, app_o_file, &binary_path)
|
roc_linker::link_preprocessed_host(target, &host_input_path, app_o_file, &binary_path)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
todo!(
|
todo!(
|
||||||
|
@ -251,12 +235,12 @@ pub fn build_file<'a>(
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
})?;
|
})?;
|
||||||
BuildOutcome::NoProblems
|
problems
|
||||||
} else if matches!(link_type, LinkType::None) {
|
} else if matches!(link_type, LinkType::None) {
|
||||||
// Just copy the object file to the output folder.
|
// Just copy the object file to the output folder.
|
||||||
binary_path.set_extension(app_extension);
|
binary_path.set_extension(app_extension);
|
||||||
std::fs::copy(app_o_file, &binary_path).unwrap();
|
std::fs::copy(app_o_file, &binary_path).unwrap();
|
||||||
BuildOutcome::NoProblems
|
problems
|
||||||
} else {
|
} else {
|
||||||
let mut inputs = vec![
|
let mut inputs = vec![
|
||||||
host_input_path.as_path().to_str().unwrap(),
|
host_input_path.as_path().to_str().unwrap(),
|
||||||
|
@ -281,11 +265,15 @@ pub fn build_file<'a>(
|
||||||
todo!("gracefully handle error after `ld` spawned");
|
todo!("gracefully handle error after `ld` spawned");
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// TODO change this to report whether there were errors or warnings!
|
|
||||||
if exit_status.success() {
|
if exit_status.success() {
|
||||||
BuildOutcome::NoProblems
|
problems
|
||||||
} else {
|
} else {
|
||||||
BuildOutcome::Errors
|
let mut problems = problems;
|
||||||
|
|
||||||
|
// Add an error for `ld` failing
|
||||||
|
problems.errors += 1;
|
||||||
|
|
||||||
|
problems
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let linking_time = link_start.elapsed().unwrap();
|
let linking_time = link_start.elapsed().unwrap();
|
||||||
|
@ -298,7 +286,7 @@ pub fn build_file<'a>(
|
||||||
|
|
||||||
Ok(BuiltFile {
|
Ok(BuiltFile {
|
||||||
binary_path,
|
binary_path,
|
||||||
outcome,
|
problems,
|
||||||
total_time,
|
total_time,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -350,10 +338,6 @@ fn spawn_rebuild_thread(
|
||||||
}
|
}
|
||||||
let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
|
let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
|
||||||
|
|
||||||
if !precompiled {
|
|
||||||
println!("Done!");
|
|
||||||
}
|
|
||||||
|
|
||||||
rebuild_host_end.as_millis()
|
rebuild_host_end.as_millis()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -364,7 +348,7 @@ pub fn check_file(
|
||||||
src_dir: PathBuf,
|
src_dir: PathBuf,
|
||||||
roc_file_path: PathBuf,
|
roc_file_path: PathBuf,
|
||||||
emit_timings: bool,
|
emit_timings: bool,
|
||||||
) -> Result<usize, LoadingProblem> {
|
) -> Result<program::Problems, LoadingProblem> {
|
||||||
let compilation_start = SystemTime::now();
|
let compilation_start = SystemTime::now();
|
||||||
|
|
||||||
// only used for generating errors. We don't do code generation, so hardcoding should be fine
|
// only used for generating errors. We don't do code generation, so hardcoding should be fine
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate const_format;
|
extern crate const_format;
|
||||||
|
|
||||||
use build::{BuildOutcome, BuiltFile};
|
use build::BuiltFile;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use clap::{App, AppSettings, Arg, ArgMatches};
|
use clap::{App, AppSettings, Arg, ArgMatches};
|
||||||
use roc_build::link::LinkType;
|
use roc_build::link::LinkType;
|
||||||
|
@ -24,6 +24,7 @@ mod format;
|
||||||
pub use format::format;
|
pub use format::format;
|
||||||
|
|
||||||
pub const CMD_BUILD: &str = "build";
|
pub const CMD_BUILD: &str = "build";
|
||||||
|
pub const CMD_RUN: &str = "run";
|
||||||
pub const CMD_REPL: &str = "repl";
|
pub const CMD_REPL: &str = "repl";
|
||||||
pub const CMD_EDIT: &str = "edit";
|
pub const CMD_EDIT: &str = "edit";
|
||||||
pub const CMD_DOCS: &str = "docs";
|
pub const CMD_DOCS: &str = "docs";
|
||||||
|
@ -142,6 +143,14 @@ pub fn build_app<'a>() -> App<'a> {
|
||||||
.subcommand(App::new(CMD_REPL)
|
.subcommand(App::new(CMD_REPL)
|
||||||
.about("Launch the interactive Read Eval Print Loop (REPL)")
|
.about("Launch the interactive Read Eval Print Loop (REPL)")
|
||||||
)
|
)
|
||||||
|
.subcommand(App::new(CMD_RUN)
|
||||||
|
.about("Run a .roc file even if it has build errors")
|
||||||
|
.arg(
|
||||||
|
Arg::new(ROC_FILE)
|
||||||
|
.about("The .roc file of an app to run")
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
|
)
|
||||||
.subcommand(App::new(CMD_FORMAT)
|
.subcommand(App::new(CMD_FORMAT)
|
||||||
.about("Format a .roc file using standard Roc formatting")
|
.about("Format a .roc file using standard Roc formatting")
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -168,7 +177,7 @@ pub fn build_app<'a>() -> App<'a> {
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(ROC_FILE)
|
Arg::new(ROC_FILE)
|
||||||
.about("The .roc file of an app to run")
|
.about("The .roc file of an app to check")
|
||||||
.required(true),
|
.required(true),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -273,6 +282,7 @@ pub fn docs(files: Vec<PathBuf>) {
|
||||||
pub enum BuildConfig {
|
pub enum BuildConfig {
|
||||||
BuildOnly,
|
BuildOnly,
|
||||||
BuildAndRun { roc_file_arg_index: usize },
|
BuildAndRun { roc_file_arg_index: usize },
|
||||||
|
BuildAndRunIfNoErrors { roc_file_arg_index: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FormatMode {
|
pub enum FormatMode {
|
||||||
|
@ -380,7 +390,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||||
match res_binary_path {
|
match res_binary_path {
|
||||||
Ok(BuiltFile {
|
Ok(BuiltFile {
|
||||||
binary_path,
|
binary_path,
|
||||||
outcome,
|
problems,
|
||||||
total_time,
|
total_time,
|
||||||
}) => {
|
}) => {
|
||||||
match config {
|
match config {
|
||||||
|
@ -401,9 +411,51 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Return a nonzero exit code if there were problems
|
// Return a nonzero exit code if there were problems
|
||||||
Ok(outcome.status_code())
|
Ok(problems.exit_code())
|
||||||
}
|
}
|
||||||
BuildAndRun { roc_file_arg_index } => {
|
BuildAndRun { roc_file_arg_index } => roc_run(
|
||||||
|
&arena,
|
||||||
|
&original_cwd,
|
||||||
|
triple,
|
||||||
|
roc_file_arg_index,
|
||||||
|
&binary_path,
|
||||||
|
),
|
||||||
|
BuildAndRunIfNoErrors { roc_file_arg_index } => {
|
||||||
|
if problems.errors == 0 {
|
||||||
|
roc_run(
|
||||||
|
&arena,
|
||||||
|
&original_cwd,
|
||||||
|
triple,
|
||||||
|
roc_file_arg_index,
|
||||||
|
&binary_path,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Ok(problems.exit_code())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(LoadingProblem::FormattedReport(report)) => {
|
||||||
|
print!("{}", report);
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
Err(other) => {
|
||||||
|
panic!("build_file failed with error:\n{:?}", other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
fn roc_run(
|
||||||
|
arena: &Bump,
|
||||||
|
cwd: &Path,
|
||||||
|
triple: Triple,
|
||||||
|
roc_file_arg_index: usize,
|
||||||
|
binary_path: &Path,
|
||||||
|
) -> io::Result<i32> {
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
|
|
||||||
let mut cmd = 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.
|
||||||
|
@ -442,30 +494,8 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match outcome {
|
|
||||||
BuildOutcome::Errors => Ok(outcome.status_code()),
|
|
||||||
_ => roc_run(cmd.current_dir(original_cwd)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(LoadingProblem::FormattedReport(report)) => {
|
|
||||||
print!("{}", report);
|
|
||||||
|
|
||||||
Ok(1)
|
|
||||||
}
|
|
||||||
Err(other) => {
|
|
||||||
panic!("build_file failed with error:\n{:?}", other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
|
||||||
fn roc_run(cmd: &mut Command) -> io::Result<i32> {
|
|
||||||
use std::os::unix::process::CommandExt;
|
|
||||||
|
|
||||||
// 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.exec();
|
let err = cmd.current_dir(cwd).exec();
|
||||||
|
|
||||||
// If exec actually returned, it was definitely an error! (Otherwise,
|
// If exec actually returned, it was definitely an error! (Otherwise,
|
||||||
// this process would have been replaced by the other one, and we'd
|
// this process would have been replaced by the other one, and we'd
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use roc_cli::build::check_file;
|
use roc_cli::build::check_file;
|
||||||
use roc_cli::{
|
use roc_cli::{
|
||||||
build_app, docs, format, BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT,
|
build_app, docs, format, BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT,
|
||||||
CMD_FORMAT, CMD_REPL, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_TIME, ROC_FILE,
|
CMD_FORMAT, CMD_REPL, CMD_RUN, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_TIME,
|
||||||
|
ROC_FILE,
|
||||||
};
|
};
|
||||||
use roc_load::LoadingProblem;
|
use roc_load::LoadingProblem;
|
||||||
use std::fs::{self, FileType};
|
use std::fs::{self, FileType};
|
||||||
|
@ -27,7 +28,10 @@ fn main() -> io::Result<()> {
|
||||||
Some(arg_index) => {
|
Some(arg_index) => {
|
||||||
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
||||||
|
|
||||||
build(&matches, BuildConfig::BuildAndRun { roc_file_arg_index })
|
build(
|
||||||
|
&matches,
|
||||||
|
BuildConfig::BuildAndRunIfNoErrors { roc_file_arg_index },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
|
@ -37,6 +41,21 @@ fn main() -> io::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some((CMD_RUN, matches)) => {
|
||||||
|
match matches.index_of(ROC_FILE) {
|
||||||
|
Some(arg_index) => {
|
||||||
|
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
||||||
|
|
||||||
|
build(&matches, BuildConfig::BuildAndRun { roc_file_arg_index })
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) => Ok(build(matches, BuildConfig::BuildOnly)?),
|
Some((CMD_BUILD, matches)) => Ok(build(matches, BuildConfig::BuildOnly)?),
|
||||||
Some((CMD_CHECK, matches)) => {
|
Some((CMD_CHECK, matches)) => {
|
||||||
let arena = bumpalo::Bump::new();
|
let arena = bumpalo::Bump::new();
|
||||||
|
@ -47,10 +66,7 @@ fn main() -> io::Result<()> {
|
||||||
let src_dir = roc_file_path.parent().unwrap().to_owned();
|
let src_dir = roc_file_path.parent().unwrap().to_owned();
|
||||||
|
|
||||||
match check_file(&arena, src_dir, roc_file_path, emit_timings) {
|
match check_file(&arena, src_dir, roc_file_path, emit_timings) {
|
||||||
Ok(number_of_errors) => {
|
Ok(problems) => Ok(problems.exit_code()),
|
||||||
let exit_code = if number_of_errors != 0 { 1 } else { 0 };
|
|
||||||
Ok(exit_code)
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(LoadingProblem::FormattedReport(report)) => {
|
Err(LoadingProblem::FormattedReport(report)) => {
|
||||||
print!("{}", report);
|
print!("{}", report);
|
||||||
|
|
|
@ -28,7 +28,7 @@ const LLVM_VERSION: &str = "12";
|
||||||
// them after type checking (like Elm does) so we can complete the entire
|
// them after type checking (like Elm does) so we can complete the entire
|
||||||
// `roc check` process without needing to monomorphize.
|
// `roc check` process without needing to monomorphize.
|
||||||
/// Returns the number of problems reported.
|
/// Returns the number of problems reported.
|
||||||
pub fn report_problems_monomorphized(loaded: &mut MonomorphizedModule) -> usize {
|
pub fn report_problems_monomorphized(loaded: &mut MonomorphizedModule) -> Problems {
|
||||||
report_problems_help(
|
report_problems_help(
|
||||||
loaded.total_problems(),
|
loaded.total_problems(),
|
||||||
&loaded.sources,
|
&loaded.sources,
|
||||||
|
@ -39,7 +39,7 @@ pub fn report_problems_monomorphized(loaded: &mut MonomorphizedModule) -> usize
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_problems_typechecked(loaded: &mut LoadedModule) -> usize {
|
pub fn report_problems_typechecked(loaded: &mut LoadedModule) -> Problems {
|
||||||
report_problems_help(
|
report_problems_help(
|
||||||
loaded.total_problems(),
|
loaded.total_problems(),
|
||||||
&loaded.sources,
|
&loaded.sources,
|
||||||
|
@ -50,6 +50,23 @@ pub fn report_problems_typechecked(loaded: &mut LoadedModule) -> usize {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub struct Problems {
|
||||||
|
pub errors: usize,
|
||||||
|
pub warnings: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Problems {
|
||||||
|
pub fn exit_code(&self) -> i32 {
|
||||||
|
// 0 means no problems, 1 means errors, 2 means warnings
|
||||||
|
if self.errors > 0 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
self.warnings.min(1) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn report_problems_help(
|
fn report_problems_help(
|
||||||
total_problems: usize,
|
total_problems: usize,
|
||||||
sources: &MutMap<ModuleId, (PathBuf, Box<str>)>,
|
sources: &MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||||
|
@ -57,7 +74,7 @@ fn report_problems_help(
|
||||||
can_problems: &mut MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
can_problems: &mut MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||||
type_problems: &mut MutMap<ModuleId, Vec<roc_solve::solve::TypeError>>,
|
type_problems: &mut MutMap<ModuleId, Vec<roc_solve::solve::TypeError>>,
|
||||||
mono_problems: &mut MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
mono_problems: &mut MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
||||||
) -> usize {
|
) -> Problems {
|
||||||
use roc_reporting::report::{
|
use roc_reporting::report::{
|
||||||
can_problem, mono_problem, type_problem, Report, RocDocAllocator, Severity::*,
|
can_problem, mono_problem, type_problem, Report, RocDocAllocator, Severity::*,
|
||||||
DEFAULT_PALETTE,
|
DEFAULT_PALETTE,
|
||||||
|
@ -144,13 +161,13 @@ fn report_problems_help(
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
problems_reported = warnings.len();
|
problems_reported = warnings.len();
|
||||||
|
|
||||||
for warning in warnings {
|
for warning in warnings.iter() {
|
||||||
println!("\n{}\n", warning);
|
println!("\n{}\n", warning);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
problems_reported = errors.len();
|
problems_reported = errors.len();
|
||||||
|
|
||||||
for error in errors {
|
for error in errors.iter() {
|
||||||
println!("\n{}\n", error);
|
println!("\n{}\n", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,7 +182,10 @@ fn report_problems_help(
|
||||||
println!("{}\u{001B}[0m\n", Report::horizontal_rule(&palette));
|
println!("{}\u{001B}[0m\n", Report::horizontal_rule(&palette));
|
||||||
}
|
}
|
||||||
|
|
||||||
problems_reported
|
Problems {
|
||||||
|
errors: errors.len(),
|
||||||
|
warnings: warnings.len(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "llvm"))]
|
#[cfg(not(feature = "llvm"))]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue