-
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
Design document for "features" #385
Comments
Sadly, you cannot nest in Toml the way you show: [features.postgres]
[dependencies.postgres]
git = "https://github.com/sfackler/rust-postgres" Instead, it will likely need to be something like: [dependencies.postgres]
git = "https://github.com/sfackler/rust-postgres"
[features.postgres]
dependencies = ["postgres"]
cfg = ["db_posgres"] One nice thing about this structure is that you can easily use the same dependency in multiple feature sets. |
Addition: dependencies listed as part of a feature are not built if that feature isn't specified. That is, they aren't built by default anymore. Addition: |
Maybe |
|
I'm not exactly sure if I understand how this would be used on the code. From the example:
Does it means that the cfg cargo_feature has a value "postgres"? If so it would imply that you can only have one feature, right? Wouldn't the following idiom be preferred?
Or simply
Additionally I would take a look at gentoo's portage use flags system, it has a powerful and mature implementation of a very similar system, having already designed depending on a package with at least an specific set of features (use flags), default features, deep features, and many of the sort. |
@jansegre see the gist I linked for how it works with multiple features. A complex system like portage is completely unreasonable, both in terms of complexity of solving for dependencies and in terms of needless features. |
Ok, I got it now. Made this simple test based on your gist and it worked as expected: fn main() {
if cfg!(cargo_feature = "foo") { println!("foo") }
if cfg!(cargo_feature = "bar") { println!("bar") }
} $ rustc --cfg cargo_feature=\"foo\" a.rs && ./a
foo $ rustc --cfg cargo_feature=\"foo\" --cfg cargo_feature=\"bar\" a.rs && ./a
foo
bar Although I had to literally escape the |
Here is a more fleshed out version of the design: https://gist.github.com/wycats/199f09a3ff1c8e6dcebd. I have embedded it below. |
"Features" DesignThe goal of "features" is to make it possible to express:
We originally thought that a related goal, the ability to specify a list of "pick exactly one" options to choose from, was in scope for "features", but have since come to believe that those choices are better expressed as "Provides"-style virtual dependencies. FeaturesTo flesh out the design, we used Rails, which has a combination of mandatory dependencies, optional opt-in dependencies and optional opt-out dependencies. Rails uses a Bundler-specific structure to express these dependencies (using a generated The Rails package, with optional dependencies expressed as features. [package]
name = "rails"
[features]
# The "default" set of optional packages. Most people will want
# to use these packages, but they are strictly optional
default = ["sass-rails", "uglifier", "jquery-rails", "sdoc"]
# The "omakase" set of optional packages. These are packages
# curated by DHH as desirable for normal usage. Some developers
# will leave the "default" packages, but disable the hand-curated
# Omakase packages.
omakase = ["coffee-rails", "turbolinks", "jbuilder"]
# The "secure-password" feature depends on the bcrypt gem. This
# aliasing will allow people to talk about the feature in a
# higher-level way and allow Rails to add more requirements to
# the feature in the future.
secure-password = ["bcrypt"]
[dependencies]
# These packages are mandatory, and form the core of the Rails
# distribution. They are locked to particular versions to make
# sure that Cargo will only include one, precise version of
# Rails in the compiled binary.
actionmailer = "=4.1.5"
actionpack = "=4.1.5"
actionview = "=4.1.5"
activemodel = "=4.1.5"
activerecord = "=4.1.5"
activesupport = "=4.1.5"
railties = "=4.1.5"
# A few other mandatory dependencies that do not have strict
# version dependencies
bundler = "1.3.0"
sprockets-rails = "2.0.0"
# A list of all of the optional dependencies, some of which
# are included in the above "features". They can be opted
# into by apps.
sass-rails = { version = "4.0.3", optional = true }
uglifier = { version = "1.3.0", optional = true }
coffee-rails = { version = "4.0.0", optional = true }
bcrypt = { version = "3.1.7", optional = true }
unicorn = { version = "*", optional = true }
turbolinks = { version = "*", optional = true }
jbuilder = { version = "*", optional = true }
[dev-dependencies]
sdoc = { version = "0.4.0", optional = true }
debugger = { version = "*", optional = true }
spring = { version = "*", optional = true } To use Rails, [package]
name = "my-app"
[dependencies.rails]
version = "4.1.5"
features = ["omakase", "unicorn"]
# do not include the default features, and optionally
# cherry-pick individual features
# default-features = false Rules
General UsageIn End ProductsOne major use-case for this feature is specifying optional features in end-products. For example, the Servo project may want to include optional features that people can enable or disable when they build it. In that case, Servo will describe features in its $ cargo build --release --features "shumway pdf" Default features could be excluded using In PackagesIn most cases, the concept of "optional dependency" in a library is best expressed as a separate package that the top-level application depends on. However, high-level packages, like Iron or Piston, may want the ability to curate a number of packages for easy installation. The current Cargo system allows them to curate a number of mandatory dependencies into a single package for easy installation. In some cases, packages may want to provide additional curation for optional dependencies:
In almost all cases, it is an antipattern to use these features outside of high-level packages that are designed for curation. If a feature is optional, it can almost certainly be expressed as a separate package. |
To clarify, is the uniqueness global (i.e. must not conflict with any package in the registry), or just local (i.e. must not conflict with any dependencies etc.)? |
That is strange, it leaves out features names like "mysql", which is a On Fri, Aug 29, 2014 at 9:47 PM, Huon Wilson notifications@github.com
Att, |
Just a question, since #509 hasn't been merged yet this hasn't landed, right? I ask because I was looking at the following Servo issue: servo/servo#3278. |
I agree with @jansegre, it seems odd that feature names would conflict with package names. I'm assuming that the intent here is that The ability to do this strikes me as odd. It means that I cannot have an optional dependency that only makes sense when bundled with another optional dependency. I can use a feature group that includes both, but any client of mine could treat them as distinct features and try to enable one without the other. Instead, I'd much rather see every feature have to be explicitly listed. Features could either be an array of other feature names, or could be a declaration of dependencies. The former is for feature clusters (such as the [features]
# Feature clusters: each of the next 3 features contain arrays
# that refer specifically to other features.
# The "default" set of optional packages. Most people will want
# to use these packages, but they are strictly optional
default = ["sass-rails", "uglifier", "jquery-rails", "sdoc"]
# The "omakase" set of optional packages. These are packages
# curated by DHH as desirable for normal usage. Some developers
# will leave the "default" packages, but disable the hand-curated
# Omakase packages.
omakase = ["coffee-rails", "turbolinks", "jbuilder"]
# The "secure-password" feature depends on the bcrypt gem. This
# aliasing will allow people to talk about the feature in a
# higher-level way and allow Rails to add more requirements to
# the feature in the future.
secure-password = ["bcrypt"]
[features.sass-rails]
dependencies = ["sass-rails"]
[features.uglifier]
dependencies = ["uglifier"]
[features.jquery-rails]
dependencies = ["jquery-rails"]
[features.sdoc]
dependencies = ["sdoc"]
[features.coffee-rails]
dependencies = ["coffee-rails"]
[features.turbolinks]
dependencies = ["turbolinks"]
[features.jbuilder]
dependencies = ["jbuilder"]
[features.bcrypt]
dependencies = ["bcrypt"] This looks kind of verbose, although I think that the example here of rails wanting to provide an easy way to get external packages is kind of unusual, as I would normally expect the clients of rails to simply pull in these other packages themselves. More likely, I would think, is something like [features.python]
dependencies = ["python", "py-awesomepackage"] where the one feature named I also question whether "feature clusters" are common enough to warrant having the simplest syntax. An alternative approach would look like [feature-clusters]
# The "default" set of optional packages. Most people will want
# to use these packages, but they are strictly optional
default = ["sass-rails", "uglifier", "jquery-rails", "sdoc"]
# The "omakase" set of optional packages. These are packages
# curated by DHH as desirable for normal usage. Some developers
# will leave the "default" packages, but disable the hand-curated
# Omakase packages.
omakase = ["coffee-rails", "turbolinks", "jbuilder"]
# The "secure-password" feature depends on the bcrypt gem. This
# aliasing will allow people to talk about the feature in a
# higher-level way and allow Rails to add more requirements to
# the feature in the future.
secure-password = ["bcrypt"]
[features]
sass-rails = ["sass-rails"]
uglifier = ["uglifier"]
jquery-rails = ["jquery-rails"]
sdoc = ["sdoc"]
coffee-rails = ["coffee-rails"]
turbolinks = ["turbolinks"]
jbuilder = ["jbuilder"]
bcrypt = ["bcrypt"] This still has the oddity of duplicating each optional package name as a feature, though again, I think the way rails works here is kind of unusual. More generally, feature names are not necessarily going to want to mirror the particular packages they depend on. |
I'm also not sure I agree with the premise of a "default" feature cluster that can be opted out in its entirety. It makes more sense to me to simply have certain features be defaulted on, and to be able to opt-out on an individual feature basis. The reason being that I think it's more common to want to disable specific functionality rather than disabling everything. After all, default features are defaulted because they're expected to be wanted by most people. If I only want to turn off one feature, then I need to know what the complete default feature set is so I can turn everything else back on. Instead, with the original syntax, we could add a
I'm partial to the second approach, but it has the issue of needing to handle the case of a feature named |
This feature was outlined in rust-lang#385 [1], and documentation has been included as part of this commit. [1]: rust-lang#385 (comment)
This feature was outlined in #385 [1], and documentation has been included as part of this commit. [1]: #385 (comment) Closes #385
The basic idea is to have "feature sets". When writing a package, you can specify a list of "features" for a dependency to be built with. And in the dependency, it can do various operations depending on a feature. So for example, the top-level package:
Then, in the manifest for
db
:When a dependency is specified as part of a feature, it is only built when that feature is specified.
The "features" for a crate would also be exposed as a
--cfg
flag to rustc for that package and as a comma-separated list of features in an environment variable,CARGO_FEATURES
. The following would work:(Specifically, a
--cfg cargo_feature="$FEAT"
would be passed for every feature. See this gist for an example of how this can work)There is some subtlety in this feature. Consider the following:
Main manifest:
Foo manifest:
The problem is that transitive dependencies may have a different set of features.
To solve this, a package which supports features can specify a list of mutually exclusive features. When this package is used as a dependency, every feature specified in the dependency graph will be taken as a list and compared against the mutually exclusive feature lists. For example,
db
could have:If not specified, no features are mutually exclusive.
The text was updated successfully, but these errors were encountered: