From 367e3096f064d52da462f3cee183ea88ea97dc66 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 Nov 2015 10:51:42 -0800 Subject: [PATCH 1/3] RFC: Improve Cargo target-specific dependencies Improve the target-specific dependency experience in Cargo by leveraging the same `#[cfg]` syntax that Rust has. [Rendered](https://github.com/alexcrichton/rfcs/blob/cargo-cfg-dependencies/text/0000-cargo-cfg-dependencies.md) --- text/0000-cargo-cfg-dependencies.md | 168 ++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 text/0000-cargo-cfg-dependencies.md diff --git a/text/0000-cargo-cfg-dependencies.md b/text/0000-cargo-cfg-dependencies.md new file mode 100644 index 00000000000..b55c1d27207 --- /dev/null +++ b/text/0000-cargo-cfg-dependencies.md @@ -0,0 +1,168 @@ +- Feature Name: N/A +- Start Date: 2015-11-10 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Improve the target-specific dependency experience in Cargo by leveraging the +same `#[cfg]` syntax that Rust has. + +# Motivation +[motivation]: #motivation + +Currently in Cargo it's [relatively painful][issue] to list target-specific +dependencies. This can only be done by listing out the entire target string as +opposed to using the more-convenient `#[cfg]` annotations that Rust source code +has access to. Consequently a Windows-specific dependency ends up having to be +defined for four triples: `{i686,x86_64}-pc-windows-{gnu,msvc}`, and this is +unfortunately not forwards compatible as well! + +[issue]: https://github.com/rust-lang/cargo/issues/1007 + +As a result most crates end up unconditionally depending on target-specific +dependencies and rely on the crates themselves to have the relevant `#[cfg]` to +only be compiled for the right platforms. This experience leads to excessive +downloads, excessive compilations, and overall "unclean methods" to have a +platform specific dependency. + +This RFC proposes leveraging the same familiar syntax used in Rust itself to +define these dependencies. + +# Detailed design +[design]: #detailed-design + +The target-specific dependency syntax in Cargo will be expanded to to include +not only full target strings but also `#[cfg]` expressions: + +```toml +[target."cfg(windows)".dependencies] +winapi = "0.2" + +[target."cfg(unix)".dependencies] +unix-socket = "0.4" + +[target."cfg(target_os = \"macos\")".dependencies] +core-foundation = "0.2" +``` + +Specifically, the "target" listed here is considered special if it starts with +the string "cfg(" and ends with ")". If this is not true then Cargo will +continue to treat it as an opaque string and pass it to the compiler via +`--target` (Cargo's current behavior). + +> **Note**: There's an [issue open against TOML][toml-issue] to support +> single-quoted keys allowing more ergonomic syntax in some cases like: +> +> ```toml +> [target.'cfg(target_os = "macos")'.dependencies] +> core-foundation = "0.2" +> ``` + +[toml-issue]: https://github.com/toml-lang/toml/issues/354 + +Cargo will implement its own parser of this syntax inside the `cfg` expression, +it will not rely on the compiler itself. The grammar, however, will be the same +as the compiler for now: + +``` +cfg := "cfg(" meta-item * ")" +meta-item := ident | + ident "=" string | + ident "(" meta-item * ")" +``` + +Like Rust, Cargo will implement the `any`, `all`, and `not` operators for the +`ident(list)` syntax. The last missing piece is simply understand what `ident` +and `ident = "string"` values are defined for a particular target. To learn this +information Cargo will query the compiler via a new command line flag: + +``` +$ rustc --print cfg +unix +target_os="apple" +target_pointer_width="64" +... + +$ rustc --print cfg --target i686-pc-windows-msvc +windows +target_os="windows" +target_pointer_width="32" +... +``` + +The `--print cfg` command line flag will print out all built-in `#[cfg]` +directives defined by the compiler onto standard output. Each cfg will be +printed on its own line to allow external parsing. Cargo will use this to call +the compiler once (or twice if an explicit target is requested) when resolution +starts, and it will use these key/value pairs to execute the `cfg` queries in +the dependency graph being constructed. + +# Drawbacks +[drawbacks]: #drawbacks + +This is not a forwards-compatible extension to Cargo, so this will break +compatibility with older Cargo versions. If a crate is published with a Cargo +that supports this `cfg` syntax, it will not be buildable by a Cargo that does +not understand the `cfg` syntax. The registry itself is prepared to handle this +sort of situation as the "target" string is just opaque, however. + +This can be perhaps mitigated via a number of strategies: + +1. Have crates.io reject the `cfg` syntax until the implementation has landed on + stable Cargo for at least one full cycle. Applications, path dependencies, + and git dependencies would still be able to use this syntax, but crates.io + wouldn't be able to leverage it immediately. +2. Crates on crates.io wishing for compatibility could simply hold off on using + this syntax until this implementation has landed in stable Cargo for at least + a full cycle. This would mean that everyone could use it immediately but "big + crates" would be advised to hold off for compatibility for awhile. +3. Have crates.io rewrite dependencies as they're published. If you publish a + crate with a `cfg(windows)` dependency then crates.io could expand this to + all known triples which match `cfg(windows)` when storing the metadata + internally. This would mean that crates using `cfg` syntax would continue to + be compatible with older versions of Cargo so long as they were only used as + a crates.io dependency. + +For ease of implementation this RFC would recommend strategy (1) to help ease +this into the ecosystem without too much pain in terms of compatibility or +implementation. + +# Alternatives +[alternatives]: #alternatives + +Instead of using Rust's `#[cfg]` syntax, Cargo could support other options such +as patterns over the target string. For example it could accept something along +the lines of: + +```toml +[target."*-pc-windows-*".dependencies] +winapi = "0.2" + +[target."*-apple-*".dependencies] +core-foundation = "0.2" +``` + +While certainly more flexible than today's implementation, it unfortunately is +relatively error prone and doesn't cover all the use cases one may want: + +* Matching against a string isn't necessarily guaranteed to be robust moving + forward into the future. +* This doesn't support negation and other operators, e.g. `all(unix, not(osx))`. +* This doesn't support meta-families like `cfg(unix)`. + +Another possible alternative would be to have Cargo supply pre-defined families +such as `windows` and `unix` as well as the above pattern matching, but this +eventually just moves into the territory of what `#[cfg]` already provides but +may not always quite get there. + +# Unresolved questions +[unresolved]: #unresolved-questions + +* This is no the only change that's known to Cargo which is known to not be + forwards-compatible, so it may be best to lump them all together into one + Cargo release instead of releasing them over time, but should this be blocked + on those ideas? (note they have not been formed into an RFC yet) + + From 65ea6716d0383d4174286b074ded37722bde1562 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 21 Dec 2015 09:25:15 -0800 Subject: [PATCH 2/3] Typos --- text/0000-cargo-cfg-dependencies.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-cargo-cfg-dependencies.md b/text/0000-cargo-cfg-dependencies.md index b55c1d27207..055a389cfbd 100644 --- a/text/0000-cargo-cfg-dependencies.md +++ b/text/0000-cargo-cfg-dependencies.md @@ -33,7 +33,7 @@ define these dependencies. # Detailed design [design]: #detailed-design -The target-specific dependency syntax in Cargo will be expanded to to include +The target-specific dependency syntax in Cargo will be expanded to include not only full target strings but also `#[cfg]` expressions: ```toml @@ -160,7 +160,7 @@ may not always quite get there. # Unresolved questions [unresolved]: #unresolved-questions -* This is no the only change that's known to Cargo which is known to not be +* This is not the only change that's known to Cargo which is known to not be forwards-compatible, so it may be best to lump them all together into one Cargo release instead of releasing them over time, but should this be blocked on those ideas? (note they have not been formed into an RFC yet) From 60e6d04e5b08df5a8af155e7e2f003f58a30abd7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 22 Jan 2016 23:53:19 -0800 Subject: [PATCH 3/3] The TOML spec has been updated to allow literal strings Note is now removed --- text/0000-cargo-cfg-dependencies.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/text/0000-cargo-cfg-dependencies.md b/text/0000-cargo-cfg-dependencies.md index 055a389cfbd..f9419f5fe7f 100644 --- a/text/0000-cargo-cfg-dependencies.md +++ b/text/0000-cargo-cfg-dependencies.md @@ -43,7 +43,7 @@ winapi = "0.2" [target."cfg(unix)".dependencies] unix-socket = "0.4" -[target."cfg(target_os = \"macos\")".dependencies] +[target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.2" ``` @@ -52,16 +52,6 @@ the string "cfg(" and ends with ")". If this is not true then Cargo will continue to treat it as an opaque string and pass it to the compiler via `--target` (Cargo's current behavior). -> **Note**: There's an [issue open against TOML][toml-issue] to support -> single-quoted keys allowing more ergonomic syntax in some cases like: -> -> ```toml -> [target.'cfg(target_os = "macos")'.dependencies] -> core-foundation = "0.2" -> ``` - -[toml-issue]: https://github.com/toml-lang/toml/issues/354 - Cargo will implement its own parser of this syntax inside the `cfg` expression, it will not rely on the compiler itself. The grammar, however, will be the same as the compiler for now: