RFC: Cargo --crate-type CLI Argument

Summary

Add the ability to provide --crate-type <crate-type> as an argument to cargo rustc. This would have the same affect of adding crate-type in the Cargo.toml, while taking higher priority than any value specified there.

Previous implementation PR

Motivation

A crate can declare in its Cargo.toml manifest what sort of sort of compilation artifact to produce. However, there are times when the user of such a crate, as opposed to the author, would want to alter what artifacts are produced.

Some crates may provide both a Rust API and an optional C API. A current example is the hyper crate. Most users of the Rust API only need an rlib, so forcing the compilation of a cdylib as well is a waste. It can also cause problems for people including such a crate as a dependency when cross-compiling, or when combining with -C prefer-dynamic (example).

Another usecase is sharing a library across different platforms (e.g. iOS, Android, WASM). iOS requires static linking (staticlib) [1], [2], Android and WASM require dynamic linking (cdylib) and in order to use it as a dependency in Rust requires rlib.

Lastly, being able to pick a specific crate type also decreases build times when you already know which platform you’re targeting.

Guide-level explanation

When a user builds a library, using cargo rustc, they can provide a --crate-type argument to adjust the crate type that is compiled. The argument can be any that can also be listed in the Cargo.toml.

Some examples:

cargo rustc --crate-type staticlib

cargo rustc --crate-type cdylib --features ffi

Reference-level explanation

A new command-line argument, --crate-type, will be added to Cargo. It must be provided a comma-separated list of 1 or more crate types, of which the allowed values are the same as can be provided in the manifest.

The argument will be added for cargo rustc.

As with the existing crate-type manifest property, this will only work when building a lib or example.

If the manifest contains a list, and this new command-line argument is provided by the user, the command-line argument value will override what is in the manifest. For example:

[lib]
crate-type = ["lib", "staticlib", "cdylib"]
cargo rustc --crate-type staticlib

This will produce output only as a staticlib, ignoring the other values in the manifest.

Drawbacks

The usual reasons to not do this apply here:

  • An additional feature means more surface area to maintain, and more possibility of bugs.
  • The Cargo team is already stretched too thin to ask for another feature. However, in this case, an implementation is already written.

Rationale and alternatives

This gives direct control of the compilation to the end user, instead of making them depend on whatever the crate author put in the Cargo.toml.

An alternative detail to this proposal is to make it so specifying --crate-type adds to the list in the Cargo.toml, instead of overriding it. However, there would still be a need for end users to override, so there would need to be an additional argument, such as --no-default-crate-type. Overriding feels like the less complex solution for a user to comprehend.

The story around compiling Rust for different targets, and especially in ways that are compatible with C, needs to grow stronger. Choosing not to do this would mean this pain point would continue to exist, which hurts the adoption of writing libraries in Rust instead of C.

Prior art

There are a couple similar-looking features already in cargo:

  • --target: When building a crate, a user can specify the specific target architecture of the compilation output. When not specified, it defaults to the host architecture.
  • --features: A user can specify a list of features to enable when building a crate directly with cargo build. The Cargo.toml can provide a default set of features to compile. This differs from the other art, since specifying --features will add to the default, instead of overriding it.

Unresolved questions

Should a user be able to configure the crate-type of all crates in the dependency graph?

When this feature was first proposed, it was suggested that a user may wish to configure many dependencies at once, not just the top level crate. This RFC doesn’t propose how to solve that, but claims that it can be safely considered out of scope.

First, such a feature is much larger, and there isn’t prior art in Cargo to have command-line arguments configuring other dependencies. Designing that would take much more effort. There doesn’t seem to be resources available to explore that.

Additionally, the small focus of the feature proposed in this RFC doesn’t prevent that larger design from being explored and added at a later point. Figuring out a way to specify configuration arguments for other dependencies would likely need to work for the existing --features argument. Therefore, this isn’t a 1-way-door decision, and thus we don’t need to stop fixing this particular pain before that is figured out.

Future possibilities

The command-line argument may be useful for other cargo commands in the future. This RFC starts with a conservative list.

It may also be of interest to allow a crate “feature” enable different crate-types, such as --features ffi enabling --crate-type cdylib.