mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-02 19:32:17 +00:00
337 lines
11 KiB
Rust
337 lines
11 KiB
Rust
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_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;
|
|
use roc_load::{LoadingProblem, Threading};
|
|
use std::fs::{self, FileType};
|
|
use std::io;
|
|
use std::path::{Path, PathBuf};
|
|
use target_lexicon::Triple;
|
|
|
|
#[macro_use]
|
|
extern crate const_format;
|
|
|
|
#[global_allocator]
|
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
|
|
|
use std::ffi::{OsStr, OsString};
|
|
|
|
use roc_cli::build;
|
|
|
|
fn main() -> io::Result<()> {
|
|
let matches = build_app().get_matches();
|
|
|
|
let exit_code = match matches.subcommand() {
|
|
None => {
|
|
if matches.is_present(ROC_FILE) {
|
|
build(
|
|
&matches,
|
|
BuildConfig::BuildAndRunIfNoErrors,
|
|
Triple::host(),
|
|
LinkType::Executable,
|
|
)
|
|
} else {
|
|
launch_editor(None)?;
|
|
|
|
Ok(0)
|
|
}
|
|
}
|
|
Some((CMD_RUN, matches)) => {
|
|
if matches.is_present(ROC_FILE) {
|
|
build(
|
|
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.");
|
|
|
|
Ok(1)
|
|
}
|
|
}
|
|
Some((CMD_TEST, matches)) => {
|
|
if matches.is_present(ROC_FILE) {
|
|
test(matches, Triple::host())
|
|
} else {
|
|
eprintln!("What .roc file do you want to test? Specify it at the end of the `roc test` command.");
|
|
|
|
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());
|
|
|
|
if Some("rs") == output_path.extension().and_then(OsStr::to_str) {
|
|
roc_glue::generate(input_path, output_path)
|
|
} else {
|
|
eprintln!("Currently, `roc glue` only supports generating Rust glue files (with the .rs extension). In the future, the plan is to decouple `roc glue` from any particular output format, by having it accept a second .roc file which gets executed as a plugin to generate glue code for any desired language. However, this has not yet been implemented, and for now only .rs is supported.");
|
|
|
|
Ok(1)
|
|
}
|
|
}
|
|
Some((CMD_BUILD, matches)) => {
|
|
let target: Target = matches.value_of_t(FLAG_TARGET).unwrap_or_default();
|
|
|
|
let link_type = match (
|
|
matches.is_present(FLAG_LIB),
|
|
matches.is_present(FLAG_NO_LINK),
|
|
) {
|
|
(true, false) => LinkType::Dylib,
|
|
(true, true) => user_error!("build can only be one of `--lib` or `--no-link`"),
|
|
(false, true) => LinkType::None,
|
|
(false, false) => LinkType::Executable,
|
|
};
|
|
|
|
Ok(build(
|
|
matches,
|
|
BuildConfig::BuildOnly,
|
|
target.to_triple(),
|
|
link_type,
|
|
)?)
|
|
}
|
|
Some((CMD_CHECK, matches)) => {
|
|
let arena = bumpalo::Bump::new();
|
|
|
|
let emit_timings = matches.is_present(FLAG_TIME);
|
|
let filename = matches.value_of_os(ROC_FILE).unwrap();
|
|
let roc_file_path = PathBuf::from(filename);
|
|
let threading = match matches
|
|
.value_of(roc_cli::FLAG_MAX_THREADS)
|
|
.and_then(|s| s.parse::<usize>().ok())
|
|
{
|
|
None => Threading::AllAvailable,
|
|
Some(0) => user_error!("cannot build with at most 0 threads"),
|
|
Some(1) => Threading::Single,
|
|
Some(n) => Threading::AtMost(n),
|
|
};
|
|
|
|
match check_file(&arena, roc_file_path, emit_timings, threading) {
|
|
Ok((problems, total_time)) => {
|
|
println!(
|
|
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.",
|
|
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(),
|
|
);
|
|
|
|
Ok(problems.exit_code())
|
|
}
|
|
|
|
Err(LoadingProblem::FormattedReport(report)) => {
|
|
print!("{}", report);
|
|
|
|
Ok(1)
|
|
}
|
|
Err(other) => {
|
|
panic!("build_file failed with error:\n{:?}", other);
|
|
}
|
|
}
|
|
}
|
|
Some((CMD_REPL, _)) => {
|
|
{
|
|
roc_repl_cli::main()?;
|
|
|
|
// Exit 0 if the repl exited normally
|
|
Ok(0)
|
|
}
|
|
}
|
|
Some((CMD_EDIT, matches)) => {
|
|
match matches
|
|
.values_of_os(DIRECTORY_OR_FILES)
|
|
.map(|mut values| values.next())
|
|
{
|
|
Some(Some(os_str)) => {
|
|
launch_editor(Some(Path::new(os_str)))?;
|
|
}
|
|
_ => {
|
|
launch_editor(None)?;
|
|
}
|
|
}
|
|
|
|
// Exit 0 if the editor exited normally
|
|
Ok(0)
|
|
}
|
|
Some((CMD_DOCS, matches)) => {
|
|
let maybe_values = matches.values_of_os(DIRECTORY_OR_FILES);
|
|
|
|
let mut values: Vec<OsString> = Vec::new();
|
|
|
|
match maybe_values {
|
|
None => {
|
|
let mut os_string_values: Vec<OsString> = Vec::new();
|
|
read_all_roc_files(
|
|
&std::env::current_dir()?.as_os_str().to_os_string(),
|
|
&mut os_string_values,
|
|
)?;
|
|
for os_string in os_string_values {
|
|
values.push(os_string);
|
|
}
|
|
}
|
|
Some(os_values) => {
|
|
for os_str in os_values {
|
|
values.push(os_str.to_os_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut roc_files = Vec::new();
|
|
|
|
// Populate roc_files
|
|
for os_str in values {
|
|
let metadata = fs::metadata(os_str.clone())?;
|
|
roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?;
|
|
}
|
|
|
|
generate_docs_html(roc_files);
|
|
|
|
Ok(0)
|
|
}
|
|
Some((CMD_FORMAT, matches)) => {
|
|
let maybe_values = matches.values_of_os(DIRECTORY_OR_FILES);
|
|
|
|
let mut values: Vec<OsString> = Vec::new();
|
|
|
|
match maybe_values {
|
|
None => {
|
|
let mut os_string_values: Vec<OsString> = Vec::new();
|
|
read_all_roc_files(
|
|
&std::env::current_dir()?.as_os_str().to_os_string(),
|
|
&mut os_string_values,
|
|
)?;
|
|
for os_string in os_string_values {
|
|
values.push(os_string);
|
|
}
|
|
}
|
|
Some(os_values) => {
|
|
for os_str in os_values {
|
|
values.push(os_str.to_os_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut roc_files = Vec::new();
|
|
|
|
// Populate roc_files
|
|
for os_str in values {
|
|
let metadata = fs::metadata(os_str.clone())?;
|
|
roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?;
|
|
}
|
|
|
|
let format_mode = match matches.is_present(FLAG_CHECK) {
|
|
true => FormatMode::CheckOnly,
|
|
false => FormatMode::Format,
|
|
};
|
|
|
|
let format_exit_code = match format(roc_files, format_mode) {
|
|
Ok(_) => 0,
|
|
Err(message) => {
|
|
eprintln!("{}", message);
|
|
1
|
|
}
|
|
};
|
|
|
|
Ok(format_exit_code)
|
|
}
|
|
Some((CMD_VERSION, _)) => {
|
|
print!(
|
|
"{}",
|
|
concatcp!("roc ", include_str!("../../../version.txt"))
|
|
);
|
|
|
|
Ok(0)
|
|
}
|
|
_ => unreachable!(),
|
|
}?;
|
|
|
|
std::process::exit(exit_code);
|
|
}
|
|
|
|
fn read_all_roc_files(
|
|
dir: &OsString,
|
|
roc_file_paths: &mut Vec<OsString>,
|
|
) -> Result<(), std::io::Error> {
|
|
let entries = fs::read_dir(dir)?;
|
|
|
|
for entry in entries {
|
|
let path = entry?.path();
|
|
|
|
if path.is_dir() {
|
|
read_all_roc_files(&path.into_os_string(), roc_file_paths)?;
|
|
} else if path.extension().and_then(OsStr::to_str) == Some("roc") {
|
|
let file_path = path.into_os_string();
|
|
roc_file_paths.push(file_path);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn roc_files_recursive<P: AsRef<Path>>(
|
|
path: P,
|
|
file_type: FileType,
|
|
roc_files: &mut Vec<PathBuf>,
|
|
) -> io::Result<()> {
|
|
if file_type.is_dir() {
|
|
for entry_res in fs::read_dir(path)? {
|
|
let entry = entry_res?;
|
|
|
|
roc_files_recursive(entry.path(), entry.file_type()?, roc_files)?;
|
|
}
|
|
} else {
|
|
roc_files.push(path.as_ref().to_path_buf());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "editor")]
|
|
fn launch_editor(project_dir_path: Option<&Path>) -> io::Result<()> {
|
|
roc_editor::launch(project_dir_path)
|
|
}
|
|
|
|
#[cfg(not(feature = "editor"))]
|
|
fn launch_editor(_project_dir_path: Option<&Path>) -> io::Result<()> {
|
|
panic!("Cannot launch the editor because this build of roc did not include `feature = \"editor\"`!");
|
|
}
|