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::process::Command;
use tokio::runtime::Builder; use tokio::runtime::Builder;
fn main() -> io::Result<()> {
run(build_app().get_matches())
}
pub static FLAG_OPTIMIZE: &str = "optimize"; pub static FLAG_OPTIMIZE: &str = "optimize";
pub static FLAG_ROC_FILE: &str = "ROC_FILE"; pub static FLAG_ROC_FILE: &str = "ROC_FILE";
pub fn build_app<'a>() -> App<'a> { pub fn build_app<'a>() -> App<'a> {
App::new("roc") App::new("roc")
.version(crate_version!()) .version(crate_version!())
.arg( .subcommand(App::new("build")
Arg::with_name(FLAG_ROC_FILE) .about("Build a program")
.help("The .roc file to compile and run") .arg(
.required(true), 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( .subcommand(App::new("run")
Arg::with_name(FLAG_OPTIMIZE) .about("Build and run a program")
.long(FLAG_OPTIMIZE) .arg(
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)") Arg::with_name(FLAG_ROC_FILE)
.required(false), .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. fn main() -> io::Result<()> {
pub fn run(matches: ArgMatches) -> 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 filename = matches.value_of(FLAG_ROC_FILE).unwrap();
let opt_level = if matches.is_present(FLAG_OPTIMIZE) { let opt_level = if matches.is_present(FLAG_OPTIMIZE) {
OptLevel::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(()) Ok(())
} }
async fn load_file( async fn build_file(
src_dir: PathBuf, src_dir: PathBuf,
filename: PathBuf, filename: PathBuf,
opt_level: OptLevel, opt_level: OptLevel,
) -> Result<(), LoadingProblem> { ) -> Result<PathBuf, LoadingProblem> {
let compilation_start = SystemTime::now(); let compilation_start = SystemTime::now();
let arena = Bump::new(); let arena = Bump::new();
@ -176,22 +212,7 @@ async fn load_file(
todo!("gracefully handle error after `rustc` spawned"); todo!("gracefully handle error after `rustc` spawned");
})?; })?;
// Step 4: Run the compiled app Ok(binary_path)
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(())
} }
fn gen( fn gen(

View file

@ -92,13 +92,12 @@ mod cli_run {
#[test] #[test]
fn run_hello_world() { 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, ""); 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.stdout.ends_with("Hello, World!\n"));
assert!(out.status.success()); assert!(out.status.success());
} }

View file

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

View file

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