diff --git a/blockless-cli/Cargo.toml b/blockless-cli/Cargo.toml index 6f799a4..539285a 100644 --- a/blockless-cli/Cargo.toml +++ b/blockless-cli/Cargo.toml @@ -15,6 +15,7 @@ tokio = {version = "1", features = ["rt", "net", "time"]} log = "0.4.17" rust-car = {workspace = true} md5 = {workspace = true} +clap = "4.2.1" [dependencies.env_logger] version = "0.8" diff --git a/blockless-cli/src/cli_clap.rs b/blockless-cli/src/cli_clap.rs new file mode 100644 index 0000000..52da25b --- /dev/null +++ b/blockless-cli/src/cli_clap.rs @@ -0,0 +1,125 @@ +#![allow(unused)] +use blockless::BlocklessConfig; +use clap::{Arg, ArgMatches, Command}; + +pub(crate) fn cli_command() -> Command { + Command::new("blockless_cli") + .arg( + Arg::new("input") + .help("the input file is wasm file, configure file, or car file") + .required(true) + ) + .arg( + Arg::new("debug_info") + .long("debug_info") + .help("the debug info for the runtime.") + .required(false), + ) + .arg( + Arg::new("fs_root_path") + .long("fs_root_path") + .help("the root directory for the runtime.") + .required(false), + ) + .arg( + Arg::new("runtime_logger") + .long("runtime_logger") + .help("the logger file for the runtime.") + .required(false), + ) + .arg( + Arg::new("limited_memory") + .long("limited_memory") + .help("the limited memory for the runtime, default is infine.") + .required(false), + ) + .arg( + Arg::new("run_time") + .long("run_time") + .help("the run time for the runtime, default is infine.") + .required(false), + ) + .arg( + Arg::new("entry") + .long("entry") + .help("the entry for wasm, default is _start") + .required(false), + ) + .arg( + Arg::new("limited_fuel") + .long("limited_fuel") + .help("the limited fuel for runtime, default is infine") + .required(false), + ) + .allow_missing_positional(true) +} + +#[rustfmt::skip] +pub(crate) fn apply_config(cfg: &mut BlocklessConfig, matches: &ArgMatches) { + matches.get_one::("debug_info").map(|d| cfg.debug_info(*d)); + matches.get_one::("fs_root_path").map(|f| cfg.fs_root_path(Some(f.clone()))); + matches.get_one::("runtime_logger").map(|f| cfg.runtime_logger(Some(f.clone()))); + matches.get_one::("entry").map(|f| cfg.entry(f.clone())); + matches.get_one::("limited_memory").map(|f| cfg.limited_memory(Some(*f))); + matches.get_one::("run_time").map(|f| cfg.set_run_time(Some(*f))); + matches.get_one::("limited_fuel").map(|f| cfg.limited_fuel(Some(*f))); +} + +#[cfg(test)] +mod test { + + #[allow(unused)] + use super::*; + + #[test] + fn test_cli_command_input() { + let cli_cmd = cli_command(); + let command_line = r#"blockless_cli test.wasm"#; + let command_line = command_line + .split(" ") + .map(str::to_string) + .collect::>(); + let matches = cli_cmd.try_get_matches_from(command_line).unwrap(); + let input = matches.get_one::("input"); + let pat = match input { + Some(p) => p, + None => unreachable!("can't reach here!"), + }; + assert_eq!(pat, &"test.wasm"); + } + + #[test] + fn test_cli_command_runtime_log() { + let cli_cmd = cli_command(); + let command_line = r#"blockless_cli test.wasm --runtime_logger runtime.log"#; + let command_line = command_line + .split(" ") + .map(str::to_string) + .collect::>(); + let matches = cli_cmd.try_get_matches_from(command_line).unwrap(); + let input = matches.get_one::("runtime_logger"); + let pat = match input { + Some(p) => p, + None => unreachable!("can't reach here!"), + }; + assert_eq!(pat, &"runtime.log"); + } + + #[test] + fn test_cli_command_fs_root_path() { + let cli_cmd = cli_command(); + let command_line = r#"blockless_cli test.wasm --fs_root_path /"#; + let command_line = command_line + .split(" ") + .map(str::to_string) + .collect::>(); + let matches = cli_cmd.try_get_matches_from(command_line).unwrap(); + let input = matches.get_one::("fs_root_path"); + let pat = match input { + Some(p) => p, + None => unreachable!("can't reach here!"), + }; + assert_eq!(pat, &"/"); + } + +} diff --git a/blockless-cli/src/config.rs b/blockless-cli/src/config.rs index 4a68a52..7d03bb8 100644 --- a/blockless-cli/src/config.rs +++ b/blockless-cli/src/config.rs @@ -3,7 +3,9 @@ use blockless::{self, LoggerLevel, BlocklessModule, ModuleType}; use blockless::{BlocklessConfig, DriverConfig, MultiAddr, Permission}; use json::{self, JsonValue}; use std::env::VarError; +use std::ffi::OsStr; use std::fs; +use std::os::unix::prelude::OsStrExt; use std::path::{PathBuf, Path}; pub(crate) struct CliConfig(pub(crate) BlocklessConfig); @@ -15,6 +17,33 @@ struct EnvVar { impl CliConfig { + fn defaut_logger_file(filen: &OsStr) -> Option { + let filen = filen.as_bytes(); + let p = match filen.iter().position(|b| *b == b'.') { + Some(p) => p, + None => return Some("runtime".to_string()), + }; + OsStr::from_bytes(&filen[..p]) + .to_os_string() + .to_str() + .map(String::from) + } + + /// config the wasm file as entry file + /// current directory as the root path + pub fn new_with_wasm(wasm_file: impl AsRef) -> CliConfig { + let file_path = wasm_file.as_ref(); + let file_name = file_path.file_name().unwrap(); + let log_file = Self::defaut_logger_file(file_name); + let mut bconf = BlocklessConfig::new(file_path.to_str().unwrap()); + bconf.fs_root_path(Some(".".to_string())); + bconf.runtime_logger_level(LoggerLevel::WARN); + log_file.as_ref().map(|log_file| { + bconf.runtime_logger(Some(format!("{log_file}.log"))); + }); + CliConfig(bconf) + } + fn permissions(permission_json: &JsonValue) -> Vec { match *permission_json { JsonValue::Array(ref perms) => perms @@ -184,10 +213,39 @@ impl CliConfig { #[cfg(test)] mod test { #![allow(unused)] + use std::ffi::OsString; + use blockless::BlocklessConfigVersion; use super::*; + #[test] + fn test_defaut_logger_file() { + let filen: OsString = "test.wasm".into(); + let filen = CliConfig::defaut_logger_file(&filen); + let filen = filen.unwrap(); + assert_eq!(filen, "test".to_string()); + + let filen: OsString = "test".into(); + let filen = CliConfig::defaut_logger_file(&filen); + let filen = filen.unwrap(); + assert_eq!(filen, "runtime".to_string()); + } + + #[test] + fn test_new_with_wasm() { + let cliconf = CliConfig::new_with_wasm("test.wasm"); + let current = Some("."); + let root_path = cliconf.0.fs_root_path_ref(); + assert_eq!(root_path, current); + let config_logger_ref = cliconf.0.runtime_logger_ref(); + let logger_ref = Some("./test.log".into()); + assert_eq!(logger_ref, config_logger_ref); + + let logger_level = cliconf.0.runtime_logger_level_ref(); + assert!(matches!(&LoggerLevel::WARN, logger_level)); + } + #[test] fn test_load_config() { let data = r#"{ diff --git a/blockless-cli/src/main.rs b/blockless-cli/src/main.rs index 858ad41..8db4fb2 100644 --- a/blockless-cli/src/main.rs +++ b/blockless-cli/src/main.rs @@ -1,17 +1,19 @@ +mod cli_clap; mod config; use blockless::{ blockless_run, LoggerLevel }; +#[allow(unused_imports)] +use cli_clap::apply_config; use config::CliConfig; use anyhow::Result; use std::{ - env, io::{self, Read}, fs::File, path::PathBuf, time::Duration, - process::ExitCode + process::ExitCode, env }; use env_logger::Target; use tokio::runtime::Builder; @@ -117,19 +119,30 @@ fn check_module_sum(cfg: &CliConfig) -> Option { None } -fn load_cli_config(conf_path: &str) -> Result { - let ext = Path::new(conf_path).extension(); +fn load_wasm_directly(wasmfile: &str) -> Result { + Ok(CliConfig::new_with_wasm(wasmfile)) +} + +/// the cli support 3 type file, +/// 1. the car file format, all files archive into the car file. +/// 2. the wasm or wasi file format, will run wasm directly. +/// 3. the the config file, format, all files is define in the config file. +fn load_cli_config(file_path: &str) -> Result { + let ext = Path::new(file_path).extension(); let cfg = ext.and_then(|ext| ext.to_str().map(str::to_ascii_lowercase)); let cli_config = match cfg { - Some(f) if f == "car" => { + Some(ext) if ext == "car" => { let file = fs::OpenOptions::new() .read(true) - .open(conf_path)?; + .open(file_path)?; Some(load_extract_from_car(file)) }, + Some(ext) if ext == "wasm" || ext == "wasi" => { + Some(load_wasm_directly(file_path)) + }, _ => None, }; - cli_config.unwrap_or_else(|| CliConfig::from_file(conf_path)) + cli_config.unwrap_or_else(|| CliConfig::from_file(file_path)) } fn main() -> ExitCode { @@ -191,6 +204,16 @@ mod test { }; use super::*; + #[test] + fn test_load_cli_wasm_config() { + let wasm_conf = load_cli_config("test.wasm"); + let wasm_conf = wasm_conf.unwrap(); + let entry_ref = wasm_conf.0.entry_ref(); + assert_eq!(entry_ref, "test.wasm"); + let root_path_ref = wasm_conf.0.fs_root_path_ref(); + assert_eq!(root_path_ref, Some(".")); + } + #[test] fn test_load_from_car() { let mut buf = Vec::new(); diff --git a/crates/wasi-common/src/blockless/config.rs b/crates/wasi-common/src/blockless/config.rs index db71be1..9b11c12 100644 --- a/crates/wasi-common/src/blockless/config.rs +++ b/crates/wasi-common/src/blockless/config.rs @@ -145,6 +145,11 @@ impl BlocklessConfig { self.veriosn } + #[inline(always)] + pub fn entry(&mut self, entry: String) { + self.entry = entry; + } + #[inline(always)] pub fn set_version(&mut self, version: BlocklessConfigVersion) { self.veriosn = version; @@ -258,7 +263,7 @@ impl BlocklessConfig { entry: String::from(entry), permisions: Default::default(), group_permisions: HashMap::new(), - runtime_logger_level: LoggerLevel::INFO, + runtime_logger_level: LoggerLevel::WARN, veriosn: BlocklessConfigVersion::Version0, } } @@ -396,6 +401,10 @@ mod test { config.runtime_logger(test); let result = PathBuf::new().join("/root").join("test.log"); assert_eq!(config.runtime_logger_ref().unwrap(), result); + + assert_eq!(config.entry_ref(), "test"); + config.entry("_start".into()); + assert_eq!(config.entry_ref(), "_start"); } #[test]