-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Support [target.'cfg(...)'.lib] sections #12260
Comments
The proposed feature |
Thanks for the detailed report! This seems mostly a duplicate of #4881 with a specific solution. There is also a proposal trying to scope a package under a set of target platforms from a different angle (#6179). While these are all valid feature requests, I'd like to recommend For the WebAssembly use case, is it possible for folks to remove |
Oh… that's a very useful feature actually. It does not seem to help in this specific case. I can think of two ways to apply it and neither appear to work in my testing:
Another small note, I mentioned |
If you put it in However, you can also put it in This may not have much of an impact on whether it is a good idea, your mileage may vary, and so on. I do not necessarily consider this a "solution", merely an observation that the fact Cargo's configuration files can be layered in this way is directly related to why it's not unusual to check in such a configuration file into a repo: they have at least one "out". |
Not sure if we are on the same page. By removing the entire cargo rustc --lib --crate-type cdylib If that doesn't work, please share a reproducible example and we can investigate together. For the |
Okay, thank you for explaining. I don't think I know how to apply this even for testing because as explained I don't currently have a situation where I directly invoke cargo while building a lib for wasm. |
Triage: @rustbot label +S-needs-design -S-needs-info +E-hard Thank you @mcclure and @workingjubilee for your patient and well-written report! |
That's exactly what the Rollup Rust Wasm plugin does. It's a very clean solution, since it doesn't require any changes to the user's Cargo.toml, so it reduces boilerplate. |
Its actually not necessary to compile a The reason why its often recommended to use However in Android this is not the case, there are actually Android executables, but when trying to build an APK the Considering that I believe that I'm not sure what the best approach is here, but probably introducing a new crate type like I didn't mention iOS because I'm clueless about that platform. I'm also unsure if there are other targets that require different EDIT: I'm also wondering why |
Coming from #14225: I propose allowing |
@daxpedda If I understand it correctly, the If that is true, I'd like to know why having a separate |
This does work indeed. I was just exploring an idea with the same motivation as the OP: not having users required to create multiple cargo targets to compile to multiple platforms and the extra setup that is included. I believe the proposal would make much more sense if we have a binary target compile to a library instead of the other way around: The other way around we would use a This is obviously a small improvement that has a very solid workaround right now. It makes more sense to tackle the issue described in OP first. Just to clarify: the proposal in #12260 (comment) is unrelated to the whole "how to export |
A note on this "workaround to provide an Android library and a desktop executable from the same crate": If you effectively have this: [lib]
crate-type = ["rlib", "cdylib"] For a Important I dug this up from our commit history >2 years ago, it may have been fixed since!
|
Problem
I have a simple WebGPU app. (See this repo, commit 19dbf4a0. This is a simplified version that only draws a stripe. The actual repo is an in-progress "video game".) I build this application both to run on Desktop on Windows, and to run embedded in a web page (using wasm-pack; the wasm-pack entry point is fn main).
When building for wasm, you build your executable as a lib. When building for desktop, you build your executable as an exe. However, adding the
[lib]
section to your Cargo.toml as recommended by the wasm-pack documentation means that when building for desktop you build an exe and a lib, even though you only need the exe, and when building for web you build an exe and a lib even though you only need the lib. This is a waste of cpu time (even if it's only a small one) and can cause ancillary problems (see below).Proposed Solution
The simplest and most logical fix for this would be to allow me to put in Cargo.toml a
[target.'cfg(target_arch = "wasm32")'.lib]
… and to pair with it, a…
[target.'cfg(not(target_arch = "wasm32"))'.bin]
section, mirroring (and thus completing the user intuition from) what I can already do with dependencies. Currently, either of these sections in Cargo.toml just gets a warning of an "unused manifest key".
Supporting these would not only help with wasm (an increasingly common use case where it is naturally the case that projects that built as executables-only on every other platform, would be built as libraries-only) but any other situation where the set of libraries needed for a project varies between platforms. For example, in the past when writing other languages I have developed for platforms that do not support dynamic libraries at all, or otherwise worked with build scripts that for whatever practical reason needed to build static only on certain platforms; but right now there's no way to say "build rlib+dylib on Windows, but only build rlib on WEIRD_EMBEDDED_PLATFORM".
What ifs
You are about to recommend to me an alternate way of solving my problem. But I have already tried whatever it was
This is a long story and I think it's really a distraction. But, here are some things I looked into while trying to deal with the specific problem above. I found all but one option inadequate and frustrating, and the one option I finally found adequate I feel like I shouldn't have had to do it.
Let me tell you about my ancillary problems.
The naive approach
The first thing I tried was:
This horrifies most Rust people I show it to, but the thing is, it is correct for my application. My program has only one entry point (fn main). When it is built as a lib, the only thing called is the fn main entry point.
This works, but doing it creates a lot of spurious warnings. In particular:
and:
And… yes. I am using main.rs in both the lib and bin file targets. But the thing is, there shouldn't be a lib file target. If I built lib only on wasm and bin only on desktop there wouldn't be a conflict here.
Oh, in addition to this, when building as an executable on desktop, every symbol in my application produces an "unused symbol" warning. It is a lot of warnings. This is because the lib (which I don't want to build) has no entry points. fn main is decorated with a
#[cfg_attr(target_arch="wasm32", wasm_bindgen(start))]
which makes it an entry point on wasm, but this doesn't apply on desktop.Add a stub lib.rs that calls into main.rs
This would trivially, and idiomatically, resolve the "file found to be present in multiple build targets" warning.
I don't like it, because it means I've got a junk lib.rs file that serves no purpose, and when I tried it it turned out I would have to maintain two sets of
mod
lists, one in main.rs in and one in lib.rs, and keep them eternally in sync. Yeuch. I admit my objections here are mostly aesthetic, but aesthetically, especially for a minimal or single-file app, I don't like it.Also, I think if I'd ever gotten this approach working, I don't think it would actually solve the problem with the many "unused symbol" warnings, because
fn fakemain()
would still not be an exported symbol on desktop. Because in addition to being aesthetically unpleasant, this approach doesn't solve the problem! It only solves the warning. With this approach I'm still building an unnecessary library, the library is just easier to ignore because it's not emitting warnings.Split my project up into three crates.
This was recommended to me by several people and is the "correct" way to solve the problem. I would have a "project" crate that builds an rlib, a "bin" crate that builds my Windows exe, and an "lib" crate that builds a dylib for my wasm output. The bin crate contains a minimal main.rs that calls "realmain" in the project crate, the lib crate contains a minimal lib.rs that calls "realmain" in the project crate. This produces no warnings, builds no unnecessary binaries, and is "aesthetic" in the sense that it doesn't require junk files in the project crate or extra "mod" statements.
This is incredibly unnecessary. It is, simply, overpowered for what I am trying to do. This is the correct way to structure a project that builds a library and also a binary where a user might need to build either or both. But my project builds a library xor a binary. I always know which I need to build and I never need both.
It is also disruptive to the project, due to the rule that 1 crate = 1 folder. My nice, succinct 1 cargo file + 1 src/main file project is now split across three directories, each with their own Cargo.toml and src/ file, and out of the seven files in the project now the only one I ever edit is project/src/main.rs. I feel I should not have to change my project directory structure to support an additional platform (which remember, is the only reason I'm doing to any of this— to support wasm), especially because directory restructuring can be disruptive to VCS merges— imagine if instead of considering this problem when the project was born, I'd gotten three years into the project before deciding to add wasm support and then done the directory restructure when multiple PRs were live on github.
Anyway after putting this off for weeks I realized I didn't actually have to do it, because of:
cargo run --bin
This was the closest to a happy solution I found with wasm-pack. Imagine I change my
[lib]
a little:Now imagine I want to build my binary. Instead of
cargo run
I run:cargo run --bin wgpu-hello
. Now, by explicit request, I am building only the bin. If I wanted to build only the lib, by comparison, I could build with --lib, or I could usewasm-pack
which already avoids building the bin.Problem solved, actually! But there are two issues with this approach:
--bin
target, so it changes from project to project and when I change the exe name I have to update my build instructions. I feel unfair complaining about this, but it's there.Use Trunk.
After playing with all these possibilities for a long time, I tried switching from wasm-pack to trunk. Trunk just solves the problem. You just configure your Cargo.toml as if for a exe, and run
trunk build
, and it… works, somehow, I don't know how, it doesn't even need the#[]
on fn main. It's great. I should have done this to begin with. All my problems are solved.If my problems are solved, why am I still complaining? Because this took me two months, and testing out all these different options above plus a couple I didn't even list (
.cargo/
?!) all just to make warnings not actually relevant to my project go away, was time I was not working on the actual project code. If I could have simply marked my initial[libs]
section as specific tocfg(target_arch = "wasm32")
, which it was, I could have solved this on day one (because I actually did try this on day one) and without having to learn the deep Cargo mysteries. Even if the absence of this feature can be worked around, it's simply a reasonable thing to want to do."There is some horrible reason, obvious to implementors but not users, why
[target.'cfg(...)'.lib]
cannot, in general, be allowed"Okay if this (hypothetically) is true then could you at least special-case
[target.'cfg(target_arch=...)'.lib]
(and any other cfg() usages that specifically relate to platform) as allowed?See also
The text was updated successfully, but these errors were encountered: