66) ]
77
88use anyhow:: { anyhow, bail, Context as _, Error , Result } ;
9+ use clap:: builder:: { OsStringValueParser , TypedValueParser } ;
910use clap:: Parser ;
1011use once_cell:: sync:: Lazy ;
12+ use std:: ffi:: OsStr ;
13+ use std:: ffi:: OsString ;
1114use std:: fs:: File ;
1215use std:: io:: Write ;
1316use std:: path:: { Path , PathBuf } ;
@@ -35,6 +38,18 @@ use wasmtime_wasi_threads::WasiThreadsCtx;
3538// #[cfg(feature = "wasi-http")]
3639// use wasmtime_wasi_http::WasiHttpCtx;
3740
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+
3853fn parse_env_var ( s : & str ) -> Result < ( String , Option < String > ) > {
3954 let mut parts = s. splitn ( 2 , '=' ) ;
4055 Ok ( (
@@ -103,7 +118,7 @@ static AFTER_HELP: Lazy<String> = Lazy::new(|| crate::FLAG_EXPLANATIONS.to_strin
103118
104119/// Runs a WebAssembly module
105120#[ 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( ) ) ]
107122pub struct RunCommand {
108123 #[ clap( flatten) ]
109124 common : CommonOptions ,
@@ -177,6 +192,14 @@ pub struct RunCommand {
177192 #[ clap( long = "wasi-nn-graph" , value_name = "FORMAT::HOST_DIR" , value_parser = parse_graphs) ]
178193 graphs : Vec < ( String , String ) > ,
179194
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+
180203 /// Load the given WebAssembly module before the main module
181204 #[ clap(
182205 long = "preload" ,
@@ -220,6 +243,11 @@ pub struct RunCommand {
220243 #[ clap( long = "coredump-on-trap" , value_name = "PATH" ) ]
221244 coredump_on_trap : Option < String > ,
222245
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+
223251 /// Maximum size, in bytes, that a linear memory is allowed to reach.
224252 ///
225253 /// Growth beyond this limit will cause `memory.grow` instructions in
@@ -258,14 +286,6 @@ pub struct RunCommand {
258286 #[ clap( long) ]
259287 wmemcheck : bool ,
260288
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-
269289 /// Indicates that the implementation of WASI preview1 should be backed by
270290 /// the preview2 implementation for components.
271291 ///
@@ -337,7 +357,7 @@ impl RunCommand {
337357 let engine = Engine :: new ( & config) ?;
338358
339359 // 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 ) ?;
341361
342362 // Validate coredump-on-trap argument
343363 if let Some ( coredump_path) = self . coredump_on_trap . as_ref ( ) {
@@ -429,12 +449,8 @@ impl RunCommand {
429449 // Load the main wasm module.
430450 match self
431451 . 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+ {
438454 Ok ( ( ) ) => ( ) ,
439455 Err ( e) => {
440456 // Exit the process if Wasmtime understands the error;
@@ -483,25 +499,27 @@ impl RunCommand {
483499 Ok ( listeners)
484500 }
485501
486- fn compute_argv ( & self ) -> Result < Vec < String > > {
502+ fn compute_argv ( & self ) -> Vec < String > {
487503 let mut result = Vec :: new ( ) ;
488504
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 ( ) ) ;
502520 }
503521
504- Ok ( result)
522+ result
505523 }
506524
507525 fn setup_epoch_handler (
@@ -510,7 +528,7 @@ impl RunCommand {
510528 modules : Vec < ( String , Module ) > ,
511529 ) -> Box < dyn FnOnce ( & mut Store < Host > ) > {
512530 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>" ) ;
514532 let interval = * interval;
515533 store. data_mut ( ) . guest_profiler =
516534 Some ( Arc :: new ( GuestProfiler :: new ( module_name, interval, modules) ) ) ;
@@ -616,10 +634,9 @@ impl RunCommand {
616634 CliLinker :: Core ( linker) => {
617635 // Use "" as a default module name.
618636 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) ) ?;
623640
624641 // If a function to invoke was given, invoke it.
625642 let func = if let Some ( name) = & self . invoke {
@@ -678,7 +695,7 @@ impl RunCommand {
678695 is experimental and may break in the future"
679696 ) ;
680697 }
681- let mut args = self . module_and_args . iter ( ) . skip ( 1 ) ;
698+ let mut args = self . module_args . iter ( ) ;
682699 let mut values = Vec :: new ( ) ;
683700 for ty in ty. params ( ) {
684701 let val = match args. next ( ) {
@@ -691,9 +708,6 @@ impl RunCommand {
691708 }
692709 }
693710 } ;
694- let val = val
695- . to_str ( )
696- . ok_or_else ( || anyhow ! ( "argument is not valid utf-8: {val:?}" ) ) ?;
697711 values. push ( match ty {
698712 // TODO: integer parsing here should handle hexadecimal notation
699713 // like `0x0...`, but the Rust standard library currently only
@@ -751,9 +765,7 @@ impl RunCommand {
751765 if !err. is :: < wasmtime:: Trap > ( ) {
752766 return err;
753767 }
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" ) ;
757769
758770 if let Err ( coredump_err) = generate_coredump ( & err, & source_name, coredump_path) {
759771 eprintln ! ( "warning: coredump failed to generate: {}" , coredump_err) ;
@@ -990,7 +1002,7 @@ impl RunCommand {
9901002
9911003 fn set_preview1_ctx ( & self , store : & mut Store < Host > ) -> Result < ( ) > {
9921004 let mut builder = WasiCtxBuilder :: new ( ) ;
993- builder. inherit_stdio ( ) . args ( & self . compute_argv ( ) ? ) ?;
1005+ builder. inherit_stdio ( ) . args ( & self . compute_argv ( ) ) ?;
9941006
9951007 for ( key, value) in self . vars . iter ( ) {
9961008 let value = match value {
@@ -1022,7 +1034,7 @@ impl RunCommand {
10221034
10231035 fn set_preview2_ctx ( & self , store : & mut Store < Host > ) -> Result < ( ) > {
10241036 let mut builder = preview2:: WasiCtxBuilder :: new ( ) ;
1025- builder. inherit_stdio ( ) . args ( & self . compute_argv ( ) ? ) ;
1037+ builder. inherit_stdio ( ) . args ( & self . compute_argv ( ) ) ;
10261038
10271039 for ( key, value) in self . vars . iter ( ) {
10281040 let value = match value {
0 commit comments