6
6
) ]
7
7
8
8
use anyhow:: { anyhow, bail, Context as _, Error , Result } ;
9
+ use clap:: builder:: { OsStringValueParser , TypedValueParser } ;
9
10
use clap:: Parser ;
10
11
use once_cell:: sync:: Lazy ;
12
+ use std:: ffi:: OsStr ;
13
+ use std:: ffi:: OsString ;
11
14
use std:: fs:: File ;
12
15
use std:: io:: Write ;
13
16
use std:: path:: { Path , PathBuf } ;
@@ -35,6 +38,18 @@ use wasmtime_wasi_threads::WasiThreadsCtx;
35
38
// #[cfg(feature = "wasi-http")]
36
39
// use wasmtime_wasi_http::WasiHttpCtx;
37
40
41
+ fn parse_module ( s : OsString ) -> anyhow:: Result < PathBuf > {
42
+ // Do not accept wasmtime subcommand names as the module name
43
+ match s. to_str ( ) {
44
+ Some ( "help" ) | Some ( "config" ) | Some ( "run" ) | Some ( "wast" ) | Some ( "compile" ) => {
45
+ bail ! ( "module name cannot be the same as a subcommand" )
46
+ }
47
+ #[ cfg( unix) ]
48
+ Some ( "-" ) => Ok ( PathBuf :: from ( "/dev/stdin" ) ) ,
49
+ _ => Ok ( s. into ( ) ) ,
50
+ }
51
+ }
52
+
38
53
fn parse_env_var ( s : & str ) -> Result < ( String , Option < String > ) > {
39
54
let mut parts = s. splitn ( 2 , '=' ) ;
40
55
Ok ( (
@@ -103,7 +118,7 @@ static AFTER_HELP: Lazy<String> = Lazy::new(|| crate::FLAG_EXPLANATIONS.to_strin
103
118
104
119
/// Runs a WebAssembly module
105
120
#[ derive( Parser ) ]
106
- #[ structopt( name = "run" , after_help = AFTER_HELP . as_str( ) ) ]
121
+ #[ structopt( name = "run" , trailing_var_arg = true , after_help = AFTER_HELP . as_str( ) ) ]
107
122
pub struct RunCommand {
108
123
#[ clap( flatten) ]
109
124
common : CommonOptions ,
@@ -177,6 +192,14 @@ pub struct RunCommand {
177
192
#[ clap( long = "wasi-nn-graph" , value_name = "FORMAT::HOST_DIR" , value_parser = parse_graphs) ]
178
193
graphs : Vec < ( String , String ) > ,
179
194
195
+ /// The path of the WebAssembly module to run
196
+ #[ clap(
197
+ required = true ,
198
+ value_name = "MODULE" ,
199
+ value_parser = OsStringValueParser :: new( ) . try_map( parse_module) ,
200
+ ) ]
201
+ module : PathBuf ,
202
+
180
203
/// Load the given WebAssembly module before the main module
181
204
#[ clap(
182
205
long = "preload" ,
@@ -220,6 +243,11 @@ pub struct RunCommand {
220
243
#[ clap( long = "coredump-on-trap" , value_name = "PATH" ) ]
221
244
coredump_on_trap : Option < String > ,
222
245
246
+ // NOTE: this must come last for trailing varargs
247
+ /// The arguments to pass to the module
248
+ #[ clap( value_name = "ARGS" ) ]
249
+ module_args : Vec < String > ,
250
+
223
251
/// Maximum size, in bytes, that a linear memory is allowed to reach.
224
252
///
225
253
/// Growth beyond this limit will cause `memory.grow` instructions in
@@ -258,14 +286,6 @@ pub struct RunCommand {
258
286
#[ clap( long) ]
259
287
wmemcheck : bool ,
260
288
261
- /// The WebAssembly module to run and arguments to pass to it.
262
- ///
263
- /// Arguments passed to the wasm module will be configured as WASI CLI
264
- /// arguments unless the `--invoke` CLI argument is passed in which case
265
- /// arguments will be interpreted as arguments to the function specified.
266
- #[ clap( value_name = "WASM" , trailing_var_arg = true , required = true ) ]
267
- module_and_args : Vec < PathBuf > ,
268
-
269
289
/// Indicates that the implementation of WASI preview1 should be backed by
270
290
/// the preview2 implementation for components.
271
291
///
@@ -337,7 +357,7 @@ impl RunCommand {
337
357
let engine = Engine :: new ( & config) ?;
338
358
339
359
// Read the wasm module binary either as `*.wat` or a raw binary.
340
- let main = self . load_module ( & engine, & self . module_and_args [ 0 ] ) ?;
360
+ let main = self . load_module ( & engine, & self . module ) ?;
341
361
342
362
// Validate coredump-on-trap argument
343
363
if let Some ( coredump_path) = self . coredump_on_trap . as_ref ( ) {
@@ -429,12 +449,8 @@ impl RunCommand {
429
449
// Load the main wasm module.
430
450
match self
431
451
. load_main_module ( & mut store, & mut linker, & main, modules)
432
- . with_context ( || {
433
- format ! (
434
- "failed to run main module `{}`" ,
435
- self . module_and_args[ 0 ] . display( )
436
- )
437
- } ) {
452
+ . with_context ( || format ! ( "failed to run main module `{}`" , self . module. display( ) ) )
453
+ {
438
454
Ok ( ( ) ) => ( ) ,
439
455
Err ( e) => {
440
456
// Exit the process if Wasmtime understands the error;
@@ -483,25 +499,27 @@ impl RunCommand {
483
499
Ok ( listeners)
484
500
}
485
501
486
- fn compute_argv ( & self ) -> Result < Vec < String > > {
502
+ fn compute_argv ( & self ) -> Vec < String > {
487
503
let mut result = Vec :: new ( ) ;
488
504
489
- for ( i, arg) in self . module_and_args . iter ( ) . enumerate ( ) {
490
- // For argv[0], which is the program name. Only include the base
491
- // name of the main wasm module, to avoid leaking path information.
492
- let arg = if i == 0 {
493
- arg. components ( ) . next_back ( ) . unwrap ( ) . as_os_str ( )
494
- } else {
495
- arg. as_ref ( )
496
- } ;
497
- result. push (
498
- arg. to_str ( )
499
- . ok_or_else ( || anyhow ! ( "failed to convert {arg:?} to utf-8" ) ) ?
500
- . to_string ( ) ,
501
- ) ;
505
+ // Add argv[0], which is the program name. Only include the base name of the
506
+ // main wasm module, to avoid leaking path information.
507
+ result. push (
508
+ self . module
509
+ . components ( )
510
+ . next_back ( )
511
+ . map ( |c| c. as_os_str ( ) )
512
+ . and_then ( OsStr :: to_str)
513
+ . unwrap_or ( "" )
514
+ . to_owned ( ) ,
515
+ ) ;
516
+
517
+ // Add the remaining arguments.
518
+ for arg in self . module_args . iter ( ) {
519
+ result. push ( arg. clone ( ) ) ;
502
520
}
503
521
504
- Ok ( result)
522
+ result
505
523
}
506
524
507
525
fn setup_epoch_handler (
@@ -510,7 +528,7 @@ impl RunCommand {
510
528
modules : Vec < ( String , Module ) > ,
511
529
) -> Box < dyn FnOnce ( & mut Store < Host > ) > {
512
530
if let Some ( Profile :: Guest { path, interval } ) = & self . profile {
513
- let module_name = self . module_and_args [ 0 ] . to_str ( ) . unwrap_or ( "<main module>" ) ;
531
+ let module_name = self . module . to_str ( ) . unwrap_or ( "<main module>" ) ;
514
532
let interval = * interval;
515
533
store. data_mut ( ) . guest_profiler =
516
534
Some ( Arc :: new ( GuestProfiler :: new ( module_name, interval, modules) ) ) ;
@@ -616,10 +634,9 @@ impl RunCommand {
616
634
CliLinker :: Core ( linker) => {
617
635
// Use "" as a default module name.
618
636
let module = module. unwrap_core ( ) ;
619
- linker. module ( & mut * store, "" , & module) . context ( format ! (
620
- "failed to instantiate {:?}" ,
621
- self . module_and_args[ 0 ]
622
- ) ) ?;
637
+ linker
638
+ . module ( & mut * store, "" , & module)
639
+ . context ( format ! ( "failed to instantiate {:?}" , self . module) ) ?;
623
640
624
641
// If a function to invoke was given, invoke it.
625
642
let func = if let Some ( name) = & self . invoke {
@@ -678,7 +695,7 @@ impl RunCommand {
678
695
is experimental and may break in the future"
679
696
) ;
680
697
}
681
- let mut args = self . module_and_args . iter ( ) . skip ( 1 ) ;
698
+ let mut args = self . module_args . iter ( ) ;
682
699
let mut values = Vec :: new ( ) ;
683
700
for ty in ty. params ( ) {
684
701
let val = match args. next ( ) {
@@ -691,9 +708,6 @@ impl RunCommand {
691
708
}
692
709
}
693
710
} ;
694
- let val = val
695
- . to_str ( )
696
- . ok_or_else ( || anyhow ! ( "argument is not valid utf-8: {val:?}" ) ) ?;
697
711
values. push ( match ty {
698
712
// TODO: integer parsing here should handle hexadecimal notation
699
713
// like `0x0...`, but the Rust standard library currently only
@@ -751,9 +765,7 @@ impl RunCommand {
751
765
if !err. is :: < wasmtime:: Trap > ( ) {
752
766
return err;
753
767
}
754
- let source_name = self . module_and_args [ 0 ]
755
- . to_str ( )
756
- . unwrap_or_else ( || "unknown" ) ;
768
+ let source_name = self . module . to_str ( ) . unwrap_or_else ( || "unknown" ) ;
757
769
758
770
if let Err ( coredump_err) = generate_coredump ( & err, & source_name, coredump_path) {
759
771
eprintln ! ( "warning: coredump failed to generate: {}" , coredump_err) ;
@@ -990,7 +1002,7 @@ impl RunCommand {
990
1002
991
1003
fn set_preview1_ctx ( & self , store : & mut Store < Host > ) -> Result < ( ) > {
992
1004
let mut builder = WasiCtxBuilder :: new ( ) ;
993
- builder. inherit_stdio ( ) . args ( & self . compute_argv ( ) ? ) ?;
1005
+ builder. inherit_stdio ( ) . args ( & self . compute_argv ( ) ) ?;
994
1006
995
1007
for ( key, value) in self . vars . iter ( ) {
996
1008
let value = match value {
@@ -1022,7 +1034,7 @@ impl RunCommand {
1022
1034
1023
1035
fn set_preview2_ctx ( & self , store : & mut Store < Host > ) -> Result < ( ) > {
1024
1036
let mut builder = preview2:: WasiCtxBuilder :: new ( ) ;
1025
- builder. inherit_stdio ( ) . args ( & self . compute_argv ( ) ? ) ;
1037
+ builder. inherit_stdio ( ) . args ( & self . compute_argv ( ) ) ;
1026
1038
1027
1039
for ( key, value) in self . vars . iter ( ) {
1028
1040
let value = match value {
0 commit comments