Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

impemented --runtime-args flag #19

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ members = [
'crates/cargo-wasi-shim',
'examples/markdown',
'examples/hello-world',
'examples/read-file',
]
exclude = ['tmp', 'target']

Expand All @@ -38,6 +39,7 @@ same-file = "1.0"
semver = "0.9"
serde = { version = "1", features = ['derive'] }
serde_json = "1"
structopt = "0.3.5"
tar = "0.4"
tempfile = "3"
termcolor = "1.0.5"
Expand Down
6 changes: 4 additions & 2 deletions doc/cli-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,17 @@ $ cargo wasi check --tests
## `cargo wasi run`

Forwards everything to `cargo run`, and runs all binaries in `wasmtime`.
Arguments passed will be forwarded to `wasmtime`. Note that it's not
necessary to run `cargo wasi build` before this subcommand. Example usage looks
Arguments passed will be forwarded to binaries. Additional runtime arguments
may be passed using `--runtime-args`. Note that it's not necessary to
run `cargo wasi build` before this subcommand. Example usage looks
like:

```
$ cargo wasi run
$ cargo wasi run --release
$ cargo wasi run arg1 arg2
$ cargo wasi run -- --flag-for-wasm-binary
$ cargo wasi run -- --runtime-args "--dir=."
$ cargo wasi run --bin foo
```

Expand Down
5 changes: 5 additions & 0 deletions examples/read-file/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "read-file"
version = "0.1.0"
authors = ["cargo-wasi contributors"]
edition = "2018"
7 changes: 7 additions & 0 deletions examples/read-file/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Fs example

An example of reading a file in wasi

```bash
cargo wasi run -- --runtime-args --dir=.
```
1 change: 1 addition & 0 deletions examples/read-file/data.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
some text
8 changes: 8 additions & 0 deletions examples/read-file/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use std::fs::read;
use std::io;

fn main() -> io::Result<()> {
let data = String::from_utf8(read("data.txt")?).unwrap();
println!("File contents: {}", data);
Ok(())
}
27 changes: 26 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::cache::Cache;
use crate::config::Config;
use crate::utils::CommandExt;
use anyhow::{bail, Context, Result};
use structopt::StructOpt;
use std::env;
use std::fs;
use std::io::{self, Read};
Expand Down Expand Up @@ -44,10 +45,32 @@ enum Subcommand {
Fix,
}

#[derive(StructOpt, Debug)]
struct Build {
#[structopt(long)]
runtime_args: String,
binary_args: Vec<String>,
}

#[derive(StructOpt, Debug)]
enum Options {
Build(Build),
Run,
Test,
Bench,
Check,
Fix,
#[structopt(name = "self")]
Main,
}

fn rmain(config: &mut Config) -> Result<()> {
config.load_cache()?;

// skip the current executable and the `wasi` inserted by Cargo
println!("Raw args: {:?}", env::args());
let options = Options::from_args();
println!("Args from structopt: {:?}", options);
let mut args = env::args_os().skip(2);
let subcommand = args.next().and_then(|s| s.into_string().ok());
let subcommand = match subcommand.as_ref().map(|s| s.as_str()) {
Expand Down Expand Up @@ -177,9 +200,11 @@ fn rmain(config: &mut Config) -> Result<()> {

for run in build.runs.iter() {
config.status("Running", &format!("`{}`", run.join(" ")));
let (runtime_args, binary_args) = utils::split_args(run);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the way we'll want to handle this is do some option parsing before the -- argument. Ideally this would use a full-fledged option parser, but we'll want to recognize --runtime-args as an argument to cargo wasi run, but not forward the argument to cargo run.

Put another way, --runtime-args is a flag to cargo wasi run, which means it needs to go before the --.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this would use a full-fledged option parser

which one would you suggest to use here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have one in mind per se, I haven't looked into doing this yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just pushed an example using structopt. What do you think about it? Is it viable to continue this way?

Command::new("wasmtime")
.args(runtime_args.iter())
.arg("--")
.args(run.iter())
.args(binary_args.iter())
.run()
.map_err(|e| utils::hide_normal_process_exit(e, config))?;
}
Expand Down
70 changes: 70 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,73 @@ pub fn get(url: &str) -> Result<reqwest::Response> {
}
Ok(response)
}

pub fn split_args(args: &[String]) -> (Vec<String>, Vec<String>) {
let mut runtime_args = vec![];
let mut binary_args = vec![];

for i in 0..args.len() {
let argument = &args[i];
if argument == "--runtime-args" {
if i + 1 < args.len() {
runtime_args.extend_from_slice(&args[i + 1..]);
}
break;
} else {
binary_args.push(argument.clone());
}
}

(runtime_args, binary_args)
}

#[test]
fn split_args_empty() {
let (runtime_args, binary_args) = split_args(&[]);
assert_eq!((runtime_args, binary_args), (vec![], vec![]),);
}

#[test]
fn split_args_only_binary() {
let (runtime_args, binary_args) = split_args(&vec!["a".to_owned(), "b".to_owned()]);
assert_eq!(
(runtime_args, binary_args),
(vec![], vec!["a".to_owned(), "b".to_owned()]),
);
}

#[test]
fn split_args_only_runtime() {
let (runtime_args, binary_args) =
split_args(&vec!["--runtime-args".to_owned(), "--dir=.".to_owned()]);
assert_eq!(
(runtime_args, binary_args),
(vec!["--dir=.".to_owned()], vec![]),
);
}

#[test]
fn split_args_runtime_before_binary() {
let (runtime_args, binary_args) = split_args(&vec![
"--runtime-args".to_owned(),
"--dir=.".to_owned(),
"a".to_owned(),
]);
assert_eq!(
(runtime_args, binary_args),
(vec!["--dir=.".to_owned(), "a".to_owned()], vec![]),
);
}

#[test]
fn split_args_runtime_after_binary() {
let (runtime_args, binary_args) = split_args(&vec![
"a".to_owned(),
"--runtime-args".to_owned(),
"--dir=.".to_owned(),
]);
assert_eq!(
(runtime_args, binary_args),
(vec!["--dir=.".to_owned()], vec!["a".to_owned()]),
);
}
64 changes: 64 additions & 0 deletions tests/tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,70 @@ fn run_forward_args() -> Result<()> {
Ok(())
}

#[test]
fn run_fs_access_error() -> Result<()> {
support::project()
.file("data.txt", "text")
.file(
"src/main.rs",
r#"
use std::fs::read;
use std::io;
fn main() -> Result<(), io::Error> {
let text = String::from_utf8(read("data.txt")?).unwrap();
println!("File contents: {}", text);
Ok(())
}
"#,
)
.build()
.cargo_wasi("run")
.assert()
.stderr(is_match(
"^\
.*Compiling foo v1.0.0 .*
.*Finished dev .*
.*Running `.*`
.*Running `.*`
.*failed to find a preopened file descriptor.*
$",
)?)
.code(1);
Ok(())
}

#[test]
fn run_fs_access_success() -> Result<()> {
support::project()
.file("data.txt", "some text")
.file(
"src/main.rs",
r#"
use std::fs::read;
use std::io;
fn main() -> Result<(), io::Error> {
let text = String::from_utf8(read("data.txt")?).unwrap();
println!("File contents: {}", text);
Ok(())
}
"#,
)
.build()
.cargo_wasi("run -- --runtime-args --dir=.")
.assert()
.stdout("File contents: some text\n")
.stderr(is_match(
"^\
.*Compiling foo v1.0.0 .*
.*Finished dev .*
.*Running `.*`
.*Running `.*`
$",
)?)
.success();
Ok(())
}

#[test]
fn test() -> Result<()> {
support::project()
Expand Down