Cargo: Ability to set crate-type depending on target

Created on 31 Dec 2017  路  13Comments  路  Source: rust-lang/cargo

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.

A-crate-types C-feature-request

Most helpful comment

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

All 13 comments

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

@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.

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

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.

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

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

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?

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"]

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

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

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

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

  • We can't set multiple crate type (crate-type= ["dylib", "cdylib", "staticlib"]) makes build fail
  • We can't set the crate type as a cargo buildargument (--crate-type "dylib")
  • We can't set it as an env var..

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.)

Was this page helpful?
0 / 5 - 0 ratings