Skip to content

Commit

Permalink
Add support for packed animation plugins (wasm only)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrozycki committed Dec 1, 2024
1 parent 46649bd commit 6dc39bb
Show file tree
Hide file tree
Showing 18 changed files with 520 additions and 118 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/target
**/captures
*.crab

*.csv
*.log
Expand All @@ -14,4 +15,4 @@ webui/dist
*-wal

.DS_Store
.idea
.idea
58 changes: 52 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"animation-template-wasm",
"animation-utils",
"animation-wasm-bindings",
"animation-wrapper",
"animations",
"light-client",
"configurator",
Expand Down
3 changes: 3 additions & 0 deletions animation-template-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name = "animation-template-wasm"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
animation-utils = { git = "https://github.com/mrozycki/rustmas", rev = "835ad9e" }
animation-api = { git = "https://github.com/mrozycki/rustmas", rev = "835ad9e" }
Expand Down
20 changes: 18 additions & 2 deletions animation-wasm-bindings/src/host.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use std::{collections::HashMap, path::Path};
use std::{
collections::HashMap,
fs::File,
io::{BufReader, Read},
path::Path,
};

use animation_api::{event::Event, schema};
use exports::guest::animation::plugin::{Color, Position};
Expand Down Expand Up @@ -33,6 +38,9 @@ impl WasiView for State {
pub enum HostedPluginError {
#[error("wasmtime returned error: {0}")]
WasmtimeError(#[from] wasmtime::Error),

#[error("cannot open plugin: {0}")]
PluginOpenError(#[from] std::io::Error),
}
type Result<T> = std::result::Result<T, HostedPluginError>;

Expand All @@ -44,10 +52,18 @@ pub struct HostedPlugin {

impl HostedPlugin {
pub async fn new(executable_path: &Path, points: Vec<(f64, f64, f64)>) -> Result<Self> {
let reader = BufReader::new(File::open(executable_path)?);
Self::from_reader(reader, points).await
}

pub async fn from_reader<R: Read>(mut reader: R, points: Vec<(f64, f64, f64)>) -> Result<Self> {
let mut data = Vec::new();
reader.read_to_end(&mut data)?;

let mut config = Config::new();
config.async_support(true);
let engine = Engine::new(&config)?;
let component = Component::from_file(&engine, executable_path)?;
let component = Component::from_binary(&engine, &data)?;

let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker_async(&mut linker)?;
Expand Down
15 changes: 15 additions & 0 deletions animation-wrapper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "animation-wrapper"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
tar = "0.4.43"
thiserror = "2.0.3"

[features]
default = ["wrap", "unwrap"]
wrap = []
unwrap = []
14 changes: 14 additions & 0 deletions animation-wrapper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Rustmas Animation Wrapper
=========================

This crate contains utilities for wrapping animations into Compiled Rustmas
Animation Bundles (yes, we worked very hard on this acronym, glad you noticed).

Additionally, an executable `crabwrap` is provided to help create animation
plugin files from animation code. In order to create an animation plugin file,
run `crabwrap` from the root directory of your animation. Before that,
you might need to install the `wasm32-wasip2` target for your Rust toolchain:

```
rustup target add wasm32-wasip2
```
95 changes: 95 additions & 0 deletions animation-wrapper/src/bin/crabwrap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use std::{
fs::read_dir,
path::PathBuf,
process::{Command, ExitStatus},
};

fn build_plugin() -> std::io::Result<ExitStatus> {
let mut cmd = Command::new("cargo")
.args(["build", "--target", "wasm32-wasip2", "--release"])
.spawn()?;
cmd.wait()
}

fn find_animation_id() -> std::io::Result<String> {
std::env::current_dir()?
.components()
.last()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid project path"))
.map(|c| c.as_os_str().to_string_lossy().to_string())
.map(|s| s.replace('-', "_"))
}

fn find_manifest() -> std::io::Result<PathBuf> {
let manifest_path = std::env::current_dir()?.join("manifest.json");

if !manifest_path.exists() {
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Could not find manifest.json",
))
} else {
Ok(manifest_path)
}
}

fn find_animation_executable() -> std::io::Result<PathBuf> {
let project_root = find_project_root()?;
let animation_id = find_animation_id()?;

let executable_path = project_root
.join("target/wasm32-wasip2/release")
.join(format!("{animation_id}.wasm"));

if !executable_path.exists() {
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Could not find animation executable",
))
} else {
Ok(executable_path)
}
}

fn get_output_path() -> std::io::Result<PathBuf> {
let animation_id = find_animation_id()?;
Ok(std::env::current_dir()?
.as_path()
.join(format!("{animation_id}.crab")))
}

fn find_project_root() -> std::io::Result<PathBuf> {
std::env::current_dir()?
.as_path()
.ancestors()
.find(|p| {
let Ok(dir) = read_dir(p) else {
return false;
};
dir.into_iter()
.filter_map(|p| p.ok())
.map(|p| p.file_name().to_string_lossy().to_string())
.any(|p| p == "Cargo.lock")
})
.map(PathBuf::from)
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::NotFound,
"Could not find Cargo.lock until filesystem root",
)
})
}

fn main() {
let manifest_path = find_manifest().expect("Could not find manifest.json file");

build_plugin().expect("Failed to build the plugin");

let executable_path = find_animation_executable().expect("Could not find animation executable");
let output_path = get_output_path().expect("Could not generate output path");

animation_wrapper::wrap::wrap_plugin(&output_path, &executable_path, &manifest_path)
.expect("Failed to wrap the plugin");

println!("Plugin ready at {}", output_path.to_string_lossy());
}
Loading

0 comments on commit 6dc39bb

Please sign in to comment.