From 69da8a8f87ec01a38b482a7bf7cec887f05073ea Mon Sep 17 00:00:00 2001 From: Dana Marcuse Date: Sun, 24 Mar 2019 15:30:01 -0400 Subject: [PATCH 1/9] Initial draft of artifact-specific dependencies RFC --- ...00-cargo-artifact-specific-dependencies.md | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 text/0000-cargo-artifact-specific-dependencies.md diff --git a/text/0000-cargo-artifact-specific-dependencies.md b/text/0000-cargo-artifact-specific-dependencies.md new file mode 100644 index 00000000000..91f987f76f2 --- /dev/null +++ b/text/0000-cargo-artifact-specific-dependencies.md @@ -0,0 +1,97 @@ +- Feature Name: `cargo_artifact_specific_dependencies` +- Start Date: 2019-03-24 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +Allow specifying dependencies that are only used for one build artifact (`[lib]`, `[[bin]]`, etc) in a single crate. + +# Motivation +[motivation]: #motivation + +When developing a crate, you may want to have both a library and a binary in the same crate - for example, to implement a command line tool but also to export the functionality as a library so it may be easily used by other developers. This is possible by adding both a `[lib]` and a `[[bin]]` section to the crate's `Cargo.toml`, but both artifacts share the dependencies specified in the `[dependencies]` section. This can be an issue if you want to specify a dependency which is required for the binary, but not the library. With this example, the `clap` crate may be very useful for parsing arguments in the binary, but adding it to the `[dependencies]` section would also mean that it would be included as a dependency when using the library, adding unnecessary bloat. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +A single project may include multiple artifacts - zero or one libraries, and any number of binaries, like the following `Cargo.toml`: + +```toml +[package] +name = "myproject" +version = "0.1.0" +edition = "2018" + +[dependencies] + +[lib] +name = "myproject" +path = "src/lib.rs" + +[[bin]] +name = "myproject_cli" +path = "src/main.rs" +``` + +This project contains a library usable by other crates, but it also includes a command-line interface to allow users to invoke features of the library from the command line. + +To streamline the user experience, we may want to use an argument parsing library, lke `clap` or `structopt`. However, adding it as a dependency would add it for all artifacts - including the library, which doesn't use it at all. + +To solve this, we can add dependencies for a specific artifact, like so: + +```toml +[package] +name = "myproject" +version = "0.1.0" +edition = "2018" + +[dependencies] + +[lib] +name = "myproject" +path = "src/lib.rs" + +[[bin]] +name = "myproject_cli" +path = "src/main.rs" +dependencies = { clap = "*" } +``` + +Now, `clap` is required for the binary `myproject_cli`, but not for the library `myproject`. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +This feature would add a new `dependencies` key to the `bin`, `lib`, `test`, `bench`, and `example` sections of the `Cargo.toml` file, describing dependencies that are used only for that artifact, in addition to the ones defined in the top-level `dependencies` and `dev-dependencies` sections. These dependencies would be specified in the same way they would be in the top-level `dependencies` section. + +# Drawbacks +[drawbacks]: #drawbacks + +- This is a relatively uncommon use case, and there is overlap with the functionality of the `required-features` key. + - It may be a better idea to improve upon the existing `required-features` functionality (e.g. automatically enabling features when compiling a specific artifact) than to add another method of configuring dependencies. +- It's more difficult for a reader to determine what dependencies a project uses while reading the `Cargo.toml`. + +# 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. + +Specifically for test/bench/example artifacts, the `dev-dependencies` section may be used, but this is only useful for dependencies which are needed by test/bench/example artifacts and not needed by lib/bin artifacts. + +# Prior art +[prior-art]: #prior-art + +- CMake allows, and in fact, recommends, specifying library dependencies that only apply to a specific build artifact through its `target_link_libraries` command +- Gradle allows different build artifacts to use different source sets/dependency configurations + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +- Is cargo written in a way that it's possible to separate dependencies for different artifacts? Currently, using feature flags or `dev-dependencies` to conditionally enable dependencies results in the dependencies being disabled/enabled for all artifacts of the given crate. + +# Future possibilities +[future-possibilities]: #future-possibilities + +I believe this is a feature that fits well into the general 2019 roadmap goal of maturity, making Cargo more versatile, even if for a relatively specific use case. From 57de2020e9e48cac92e956bdbc2b945b64adb6a2 Mon Sep 17 00:00:00 2001 From: Dana Marcuse Date: Sun, 22 Mar 2020 20:57:39 -0400 Subject: [PATCH 2/9] Next draft of artifact-specific-dependencies RFC --- .../0000-cargo-artifact-specific-dependencies.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/text/0000-cargo-artifact-specific-dependencies.md b/text/0000-cargo-artifact-specific-dependencies.md index 91f987f76f2..6c33ae62ade 100644 --- a/text/0000-cargo-artifact-specific-dependencies.md +++ b/text/0000-cargo-artifact-specific-dependencies.md @@ -59,18 +59,23 @@ path = "src/main.rs" dependencies = { clap = "*" } ``` -Now, `clap` is required for the binary `myproject_cli`, but not for the library `myproject`. +Now, `clap` is required for the binary `myproject_cli`, but not for the library `myproject`, and can be used in the same way as crate-wide dependencies. In this example, `clap` could be used from the binary artifact, but attempting to use it from the library artifact would result in an unresolved import error. This has the potential to improve compile time and reduce binary size by omitting dependencies where they're not actually required. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -This feature would add a new `dependencies` key to the `bin`, `lib`, `test`, `bench`, and `example` sections of the `Cargo.toml` file, describing dependencies that are used only for that artifact, in addition to the ones defined in the top-level `dependencies` and `dev-dependencies` sections. These dependencies would be specified in the same way they would be in the top-level `dependencies` section. +This feature would add a new `dependencies` key to the `bin`, `lib`, `test`, `bench`, and `example` sections of the `Cargo.toml` file, describing dependencies that are used only for that artifact, in addition to the ones defined in the top-level `dependencies` and `dev-dependencies` sections. These dependencies would be specified in the same way they would be in the top-level `dependencies` section, allowing version patterns, features, and alternative sources to be specified [as described in the Cargo manual](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html). + +When compiling the given artifacts, the additional dependencies would be made available and linked in the same fashion as regular dependencies, allowing them to be imported with `use` and `extern crate` statements. Attempting to use the dependency from other artifacts would fail to resolve the import, raising [E0432](https://doc.rust-lang.org/error-index.html#E0432). A new help message could be added to cargo to make it easier for the user to debug the issue - e.g. `dependency specified for artifact A is not available when compiling artifact B, did you mean to specify it as a crate dependency?`. + +When dependencies with the same name are specified as both an artifact-specific dependency and a crate-wide dependency, a new error will be raised by cargo, even if the dependencies are identical, indicating that this isn't allowed. This could be changed in the future, with some ideas described below in the [future possibilities](#future-possibilities) section. # Drawbacks [drawbacks]: #drawbacks - This is a relatively uncommon use case, and there is overlap with the functionality of the `required-features` key. - - It may be a better idea to improve upon the existing `required-features` functionality (e.g. automatically enabling features when compiling a specific artifact) than to add another method of configuring dependencies. + - It may be a better idea to improve upon the existing `required-features` functionality (e.g. automatically enabling required features when compiling a specific artifact) than to add another method of configuring dependencies. +- There's also some overlap with the existing functionality to specify `dev-dependencies` - it's hard to imagine many cases where you'd want to have dependencies specific to an individual `test`, `bench`, or `example` that wouldn't be desirable as crate-wide `dev-dependencies`. - It's more difficult for a reader to determine what dependencies a project uses while reading the `Cargo.toml`. # Rationale and alternatives @@ -89,9 +94,12 @@ Specifically for test/bench/example artifacts, the `dev-dependencies` section ma # Unresolved questions [unresolved-questions]: #unresolved-questions +- How would this interact with features? Ideally, a feature could be defined that enables an artifact-specific dependency, but how would this affect compilation of other artifacts that don't include this dependency? - Is cargo written in a way that it's possible to separate dependencies for different artifacts? Currently, using feature flags or `dev-dependencies` to conditionally enable dependencies results in the dependencies being disabled/enabled for all artifacts of the given crate. # Future possibilities [future-possibilities]: #future-possibilities -I believe this is a feature that fits well into the general 2019 roadmap goal of maturity, making Cargo more versatile, even if for a relatively specific use case. +- Specifying an artifact-specific and crate-wide dependency with the same name could be allowed under certain circumstances, such as: + - Enabling a feature(s) of a dependency for a specific artifact, but not for the entire crate + - Indicating more specific version requirements for the same dependency as long as the constraints are compatible according to semantic versioning \ No newline at end of file From 81ae7b694549473a82abde3a885099666308ec7a Mon Sep 17 00:00:00 2001 From: Dana Marcuse Date: Sun, 22 Mar 2020 21:44:14 -0400 Subject: [PATCH 3/9] Add example with non-inline table --- ...000-cargo-artifact-specific-dependencies.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/text/0000-cargo-artifact-specific-dependencies.md b/text/0000-cargo-artifact-specific-dependencies.md index 6c33ae62ade..2a8a15e3c0e 100644 --- a/text/0000-cargo-artifact-specific-dependencies.md +++ b/text/0000-cargo-artifact-specific-dependencies.md @@ -64,7 +64,23 @@ Now, `clap` is required for the binary `myproject_cli`, but not for the library # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -This feature would add a new `dependencies` key to the `bin`, `lib`, `test`, `bench`, and `example` sections of the `Cargo.toml` file, describing dependencies that are used only for that artifact, in addition to the ones defined in the top-level `dependencies` and `dev-dependencies` sections. These dependencies would be specified in the same way they would be in the top-level `dependencies` section, allowing version patterns, features, and alternative sources to be specified [as described in the Cargo manual](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html). +This feature would add a new `dependencies` key to the `bin`, `lib`, `test`, `bench`, and `example` sections of the `Cargo.toml` file, accepting a table describing dependencies that are used only for that artifact, in addition to the ones defined in the top-level `dependencies` and `dev-dependencies` sections. These dependencies would be specified in the same way they would be in the top-level `dependencies` section, allowing version patterns, features, and alternative sources to be specified [as described in the Cargo manual](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html). + +Additionally, artifact-specific dependencies can be specified as a non-inline table. This is semantically identical to declaring the dependencies inline, and the syntax would look similar to crate-wide dependencies: + +```toml +[[bin]] +name = "myproject_cli" + +[bin.myproject_cli.dependencies] +clap = "*" + +[lib] +name = "myproject" + +[lib.dependencies] # only one library can be specified, so lib name isn't required +# ... +``` When compiling the given artifacts, the additional dependencies would be made available and linked in the same fashion as regular dependencies, allowing them to be imported with `use` and `extern crate` statements. Attempting to use the dependency from other artifacts would fail to resolve the import, raising [E0432](https://doc.rust-lang.org/error-index.html#E0432). A new help message could be added to cargo to make it easier for the user to debug the issue - e.g. `dependency specified for artifact A is not available when compiling artifact B, did you mean to specify it as a crate dependency?`. From ec8e6fd4f429dc7bdfeab46129b03cca913b0698 Mon Sep 17 00:00:00 2001 From: Dana Marcuse Date: Sun, 22 Mar 2020 21:49:54 -0400 Subject: [PATCH 4/9] Add RFC PR link --- text/0000-cargo-artifact-specific-dependencies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-cargo-artifact-specific-dependencies.md b/text/0000-cargo-artifact-specific-dependencies.md index 2a8a15e3c0e..d80b4690229 100644 --- a/text/0000-cargo-artifact-specific-dependencies.md +++ b/text/0000-cargo-artifact-specific-dependencies.md @@ -1,6 +1,6 @@ - Feature Name: `cargo_artifact_specific_dependencies` - Start Date: 2019-03-24 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- RFC PR: [rust-lang/rfcs#2887](https://github.com/rust-lang/rfcs/pull/2887) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary From db693e38eb0468ddbac5223521ea5671ba1aad2f Mon Sep 17 00:00:00 2001 From: Dana Marcuse Date: Mon, 23 Mar 2020 12:39:14 -0400 Subject: [PATCH 5/9] Use array-of-tables subtable TOML syntax --- text/0000-cargo-artifact-specific-dependencies.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-cargo-artifact-specific-dependencies.md b/text/0000-cargo-artifact-specific-dependencies.md index d80b4690229..bde93fe5b12 100644 --- a/text/0000-cargo-artifact-specific-dependencies.md +++ b/text/0000-cargo-artifact-specific-dependencies.md @@ -66,13 +66,13 @@ Now, `clap` is required for the binary `myproject_cli`, but not for the library This feature would add a new `dependencies` key to the `bin`, `lib`, `test`, `bench`, and `example` sections of the `Cargo.toml` file, accepting a table describing dependencies that are used only for that artifact, in addition to the ones defined in the top-level `dependencies` and `dev-dependencies` sections. These dependencies would be specified in the same way they would be in the top-level `dependencies` section, allowing version patterns, features, and alternative sources to be specified [as described in the Cargo manual](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html). -Additionally, artifact-specific dependencies can be specified as a non-inline table. This is semantically identical to declaring the dependencies inline, and the syntax would look similar to crate-wide dependencies: +Additionally, artifact-specific dependencies can be specified as a non-inline table immediately following the artifact section. This is semantically identical to declaring the dependencies inline, and the syntax would look similar to crate-wide dependencies: ```toml [[bin]] name = "myproject_cli" -[bin.myproject_cli.dependencies] +[bin.dependencies] clap = "*" [lib] From 7de23cb5d96d3a90ddd4885346e37e46c100cc8f Mon Sep 17 00:00:00 2001 From: Dana Marcuse Date: Thu, 26 Mar 2020 22:00:25 -0400 Subject: [PATCH 6/9] Add swift to prior art section and add links --- text/0000-cargo-artifact-specific-dependencies.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/text/0000-cargo-artifact-specific-dependencies.md b/text/0000-cargo-artifact-specific-dependencies.md index bde93fe5b12..f256787b18c 100644 --- a/text/0000-cargo-artifact-specific-dependencies.md +++ b/text/0000-cargo-artifact-specific-dependencies.md @@ -104,8 +104,9 @@ Specifically for test/bench/example artifacts, the `dev-dependencies` section ma # Prior art [prior-art]: #prior-art -- CMake allows, and in fact, recommends, specifying library dependencies that only apply to a specific build artifact through its `target_link_libraries` command -- Gradle allows different build artifacts to use different source sets/dependency configurations +- CMake allows, and in fact, recommends, specifying library dependencies that only apply to a specific build artifact through its `target_link_libraries` command [(Source)](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) +- Swift is implementing functionality to specify dependencies per-target by specifying dependencies in the package manifest and then enabling them for targets as necessary [(Source)](https://github.com/apple/swift-evolution/blob/master/proposals/0226-package-manager-target-based-dep-resolution.md) +- Gradle allows different build artifacts to use different source sets/dependency configurations (this is less directly comparable due to the way Java loads libraries at runtime) # Unresolved questions [unresolved-questions]: #unresolved-questions From dcc1cbdfca5fc7ab2df582f04c31cb1fc07c779a Mon Sep 17 00:00:00 2001 From: Dana Marcuse Date: Thu, 26 Mar 2020 22:13:37 -0400 Subject: [PATCH 7/9] Add clarification about duplicate dependency/dependency name checks --- ...00-cargo-artifact-specific-dependencies.md | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/text/0000-cargo-artifact-specific-dependencies.md b/text/0000-cargo-artifact-specific-dependencies.md index f256787b18c..c36deb61a50 100644 --- a/text/0000-cargo-artifact-specific-dependencies.md +++ b/text/0000-cargo-artifact-specific-dependencies.md @@ -84,7 +84,25 @@ name = "myproject" When compiling the given artifacts, the additional dependencies would be made available and linked in the same fashion as regular dependencies, allowing them to be imported with `use` and `extern crate` statements. Attempting to use the dependency from other artifacts would fail to resolve the import, raising [E0432](https://doc.rust-lang.org/error-index.html#E0432). A new help message could be added to cargo to make it easier for the user to debug the issue - e.g. `dependency specified for artifact A is not available when compiling artifact B, did you mean to specify it as a crate dependency?`. -When dependencies with the same name are specified as both an artifact-specific dependency and a crate-wide dependency, a new error will be raised by cargo, even if the dependencies are identical, indicating that this isn't allowed. This could be changed in the future, with some ideas described below in the [future possibilities](#future-possibilities) section. +When dependencies with the same name are specified as both an artifact-specific dependency and a crate-wide dependency, a new error will be raised by cargo, even if the dependencies are identical, indicating that this isn't allowed. The existing checks to prevent the same version of a single dependency being used twice would also apply between crate-wide and artifact-specific dependencies. Thus, both of the following examples would be illegal: + +```toml +[dependencies] +regex = "*" + +[[bin]] +# ... +dependencies.regex = { version = "*", package = "alternative_regex" } +``` + +```toml +[dependencies] +regex = "*" + +[[bin]] +# ... +dependencies.re = { version = "*", package = "regex" } +``` # Drawbacks [drawbacks]: #drawbacks From 55ef3d26468824683b29725c8eaa57a2fae1e678 Mon Sep 17 00:00:00 2001 From: Dana Marcuse Date: Thu, 26 Mar 2020 22:29:18 -0400 Subject: [PATCH 8/9] Reword some slightly redundant phrasing --- text/0000-cargo-artifact-specific-dependencies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-cargo-artifact-specific-dependencies.md b/text/0000-cargo-artifact-specific-dependencies.md index c36deb61a50..d7fee3418b6 100644 --- a/text/0000-cargo-artifact-specific-dependencies.md +++ b/text/0000-cargo-artifact-specific-dependencies.md @@ -123,7 +123,7 @@ Specifically for test/bench/example artifacts, the `dev-dependencies` section ma [prior-art]: #prior-art - CMake allows, and in fact, recommends, specifying library dependencies that only apply to a specific build artifact through its `target_link_libraries` command [(Source)](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) -- Swift is implementing functionality to specify dependencies per-target by specifying dependencies in the package manifest and then enabling them for targets as necessary [(Source)](https://github.com/apple/swift-evolution/blob/master/proposals/0226-package-manager-target-based-dep-resolution.md) +- Swift is implementing functionality to specify dependencies per-target by listing all dependencies in the package manifest and then enabling them for targets as necessary [(Source)](https://github.com/apple/swift-evolution/blob/master/proposals/0226-package-manager-target-based-dep-resolution.md) - Gradle allows different build artifacts to use different source sets/dependency configurations (this is less directly comparable due to the way Java loads libraries at runtime) # Unresolved questions From a5aef08eb9843414eb6bdeed948bb034e9b67e88 Mon Sep 17 00:00:00 2001 From: Dana Marcuse Date: Sat, 28 Mar 2020 15:27:48 -0400 Subject: [PATCH 9/9] Remove some redundant lines --- text/0000-cargo-artifact-specific-dependencies.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/text/0000-cargo-artifact-specific-dependencies.md b/text/0000-cargo-artifact-specific-dependencies.md index d7fee3418b6..793606ef2a2 100644 --- a/text/0000-cargo-artifact-specific-dependencies.md +++ b/text/0000-cargo-artifact-specific-dependencies.md @@ -24,8 +24,6 @@ name = "myproject" version = "0.1.0" edition = "2018" -[dependencies] - [lib] name = "myproject" path = "src/lib.rs" @@ -47,8 +45,6 @@ name = "myproject" version = "0.1.0" edition = "2018" -[dependencies] - [lib] name = "myproject" path = "src/lib.rs" @@ -78,7 +74,7 @@ clap = "*" [lib] name = "myproject" -[lib.dependencies] # only one library can be specified, so lib name isn't required +[lib.dependencies] # ... ``` @@ -137,4 +133,4 @@ Specifically for test/bench/example artifacts, the `dev-dependencies` section ma - Specifying an artifact-specific and crate-wide dependency with the same name could be allowed under certain circumstances, such as: - Enabling a feature(s) of a dependency for a specific artifact, but not for the entire crate - - Indicating more specific version requirements for the same dependency as long as the constraints are compatible according to semantic versioning \ No newline at end of file + - Indicating more specific version requirements for the same dependency as long as the constraints are compatible according to semantic versioning