Skip to content
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

Allow specifying features of the implicit lib dependency #3020

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions text/0000-cargo-target-lib-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
- Feature Name: `cargo_target_lib_features`
- Start Date: 2020-11-15
- RFC PR: [rust-lang/rfcs#3020](https://github.com/rust-lang/rfcs/pull/3020)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary
[summary]: #summary

Allow specifying features of the implicit lib dependency that need to be enabled by default on non-lib targets (`[[bin]]`, `[[example]]`, etc) in a single crate.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement is a little confusing to me. This isn't enabling features for the lib exactly, is it? For example, if you have a clap feature, it needs to also be enabled for the binary, too, right?

In general, I would probably like to see this framed as just changing the set of features enabled on a package. Cargo's features work at the resolution of packages, and changing that might be quite difficult.

Or maybe I am not understanding what the RFC is proposing. I am assuming that the features for the package would still be the union of the features enabled by the selected targets. I'm not sure I understand how this intends target selection to interact with the enable set of features.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am assuming that the features for the package would still be the union of the features enabled by the selected targets.

Yes.

But the statement talks about enabling features on non-lib targets. I am not sure what exactly caused the confusion here. Do you think you can explain a bit more on what you mean?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess my confusion boils down to, I don't understand why this RFC is talking about the implicit "lib" dependency at all.

The phrase "specifying features of the implicit lib dependency" implies to me that this is specifying the features of lib.rs. But it seems to be the opposite of that; that it is specifying the features of the binary/example targets? Or is it specifying the features for the whole package?

For example, let's say there is a package with 3 binaries and no lib.rs, what would specifying lib-features do? Why does it have the word "lib" in it, if a package doesn't even have a library?

The only thing that I see that is special about "lib" is that it is required in all circumstances, so setting features on the [lib] table doesn't make sense (which is also the case with required-features).


# Motivation
[motivation]: #motivation

When developing a crate, there are several scenarios where the user might want one of the non-library targets to activate certain features in their library. This can either be when one of the examples in the crate is documenting a non-default feature or when a user might want to have both a library and a binary in the same crate - for example, if the user wants to implement a command line tool and also to export the underlying functionality as a library so that it may be easily used by other developers.

The second case is currently possible by adding a `[[bin]]` target to the crate's `Cargo.toml`, and adding any binary specific dependencies (ex: `clap`) as optional dependencies of the library (to reduce unnecessary bloat) under a feature flag (ex: `cli`), and then adding `required-features` to the binary target. But the issue here is that if the end-user does not specify the `cli` feature, then the binary target gets skipped because of how `required-features` is designed.

Similarly, with examples, benches and test targets.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

A single project may include multiple artifacts - zero or one libraries, and any number of other targets, like the following `Cargo.toml`:

```toml
[package]
name = "myproject"
version = "0.1.0"
edition = "2018"

[lib]
name = "myproject"
path = "src/lib.rs"

[[example]]
name = "yaml"
path = "src/examples/yaml.rs"
required-features = ["yaml"]

[dependencies]
yaml-rust = { version = "*", optional = true }

[features]
default = []
yaml = ["yaml-rust"]
```

This project contains a library usable by other crates, and an example that can be run to showcase one of the non-default features of the library.

Running `cargo run --example yaml` in the crate gives us a compiler error since the `yaml` feature of the library has not been activated.

To solve this, we can specify the library features for specific targets, like so:

```toml
[package]
name = "myproject"
version = "0.1.0"
edition = "2018"

[lib]
name = "myproject"
path = "src/lib.rs"

[[example]]
name = "yaml"
path = "src/examples/yaml.rs"
required-features = ["yaml"]
lib-features = ["default", "yaml"]

[dependencies]
yaml-rust = { version = "*", optional = true }

[features]
default = []
yaml = ["yaml-rust"]
```

Now, when a user tries `cargo run --example yaml`, the `yaml` feature and the default features of the library will be implicity activated and thus the example will compile and execute as designed.

For a target, specifying `lib-features` does not implicity activate the default features of the library dependency. If needed, the target can specify `default` in the list of values for `lib-features`.

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

This feature would add a new `lib-features` key to the `bin`, `test`, `bench`, and `example` sections of the `Cargo.toml` file, a list of strings that represent the features that should be activated for the implicit library dependency.

```toml
[[example]]
name = "yaml"
lib-features = ["yaml"]
```

Adding `lib-features` to a target changes the behaviour of `cargo-run`, `cargo-test` and `cargo-install` subcommands to implicity activate only the described features thus making `no-default-features` flag irrelevant.

# Drawbacks
[drawbacks]: #drawbacks

- None as of yet

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

While the `required-features` key offers similar functionality, the feature flags must be manually enabled at compile time, and still apply to the entire crate. This adds friction for both project developers, who need to properly separate their dependencies with features and test that these configurations work properly, as well as for users, who need to manually enable the features for their use case.

We could automatically enable the required features when compiling that specific target, but it still does not solve the issue of disabling library's default features for that target.

Also, we find that `required-features` and skipping targets has their own niche use cases.

# Prior art
[prior-art]: #prior-art

- Cargo allows users to specify the features that need to be activated for dependencies as described in the [reference](https://doc.rust-lang.org/cargo/reference/features.html#features)

# Unresolved questions
[unresolved-questions]: #unresolved-questions

- Currently, using feature flags for some targets results in them being disabled/enabled for all targets of the given crate. Since we will be doing implicit activation of features in this RFC, can we find a way to make them not activated for other targets?

# Future possibilities
[future-possibilities]: #future-possibilities

- Specified features can be used with the new [feature resolver](https://doc.rust-lang.org/cargo/reference/unstable.html#features).