Skip to content

Commit de0917d

Browse files
authored
Merge pull request #2678 from da-x/named-custom-cargo-profiles
Named custom cargo profiles
2 parents 3e08228 + f8d8376 commit de0917d

File tree

1 file changed

+289
-0
lines changed

1 file changed

+289
-0
lines changed
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
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

Comments
 (0)