-
Notifications
You must be signed in to change notification settings - Fork 76
build: Add js
Cargo feature flag for WASM builds
#118
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
base: master
Are you sure you want to change the base?
Conversation
Previously, the wasm32 target implicitly assumed a browser environment, which caused issues when building for non-browser WASM environments due to the unconditional inclusion of `wasm-bindgen`. This commit introduces an explicit 'js' feature flag, making `wasm-bindgen` and `js-sys` conditional dependencies. This allows greater flexibility for different WASM execution environments. Related to anza-xyz#117
@joncinque requesting your review on this since you just did the feature powerset change. I don't have a strong opinion, if you're okay w/ it I will sign-off on it for the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution! I'm in agreement for this change, makes total sense to me.
The main issue: I believe this is a breaking change for people currently using the packages in wasm builds, since they're expecting all of the JS stuff to come with it. That's unfortunately going to complicate / slow down the release process a bit.
Also, in all the Cargo.tomls, we should keep gating the js-sys
and wasm-bindgen
crates on:
[target.'cfg(target_family = "wasm")'.dependencies]
As another note, I looked around at different feature names used for enabling JS for wasm builds, and here are some popular crates:
getrandom
useswasm_js
time
useswasm-bindgen
as the feature namechrono
useswasmbind
uuid
andjiff
usejs
So your choice of js
is good, but I could also be convinced to do something like wasm-js
if you liked that more.
@@ -29,6 +30,7 @@ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ | |||
"frozen-abi", | |||
] } | |||
solana-sanitize = { workspace = true } | |||
wasm-bindgen = { workspace = true, optional = true } | |||
|
|||
[dev-dependencies] | |||
solana-hash = { path = ".", features = ["dev-context-only-utils"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't comment lower down, but can you remove js-sys
and wasm-bindgen
from the wasm32 dependencies on lines 38-40?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per your suggestion, I've moved all the dependencies for the new js
feature back to a section gated by the target_arch = "wasm32"
.
@@ -19,7 +19,7 @@ use { | |||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't comment further up on line 6, but does a non-JS wasm build always need std? We might be able to change the std usage to:
#[cfg(feature = "std")]
Maybe more specifically -- does a wasm build like yours need std by default? And if we simplify that feature, then we shoudl also remove the target_arch
bit on line 12.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question! In our particular case, we actually do always use std
for non-JS builds, but I think what makes most sense here is probably to change most usages of target_arch = "wasm32"
to feature = "js"
. I tried to have a look in each affected file where the std
dependencies where needed and I tried to make it so that when the std
imports were only needed in code gated by the js
feature, I changed target_arch = "wasm32"
to feature = "js"
, and otherwise I removed the gate on the target architecture. WDYT?
@@ -31,7 +31,7 @@ use { | |||
num_traits::{FromPrimitive, ToPrimitive}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same bit at the top, lines 7 and 15, changing to
#[cfg(feature = "std")]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -149,7 +149,7 @@ impl From<u64> for PubkeyError { | |||
/// [ed25519]: https://ed25519.cr.yp.to/ | |||
/// [pdas]: https://solana.com/docs/core/cpi#program-derived-addresses | |||
/// [`Keypair`]: https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html | |||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] | |||
#[cfg_attr(feature = "js", wasm_bindgen)] | |||
#[repr(transparent)] | |||
#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))] | |||
#[cfg_attr( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't comment there, but there's a TryFrom<Vec<u8>> for Pubkey
impl around line 429 and some gating around new_unique()
(lines 483), and we should change the gating to just:
#[cfg(feature = "std")]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Done.
fn display_to_jsvalue<T: fmt::Display>(display: T) -> JsValue { | ||
std::string::ToString::to_string(&display).into() | ||
} | ||
|
||
#[allow(non_snake_case)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll still need the allow(non_snake_case)
directive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I mistakenly seem to have deleted #[allow(non_snake_case)]
instead of #[cfg(target_arch = "wasm32")]
. Thanks for noticing this! It is now fixed.
pubkey/src/lib.rs
Outdated
fn display_to_jsvalue<T: fmt::Display>(display: T) -> JsValue { | ||
std::string::ToString::to_string(&display).into() | ||
} | ||
|
||
#[allow(non_snake_case)] | ||
#[cfg(feature = "js")] | ||
#[cfg(target_arch = "wasm32")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably remove this since the js
feature gates it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely! Done.
hash/src/lib.rs
Outdated
@@ -19,7 +19,7 @@ use { | |||
}, | |||
solana_sanitize::Sanitize, | |||
}; | |||
#[cfg(target_arch = "wasm32")] | |||
#[cfg(feature = "js")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming that we gate std just on the std feature, let's gate this properly with:
#[cfg(all(feature = "std", feature = "js"))]
For reference, I'm looking at how uuid declares something similar here: https://github.com/uuid-rs/uuid/blob/cb19a46cf17cd9840205e0d67a32ea8a18e58374/src/timestamp.rs#L308
I don't think we need the full target_arch part too, but the std + js feature gate seems to make sense
pubkey/src/lib.rs
Outdated
@@ -1079,7 +1079,7 @@ macro_rules! impl_borsh_serialize { | |||
#[cfg(feature = "borsh")] | |||
impl_borsh_serialize!(borsh0_10); | |||
|
|||
#[cfg(all(target_arch = "wasm32", feature = "curve25519"))] | |||
#[cfg(all(feature = "js", feature = "curve25519"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one will also need the std feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Done.
pubkey/src/lib.rs
Outdated
fn display_to_jsvalue<T: fmt::Display>(display: T) -> JsValue { | ||
std::string::ToString::to_string(&display).into() | ||
} | ||
|
||
#[allow(non_snake_case)] | ||
#[cfg(feature = "js")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will also need the std feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Done.
pubkey/src/lib.rs
Outdated
@@ -1097,12 +1097,12 @@ fn js_value_to_seeds_vec(array_of_uint8_arrays: &[JsValue]) -> Result<Vec<Vec<u8 | |||
} | |||
} | |||
|
|||
#[cfg(target_arch = "wasm32")] | |||
#[cfg(feature = "js")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will also need the std feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joncinque Thank you so much for reviewing this PR so quickly!
I am aware that this will unfortunately be breaking... I believe due to Cargo's limitation to opt-in only features, there's not much we can do to mitigate this. I thought about adding the feature to default
since the dependencies are anyways gated by the wasm32
target architecture, but this seems a little bit awkward. WDYT?
I am also wondering if it would be theoretically best to add target_arch = "wasm32"
to all the places where we have feature = "js"
or if we are OK with the code simply not compiling when having the js
feature enabled for another target architecture than wasm32
. Do you have an opinion on this?
One final question: do you have any idea how long it might take before this is released given that it involves a breaking change?
Thanks again and let me know if you have any further feedback!
fn display_to_jsvalue<T: fmt::Display>(display: T) -> JsValue { | ||
std::string::ToString::to_string(&display).into() | ||
} | ||
|
||
#[allow(non_snake_case)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I mistakenly seem to have deleted #[allow(non_snake_case)]
instead of #[cfg(target_arch = "wasm32")]
. Thanks for noticing this! It is now fixed.
pubkey/src/lib.rs
Outdated
fn display_to_jsvalue<T: fmt::Display>(display: T) -> JsValue { | ||
std::string::ToString::to_string(&display).into() | ||
} | ||
|
||
#[allow(non_snake_case)] | ||
#[cfg(feature = "js")] | ||
#[cfg(target_arch = "wasm32")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely! Done.
@@ -29,6 +30,7 @@ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ | |||
"frozen-abi", | |||
] } | |||
solana-sanitize = { workspace = true } | |||
wasm-bindgen = { workspace = true, optional = true } | |||
|
|||
[dev-dependencies] | |||
solana-hash = { path = ".", features = ["dev-context-only-utils"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per your suggestion, I've moved all the dependencies for the new js
feature back to a section gated by the target_arch = "wasm32"
.
@@ -19,7 +19,7 @@ use { | |||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question! In our particular case, we actually do always use std
for non-JS builds, but I think what makes most sense here is probably to change most usages of target_arch = "wasm32"
to feature = "js"
. I tried to have a look in each affected file where the std
dependencies where needed and I tried to make it so that when the std
imports were only needed in code gated by the js
feature, I changed target_arch = "wasm32"
to feature = "js"
, and otherwise I removed the gate on the target architecture. WDYT?
@@ -31,7 +31,7 @@ use { | |||
num_traits::{FromPrimitive, ToPrimitive}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -149,7 +149,7 @@ impl From<u64> for PubkeyError { | |||
/// [ed25519]: https://ed25519.cr.yp.to/ | |||
/// [pdas]: https://solana.com/docs/core/cpi#program-derived-addresses | |||
/// [`Keypair`]: https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html | |||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] | |||
#[cfg_attr(feature = "js", wasm_bindgen)] | |||
#[repr(transparent)] | |||
#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))] | |||
#[cfg_attr( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Done.
pubkey/src/lib.rs
Outdated
@@ -1079,7 +1079,7 @@ macro_rules! impl_borsh_serialize { | |||
#[cfg(feature = "borsh")] | |||
impl_borsh_serialize!(borsh0_10); | |||
|
|||
#[cfg(all(target_arch = "wasm32", feature = "curve25519"))] | |||
#[cfg(all(feature = "js", feature = "curve25519"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Done.
pubkey/src/lib.rs
Outdated
fn display_to_jsvalue<T: fmt::Display>(display: T) -> JsValue { | ||
std::string::ToString::to_string(&display).into() | ||
} | ||
|
||
#[allow(non_snake_case)] | ||
#[cfg(feature = "js")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Done.
pubkey/src/lib.rs
Outdated
@@ -1097,12 +1097,12 @@ fn js_value_to_seeds_vec(array_of_uint8_arrays: &[JsValue]) -> Result<Vec<Vec<u8 | |||
} | |||
} | |||
|
|||
#[cfg(target_arch = "wasm32")] | |||
#[cfg(feature = "js")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Done.
That would still be breaking for users of The other option, which is unfortunately a lot of work, is to use the semver trick for all these crates: https://github.com/dtolnay/semver-trick. We can have the v2 crates depend on the v3 crates with the correct feature, and then re-export everything, but to be totally honest, I'm not too keen on doing all of that.
Yes, after thinking about it a bit more, let's do what you suggested and also gate all js usage on the wasm32 check.
I'm still figuring out the exact release process, and I need to coordinate with some of our downstream users, so it might take a month before the crates are released. |
@joncinque Thanks a lot for your feedback! Given your feedback, I've now:
I believe this way we should minimize the number of affected users (i.e. only the ones using
Thank you for the info! That should be completely fine for us as we can rely on the forked repository in the meantime. Let me know if there's anything I can do to support on that side. Let me know if you have any other feedback concerning the code! |
@joncinque May I ask if there is an updated timeline on when the breaking changes should be released? Thanks! |
We've been discussing this a bit offline and in Discord (https://discord.com/channels/428295358100013066/476811830145318912/1369891812797452288), and I'm starting to lean towards a slightly different solution, of just removing all of the wasm-bindgen code completely from the component crates, and instead factoring it all into a new crate, as in #138 We've been sidetracked by some other things, so at the latest, the breaking changes should be released by the end of the month. |
Thanks a lot for the quick reply!
I see, thanks for the link! Unfortunately it seems I don't have access to the Would someone from Anza then take the lead in this case?
I see no problem, thank you for the update! Do you know if there is somewhere I can follow this more closely? |
I'll repost the most relevant comment:
And yes, we'll take the lead on this from here, most likely in #138 -- that'll be the PR to follow. It should end up being much simpler since we're just |
Fantastic, thanks a lot! Looking forward to the release! Do you have any idea if the |
Yeah, we should probably do the same thing everywhere |
Great, thank you! I will update the issue there to reflect this. |
Previously, the wasm32 target implicitly assumed a browser environment, which caused issues when building for non-browser WASM environments due to the unconditional inclusion of
wasm-bindgen
.This commit introduces an explicit
js
feature flag, makingwasm-bindgen
andjs-sys
conditional dependencies. This allows greater flexibility for different WASM execution environments.Related to #117