From 816dcdc304354ab96447b3a9d6fc9b1749d1d9f5 Mon Sep 17 00:00:00 2001 From: Wodann Date: Fri, 4 Oct 2019 10:53:56 +0200 Subject: [PATCH] feat(cli): add subcommand 'start' to start the Mun runtime --- crates/mun/Cargo.toml | 1 + crates/mun/src/main.rs | 44 ++++++++++++++ crates/mun_runtime/src/assembly.rs | 6 +- crates/mun_runtime/src/error.rs | 37 ------------ crates/mun_runtime/src/lib.rs | 94 ++++++++++++++---------------- crates/mun_runtime/src/library.rs | 4 +- 6 files changed, 95 insertions(+), 91 deletions(-) delete mode 100644 crates/mun_runtime/src/error.rs diff --git a/crates/mun/Cargo.toml b/crates/mun/Cargo.toml index a52cc7ed5..d10e62d64 100644 --- a/crates/mun/Cargo.toml +++ b/crates/mun/Cargo.toml @@ -9,6 +9,7 @@ failure = "0.1.5" clap = "2.33.0" mun_compiler = { path = "../mun_compiler" } mun_compiler_daemon = { path = "../mun_compiler_daemon" } +mun_runtime = { path = "../mun_runtime" } [build-dependencies] lazy_static = "1.4.0" diff --git a/crates/mun/src/main.rs b/crates/mun/src/main.rs index 80b679490..989d2928c 100644 --- a/crates/mun/src/main.rs +++ b/crates/mun/src/main.rs @@ -1,7 +1,10 @@ #[macro_use] extern crate failure; +use std::time::Duration; + use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; +use mun_runtime::invoke_fn; fn main() -> Result<(), failure::Error> { let matches = App::new("mun") @@ -36,10 +39,33 @@ fn main() -> Result<(), failure::Error> { ) .about("Compiles a local Mun file into a module"), ) + .subcommand( + SubCommand::with_name("start") + .arg( + Arg::with_name("LIBRARY") + .help("Sets the library to use") + .required(true) + .index(1), + ) + .arg( + Arg::with_name("entry") + .long("entry") + .takes_value(true) + .help("the function entry point to call on startup"), + ) + .arg( + Arg::with_name("delay") + .long("delay") + .required(true) + .takes_value(true) + .help("how much to delay received filesystem events (in ms). This allows bundling of identical events, e.g. when several writes to the same file are detected. A high delay will make hot reloading less responsive."), + ), + ) .get_matches(); match matches.subcommand() { ("build", Some(matches)) => build(matches)?, + ("start", Some(matches)) => start(matches)?, _ => unreachable!(), } @@ -56,6 +82,15 @@ fn build(matches: &ArgMatches) -> Result<(), failure::Error> { } } +/// Starts the runtime with the specified library and invokes function `entry`. +fn start(matches: &ArgMatches) -> Result<(), failure::Error> { + let runtime_options = runtime_options(matches)?; + let mut runtime = mun_runtime::MunRuntime::new(runtime_options)?; + + let entry_point = matches.value_of("entry").unwrap_or("main"); + Ok(invoke_fn!(runtime, entry_point)) +} + fn compiler_options(matches: &ArgMatches) -> Result { let optimization_lvl = match matches.value_of("opt-level") { Some("0") => mun_compiler::OptimizationLevel::None, @@ -71,3 +106,12 @@ fn compiler_options(matches: &ArgMatches) -> Result Result { + let delay: u64 = matches.value_of("delay").unwrap().parse()?; + + Ok(mun_runtime::RuntimeOptions { + library_path: matches.value_of("LIBRARY").unwrap().into(), // Safe because its a required arg + delay: Duration::from_millis(delay), + }) +} diff --git a/crates/mun_runtime/src/assembly.rs b/crates/mun_runtime/src/assembly.rs index 862f71e60..c48e2dc8a 100644 --- a/crates/mun_runtime/src/assembly.rs +++ b/crates/mun_runtime/src/assembly.rs @@ -3,8 +3,8 @@ use std::fs; use std::io; use std::path::{Path, PathBuf}; -use crate::error::*; use crate::library::Library; +use failure::Error; use mun_abi::FunctionInfo; const LIB_DIR: &str = "tmp"; @@ -18,7 +18,7 @@ pub struct Assembly { impl Assembly { /// Loads an assembly for the library at `library_path` and its dependencies. - pub fn load(library_path: &Path) -> Result { + pub fn load(library_path: &Path) -> Result { let library_name = library_path.file_name().ok_or_else(|| { io::Error::new(io::ErrorKind::InvalidInput, "Incorrect library path.") })?; @@ -41,7 +41,7 @@ impl Assembly { }) } - pub fn swap(&mut self, library_path: &Path) -> Result<()> { + pub fn swap(&mut self, library_path: &Path) -> Result<(), Error> { let library_path = library_path.canonicalize()?; let library_name = library_path.file_name().ok_or(io::Error::new( io::ErrorKind::InvalidInput, diff --git a/crates/mun_runtime/src/error.rs b/crates/mun_runtime/src/error.rs deleted file mode 100644 index 51bc04e1e..000000000 --- a/crates/mun_runtime/src/error.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::convert::From; -use std::io; - -use failure; -use notify; - -pub use std::result::Result as StdResult; - -/// Mun runtime error type. -/// -/// Errors can be caused by the file watcher, cargo, or IO operations. -#[derive(Debug)] -pub enum Error { - Cargo(failure::Error), - IO(io::Error), - Watch(notify::Error), -} - -impl From for Error { - fn from(error: failure::Error) -> Self { - Error::Cargo(error) - } -} - -impl From for Error { - fn from(error: io::Error) -> Self { - Error::IO(error) - } -} - -impl From for Error { - fn from(error: notify::Error) -> Self { - Error::Watch(error) - } -} - -pub type Result = StdResult; diff --git a/crates/mun_runtime/src/lib.rs b/crates/mun_runtime/src/lib.rs index 03d34ec75..906a82904 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -1,15 +1,9 @@ -extern crate cargo; -extern crate failure; -extern crate libloading; - mod assembly; -mod error; mod library; #[macro_use] mod macros; pub use crate::assembly::Assembly; -pub use crate::error::*; pub use crate::library::Library; use std::collections::HashMap; @@ -18,9 +12,16 @@ use std::path::{Path, PathBuf}; use std::sync::mpsc::{channel, Receiver}; use std::time::Duration; +use failure::Error; use mun_abi::{FunctionInfo, Reflection}; use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; +#[derive(Clone, Debug)] +pub struct RuntimeOptions { + pub library_path: PathBuf, + pub delay: Duration, +} + /// A runtime for the Mun scripting language. pub struct MunRuntime { assemblies: HashMap, @@ -33,10 +34,10 @@ impl MunRuntime { /// Constructs a new `MunRuntime` that loads the library at `library_path` and its /// dependencies. The `MunRuntime` contains a file watcher that is triggered with an interval /// of `dur`. - pub fn new(library_path: &Path, dur: Duration) -> Result { + pub fn new(options: RuntimeOptions) -> Result { let (tx, rx) = channel(); - let watcher: RecommendedWatcher = Watcher::new(tx, dur)?; + let watcher: RecommendedWatcher = Watcher::new(tx, options.delay)?; let mut runtime = MunRuntime { assemblies: HashMap::new(), function_table: HashMap::new(), @@ -44,12 +45,12 @@ impl MunRuntime { watcher_rx: rx, }; - runtime.add_assembly(library_path)?; + runtime.add_assembly(&options.library_path)?; Ok(runtime) } /// Adds an assembly corresponding to the library at `library_path`. - fn add_assembly(&mut self, library_path: &Path) -> Result<()> { + fn add_assembly(&mut self, library_path: &Path) -> Result<(), Error> { let library_path = library_path.canonicalize()?; if self.assemblies.contains_key(&library_path) { return Err(io::Error::new( @@ -70,7 +71,6 @@ impl MunRuntime { self.watcher .watch(library_path.clone(), RecursiveMode::NonRecursive)?; - println!("Watching assembly: {}", library_path.to_string_lossy()); Ok(()) } @@ -84,43 +84,31 @@ impl MunRuntime { self.function_table.get(function_name) } - /// Updates the state of the runtime. This includes checking for file changes, and consequent - /// recompilation. - /// - /// Currently, the runtime can crash if recompilation fails. Ideally, there is a fallback. + /// Updates the state of the runtime. This includes checking for file changes, and reloading + /// compiled assemblies. pub fn update(&mut self) -> bool { while let Ok(event) = self.watcher_rx.try_recv() { use notify::DebouncedEvent::*; match event { - Write(ref path) | Create(ref path) => { - println!("{:?}", path); - return true; - // for ancestor in path.ancestors() { - // let mut library_path = ancestor.to_path_buf(); - // library_path.push("Cargo.toml"); - - // if let Some(module) = self.modules.get_mut(&manifest_path) { - // module.unload(); - // match module.compile() { - // Ok(ref output_path) => { - // if let Err(e) = module.load(output_path) { - // println!( - // "An error occured while loading library '{}': {:?}", - // module.manifest_path().to_string_lossy(), - // e, - // ) - // } - // } - // Err(e) => println!( - // "An error occured while compiling library '{}': {:?}", - // module.manifest_path().to_string_lossy(), - // e, - // ), - // } - - // return true; - // } - // } + Write(ref path) => { + if let Some(assembly) = self.assemblies.get_mut(path) { + for function in assembly.functions() { + self.function_table.remove(function.name()); + } + if let Err(e) = assembly.swap(path) { + println!( + "An error occured while reloading assembly '{}': {:?}", + path.to_string_lossy(), + e + ); + } else { + for function in assembly.functions() { + self.function_table + .insert(function.name().to_string(), function.clone()); + } + return true; + } + } } _ => {} } @@ -147,7 +135,7 @@ invoke_fn_impl! { #[cfg(all(test, windows))] mod tests { - use super::{invoke_fn, MunRuntime}; + use super::{invoke_fn, MunRuntime, RuntimeOptions}; use std::path::PathBuf; use std::time::Duration; @@ -160,14 +148,22 @@ mod tests { #[test] fn mun_new_runtime() { - let _runtime = MunRuntime::new(&test_lib_path(), Duration::from_millis(10)) - .expect("Failed to initialize Mun runtime."); + let options = RuntimeOptions { + library_path: test_lib_path(), + delay: Duration::from_millis(10), + }; + + let _runtime = MunRuntime::new(options).expect("Failed to initialize Mun runtime."); } #[test] fn mun_invoke_fn() { - let mut runtime = MunRuntime::new(&test_lib_path(), Duration::from_millis(10)) - .expect("Failed to initialize Mun runtime."); + let options = RuntimeOptions { + library_path: test_lib_path(), + delay: Duration::from_millis(10), + }; + + let mut runtime = MunRuntime::new(options).expect("Failed to initialize Mun runtime."); let a: f64 = 4.0; let b: f64 = 2.0; diff --git a/crates/mun_runtime/src/library.rs b/crates/mun_runtime/src/library.rs index b90f9997f..264f6913a 100644 --- a/crates/mun_runtime/src/library.rs +++ b/crates/mun_runtime/src/library.rs @@ -1,6 +1,6 @@ use std::path::Path; -use crate::error::*; +use failure::Error; use libloading::{self, Symbol}; use mun_abi::ModuleInfo; @@ -13,7 +13,7 @@ pub struct Library { impl Library { /// Loads the shared library at `path`, retrieves its symbol metadata, and constructs a library /// wrapper. - pub fn new(path: &Path) -> Result { + pub fn new(path: &Path) -> Result { let library = libloading::Library::new(path)?; // Check whether the library has a symbols function