Add CLI subcommands roc run and roc build

This commit is contained in:
Richard Feldman 2020-04-18 14:28:57 -04:00
parent ee481e6713
commit d4a45ed489
5 changed files with 64 additions and 59 deletions

View file

@ -1,15 +0,0 @@
# Interpreter
Usage:
```
$ roc FILENAME.roc
```
When building from Rust source, use `cargo run -- FILENAME.roc` instead of `roc FILENAME.roc`.
For example, here's how to run the "EchoName" example this way:
```
$ cargo run -- examples/EchoName.roc
```

View file

@ -32,31 +32,53 @@ use target_lexicon::{Architecture, OperatingSystem, Triple, Vendor};
use tokio::process::Command;
use tokio::runtime::Builder;
fn main() -> io::Result<()> {
run(build_app().get_matches())
}
pub static FLAG_OPTIMIZE: &str = "optimize";
pub static FLAG_ROC_FILE: &str = "ROC_FILE";
pub fn build_app<'a>() -> App<'a> {
App::new("roc")
.version(crate_version!())
.arg(
Arg::with_name(FLAG_ROC_FILE)
.help("The .roc file to compile and run")
.required(true),
.subcommand(App::new("build")
.about("Build a program")
.arg(
Arg::with_name(FLAG_ROC_FILE)
.help("The .roc file to build")
.required(true),
)
.arg(
Arg::with_name(FLAG_OPTIMIZE)
.long(FLAG_OPTIMIZE)
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
.required(false),
)
)
.arg(
Arg::with_name(FLAG_OPTIMIZE)
.long(FLAG_OPTIMIZE)
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
.required(false),
.subcommand(App::new("run")
.about("Build and run a program")
.arg(
Arg::with_name(FLAG_ROC_FILE)
.help("The .roc file to build and run")
.required(true),
)
.arg(
Arg::with_name(FLAG_OPTIMIZE)
.long(FLAG_OPTIMIZE)
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
.required(false),
)
)
}
/// Run the CLI. This is separate from main() so that tests can call it directly.
pub fn run(matches: ArgMatches) -> io::Result<()> {
fn main() -> io::Result<()> {
let matches = build_app().get_matches();
match matches.subcommand_name() {
Some("build") => build(matches.subcommand_matches("build").unwrap(), false),
Some("run") => build(matches.subcommand_matches("run").unwrap(), true),
_ => unreachable!(),
}
}
pub fn build(matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
let filename = matches.value_of(FLAG_ROC_FILE).unwrap();
let opt_level = if matches.is_present(FLAG_OPTIMIZE) {
OptLevel::Optimize
@ -92,18 +114,32 @@ pub fn run(matches: ArgMatches) -> io::Result<()> {
}
}
});
let loaded = rt.block_on(load_file(src_dir, path, opt_level));
let binary_path = rt
.block_on(build_file(src_dir, path, opt_level))
.expect("TODO gracefully handle block_on failing");
loaded.expect("TODO gracefully handle LoadingProblem");
if run_after_build {
// Run the compiled app
rt.block_on(async {
Command::new(binary_path)
.spawn()
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
.await
.map_err(|_| {
todo!("gracefully handle error after `app` spawned");
})
})
.expect("TODO gracefully handle block_on failing");
}
Ok(())
}
async fn load_file(
async fn build_file(
src_dir: PathBuf,
filename: PathBuf,
opt_level: OptLevel,
) -> Result<(), LoadingProblem> {
) -> Result<PathBuf, LoadingProblem> {
let compilation_start = SystemTime::now();
let arena = Bump::new();
@ -176,22 +212,7 @@ async fn load_file(
todo!("gracefully handle error after `rustc` spawned");
})?;
// Step 4: Run the compiled app
Command::new(binary_path)
.spawn()
.unwrap_or_else(|err| {
panic!(
"{} failed to run: {:?}",
cwd.join("app").to_str().unwrap(),
err
)
})
.await
.map_err(|_| {
todo!("gracefully handle error after `app` spawned");
})?;
Ok(())
Ok(binary_path)
}
fn gen(

View file

@ -92,13 +92,12 @@ mod cli_run {
#[test]
fn run_hello_world() {
let out = run_roc(&[example_file("hello-world", "Hello.roc").to_str().unwrap()]);
let out = run_roc(&[
"run",
example_file("hello-world", "Hello.roc").to_str().unwrap(),
]);
assert_eq!(&out.stderr, "");
// TODO make separate `roc build` and `roc run` commands, and here do
// `roc build` followed by manually executing the compiled `app` binary
// and doing an `assert_eq!` on the entire stdout of that compiled `app` binary
assert!(&out.stdout.ends_with("Hello, World!\n"));
assert!(out.status.success());
}

View file

@ -3,13 +3,13 @@
To run:
```bash
$ cargo run hello.roc
$ cargo run run Hello.roc
```
To run in release mode instead, do:
```bash
$ cargo run --release hello.roc
$ cargo run --release run Hello.roc
```
## Design Notes

View file

@ -3,11 +3,11 @@
To run:
```bash
$ cargo run qs.roc
$ cargo run run Quicksort.roc
```
To run in release mode instead, do:
```bash
$ cargo run --release qs.roc
$ cargo run --release run Quicksort.roc
```