Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add initial support for wasm target #92

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ rust-version = "1.64.0"
[workspace]
members = [
"rrule",
"rrule-debugger",
"rrule-debugger"
]

# These are the 2 packages to mainly work on.
Expand Down
10 changes: 9 additions & 1 deletion rrule/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ regex = { version = "1.5.5", default-features = false, features = ["perf", "std"
clap = { version = "4.1.9", optional = true, features = ["derive"] }
thiserror = "1.0.30"
serde_with = { version = "2.3.1", optional = true }
wasm-bindgen = { version="0.2.85", optional = true }

[dev-dependencies]
serde_json = "1.0.80"
orig_serde = { package = "serde", version = "1.0.137", default-features = false }
wasm-bindgen-test = "0.3.13"

[[bin]]
name = "rrule"
Expand All @@ -43,4 +45,10 @@ cli-tool = ["clap"]
serde = ["serde_with", "chrono/serde", "chrono-tz/serde"]

# Allows EXRULE's to be used in the `RRuleSet`.
exrule = []
exrule = []

# Allows to use WASM
wasm = ["dep:wasm-bindgen"]

[lib]
crate-type = ["cdylib", "rlib"]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this also contain the default "lib" crate type?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. In my tests, it wasn't necessary. What do you think?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should contain the default "lib" as well. I will need to read up on this setting more before merging this PR

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fmeringdal setting the Cargo.toml file as:

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

Once I run cargo build it's showing the below error:

cargo build                                                                    101 err  took 6s  at 06:42:39
    Blocking waiting for file lock on build directory
warning: output filename collision.
The lib target `rrule` in package `rrule v0.10.0 (/Users/douglasmachado/Documents/Rust/Projects/rust-rrule/rrule)` has the same output filename as the lib target `rrule` in package `rrule v0.10.0 (/Users/douglasmachado/Documents/Rust/Projects/rust-rrule/rrule)`.
Colliding filename is: /Users/douglasmachado/Documents/Rust/Projects/rust-rrule/target/debug/deps/librrule.rlib
The targets should have unique names.
Consider changing their names to be unique or compiling them separately.
This may become a hard error in the future; see <https://github.com/rust-lang/cargo/issues/6313>.
warning: output filename collision.
The lib target `rrule` in package `rrule v0.10.0 (/Users/douglasmachado/Documents/Rust/Projects/rust-rrule/rrule)` has the same output filename as the lib target `rrule` in package `rrule v0.10.0 (/Users/douglasmachado/Documents/Rust/Projects/rust-rrule/rrule)`.
Colliding filename is: /Users/douglasmachado/Documents/Rust/Projects/rust-rrule/target/debug/librrule.rlib
The targets should have unique names.
Consider changing their names to be unique or compiling them separately.
This may become a hard error in the future; see <https://github.com/rust-lang/cargo/issues/6313>.
thread 'main' panicked at 'assertion failed: mtimes.insert(output.clone(), mtime).is_none()', src/cargo/core/compiler/fingerprint/mod.rs:1096:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

How do you recommend to fix this?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it build with just "lib"? Why was "lib" replaced with ["cdylib", "rlib"]. I am not that familiar with how WASM internals work and don't want to merge anything I don't fully understand.

Copy link
Author

@dgmachado dgmachado May 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fmeringdal @antoinepairet

Assuming in our context the below command as Rust Build:

cargo build

And the below command as Wasm Build:

wasm-pack build --release --target web --features "wasm"

We can analyze the different outputs:

  1. Using the below config (Cargo.toml file):
[lib]
crate-type = ["lib"] 

1.a) the Rust Build works.

1.b) the Wasm Build fails:

Error: crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:

[lib]
crate-type = ["cdylib", "rlib"]
Caused by: crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:

[lib]
crate-type = ["cdylib", "rlib"]
  1. Using the below config (Cargo.toml file):
[lib]
crate-type = ["lib", "cdylib", "rlib"] 

2.a) the Rust Build and Wasm Build command fail and return the same output:

    Blocking waiting for file lock on build directory
warning: output filename collision.
The lib target `rrule` in package `rrule v0.10.0 (/Users/douglasmachado/Documents/Rust/Projects/rust-rrule/rrule)` has the same output filename as the lib target `rrule` in package `rrule v0.10.0 (/Users/douglasmachado/Documents/Rust/Projects/rust-rrule/rrule)`.
Colliding filename is: /Users/douglasmachado/Documents/Rust/Projects/rust-rrule/target/debug/deps/librrule.rlib
The targets should have unique names.
Consider changing their names to be unique or compiling them separately.
This may become a hard error in the future; see <https://github.com/rust-lang/cargo/issues/6313>.
warning: output filename collision.
The lib target `rrule` in package `rrule v0.10.0 (/Users/douglasmachado/Documents/Rust/Projects/rust-rrule/rrule)` has the same output filename as the lib target `rrule` in package `rrule v0.10.0 (/Users/douglasmachado/Documents/Rust/Projects/rust-rrule/rrule)`.
Colliding filename is: /Users/douglasmachado/Documents/Rust/Projects/rust-rrule/target/debug/librrule.rlib
The targets should have unique names.
Consider changing their names to be unique or compiling them separately.
This may become a hard error in the future; see <https://github.com/rust-lang/cargo/issues/6313>.
thread 'main' panicked at 'assertion failed: mtimes.insert(output.clone(), mtime).is_none()', src/cargo/core/compiler/fingerprint/mod.rs:1096:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
  1. Using the below config (Cargo.toml file):
[lib]
crate-type = ["cdylib", "rlib"] 

3.a) the Rust Build and Wasm Build command build successfully (the current PR is using this config).

I've found two docs that does not answer this type of problem:

  1. Doc
  2. Doc

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context @dgmachado . I think the wasm build is fine, my main concern is the implications of the Rust build and how it might affect applications relying on this library. Before resolving this discussion I will have to do a deeper dive into WASM myself to understand what is going on and why this is required. Sorry for holding up this PR, but I can't merge anything that I don't fully understand as I have to maintain this.

14 changes: 14 additions & 0 deletions rrule/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
install-wasm-pack:
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

build-wasm-nodejs:
wasm-pack build --target nodejs --features "wasm"

test-wasm-on-nodejs:
node examples/wasm/nodejs/app.js

build-wasm-web:
wasm-pack build --target web --features "wasm"

test-wasm-on-web-browser:
npx http-server -o /examples/wasm/web/index.html
21 changes: 21 additions & 0 deletions rrule/examples/wasm/nodejs/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { get_all_date_recurrences } = require('../../../pkg/rrule.js');

const http = require('http');
const url = require('url');
const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
const queryObject = url.parse(req.url,true).query;
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');

const rule_set = "DTSTART:20120201T093000Z\nRRULE:FREQ=DAILY;COUNT=3";
const data = get_all_date_recurrences(rule_set, 100);
console.log(data);
res.end(data.toString());
});

server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
23 changes: 23 additions & 0 deletions rrule/examples/wasm/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
</head>
<body>

<script type="module">
import init, { get_all_date_recurrences } from '../../../pkg/rrule.js';

async function run_wasm() {
await init();
const ruleSet = "DTSTART:20120201T093000Z\nRRULE:FREQ=DAILY;COUNT=3";
const data = get_all_date_recurrences(ruleSet, 100);
console.log(data);
alert(data);
}
run_wasm();
</script>
</body>
</html>
2 changes: 2 additions & 0 deletions rrule/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ mod iter;
mod parser;
mod tests;
mod validator;
#[cfg(feature = "wasm")]
mod wasm;

pub use crate::core::{Frequency, NWeekday, RRule, RRuleResult, RRuleSet, Tz};
pub use crate::core::{Unvalidated, Validated};
Expand Down
29 changes: 29 additions & 0 deletions rrule/src/wasm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use wasm_bindgen::prelude::*;

use crate::{RRuleSet, RRuleError};

/// Get all recurrences of the rrule
///
/// # Arguments
///
/// * `rule_set` - List of rrules
///
/// * `limit` - Limit must be set in order to prevent infinite loops
#[wasm_bindgen]
pub fn get_all_date_recurrences(rule_set: &str, limit: Option<u16>) -> Result<Vec<JsValue>, JsError> {
let rrule_result: Result<RRuleSet, RRuleError> = rule_set.parse();
match rrule_result {
Ok(rrule) => {
/// Set hard limit in case of infinitely recurring rules
let rule_set_collection = rrule.all(limit.unwrap_or(100));
let result: Vec<JsValue> = rule_set_collection.dates
.into_iter()
.map(|s| {
JsValue::from_str(&s.to_string())
})
.collect();
Ok(result)
},
Err(e) => Err(JsError::from(e))
}
}