|
| 1 | +- Feature Name: `custom_named_cargo_profiles` |
| 2 | +- Start Date: 2019-04-04 |
| 3 | +- RFC PR: [rust-lang/rfcs#2678](https://github.com/rust-lang/rfcs/pull/2678) |
| 4 | +- Cargo Issue: [rust-lang/cargo#6988](https://github.com/rust-lang/cargo/issues/6988) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +The proposed change to Cargo is to add the ability to specify user-defined |
| 10 | +profiles in addition to the five predefined profiles, `dev`, `release`, `test`, |
| 11 | +`bench`. It is also desired in this scope to reduce confusion regarding where |
| 12 | +final outputs reside, and increase the flexibility to specify the user-defined |
| 13 | +profile attributes. |
| 14 | + |
| 15 | +# Motivation |
| 16 | +[motivation]: #motivation |
| 17 | + |
| 18 | +Past proposal to increase flexibility of Cargo’s build flags for crates within |
| 19 | +a single cargo build invocation, has resulted in [RFC 2282](https://github.com/rust-lang/rfcs/blob/master/text/2282-profile-dependencies.md), |
| 20 | +which adds the flexibility of changing attributes of specific crates under one |
| 21 | +of the default profiles. However, it does not allow for a full custom profile |
| 22 | +name definition that can have its own additional final outputs. |
| 23 | + |
| 24 | +The motivation is illustrated by a prominent example — the ability to easily |
| 25 | +throw everything under a custom compilation mode in addition to the existing |
| 26 | +compilation modes. |
| 27 | + |
| 28 | +For example, suppose we are frequently comparing between both a release build |
| 29 | +and a super-optimized release+LTO build, we would like Cargo to having two |
| 30 | +separate `target/` directories, e.g. `target/release`, and |
| 31 | +`target/release-lto`, for which the binaries and incremental compilation is |
| 32 | +managed separately. This is so that we can easily switch between the two modes |
| 33 | +without penalty. |
| 34 | + |
| 35 | +Here's an example for a real-world user: [tikv/issue/4189](https://github.com/tikv/tikv/issues/4189) |
| 36 | + |
| 37 | +# Guide-level explanation |
| 38 | +[guide-level-explanation]: #guide-level-explanation |
| 39 | + |
| 40 | +With this proposal implemented, a user can define custom profiles under new |
| 41 | +names, provided that an `inherits` key is used in order to receive attributes |
| 42 | +from other profiles. |
| 43 | + |
| 44 | +For example: |
| 45 | + |
| 46 | + [profile.release-lto] |
| 47 | + inherits = "release" |
| 48 | + lto = true |
| 49 | + |
| 50 | +Valid profile names are: must not be empty, use only alphanumeric characters or |
| 51 | +`-` or `_`. |
| 52 | + |
| 53 | +Passing `--profile` with the profile's name to various Cargo commands will |
| 54 | +resolve to the custom profile. Overrides specified in the profiles from which |
| 55 | +the custom profile inherits will be inherited too, and all final outputs may |
| 56 | +go to a different directory by default: |
| 57 | + |
| 58 | + $ cargo build |
| 59 | + $ cargo build --release |
| 60 | + $ cargo build --profile release-lto |
| 61 | + $ ls -l target |
| 62 | + debug release release-lto |
| 63 | + |
| 64 | +Cargo will emit errors in case `inherits` loops are detected. When considering |
| 65 | +inheritance hierarchy, all profiles directly or indirectly inherit from |
| 66 | +either from `release` or from `dev`. |
| 67 | + |
| 68 | +This also affects other Cargo commands: |
| 69 | + |
| 70 | +* `cargo test` also receives `--profile`, but unless it is specified, uses |
| 71 | + the predefined `test` profile which is described below. |
| 72 | +* `cargo bench` also receives `--profile`, but unless it is specified, uses |
| 73 | + the predefined `bench` profile which is described below. |
| 74 | + |
| 75 | +## Effect over the use of profile in commands |
| 76 | + |
| 77 | +The mixtures of profiles used for `--all-targets` is still in effect, as |
| 78 | +long as `--profile` is not specified. |
| 79 | + |
| 80 | +## Combined specification with `--release` |
| 81 | + |
| 82 | +For now, `--release` is supported for backward-compatibility. |
| 83 | + |
| 84 | +Using `--profile` and `--release` together in the same invocation emits an |
| 85 | +error unless `--profile=release`. Using `--release` on its own is equivalent |
| 86 | +to specifying `--profile=release` |
| 87 | + |
| 88 | +## New `dir-name` attribute |
| 89 | + |
| 90 | +Some of the paths generated under `target/` have resulted in a de-facto "build |
| 91 | +protocol", where `cargo` is invoked as a part of a larger project build. So, to |
| 92 | +preserve the existing behavior, there is also a new attribute `dir-name`, which |
| 93 | +when left unspecified, defaults to the name of the profile. For example: |
| 94 | + |
| 95 | + [profile.release-lto] |
| 96 | + inherits = "release" |
| 97 | + dir-name = "lto" # Emits to target/lto instead of target/release-lto |
| 98 | + lto = true |
| 99 | + |
| 100 | +* The `dir-name` attribute is used mainly to direct the outputs of `bench` and |
| 101 | + `test` to their respective directories: `target/release` and `target/debug`. |
| 102 | + This preserves existing behavior. |
| 103 | +* The `dir-name` attribute is the only attribute not passed by inheritance. |
| 104 | +* Valid directory names are: must not be empty, use only alphanumeric |
| 105 | + characters or `-` or `_`. |
| 106 | + |
| 107 | +## Cross compilation |
| 108 | + |
| 109 | +Under cross compilation with a profile, paths corresponding to |
| 110 | +`target/<platform-triple>/<dir-name>` will be created. |
| 111 | + |
| 112 | +## Treatment to the pre-defined profiles |
| 113 | + |
| 114 | +* The `release` profile remains as it is, with settings overridable as |
| 115 | + before. |
| 116 | +* The `dev` profile receives the `dir-name = "debug"` attribute, so that its |
| 117 | + final outputs are emitted to `target/debug`, as existing Rust developers will |
| 118 | + expect this behavior. This should be added in the official |
| 119 | + documentation for the Cargo manifest, to make this fact clearer for users. |
| 120 | +* A `debug` profile name is not allowed, with a warning saying to use the |
| 121 | + already established `dev` name. |
| 122 | +* The `bench` profile defaults to the following definition, to preserve current |
| 123 | + behavior: |
| 124 | + |
| 125 | +``` |
| 126 | +[profile.bench] |
| 127 | +inherits = "release" |
| 128 | +dir-name = "release" |
| 129 | +``` |
| 130 | + |
| 131 | +* The `test` profile defaults to the following definition, to preserve current behavior: |
| 132 | +``` |
| 133 | +[profile.test] |
| 134 | +inherits = "dev" |
| 135 | +dir-name = "debug" |
| 136 | +``` |
| 137 | + |
| 138 | +* The (upcoming) `build` profile defaults to the following definition: |
| 139 | + |
| 140 | +``` |
| 141 | +[profile.build] |
| 142 | +inherits = "dev" |
| 143 | +dir-name = "build" |
| 144 | +debug = false |
| 145 | +``` |
| 146 | + |
| 147 | +(NOTE: the `build` profile is experimental and may be removed later) |
| 148 | + |
| 149 | +# Reference-level explanation |
| 150 | +[reference-level-explanation]: #reference-level-explanation |
| 151 | + |
| 152 | +The 'final outputs' phrasing was used in this RFC, knowing that there are |
| 153 | +intermediate outputs that live under `target/` that are usually not a concern |
| 154 | +for most Cargo users. The paths that constitute the final build outputs however, |
| 155 | +constitute as sort of a protocol for invokers of Cargo. This RFC extends on |
| 156 | +that protocol, allowing for outputs in more directories. |
| 157 | + |
| 158 | +## Cargo code changes |
| 159 | + |
| 160 | +In implementation details, there are various hand-coded references to pre-defined |
| 161 | +profiles, that we would like to remove. |
| 162 | + |
| 163 | +The `BuildConfig` structure currently has a `release` boolean. The |
| 164 | +implementation will replace it with a value of type `enum Profile {Dev, |
| 165 | +Release, Custom(String))`. |
| 166 | + |
| 167 | +* The `Profiles` struct in `cargo/core/profiles.rs` currently has hardcoded |
| 168 | + `dev`, `release`, `test`, `bench`. This should be changed into a `BTreeMap` |
| 169 | + based on profile names. The pre-defined profiles can be loaded into it, |
| 170 | + before `TomlProfile` overrides are applied to them. |
| 171 | +* Similarly, `TomlProfiles` will be changed to hold profiles in a `BTreeMap`. |
| 172 | +* We would need to compute the actual `build_override` for a profile based on |
| 173 | + resolution through the `inherits` key. |
| 174 | +* Custom build scripts: For compatibility, the `PROFILE` environment currently |
| 175 | + being passed to the `build.rs` script is set to either `release` or `debug`, |
| 176 | + based on `inherits` relationship of the specified profile, in case it is not |
| 177 | + `release` or `dev` directly. |
| 178 | + |
| 179 | +## Profile name and directory name exclusion |
| 180 | + |
| 181 | +To prevent collisions under the target directory, predefined set of string |
| 182 | +excludes both the custom profile names and the dir-name. For example, |
| 183 | +`package`, `build`, `debug`, `doc`, and strings that start with `.`. |
| 184 | + |
| 185 | +# Drawbacks |
| 186 | +[drawbacks]: #drawbacks |
| 187 | + |
| 188 | +The main drawback is that future ideas regarding Cargo workflows, if |
| 189 | +implemented, may supersede the benefits gained from implementing this RFC, |
| 190 | +making the added complexity unjustified in retrospect. |
| 191 | + |
| 192 | +# Rationale and alternatives |
| 193 | +[rationale-and-alternatives]: #rationale-and-alternatives |
| 194 | + |
| 195 | +Considering the example provided above, there could be other ways to accomplish |
| 196 | +the same result. |
| 197 | + |
| 198 | +## Direct `cargo build` flags alternative |
| 199 | + |
| 200 | +If comparing between final build outputs is the main concern to address, there |
| 201 | +could be an alternative, in the form of providing those overrides from the |
| 202 | +command line. For example, a `--enable-lto` flag to `cargo build`. Used |
| 203 | +together with `CARGO_TARGET_DIR` we would be able to do the following: |
| 204 | + |
| 205 | + |
| 206 | + $ cargo build --release |
| 207 | + $ CARGO_TARGET_DIR=target/lto cargo build --release --enable-lto |
| 208 | + |
| 209 | + $ ls -1 target/release/exe target/lto/release/exe |
| 210 | + target/release/exe target/lto/release/exe |
| 211 | + |
| 212 | +The main drawback for this alternative is invocation complexity, and not being able |
| 213 | +to utilize a future implementation of a binary cache under the target directory |
| 214 | +(see 'future possibilities'). |
| 215 | + |
| 216 | + |
| 217 | +## Workspace `Cargo.toml` auto-generation |
| 218 | + |
| 219 | +By generating the workspace's `Cargo.toml` from a script, per build, we can |
| 220 | +control the parameters of the `release` profile without editing |
| 221 | +source-controlled files. Beside build-time complexity, this has another |
| 222 | +drawback, for example — it would trip the timestamp comparison with |
| 223 | +`Cargo.lock` and cause unnecessary updates to it. |
| 224 | + |
| 225 | +## Cargo workflows |
| 226 | + |
| 227 | +It is unclear when the ideas concerning [Cargo |
| 228 | +workflows](http://aturon.github.io/2018/04/05/workflows/) will manifest in |
| 229 | +changes that would allow similar functionality. |
| 230 | + |
| 231 | +# Unresolved questions |
| 232 | +[unresolved-questions]: #unresolved-questions |
| 233 | + |
| 234 | +* Bikeshedding the `inherits` keyword name. |
| 235 | +* Should we keep user profiles under a Toml namespace of their own? |
| 236 | + |
| 237 | +For example: |
| 238 | + |
| 239 | + [profile.custom.release-lto] |
| 240 | + inherits = "release" |
| 241 | + lto = true |
| 242 | + |
| 243 | +* If so, should the `inherits` keyword be able to refer to custom and |
| 244 | + pre-defined profiles differently? |
| 245 | +* Profile names would collide with rustc target names under `target/`. Should |
| 246 | + the output directory be also under a different namespace, e.g. |
| 247 | + `target/custom/release-lto`? |
| 248 | +* Do we really need pre-defined profiles for `test`, `bench`, or |
| 249 | + can we make them obsolete? |
| 250 | +* Is it worthwhile keeping `test` and `bench` outputs in `target/debug` and |
| 251 | + `target/release`? Doing so would save compilation time and space. |
| 252 | +* If so, is the `dir-name` keyword necessary? Alternatively, we can hand-code |
| 253 | + the output directory of `bench` and `test` to be `release` and `debug` to |
| 254 | + keep the current behavior. This may be sufficient until a "global binary cache" |
| 255 | + feature is implemented, or a per-workspace `target/.cache` |
| 256 | + ([related discussion](https://github.com/rust-lang/cargo/pull/6577#issuecomment-459415283)). |
| 257 | + |
| 258 | +## Existing `--profile` parameters in Cargo |
| 259 | + |
| 260 | +The `check`, `fix` and `rustc` commands receive a profile name via `--profile`. |
| 261 | +However these only control how `rustc` is invoked and is not related directly |
| 262 | +to the actual Cargo profile whether pre-defined or custom. For example, `cargo rustc` |
| 263 | +can receive `--profile bench` and `--release` together or separately, with |
| 264 | +rather confusing results. If we move forward with this change, it's maybe |
| 265 | +worthwhile to remove this parameter to avoid further confusion, and provide a |
| 266 | +similar functionality in a different way. |
| 267 | + |
| 268 | + |
| 269 | +# Future possibilities |
| 270 | +[future-possibilities]: #future-possibilities |
| 271 | + |
| 272 | +This RFC mentions a global binary cache. A global binary cache can reside under |
| 273 | +`target/.cache` or in the user home directory under `.cargo`, to be shared by |
| 274 | +multiple workspaces. This may further assist in reducing compilation times when |
| 275 | +switching between compilation flags. |
| 276 | + |
| 277 | +## Treatment to Cargo's 'Finished' print |
| 278 | + |
| 279 | +Currently, the `Finished` line being emitted when Cargo is done building, is |
| 280 | +confusing, and sometimes does not bear a relation to the specified profile. We |
| 281 | +may take this opportunity to revise the output of this line to include the name |
| 282 | +of the profile. |
| 283 | + |
| 284 | +Some targets use more than one profile in their compilation process, so we may |
| 285 | +want to pick a different scheme than simply printing out the name of the main |
| 286 | +profile being used. One option is to print a line for each one of the built |
| 287 | +targets with concise description of profiles that used to build it, but there |
| 288 | +may be better options worth considering following the implementation of this |
| 289 | +RFC. |
0 commit comments