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

Integrate wasm-opt into the rust-app pipeline #115

Merged
merged 9 commits into from
Mar 5, 2021
Merged

Integrate wasm-opt into the rust-app pipeline #115

merged 9 commits into from
Mar 5, 2021

Conversation

dnaka91
Copy link
Contributor

@dnaka91 dnaka91 commented Jan 30, 2021

This PR adds integration with wasm-opt as mentioned in #7 and #114. Currently the wasm-opt binary must be installed on the system for trunk to work (or the the attribute set to wasm-opt="0").

I thought of just skipping the optimization step if the binary is missing but that may cause unexpected side effects. My plan is to add an auto-download feature for wasm-opt and wasm-bindgen-cli in the future so that trunk can install any missing external programs.

Checklist

  • Updated CHANGELOG.md describing pertinent changes.
  • Updated README.md with pertinent info (may not always apply).

Fixes #114.


  • (Dodd) post-merge: open an issue to track work on automatic download of wasm-opt. Maybe lump this in with the work on auto-download of wasm-bindgen-cli (we already have a ticket for the later), but at a minimum let's try to consider both use cases.

README.md Outdated
@@ -41,6 +41,18 @@ Next, we will need to install `wasm-bindgen-cli`. In the future Trunk will handl
cargo install wasm-bindgen-cli
```

In addition, we will need to install `wasm-opt` which is part of `binaryen`. On MacOS we can install
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we bundle the binary with trunk or download on command invocation (and cache it)?

Copy link
Contributor Author

@dnaka91 dnaka91 Jan 30, 2021

Choose a reason for hiding this comment

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

As I wrote in the description I thought of adding that as a separate PR because it would involve wasm-bindgen-cli as well so we don't need any binaries present.

And there is #105 which would require to re-write download part again as the HTTP client would change as well.

@thedodd
Copy link
Member

thedodd commented Feb 1, 2021

Awesome! Really happy to see this PR! We are currently hacking on getting the 0.8.0 release prepped. After that, I will be moving over to finishing review on our Tokio PR and then on to this one! 🍻

@dnaka91
Copy link
Contributor Author

dnaka91 commented Feb 1, 2021

@thedodd great to hear. Once the Tokio PR is merged I'll start preparing changes to auto-download pre-compiled binaries of wasm-opt and wasm-bindgen-cli as a separate PR.

README.md Outdated Show resolved Hide resolved
Copy link
Member

@thedodd thedodd left a comment

Choose a reason for hiding this comment

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

Thanks for all of the work here @dnaka91! A few items here. Let me know what you think.

src/pipelines/rust_app.rs Show resolved Hide resolved
@@ -57,6 +61,7 @@ impl RustApp {
})
.unwrap_or_else(|| html_dir.join("Cargo.toml"));
let bin = el.attr("data-bin").map(|val| val.to_string());
let wasm_opt = el.attr("wasm-opt").map(|val| val.to_string());
Copy link
Member

Choose a reason for hiding this comment

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

Let's add a struct to wrap and validate this value. Something like (using https://jeltef.github.io/derive_more/derive_more/deref.html):

#[derive(Deref)]
#[deref(forward)]
struct WasmOptValue(String);

impl WasmOptValue {
    pub fn validate(val: &str) -> Result<Self> {
        // validate the value then wrap it in self
    }
}

Then we can update this line to be let wasm_opt = el.attr("wasm-opt").map(|val| WasmOptValue::validate(val)).transpose()?;.

Then, down in the code where it is actually used, we can simply do self.wasm_opt.as_deref().as_str() which is compatible with the AsRef<std::ffi::OsStr> constraint used below.

This will allow folks to see if they've supplied an incorrect value as soon as Trunk is invoked, instead of having to wait until the very end of the cargo build.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How about creating an enum for the possible values and implement FromStr and AsRef<str> on it?
That would be a more type safe approach.

Initially I thought it's okay to let the user just pass any value as wasm-opt would just throw an error and we wouldn't have to adjust whenever wasm-opt changes its options. But especially this option probably never changes (or extremely rarely) and can be adjusted quickly.

Copy link
Member

Choose a reason for hiding this comment

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

I dig it. I was going to suggest that initially, but I wasn't sure if it would work with structopt. But if it does, then I definitely agree that it is a better approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just checked, FromStr is what structopt requires for the argument types.

https://docs.rs/structopt/0.3.21/structopt/#type-magic

So this should work just fine with enum and FromStr 👍

Comment on lines 243 to 245
if self.wasm_opt.as_deref() == Some("0") {
return Ok(());
}
Copy link
Member

Choose a reason for hiding this comment

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

I would say that we should do:

let wasm_opt = match self.wasm_opt.as_deref().as_str() {
    Some(val) if val != "0" => val,
    _ => return Ok(());
};

Basically this will allow Trunk workflows to continue to work even without wasm-opt present on their machine. If they decide that they want to start using it, then they can set it to a non-zero value, and they are g2g.

Once we implement the work to actually download wasm-opt for the user (and probably download wasm-bindgen-cli for them as well), then we can make this default to opt level 4 or something like that, as it will be mostly transparent to the user and will not break existing workflows.

Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that's probably a better approach and would not cause half the users to get errors when running Trunk after the update 😅.

I think "" (no opt level) should be the default as that already applies many optimizations (maybe even equivalent to 3 or 4) and wasm-pack does the same.

README.md Outdated
@@ -41,6 +41,18 @@ Next, we will need to install `wasm-bindgen-cli`. In the future Trunk will handl
cargo install wasm-bindgen-cli
```

In addition, we will need to install `wasm-opt` which is part of `binaryen`. On MacOS we can install
Copy link
Member

Choose a reason for hiding this comment

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

Per my comments below, until we get to the point where we are actually downloading wasm-opt for the user, I would say that we should update this verbiage to something like: If using wasm-opt, we will need to install ....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely. Have been a bit occopied this week but I'll continue on this soon and adjust to your comments.

@dnaka91 dnaka91 requested a review from thedodd February 19, 2021 14:49
@dnaka91
Copy link
Contributor Author

dnaka91 commented Feb 19, 2021

Okay just made a few changes according to our discussions. Hope I didn't miss anything 😰

@dnaka91
Copy link
Contributor Author

dnaka91 commented Mar 3, 2021

It's been a while, so just wanted to know whether there is anything holding this off that I missed? (besides obviously pulling the latest changed from master. Will do that tomorrow)

@thedodd
Copy link
Member

thedodd commented Mar 3, 2021

@dnaka91 nope, you're good. I have a few changes in flight for the WebSockets/Proxy updates, but I should be able to circle back here very soon to finish up final review and merge.

Thanks again!

Copy link
Member

@thedodd thedodd left a comment

Choose a reason for hiding this comment

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

A few small items, though I suspect I will need to do one more pass after this branch is rebased onto master.

Keep me posted! I'm hoping to release this as part of 0.9.0 along with the WebSocket proxy code.


impl Default for WasmOptLevel {
fn default() -> Self {
// Current default is off until automatic download of wasm-opt is implemented.\
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Current default is off until automatic download of wasm-opt is implemented.\
// Current default is off until automatic download of wasm-opt is implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oups, how did that one slip in there :D

@@ -57,6 +123,7 @@ impl RustApp {
})
.unwrap_or_else(|| html_dir.join("Cargo.toml"));
let bin = el.attr("data-bin").map(|val| val.to_string());
let wasm_opt = el.attr("wasm-opt").map(|val| val.parse()).transpose()?.unwrap_or_default();
Copy link
Member

Choose a reason for hiding this comment

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

After rebasing onto master, this will have to be updated as a different mechanism is being used to pass along attributes.

Comment on lines 300 to 302
// Zero means no optimizations (valid arg for wasm-opt), so we can skip calling wasm-opt as
// it wouldn't have any effect.
if self.wasm_opt == WasmOptLevel::Off {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Zero means no optimizations (valid arg for wasm-opt), so we can skip calling wasm-opt as
// it wouldn't have any effect.
if self.wasm_opt == WasmOptLevel::Off {
// If opt level is off, we skip calling wasm-opt as it wouldn't have any effect.
if matches!(self.wasm_opt, WasmOptLevel::Off) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's definitely a better description, especially now where I turned this into an enum 👍

@dnaka91
Copy link
Contributor Author

dnaka91 commented Mar 5, 2021

Okay, all updated. Hopefully I didn't miss anything.

@dnaka91
Copy link
Contributor Author

dnaka91 commented Mar 5, 2021

@thedodd I saw the tokio PR was closed in favor of async-std. So I guess I can start working on a basic version of the auto-download feature after this is merged?

@thedodd
Copy link
Member

thedodd commented Mar 5, 2021

@dnaka91 yes sir. That would be awesome!

@thedodd thedodd merged commit fd347f3 into trunk-rs:master Mar 5, 2021
@thedodd thedodd mentioned this pull request Mar 5, 2021
3 tasks
@thedodd
Copy link
Member

thedodd commented Mar 5, 2021

@dnaka91 I've added this issue: #139 let me know what you think! And thanks for all of the great work on this front!

@dnaka91 dnaka91 deleted the wasm-opt branch March 5, 2021 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Optimize wasm file with wasm-opt
3 participants