Skip to content

Commit

Permalink
feat(cli): add subcommand 'start' to start the Mun runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
Wodann committed Oct 6, 2019
1 parent caac5c6 commit 816dcdc
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 91 deletions.
1 change: 1 addition & 0 deletions crates/mun/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
44 changes: 44 additions & 0 deletions crates/mun/src/main.rs
Original file line number Diff line number Diff line change
@@ -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")
Expand Down Expand Up @@ -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!(),
}

Expand All @@ -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<mun_compiler::CompilerOptions, failure::Error> {
let optimization_lvl = match matches.value_of("opt-level") {
Some("0") => mun_compiler::OptimizationLevel::None,
Expand All @@ -71,3 +106,12 @@ fn compiler_options(matches: &ArgMatches) -> Result<mun_compiler::CompilerOption
optimization_lvl,
})
}

fn runtime_options(matches: &ArgMatches) -> Result<mun_runtime::RuntimeOptions, failure::Error> {
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),
})
}
6 changes: 3 additions & 3 deletions crates/mun_runtime/src/assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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<Self> {
pub fn load(library_path: &Path) -> Result<Self, Error> {
let library_name = library_path.file_name().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "Incorrect library path.")
})?;
Expand All @@ -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,
Expand Down
37 changes: 0 additions & 37 deletions crates/mun_runtime/src/error.rs

This file was deleted.

94 changes: 45 additions & 49 deletions crates/mun_runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<PathBuf, Assembly>,
Expand All @@ -33,23 +34,23 @@ 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<MunRuntime> {
pub fn new(options: RuntimeOptions) -> Result<MunRuntime, Error> {
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(),
watcher,
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(
Expand All @@ -70,7 +71,6 @@ impl MunRuntime {
self.watcher
.watch(library_path.clone(), RecursiveMode::NonRecursive)?;

println!("Watching assembly: {}", library_path.to_string_lossy());
Ok(())
}

Expand All @@ -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;
}
}
}
_ => {}
}
Expand All @@ -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;

Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions crates/mun_runtime/src/library.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::path::Path;

use crate::error::*;
use failure::Error;
use libloading::{self, Symbol};
use mun_abi::ModuleInfo;

Expand All @@ -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<Library> {
pub fn new(path: &Path) -> Result<Library, Error> {
let library = libloading::Library::new(path)?;

// Check whether the library has a symbols function
Expand Down

0 comments on commit 816dcdc

Please sign in to comment.