rust-lang / cargo

The Rust package manager
https://doc.rust-lang.org/cargo
Apache License 2.0
12.58k stars 2.39k forks source link

Ability to set crate-type depending on target #4881

Closed fschutt closed 10 months ago

fschutt commented 6 years ago

This is an issue that currently blocks me from porting a library to WASM:

I have a library called proj5, which I use in a backend server (in a regular Rust program). I wanted to port the library to wasm (the wasm32-unknown-unknown target) that is now available on nightly and found it to be impossible to do it while still being able to use it in the backend:

The only way to get a WASM binary is by setting the crate-type to "cdylib" in the Cargo.toml file. However, if I do this, I can't use the crate in my regular rust program anymore! And if I leave it out, there is not WASM output.

So currently a crate can be compiled either for WASM or for use in a regular Rust program, not both (controlled by the target). I tried the following so far:

  1. Setting the crate-type in Cargo.toml
[target.'cfg(target_arch = "wasm32")']
crate-type = ["dylib"]

This doesn't work, the argument goes unused:

warning: unused manifest key: target.cfg(target_arch = "wasm32").crate-type

I also tried:

[target.wasm32-unknown-unknown]
crate-type = ["dylib"]

Same thing, same error message. The crate builds, but doesn't produce a WASM binary.

  1. Setting RUSTFLAGS (in the .cargo/config) does not seem to have any effect. I tried:
[target.wasm32-unknown-unknown]
rustflags = [ "--crate-type=cdylib" ]

This just gives a cryptic error, and sets the crate-type twice:

error: unexpected character in cfg `/`, expected parens, a comma, an identifier, or a string

No line number, no column number, nothing. Not sure where it failed - the error message could be heavily improved upon.

I also tried it with crate_type, -- --crate-type, --crate-type dylib. None of which work. I expected --crate-type=dylib to work, because it is documented this way, however I suspect that the documentation is incorrect or out of date.

  1. Setting the crate type in lib.rs

So the last thing I tried was to override the crate type via cfg_attr:

// lib.rs
#![cfg_attr(target_arch = "wasm32", crate_type = "cdylib")]

This is simply ignored by cargo. I still get a .rlib file, not a .wasm file.

So right now I'm out of options. Why is is so hard to build a library for both regular Rust use and WASM? Right now I can only choose either-or.

There is a workaround in that I make a second crate (as cdylib), which just exposes the first one (the rlib), but I don't think this is the way to go. This is important for feature-gating crates so that they can be compiled to WASM without any workarounds.

y-ich commented 6 years ago

How about adding an example for wasm in Cargo.toml?

...
[[example]]
name = "wasm"
path = "src/lib.rs"
crate-type = ["cdylib"]

And build by

cargo +nightly build --example wasm --target wasm32-unknown-unknown --release
flukejones commented 6 years ago

@fschutt I currently have the same issue, my workaround is basically this for WASM and rlib releases:

Cargo.toml

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

[[bin]]
name = "test_wasm"
path = "src/lib.rs"

src/lib.rs

#[cfg(target_arch = "wasm32")]
fn main() {}

So

cargo build --lib

will build the rlib, and

cargo build --bin test_wasm

builds the WASM.

Seems to work well for all lib types.

alvitawa commented 6 years ago

Noob here, but I came here with the same (I think) problem. and solved it by trying

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

Both cargo +nightly build --target wasm32-unknown-unknown and cargo run from a crate including the wasm-compilable crate work, and I am able to use the functionality from wasm_or_lib as wasm and as a rust lib.

"rlib" instead of "lib" works too

prographo commented 6 years ago

Is it possible to specify create type by target in the .cargo/config file?

Android targets need cdylib iOS need staticlib

I've tried but it does not seem to accept.

alexcrichton commented 6 years ago

@prographo no, unfortunately that's not supported by Cargo right now

J-F-Liu commented 5 years ago

@alvitawa Specified crate-type = ["cdylib", "lib"], but the generated wasm file size goes from 166KB to 2MB in my case.

dvc94ch commented 4 years ago

Is it possible to specify create type by target in the .cargo/config file?

setting it from the env like CARGO_TARGET_{triple}_CRATE_TYPE would also be nice for tooling.

Android targets need cdylib iOS need staticlib

any updates?

getreu commented 4 years ago

Why is is so hard to build a library for both regular Rust use and WASM? Right now I can only choose either-or.

Is there any progress on this?

Proposal: in analogy to:

[target.'cfg(target_arch = "wasm32")'.dependencies]
rand = { version = "0.7.3", features = ["getrandom"] }

This should be allowed:

[target.'cfg(target_arch = "wasm32")'.lib]     
crate-type = ["cdylib", "rlib"]

Unfortunately, at the moment I still get:

$ wasm-pack build  --target web --out-name wasm
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"]
jonas-schievink commented 3 years ago

People have also run into this issue when targeting iOS and Android: https://github.com/rust-lang/rust/pull/77716

vimmerru commented 3 years ago

This issue becomes critical after merging https://github.com/rust-lang/rust/pull/73516/ that added support of cdylib on iOS.

This new feature in Rust 1.46 added a lot of headache for iOS builds with cdylib targets. cdylib target is near impossible to build if you are using any crate with native dependencies (ex. openssl, libsodium, zmq). You can't just find .so files for all architectures to perform correct linking. Usual workflow is the following:

  1. You build staticlib and rely that native dependencies will be linked as frameworks later
  2. You setup right cocoapods in ObjectiveC/Swift wrapper.

As cargo doesn't support platform-dependent crate types https://github.com/rust-lang/cargo/issues/4881 as a result a lot of projects now broken on Rust 1.46

johnoneil commented 3 years ago

So what's the best practice here? Lot of workarounds above but I don't see a clear answer.

ManevilleF commented 3 years ago

I have the same issue, I need to build on Android, iOS and Wasm but:

saurik commented 3 years ago

FWIW, I will throw in the notion that really, the integration form of how something is built is not something that a package should almost ever decide for itself, but is instead something that the containing dependency of that package needs influence over (quite possibly even multiple levels deep, to some extent). I realize that "the ship has sailed" in some sense with this notion of how cargo is designed, but if I were to ever sit down and apply my 25 years of toolchain and build engineering experience to the problem of creating a package management tool (which I'm not sure is even a good idea in the abstract... I do this thing for specific projects, but I'm not sold on the idea of it being an entire ecosystem yet, given my experiences with every single attempt in this space having not worked well), I'd definitely make this be something that is directed in some sense by the top-level package (which might be something like "I want a .deb file with a dynamic .so in it", but is very often going to be "I want a .ipa file with a .app in it for distribution via the Apple App Store"... which is notably different than "I want a .deb file with a .app in it for distribution via Cydia Installer" and notably different even than "I want a .ipa file with a .app in it for distribution via Cydia Impactor by a user with access to only a free developer profile") along with the ability to flow through overrides on virtually everything about the tree of builds below it, as that just feels like "table stakes" for being able to deal with arbitrary packaging restrictions without having to like, constantly go beg upstream repositories to add more boilerplate build configuration for weird platforms (which Android, iOS, and Wasm all certainly are). (I don't even feel like autotools got this right, and I generally feel like autotools nailed a lot about complex build configuration... libtool just makes too many assumptions about the meaning of a "static" vs. a "dynamic" library to be able to correctly target every platform out of the box; that said, autotools does allow me to override almost everything quite easily, so there's that ;P.)

alex commented 3 years ago

This also impacts folks trying to builds Python extension modules for iOS.

Is there consensus that the fix is to support specifying arbitrary keys on a per target basis, or is that merely a proposal and more design is required?

dvc94ch commented 3 years ago

Last I heard the cargo team is not willing to take a PR to fix this currently and an RFC would need to happen. I think the more deeper problem that makes this hard to fix is that cargo parses the toml and then freezes it by passing around a reference. So there isn't an easy way to adjust the crate-type key in the toml based on information gained later in the compilation.

https://github.com/rust-lang/cargo/pull/7900

str4d commented 3 years ago

A related request I have (which likely would use the same mechanics as a fix for this issue, so I'm mentioning it here, but can open a separate issue if desired) is enabling crate-type to be controlled by feature flags. hyper 0.14.3 added a C FFI behind an ffi feature flag, but to do so they added crate-type = ["lib", "staticlib", "cdylib"] to their Cargo.toml. This breaks cross-compilation for downstream staticlib Rust users such as myself, because even though the ffi feature flag is disabled, the (completely unused) cdylib is still compiled for hyper, and rustc needs to know how to link for the target architecture (which is completely unnecessary for the staticlib I want to compile for use in my C++ binary, so wasn't plumbed in). If hyper could have placed the staticlib, cdylib behind the ffi feature flag, there would have been no downstream effects.

VladasZ commented 2 years ago

This really needs to be implemented. Without crate-type depending on target building cross platform crates supporting mobile platforms such as iOS and Android requires a lot fuss and workarounds.

flukejones commented 2 years ago

Related issue: https://github.com/rust-lang/cargo/issues/6179

daxpedda commented 2 years ago

Related: rust-lang/rfcs#3180, and the follow-up: #10083. Cargo teams opinion on (parts of) this at the moment: https://github.com/rust-lang/rfcs/pull/3180#issuecomment-947032972.

epage commented 10 months ago

While #12260 is a duplicate of this, it has more input from the cargo team, so I'm going to close in favor of that. If there is a reason to keep this open separately that I overlooked, let us know!