Code changes for clap 4 upgrade

This commit is contained in:
David Smith 2023-05-13 12:24:14 -04:00
parent 66f71b237b
commit 0f943c3e64
No known key found for this signature in database
GPG key ID: 979D8D09D9570EED
4 changed files with 125 additions and 116 deletions

View file

@ -4,7 +4,10 @@
extern crate const_format; extern crate const_format;
use bumpalo::Bump; use bumpalo::Bump;
use clap::{Arg, ArgMatches, Command, ValueSource}; use clap::{
builder::PossibleValuesParser, parser::ValueSource, value_parser, Arg, ArgAction, ArgMatches,
Command,
};
use roc_build::link::{LinkType, LinkingStrategy}; use roc_build::link::{LinkType, LinkingStrategy};
use roc_build::program::{ use roc_build::program::{
handle_error_module, handle_loading_problem, standard_load_config, BuildFileError, handle_error_module, handle_loading_problem, standard_load_config, BuildFileError,
@ -18,7 +21,7 @@ use roc_mono::ir::OptLevel;
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use roc_packaging::tarball::Compression; use roc_packaging::tarball::Compression;
use std::env; use std::env;
use std::ffi::{CString, OsStr}; use std::ffi::{CString, OsStr, OsString};
use std::io; use std::io;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::os::raw::{c_char, c_int}; use std::os::raw::{c_char, c_int};
@ -72,69 +75,73 @@ pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP";
const VERSION: &str = include_str!("../../../version.txt"); const VERSION: &str = include_str!("../../../version.txt");
pub fn build_app<'a>() -> Command<'a> { pub fn build_app() -> Command {
let flag_optimize = Arg::new(FLAG_OPTIMIZE) let flag_optimize = Arg::new(FLAG_OPTIMIZE)
.long(FLAG_OPTIMIZE) .long(FLAG_OPTIMIZE)
.help("Optimize the compiled program to run faster\n(Optimization takes time to complete.)") .help("Optimize the compiled program to run faster\n(Optimization takes time to complete.)")
.action(ArgAction::SetTrue)
.required(false); .required(false);
let flag_max_threads = Arg::new(FLAG_MAX_THREADS) let flag_max_threads = Arg::new(FLAG_MAX_THREADS)
.long(FLAG_MAX_THREADS) .long(FLAG_MAX_THREADS)
.help("Limit the number of threads (and hence cores) used during compilation") .help("Limit the number of threads (and hence cores) used during compilation")
.takes_value(true) .num_args(0..=1)
.validator(|s| s.parse::<usize>()) .value_parser(value_parser!(usize))
.required(false); .required(false);
let flag_opt_size = Arg::new(FLAG_OPT_SIZE) let flag_opt_size = Arg::new(FLAG_OPT_SIZE)
.long(FLAG_OPT_SIZE) .long(FLAG_OPT_SIZE)
.help("Optimize the compiled program to have a small binary size\n(Optimization takes time to complete.)") .help("Optimize the compiled program to have a small binary size\n(Optimization takes time to complete.)")
.action(ArgAction::SetTrue)
.required(false); .required(false);
let flag_dev = Arg::new(FLAG_DEV) let flag_dev = Arg::new(FLAG_DEV)
.long(FLAG_DEV) .long(FLAG_DEV)
.help("Make compilation finish as soon as possible, at the expense of runtime performance") .help("Make compilation finish as soon as possible, at the expense of runtime performance")
.action(ArgAction::SetTrue)
.required(false); .required(false);
let flag_debug = Arg::new(FLAG_DEBUG) let flag_debug = Arg::new(FLAG_DEBUG)
.long(FLAG_DEBUG) .long(FLAG_DEBUG)
.help("Store LLVM debug information in the generated program") .help("Store LLVM debug information in the generated program")
.action(ArgAction::SetTrue)
.required(false); .required(false);
let flag_time = Arg::new(FLAG_TIME) let flag_time = Arg::new(FLAG_TIME)
.long(FLAG_TIME) .long(FLAG_TIME)
.help("Print detailed compilation time information") .help("Print detailed compilation time information")
.action(ArgAction::SetTrue)
.required(false); .required(false);
let flag_linker = Arg::new(FLAG_LINKER) let flag_linker = Arg::new(FLAG_LINKER)
.long(FLAG_LINKER) .long(FLAG_LINKER)
.help("Set which linker to use\n(The surgical linker is enabled by default only when building for wasm32 or x86_64 Linux, because those are the only targets it currently supports. Otherwise the legacy linker is used by default.)") .help("Set which linker to use\n(The surgical linker is enabled by default only when building for wasm32 or x86_64 Linux, because those are the only targets it currently supports. Otherwise the legacy linker is used by default.)")
.possible_values(["surgical", "legacy"]) .value_parser(["surgical", "legacy"])
.required(false); .required(false);
let flag_prebuilt = Arg::new(FLAG_PREBUILT) let flag_prebuilt = Arg::new(FLAG_PREBUILT)
.long(FLAG_PREBUILT) .long(FLAG_PREBUILT)
.help("Assume the platform has been prebuilt and skip rebuilding the platform\n(This is enabled by default when using `roc build` with a --target other than `--target <current machine>`.)") .help("Assume the platform has been prebuilt and skip rebuilding the platform\n(This is enabled by default when using `roc build` with a --target other than `--target <current machine>`.)")
.possible_values(["true", "false"]) .value_parser(["true", "false"])
.required(false); .required(false);
let flag_wasm_stack_size_kb = Arg::new(FLAG_WASM_STACK_SIZE_KB) let flag_wasm_stack_size_kb = Arg::new(FLAG_WASM_STACK_SIZE_KB)
.long(FLAG_WASM_STACK_SIZE_KB) .long(FLAG_WASM_STACK_SIZE_KB)
.help("Stack size in kilobytes for wasm32 target\n(This only applies when --dev also provided.)") .help("Stack size in kilobytes for wasm32 target\n(This only applies when --dev also provided.)")
.takes_value(true) .num_args(0..=1)
.validator(|s| s.parse::<u32>()) .value_parser(value_parser!(u32))
.required(false); .required(false);
let roc_file_to_run = Arg::new(ROC_FILE) let roc_file_to_run = Arg::new(ROC_FILE)
.help("The .roc file of an app to run") .help("The .roc file of an app to run")
.allow_invalid_utf8(true) .value_parser(value_parser!(PathBuf))
.required(false) .required(false)
.default_value(DEFAULT_ROC_FILENAME); .default_value(DEFAULT_ROC_FILENAME);
let args_for_app = Arg::new(ARGS_FOR_APP) let args_for_app = Arg::new(ARGS_FOR_APP)
.help("Arguments to pass into the app being run\ne.g. `roc run -- arg1 arg2`") .help("Arguments to pass into the app being run\ne.g. `roc run -- arg1 arg2`")
.allow_invalid_utf8(true) .value_parser(value_parser!(OsString))
.multiple_values(true) .num_args(0..)
.takes_value(true)
.allow_hyphen_values(true) .allow_hyphen_values(true)
.last(true); .last(true);
@ -151,21 +158,25 @@ pub fn build_app<'a>() -> Command<'a> {
.arg(flag_time.clone()) .arg(flag_time.clone())
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_prebuilt.clone()) .arg(flag_prebuilt.clone())
.arg(flag_wasm_stack_size_kb.clone()) .arg(flag_wasm_stack_size_kb)
.arg( .arg(
Arg::new(FLAG_TARGET) Arg::new(FLAG_TARGET)
.long(FLAG_TARGET) .long(FLAG_TARGET)
.help("Choose a different target") .help("Choose a different target")
.default_value(Target::default().into()) .default_value(Into::<&'static str>::into(Target::default()))
.possible_values(Target::iter().map(|target| { .value_parser(
Into::<&'static str>::into(target) PossibleValuesParser::new(
})) Target::iter().map(|target| {
Into::<&'static str>::into(target)
})
))
.required(false), .required(false),
) )
.arg( .arg(
Arg::new(FLAG_LIB) Arg::new(FLAG_LIB)
.long(FLAG_LIB) .long(FLAG_LIB)
.help("Build a C library instead of an executable") .help("Build a C library instead of an executable")
.action(ArgAction::SetTrue)
.required(false), .required(false),
) )
.arg( .arg(
@ -173,19 +184,20 @@ pub fn build_app<'a>() -> Command<'a> {
.long(FLAG_BUNDLE) .long(FLAG_BUNDLE)
.help("Create an archive of a package (for example, a .tar, .tar.gz, or .tar.br file), so others can add it as a HTTPS dependency.") .help("Create an archive of a package (for example, a .tar, .tar.gz, or .tar.br file), so others can add it as a HTTPS dependency.")
.conflicts_with(FLAG_TARGET) .conflicts_with(FLAG_TARGET)
.possible_values([".tar", ".tar.gz", ".tar.br"]) .value_parser([".tar", ".tar.gz", ".tar.br"])
.required(false), .required(false),
) )
.arg( .arg(
Arg::new(FLAG_NO_LINK) Arg::new(FLAG_NO_LINK)
.long(FLAG_NO_LINK) .long(FLAG_NO_LINK)
.help("Do not link\n(Instead, just output the `.o` file.)") .help("Do not link\n(Instead, just output the `.o` file.)")
.action(ArgAction::SetTrue)
.required(false), .required(false),
) )
.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) .value_parser(value_parser!(PathBuf))
.required(false) .required(false)
.default_value(DEFAULT_ROC_FILENAME), .default_value(DEFAULT_ROC_FILENAME),
) )
@ -203,7 +215,7 @@ pub fn build_app<'a>() -> Command<'a> {
.arg( .arg(
Arg::new(ROC_FILE) Arg::new(ROC_FILE)
.help("The .roc file for the main module") .help("The .roc file for the main module")
.allow_invalid_utf8(true) .value_parser(value_parser!(PathBuf))
.required(false) .required(false)
.default_value(DEFAULT_ROC_FILENAME) .default_value(DEFAULT_ROC_FILENAME)
) )
@ -243,13 +255,14 @@ pub fn build_app<'a>() -> Command<'a> {
.arg( .arg(
Arg::new(DIRECTORY_OR_FILES) Arg::new(DIRECTORY_OR_FILES)
.index(1) .index(1)
.multiple_values(true) .num_args(0..)
.required(false) .required(false)
.allow_invalid_utf8(true)) .value_parser(value_parser!(OsString)))
.arg( .arg(
Arg::new(FLAG_CHECK) Arg::new(FLAG_CHECK)
.long(FLAG_CHECK) .long(FLAG_CHECK)
.help("Checks that specified files are formatted\n(If formatting is needed, return a non-zero exit code.)") .help("Checks that specified files are formatted\n(If formatting is needed, return a non-zero exit code.)")
.action(ArgAction::SetTrue)
.required(false), .required(false),
) )
) )
@ -262,7 +275,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) .value_parser(value_parser!(PathBuf))
.required(false) .required(false)
.default_value(DEFAULT_ROC_FILENAME), .default_value(DEFAULT_ROC_FILENAME),
) )
@ -271,9 +284,9 @@ pub fn build_app<'a>() -> Command<'a> {
Command::new(CMD_DOCS) Command::new(CMD_DOCS)
.about("Generate documentation for a Roc package") .about("Generate documentation for a Roc package")
.arg(Arg::new(ROC_FILE) .arg(Arg::new(ROC_FILE)
.multiple_values(true)
.help("The package's main .roc file") .help("The package's main .roc file")
.allow_invalid_utf8(true) .num_args(0..0)
.value_parser(value_parser!(PathBuf))
.required(false) .required(false)
.default_value(DEFAULT_ROC_FILENAME), .default_value(DEFAULT_ROC_FILENAME),
) )
@ -284,19 +297,19 @@ pub fn build_app<'a>() -> Command<'a> {
.arg( .arg(
Arg::new(GLUE_SPEC) Arg::new(GLUE_SPEC)
.help("The specification for how to translate Roc types into output files.") .help("The specification for how to translate Roc types into output files.")
.allow_invalid_utf8(true) .value_parser(value_parser!(PathBuf))
.required(true) .required(true)
) )
.arg( .arg(
Arg::new(GLUE_DIR) Arg::new(GLUE_DIR)
.help("The directory for the generated glue code.\nNote: The implementation can write to any file in this directory.") .help("The directory for the generated glue code.\nNote: The implementation can write to any file in this directory.")
.allow_invalid_utf8(true) .value_parser(value_parser!(PathBuf))
.required(true) .required(true)
) )
.arg( .arg(
Arg::new(ROC_FILE) Arg::new(ROC_FILE)
.help("The .roc file whose exposed types should be translated.") .help("The .roc file whose exposed types should be translated.")
.allow_invalid_utf8(true) .value_parser(value_parser!(PathBuf))
.required(false) .required(false)
.default_value(DEFAULT_ROC_FILENAME) .default_value(DEFAULT_ROC_FILENAME)
) )
@ -306,23 +319,25 @@ pub fn build_app<'a>() -> Command<'a> {
.arg( .arg(
Arg::new(ROC_FILE) Arg::new(ROC_FILE)
.help("The .roc file for an app using the platform") .help("The .roc file for an app using the platform")
.allow_invalid_utf8(true) .value_parser(value_parser!(PathBuf))
.required(true) .required(true)
) )
.arg( .arg(
Arg::new(FLAG_TARGET) Arg::new(FLAG_TARGET)
.long(FLAG_TARGET) .long(FLAG_TARGET)
.help("Choose a different target") .help("Choose a different target")
.default_value(Target::default().into()) .default_value(Into::<&'static str>::into(Target::default()))
.possible_values(Target::iter().map(|target| { .value_parser(
Into::<&'static str>::into(target) PossibleValuesParser::new(
})) Target::iter().map(|target| {
Into::<&'static str>::into(target)
})
))
.required(false), .required(false),
) )
) )
.trailing_var_arg(true)
.arg(flag_optimize) .arg(flag_optimize)
.arg(flag_max_threads.clone()) .arg(flag_max_threads)
.arg(flag_opt_size) .arg(flag_opt_size)
.arg(flag_dev) .arg(flag_dev)
.arg(flag_debug) .arg(flag_debug)
@ -338,9 +353,9 @@ pub fn build_app<'a>() -> Command<'a> {
.about("Launch the Roc editor (Work In Progress)") .about("Launch the Roc editor (Work In Progress)")
.arg( .arg(
Arg::new(DIRECTORY_OR_FILES) Arg::new(DIRECTORY_OR_FILES)
.multiple_values(true) .num_args(0..)
.required(false) .required(false)
.allow_invalid_utf8(true) .value_parser(value_parser!(OsString))
.help("(optional) The directory or files to open on launch"), .help("(optional) The directory or files to open on launch"),
), ),
) )
@ -363,9 +378,9 @@ pub enum FormatMode {
fn opt_level_from_flags(matches: &ArgMatches) -> OptLevel { fn opt_level_from_flags(matches: &ArgMatches) -> OptLevel {
match ( match (
matches.is_present(FLAG_OPTIMIZE), matches.get_flag(FLAG_OPTIMIZE),
matches.is_present(FLAG_OPT_SIZE), matches.get_flag(FLAG_OPT_SIZE),
matches.is_present(FLAG_DEV), matches.get_flag(FLAG_DEV),
) { ) {
(true, false, false) => OptLevel::Optimize, (true, false, false) => OptLevel::Optimize,
(false, true, false) => OptLevel::Size, (false, true, false) => OptLevel::Size,
@ -389,20 +404,16 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
let start_time = Instant::now(); let start_time = Instant::now();
let arena = Bump::new(); let arena = Bump::new();
let filename = matches.value_of_os(ROC_FILE).unwrap();
let opt_level = opt_level_from_flags(matches); let opt_level = opt_level_from_flags(matches);
let threading = match matches let threading = match matches.get_one::<usize>(FLAG_MAX_THREADS) {
.value_of(FLAG_MAX_THREADS)
.and_then(|s| s.parse::<usize>().ok())
{
None => Threading::AllAvailable, None => Threading::AllAvailable,
Some(0) => user_error!("cannot build with at most 0 threads"), Some(0) => user_error!("cannot build with at most 0 threads"),
Some(1) => Threading::Single, Some(1) => Threading::Single,
Some(n) => Threading::AtMost(n), Some(n) => Threading::AtMost(*n),
}; };
let path = Path::new(filename); let path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
// Spawn the root task // Spawn the root task
if !path.exists() { if !path.exists() {
@ -454,7 +465,7 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
return handle_loading_problem(problem); return handle_loading_problem(problem);
} }
Err(LoadMonomorphizedError::ErrorModule(module)) => { Err(LoadMonomorphizedError::ErrorModule(module)) => {
return handle_error_module(module, start_time.elapsed(), filename, false); return handle_error_module(module, start_time.elapsed(), path.as_os_str(), false);
} }
}; };
let problems = report_problems_monomorphized(&mut loaded); let problems = report_problems_monomorphized(&mut loaded);
@ -540,10 +551,8 @@ pub fn build(
use roc_build::program::build_file; use roc_build::program::build_file;
use BuildConfig::*; use BuildConfig::*;
let filename = matches.value_of_os(ROC_FILE).unwrap(); let path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
let path_buf = { {
let path = Path::new(filename);
// Spawn the root task // Spawn the root task
if !path.exists() { if !path.exists() {
let current_dir = env::current_dir().unwrap(); let current_dir = env::current_dir().unwrap();
@ -567,11 +576,12 @@ pub fn build(
process::exit(1); process::exit(1);
} }
if config == BuildConfig::BuildOnly && matches.is_present(FLAG_BUNDLE) { if config == BuildConfig::BuildOnly && matches.contains_id(FLAG_BUNDLE) {
let start_time = Instant::now(); let start_time = Instant::now();
let compression = let compression =
Compression::try_from(matches.value_of(FLAG_BUNDLE).unwrap()).unwrap(); Compression::try_from(matches.get_one::<String>(FLAG_BUNDLE).unwrap().as_str())
.unwrap();
// Print a note of advice. This is mainly here because brotli takes so long but produces // Print a note of advice. This is mainly here because brotli takes so long but produces
// such smaller output files; the idea is to encourage people to wait for brotli, // such smaller output files; the idea is to encourage people to wait for brotli,
@ -608,9 +618,7 @@ pub fn build(
return Ok(0); return Ok(0);
} }
}
path.to_path_buf()
};
// the process will end after this function, // the process will end after this function,
// so we don't want to spend time freeing these values // so we don't want to spend time freeing these values
@ -624,7 +632,7 @@ pub fn build(
// Note: This allows using `--dev` with `--optimize`. // Note: This allows using `--dev` with `--optimize`.
// This means frontend optimizations and dev backend. // This means frontend optimizations and dev backend.
let code_gen_backend = if matches.is_present(FLAG_DEV) { let code_gen_backend = if matches.get_flag(FLAG_DEV) {
if matches!(triple.architecture, Architecture::Wasm32) { if matches!(triple.architecture, Architecture::Wasm32) {
CodeGenBackend::Wasm CodeGenBackend::Wasm
} else { } else {
@ -639,17 +647,14 @@ pub fn build(
CodeGenBackend::Llvm(backend_mode) CodeGenBackend::Llvm(backend_mode)
}; };
let emit_debug_info = matches.is_present(FLAG_DEBUG); let emit_debug_info = matches.get_flag(FLAG_DEBUG);
let emit_timings = matches.is_present(FLAG_TIME); let emit_timings = matches.get_flag(FLAG_TIME);
let threading = match matches let threading = match matches.get_one::<usize>(FLAG_MAX_THREADS) {
.value_of(FLAG_MAX_THREADS)
.and_then(|s| s.parse::<usize>().ok())
{
None => Threading::AllAvailable, None => Threading::AllAvailable,
Some(0) => user_error!("cannot build with at most 0 threads"), Some(0) => user_error!("cannot build with at most 0 threads"),
Some(1) => Threading::Single, Some(1) => Threading::Single,
Some(n) => Threading::AtMost(n), Some(n) => Threading::AtMost(*n),
}; };
let wasm_dev_backend = matches!(code_gen_backend, CodeGenBackend::Wasm); let wasm_dev_backend = matches!(code_gen_backend, CodeGenBackend::Wasm);
@ -657,15 +662,15 @@ pub fn build(
let linking_strategy = if wasm_dev_backend { let linking_strategy = if wasm_dev_backend {
LinkingStrategy::Additive LinkingStrategy::Additive
} else if !roc_linker::supported(link_type, &triple) } else if !roc_linker::supported(link_type, &triple)
|| matches.value_of(FLAG_LINKER) == Some("legacy") || matches.get_one::<String>(FLAG_LINKER).map(|s| s.as_str()) == Some("legacy")
{ {
LinkingStrategy::Legacy LinkingStrategy::Legacy
} else { } else {
LinkingStrategy::Surgical LinkingStrategy::Surgical
}; };
let prebuilt = if matches.is_present(FLAG_PREBUILT) { let prebuilt = if matches.contains_id(FLAG_PREBUILT) {
matches.value_of(FLAG_PREBUILT) == Some("true") matches.get_one::<String>(FLAG_PREBUILT).map(|s| s.as_str()) == Some("true")
} else { } else {
// When compiling for a different target, default to assuming a prebuilt platform. // When compiling for a different target, default to assuming a prebuilt platform.
// Otherwise compilation would most likely fail because many toolchains assume you're compiling for the current machine. // Otherwise compilation would most likely fail because many toolchains assume you're compiling for the current machine.
@ -674,10 +679,9 @@ pub fn build(
}; };
let wasm_dev_stack_bytes: Option<u32> = matches let wasm_dev_stack_bytes: Option<u32> = matches
.try_get_one::<&str>(FLAG_WASM_STACK_SIZE_KB) .try_get_one::<u32>(FLAG_WASM_STACK_SIZE_KB)
.ok() .ok()
.flatten() .flatten()
.and_then(|s| s.parse::<u32>().ok())
.map(|x| x * 1024); .map(|x| x * 1024);
let build_ordering = match config { let build_ordering = match config {
@ -696,7 +700,7 @@ pub fn build(
let res_binary_path = build_file( let res_binary_path = build_file(
&arena, &arena,
&triple, &triple,
path_buf, path.to_owned(),
code_gen_options, code_gen_options,
emit_timings, emit_timings,
link_type, link_type,
@ -752,7 +756,10 @@ pub fn build(
); );
} }
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default(); let args = matches
.get_many::<OsString>(ARGS_FOR_APP)
.unwrap_or_default()
.map(|s| s.as_os_str());
// don't waste time deallocating; the process ends anyway // don't waste time deallocating; the process ends anyway
// ManuallyDrop will leak the bytes because we don't drop manually // ManuallyDrop will leak the bytes because we don't drop manually
@ -784,7 +791,10 @@ pub fn build(
); );
} }
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default(); let args = matches
.get_many::<OsString>(ARGS_FOR_APP)
.unwrap_or_default()
.map(|s| s.as_os_str());
// don't waste time deallocating; the process ends anyway // don't waste time deallocating; the process ends anyway
// ManuallyDrop will leak the bytes because we don't drop manually // ManuallyDrop will leak the bytes because we don't drop manually
@ -795,7 +805,7 @@ pub fn build(
} }
} }
Err(BuildFileError::ErrorModule { module, total_time }) => { Err(BuildFileError::ErrorModule { module, total_time }) => {
handle_error_module(module, total_time, filename, true) handle_error_module(module, total_time, path.as_os_str(), true)
} }
Err(BuildFileError::LoadingProblem(problem)) => handle_loading_problem(problem), Err(BuildFileError::LoadingProblem(problem)) => handle_loading_problem(problem),
} }

View file

@ -16,6 +16,7 @@ use roc_packaging::cache::{self, RocCacheDir};
use std::fs::{self, FileType}; use std::fs::{self, FileType};
use std::io; use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr;
use target_lexicon::Triple; use target_lexicon::Triple;
#[macro_use] #[macro_use]
@ -35,7 +36,7 @@ fn main() -> io::Result<()> {
let exit_code = match matches.subcommand() { let exit_code = match matches.subcommand() {
None => { None => {
if matches.is_present(ROC_FILE) { if matches.contains_id(ROC_FILE) {
build( build(
&matches, &matches,
BuildConfig::BuildAndRunIfNoErrors, BuildConfig::BuildAndRunIfNoErrors,
@ -50,7 +51,7 @@ fn main() -> io::Result<()> {
} }
} }
Some((CMD_RUN, matches)) => { Some((CMD_RUN, matches)) => {
if matches.is_present(ROC_FILE) { if matches.contains_id(ROC_FILE) {
build( build(
matches, matches,
BuildConfig::BuildAndRun, BuildConfig::BuildAndRun,
@ -65,7 +66,7 @@ fn main() -> io::Result<()> {
} }
} }
Some((CMD_TEST, matches)) => { Some((CMD_TEST, matches)) => {
if matches.is_present(ROC_FILE) { if matches.contains_id(ROC_FILE) {
test(matches, Triple::host()) test(matches, Triple::host())
} else { } else {
eprintln!("What .roc file do you want to test? Specify it at the end of the `roc test` command."); eprintln!("What .roc file do you want to test? Specify it at the end of the `roc test` command.");
@ -74,7 +75,7 @@ fn main() -> io::Result<()> {
} }
} }
Some((CMD_DEV, matches)) => { Some((CMD_DEV, matches)) => {
if matches.is_present(ROC_FILE) { if matches.contains_id(ROC_FILE) {
build( build(
matches, matches,
BuildConfig::BuildAndRunIfNoErrors, BuildConfig::BuildAndRunIfNoErrors,
@ -89,12 +90,12 @@ fn main() -> io::Result<()> {
} }
} }
Some((CMD_GLUE, matches)) => { Some((CMD_GLUE, matches)) => {
let input_path = Path::new(matches.value_of_os(ROC_FILE).unwrap()); let input_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
let output_path = Path::new(matches.value_of_os(GLUE_DIR).unwrap()); let output_path = matches.get_one::<PathBuf>(GLUE_DIR).unwrap();
let spec_path = Path::new(matches.value_of_os(GLUE_SPEC).unwrap()); let spec_path = matches.get_one::<PathBuf>(GLUE_SPEC).unwrap();
// have the backend supply `roc_alloc` and friends // have the backend supply `roc_alloc` and friends
let backend = match matches.is_present(FLAG_DEV) { let backend = match matches.get_flag(FLAG_DEV) {
true => CodeGenBackend::Assembly(AssemblyBackendMode::Test), true => CodeGenBackend::Assembly(AssemblyBackendMode::Test),
false => CodeGenBackend::Llvm(LlvmBackendMode::BinaryGlue), false => CodeGenBackend::Llvm(LlvmBackendMode::BinaryGlue),
}; };
@ -108,8 +109,12 @@ fn main() -> io::Result<()> {
} }
} }
Some((CMD_GEN_STUB_LIB, matches)) => { Some((CMD_GEN_STUB_LIB, matches)) => {
let input_path = Path::new(matches.value_of_os(ROC_FILE).unwrap()); let input_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
let target: Target = matches.value_of_t(FLAG_TARGET).unwrap_or_default(); let target = matches
.get_one::<String>(FLAG_TARGET)
.map(|s| Target::from_str(s).ok())
.flatten()
.unwrap_or_default();
roc_linker::generate_stub_lib( roc_linker::generate_stub_lib(
input_path, input_path,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
@ -117,11 +122,12 @@ fn main() -> io::Result<()> {
) )
} }
Some((CMD_BUILD, matches)) => { Some((CMD_BUILD, matches)) => {
let target: Target = matches.value_of_t(FLAG_TARGET).unwrap_or_default(); let target = matches
let link_type = match ( .get_one::<String>(FLAG_TARGET)
matches.is_present(FLAG_LIB), .map(|s| Target::from_str(s).ok())
matches.is_present(FLAG_NO_LINK), .flatten()
) { .unwrap_or_default();
let link_type = match (matches.get_flag(FLAG_LIB), matches.get_flag(FLAG_NO_LINK)) {
(true, false) => LinkType::Dylib, (true, false) => LinkType::Dylib,
(true, true) => user_error!("build can only be one of `--lib` or `--no-link`"), (true, true) => user_error!("build can only be one of `--lib` or `--no-link`"),
(false, true) => LinkType::None, (false, true) => LinkType::None,
@ -139,22 +145,18 @@ fn main() -> io::Result<()> {
Some((CMD_CHECK, matches)) => { Some((CMD_CHECK, matches)) => {
let arena = bumpalo::Bump::new(); let arena = bumpalo::Bump::new();
let emit_timings = matches.is_present(FLAG_TIME); let emit_timings = matches.get_flag(FLAG_TIME);
let filename = matches.value_of_os(ROC_FILE).unwrap(); let roc_file_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
let roc_file_path = PathBuf::from(filename); let threading = match matches.get_one::<usize>(roc_cli::FLAG_MAX_THREADS) {
let threading = match matches
.value_of(roc_cli::FLAG_MAX_THREADS)
.and_then(|s| s.parse::<usize>().ok())
{
None => Threading::AllAvailable, None => Threading::AllAvailable,
Some(0) => user_error!("cannot build with at most 0 threads"), Some(0) => user_error!("cannot build with at most 0 threads"),
Some(1) => Threading::Single, Some(1) => Threading::Single,
Some(n) => Threading::AtMost(n), Some(n) => Threading::AtMost(*n),
}; };
match check_file( match check_file(
&arena, &arena,
roc_file_path, roc_file_path.to_owned(),
emit_timings, emit_timings,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
threading, threading,
@ -203,11 +205,11 @@ fn main() -> io::Result<()> {
Some((CMD_REPL, _)) => Ok(roc_repl_cli::main()), Some((CMD_REPL, _)) => Ok(roc_repl_cli::main()),
Some((CMD_EDIT, matches)) => { Some((CMD_EDIT, matches)) => {
match matches match matches
.values_of_os(DIRECTORY_OR_FILES) .get_many::<OsString>(DIRECTORY_OR_FILES)
.map(|mut values| values.next()) .map(|mut values| values.next())
{ {
Some(Some(os_str)) => { Some(Some(os_string)) => {
launch_editor(Some(Path::new(os_str)))?; launch_editor(Some(Path::new(os_string)))?;
} }
_ => { _ => {
launch_editor(None)?; launch_editor(None)?;
@ -218,14 +220,14 @@ fn main() -> io::Result<()> {
Ok(0) Ok(0)
} }
Some((CMD_DOCS, matches)) => { Some((CMD_DOCS, matches)) => {
let root_filename = matches.value_of_os(ROC_FILE).unwrap(); let root_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
generate_docs_html(PathBuf::from(root_filename)); generate_docs_html(root_path.to_owned());
Ok(0) Ok(0)
} }
Some((CMD_FORMAT, matches)) => { Some((CMD_FORMAT, matches)) => {
let maybe_values = matches.values_of_os(DIRECTORY_OR_FILES); let maybe_values = matches.get_many::<OsString>(DIRECTORY_OR_FILES);
let mut values: Vec<OsString> = Vec::new(); let mut values: Vec<OsString> = Vec::new();
@ -241,8 +243,8 @@ fn main() -> io::Result<()> {
} }
} }
Some(os_values) => { Some(os_values) => {
for os_str in os_values { for os_string in os_values {
values.push(os_str.to_os_string()); values.push(os_string.to_owned());
} }
} }
} }
@ -255,7 +257,7 @@ fn main() -> io::Result<()> {
roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?; roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?;
} }
let format_mode = match matches.is_present(FLAG_CHECK) { let format_mode = match matches.get_flag(FLAG_CHECK) {
true => FormatMode::CheckOnly, true => FormatMode::CheckOnly,
false => FormatMode::Format, false => FormatMode::Format,
}; };

View file

@ -1,5 +1,5 @@
//! Provides a binary that is only used for static build servers. //! Provides a binary that is only used for static build servers.
use clap::{Arg, Command}; use clap::{value_parser, Arg, Command};
use roc_docs::generate_docs_html; use roc_docs::generate_docs_html;
use std::io; use std::io;
use std::path::PathBuf; use std::path::PathBuf;
@ -12,16 +12,15 @@ fn main() -> io::Result<()> {
.about("Generate documentation for a Roc package") .about("Generate documentation for a Roc package")
.arg( .arg(
Arg::new(ROC_FILE) Arg::new(ROC_FILE)
.multiple_values(true)
.help("The package's main .roc file") .help("The package's main .roc file")
.allow_invalid_utf8(true) .num_args(0..)
.required(false) .value_parser(value_parser!(PathBuf))
.default_value(DEFAULT_ROC_FILENAME), .default_value(DEFAULT_ROC_FILENAME),
) )
.get_matches(); .get_matches();
// Populate roc_files // Populate roc_files
generate_docs_html(PathBuf::from(matches.value_of_os(ROC_FILE).unwrap())); generate_docs_html(matches.get_one::<PathBuf>(ROC_FILE).unwrap().to_owned());
Ok(()) Ok(())
} }

View file

@ -44,8 +44,7 @@ fn main() -> io::Result<()> {
let args_for_app = Arg::new(ARGS_FOR_APP) let args_for_app = Arg::new(ARGS_FOR_APP)
.help("Arguments to pass into the WebAssembly app\ne.g. `roc_wasm_interp app.wasm 123 123.45`") .help("Arguments to pass into the WebAssembly app\ne.g. `roc_wasm_interp app.wasm 123 123.45`")
.multiple_values(true) .num_args(0..);
.takes_value(true);
let app = Command::new("roc_wasm_interp") let app = Command::new("roc_wasm_interp")
.about("Run the given .wasm file") .about("Run the given .wasm file")
@ -53,7 +52,6 @@ fn main() -> io::Result<()> {
.arg(flag_debug) .arg(flag_debug)
.arg(flag_hex) .arg(flag_hex)
.arg(wasm_file_to_run) .arg(wasm_file_to_run)
.trailing_var_arg(true)
.arg(args_for_app); .arg(args_for_app);
// Parse the command line arguments // Parse the command line arguments