-
Notifications
You must be signed in to change notification settings - Fork 59
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
New targets #38
Comments
While I agree that we may want to have multiple targets in the long run (and that they could reduce conflict), I'm also wary of trying to anticipate targets before we have clear needs. Do you have thoughts about what this "npm" target would provide? I'm a bit surprised to see a target related to a package manager, rather than host environment. |
@aturon I agree. There are npm packages that work on Node only, there are npm packages that work in the browser only, and there are npm packages that work on both Node and the browser. I think the only way an "npm-only" target makes sense is if compiling WebAssembly as an npm package is very different from compiling WebAssembly for other environments (such as Node or the browser), which seems unlikely to me. As for the topic of new targets, I think there are two targets which are unambiguously needed (for obvious reasons): |
I chose "npm" because that seemed to be the goal - to write tooling to support deploying rust as an npm package. I'm using "npm" here in reference to the conventions it set around packages, such as Perhaps it would be better to name it after the module system "es6", or just "javascript" given how common npm is now? Although the javascript ecosystem moves so quickly it's difficult to say that anything is de-facto standard and be sure that it will continue to be that way for very long...
It doesn't make sense to jump straight to the most specific targets: if you do that then there's no easy way to write code that doesn't care whether it's in the browser or on node. The initial changes that have been proposed so far are things like how webassembly modules are packaged, deployed, and then loaded by a javascript client - until we get to the point where we're actually implementing |
#[cfg(all(target_arch = "wasm32", any(target_os = "node", target_os = "web")))] If that's too verbose (and it probably is), then there can be a shorter synonym (similar to how we have the
That's true, but we will need it, there's no doubt about that. I don't see a problem with discussing long-term plans (with the obvious caveat that it's hard to precisely predict the future, and some details will change). Also, it's not just about Applications can also benefit: I might want to write a Rust application which can work on either Node or the browser, and it selects the best / most performant APIs depending on which target it is compiled for. Right now I have to use |
So in my mind, the use of
On the second bullet, the idea is to eventually move away from manual wasm instantiations and instead use the host package manager/bundler/etc to do this "linking". There's a bit more detail here. In my mind, then, what happens within pure Rust code is strictly the definition of the imports, via the evolving ABI. The specification of how those imports should be fulfilled is then managed by out-of-band tooling. Not unlike the |
That's not what I mean: there's no reason you should have to rebuild your rust code to run it in the browser compared to in node - you should just be able to build a host-agnostic npm package. Your solution doesn't allow for that. Eventually we may have browser/node targets in addition to the javascript/es6 target, but I imagine those targets will inherit the semantics that have already been established for the javascript/es6 target.
That makes sense - in that case it makes more sense to name it just the "javascript" target, or the "es6" target. |
That sounds like purely an optimization that could be done in rustc. In other words, if you have some code which specifies that it works with two different targets (e.g. Also, you might need it to recompile! Let's say there is a function #[cfg(all(target_arch = "wasm32", target_os = "node"))]
fn foo() {
// Uses Node APIs
}
#[cfg(all(target_arch = "wasm32", target_os = "web"))]
fn foo() {
// Uses web APIs
} This is necessary because the APIs on the web are different from the APIs on Node. As an example, you might want to use network APIs, but the browser uses Okay, so far so good. But now let's say you create a #[cfg(all(target_arch = "wasm32", any(target_os = "node", target_os = "web")))]
fn bar() {
foo()
} In this case you are saying that What if you want to instead use some sort of polyfill based on runtime detection? Sure, you can do that too: #[cfg(all(target_arch = "wasm32", any(target_os = "node", target_os = "web")))]
fn foo() {
// Polyfill implementation based on runtime feature detection
} And hopefully rustc will be able to avoid recompiling it repeatedly. But separate compilation will still be useful, both for correctness and performance. |
@Pauan that's just not possible - when you deploy your package, you'll be deploying compiled webassembly - you can't have two different targets and have the same package work for both, unless you compile in two versions of the webassembly, but then you're bloating the package for no reason. In addition, rustc will likely never be able to cache any information about the build between different targets, that's just not how it works. If you want to build a package that works in both node and the browser, then you need a single target for both, and either you stick to APIs available on both, or you switch at runtime. |
Yes you'll need to compile it twice, and JavaScript developers already do that. That's normal in the npm ecosystem. Right now npm developers compile a CommonJS / UMD version of their code and also an ES6 version of their code. It's also somewhat common to have both TypeScript and JavaScript builds. Some packages also contain both browser and Node builds. And it's common to include both regular and minified versions of the same code (especially code which is intended for the browser). Npm is quite used to bloat and code duplication. In addition, there's a de-facto standard which uses the Separate compilation isn't done for "no reason", it's done for increased performance, since it doesn't need to include a complex polyfill which slows down performance. Another reason to avoid polyfills is that they bloat up the code size of the final compiled code. The code size of the package doesn't matter, what matters is the code size of the final compiled code, and JavaScript developers obsess over code size, almost as much as embedded programmers.
I don't see any technical reason why rustc cannot cache the example I gave, although it might not do so right now. You're right that it's unlikely to happen anytime soon, but in any case that's an optimization, it shouldn't affect our decisions about semantics.
Sure, if you don't want to compile twice then you should use the Of course it's also possible to mix-and-match: you might compile to three separate WebAssembly modules:
Then the three modules are linked together at runtime (or during the wasm linking stage, which is separate from the Rust linking stage). |
So, I still don't quite understand how you envision this target differing from -unknown today. Can you elaborate a bit? |
It's not uncommon, but plenty of packages do not do that: they just expose a javascript module that can be imported anywhere, and tools like webpack mean that it can be optimised later. Without a generic "javascript" target rust can't do that.
We will presumably want libstd to work when used from javascript modules: either we can base such an implementation off something similar to my "extensible syscall interface" PR, and have the javascript-specific parts sit outside the compiler, or we have a separate target that can do javascript-specific things. The former solution is going to be much harder to stabilise, because it requires stabilising the interface between webassembly and the host, whereas a javascript-specific target could be stabilised without committing to a stable contract between rust and the host, we would simply need to provide a javascript package to implement the unspecified interface, and then we can ship that with the compiler, and update it if we need to make breaking changes to the interface. |
In short, we'd have to solve https://github.com/rust-lang-nursery/rust-wasm/issues/16#issuecomment-358342548 before we could have a working libstd for javascript. Nobody seems very happy with any of the solutions I presented there, and nobody has suggested any alternatives, so it looks like that's not going to be resolved any time soon. Alternatively, we have a separate target, and can put off solving that particular issue. |
Why can't it? If you have some code which works on In other words, the WebAssembly doesn't care what target it was compiled with, it will be the same with either one. Is it particularly clean or elegant? No. But it should work. |
I thought about this some more, and I think these should be the new targets:
This leaves open the possibility of later adding in a If you want to write code that works in any JavaScript environment, you can do this: #[cfg(all(target_arch = "wasm32", target_os = "javascript"))] It will now work with the And if you want it to work with only a specific target (such as #[cfg(all(target_arch = "wasm32", target_os = "javascript", target_env = "node"))] |
I was originally in favor of adding more targets, but now I think we should just have The reason is because the situation in JavaScript is... complicated. To start with you have the web environments (i.e. web browsers), and you have Node.js, but you also have things like Electron which allow you to use both web and Node APIs at the same time. So we would need a new target for that. So, what if you have an API which works on Node? You can't use Similarly, there's various headless browsers which give you access to both browser and Node APIs, we don't really want a new target for each of them. And then there's React Native... So rather than tying it directly to targets, instead we need a more fine-grained way to slice-and-dice the APIs. Ideally we would just use |
|
Oh, I have understood this issue. What Diggsey proposed was js-unknown-unknown-npm, wasn't it? Without js targets, we cannot use the libraries that uses , for example, std::fs even in the projects that target node.js. So library developer should write code around filesystems twice for std::fs and nodejs-sys::fs (not exist yet) or should use, instead of std::fs, a crate that is a facade of std::fs and nodejs-sys::fs because std::fs is not portable. |
hey! this issue seems to be getting some notice and the general idea of targets is something we‘re definitely exploring (albeit slightly differently than in the compiler itself, but more as a secondary tier of targets that the workflow tools expose) i think it may be beneficial to discuss in our next meeting so we can either close or make a comment on this issue re the working groups current thinking on this subject. cc @alexcrichton @fitzgen |
Thank you for response! I'm looking forward to see a comment. |
I may be wrong but I'm concerned that if imports are added in std on wasm32-unknown-js (not js-unknown-unknown), it is breaking change. |
What's the status of this? Not having a proper target for wasm-bindgen is causing major issues across the ecosystem. We should honestly just have a Motivation: Every crate needs to handle wasm-unknown and wasm-web very differently, but the only way to differentiate them is to pass features around. However cargo features are entirely broken for this use case (or in general really), so this doesn't work. I'd say the cleanest solution is probably even to add Although removing std from wasm32-unknown-unknown may not work just yet, because no_std is equally broken due to cargo's broken features. |
What is blocking this? Does somebody need to write a RFC (or create a Pre-RFC)? Are there still unanswered questions? |
Currently there is just a
wasm32-unknown-unknown
target. There's a lot of people with different goals for wasm, and I think as long as there's a single wasm target, there's going to be a lot of conflict about how it should behave.Therefore, before any new features are added to rust itself, we should add new targets for the obvious cases.
Based on the current interest in compatibility with NPM packages, a good start might be
wasm32-npm-unknown
This would be a target for building NPM packages. It is unspecified what environment these packages will run in (node/browser/etc.)
The text was updated successfully, but these errors were encountered: