Skip to content

Commit 8363e2a

Browse files
committed
Explain stages in terms of the compiler currently running
- Address some confusing points + stage N+1 -> stage N artifacts + Use more likely examples of an ABI break + stage N -> stage N compiler - Mention why rustc occasionally uses `cfg(bootstrap)` - Note that stage1 is built using two different versions - Add lots of examples + `test src/test/ui` and `test compiler/rustc` run different compilers 😢 + Separate examples of what to do from examples of what not to do - 'ship stage 1 artifacts' -> 'ship stage 2 compiler' This is hopefully less confusing.
1 parent c58ff8d commit 8363e2a

File tree

1 file changed

+114
-114
lines changed

1 file changed

+114
-114
lines changed

src/building/bootstrapping.md

+114-114
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ It must have been written in a different language. In Rust's case it was
1414
only way to build a modern version of rustc is a slightly less modern
1515
version.
1616

17-
This is exactly how `x.py` works: it downloads the current `beta` release of
17+
This is exactly how `x.py` works: it downloads the current beta release of
1818
rustc, then uses it to compile the new compiler.
1919

2020
## Stages of bootstrapping
@@ -71,6 +71,8 @@ These defaults are as follows:
7171

7272
You can always override the stage by passing `--stage N` explicitly.
7373

74+
For more information about stages, [see below](#understanding-stages-of-bootstrap).
75+
7476
## Complications of bootstrapping
7577

7678
Since the build system uses the current beta compiler to build the stage-1
@@ -122,50 +124,122 @@ contribution [here][bootstrap-build].
122124

123125
## Understanding stages of bootstrap
124126

125-
This is a detailed look into the separate bootstrap stages. When running
126-
`x.py` you will see output such as:
127-
128-
```txt
129-
Building stage0 std artifacts
130-
Copying stage0 std from stage0
131-
Building stage0 compiler artifacts
132-
Copying stage0 rustc from stage0
133-
Building LLVM for x86_64-apple-darwin
134-
Building stage0 codegen artifacts
135-
Assembling stage1 compiler
136-
Building stage1 std artifacts
137-
Copying stage1 std from stage1
138-
Building stage1 compiler artifacts
139-
Copying stage1 rustc from stage1
140-
Building stage1 codegen artifacts
141-
Assembling stage2 compiler
142-
Uplifting stage1 std
143-
Copying stage2 std from stage1
144-
Generating unstable book md files
145-
Building stage0 tool unstable-book-gen
146-
Building stage0 tool rustbook
147-
Documenting standalone
148-
Building rustdoc for stage2
149-
Documenting book redirect pages
150-
Documenting stage2 std
151-
Building rustdoc for stage1
152-
Documenting stage2 whitelisted compiler
153-
Documenting stage2 compiler
154-
Documenting stage2 rustdoc
155-
Documenting error index
156-
Uplifting stage1 rustc
157-
Copying stage2 rustc from stage1
158-
Building stage2 tool error_index_generator
159-
```
127+
### Overview
128+
129+
This is a detailed look into the separate bootstrap stages.
130+
131+
The convention `x.py` uses is that:
132+
- A `--stage N` flag means to run the stage N compiler (`stageN/rustc`).
133+
- A "stage N artifact" is a build artifact that is _produced_ by the stage N compiler.
134+
- The "stage (N+1) compiler" is assembled from "stage N artifacts". This
135+
process is called _uplifting_.
136+
137+
#### Examples
138+
139+
- `x.py build --stage 0` means to build with the beta `rustc`.
140+
- `x.py doc --stage 0` means to document using the beta `rustdoc`.
141+
- `x.py test --stage 0 library/std` means to run tests on the standard library
142+
without building `rustc` from source ('build with stage 0, then test the
143+
artifacts'). If you're working on the standard library, this is normally the
144+
test command you want.
145+
- `x.py test src/test/ui` means to build the stage 1 compiler and run
146+
`compiletest` on it. If you're working on the compiler, this is normally the
147+
test command you want.
148+
149+
#### Examples of what *not* to do
150+
151+
- `x.py test --stage 0 src/test/ui` is not meaningful: it runs tests on the
152+
_beta_ compiler and doesn't build `rustc` from source. Use `test src/test/ui`
153+
instead, which builds stage 1 from source.
154+
- `x.py test --stage 0 compiler/rustc` builds the compiler but runs no tests:
155+
it's running `cargo test -p rustc`, but cargo doesn't understand Rust's
156+
tests. You shouldn't need to use this, use `test` instead (without arguments).
157+
- `x.py build --stage 0 compiler/rustc` builds the compiler, but does not make
158+
it usable: the build artifacts are not uplifted ([#73519]). Use `build
159+
library/std` instead which puts the compiler in `stage1/rustc`.
160+
161+
[#73519]: https://github.com/rust-lang/rust/issues/73519
162+
163+
### Building vs. Running
164+
165+
166+
Note that `build --stage N compiler/rustc` **does not** build the stage N compiler:
167+
instead it builds the stage _N+1_ compiler _using_ the stage N compiler.
168+
169+
In short, _stage 0 uses the stage0 compiler to create stage0 artifacts which
170+
will later be uplifted to be the stage1 compiler_.
160171

161-
A deeper look into `x.py`'s phases can be seen here:
172+
In each stage, two major steps are performed:
173+
174+
1. `std` is compiled by the stage N compiler.
175+
2. That `std` is linked to programs built by the stage N compiler, including
176+
the stage N artifacts (stage (N+1) compiler).
177+
178+
This is somewhat intuitive if one thinks of the stage N artifacts as "just"
179+
another program we are building with the stage N compiler:
180+
`build --stage N compiler/rustc` is linking the stage N artifacts to the `std`
181+
built by the stage N compiler.
182+
183+
Here is a chart of a full build using `x.py`:
162184

163185
<img alt="A diagram of the rustc compilation phases" src="../img/rustc_stages.svg" class="center" />
164186

165187
Keep in mind this diagram is a simplification, i.e. `rustdoc` can be built at
166188
different stages, the process is a bit different when passing flags such as
167189
`--keep-stage`, or if there are non-host targets.
168190

191+
The stage 2 compiler is what is shipped to end-users.
192+
193+
### Stages and libstd
194+
195+
Note that there are two `std` libraries in play here:
196+
1. The library _linked_ to `stageN/rustc`, which was built by stage N-1 (stage N-1 `std`)
197+
2. The library _used to compile programs_ with `stageN/rustc`, which was
198+
built by stage N (stage N `std`).
199+
200+
Stage N `std` is pretty much necessary for any useful work with the stage N compiler.
201+
Without it, you can only compile programs with `#![no_core]` -- not terribly useful!
202+
203+
The reason these need to be different is because they aren't necessarily ABI-compatible:
204+
there could be a new layout optimizations, changes to MIR, or other changes
205+
to Rust metadata on nightly that aren't present in beta.
206+
207+
This is also where `--keep-stage 1 library/std` comes into play. Since most
208+
changes to the compiler don't actually change the ABI, once you've produced a
209+
`std` in stage 1, you can probably just reuse it with a different compiler.
210+
If the ABI hasn't changed, you're good to go, no need to spend time
211+
recompiling that `std`.
212+
`--keep-stage` simply assumes the previous compile is fine and copies those
213+
artifacts into the appropriate place, skipping the cargo invocation.
214+
215+
### Cross-compiling
216+
217+
Building stage2 `std` is different depending on whether you are cross-compiling or not
218+
(see in the table how stage2 only builds non-host `std` targets).
219+
This is because `x.py` uses a trick: if `HOST` and `TARGET` are the same,
220+
it will reuse stage1 `std` for stage2! This is sound because stage1 `std`
221+
was compiled with the stage1 compiler, i.e. a compiler using the source code
222+
you currently have checked out. So it should be identical (and therefore ABI-compatible)
223+
to the `std` that `stage2/rustc` would compile.
224+
225+
However, when cross-compiling, stage1 `std` will only run on the host.
226+
So the stage2 compiler has to recompile `std` for the target.
227+
228+
### Why does only libstd use `cfg(bootstrap)`?
229+
230+
The `rustc` generated by the stage0 compiler is linked to the freshly-built
231+
`std`, which means that for the most part only `std` needs to be cfg-gated,
232+
so that `rustc` can use features added to std immediately after their addition,
233+
without need for them to get into the downloaded beta.
234+
235+
Note this is different from any other Rust program: stage1 `rustc`
236+
is built by the _beta_ compiler, but using the _master_ version of libstd!
237+
238+
The only time `rustc` uses `cfg(bootstrap)` is when it adds internal lints
239+
that use diagnostic items. This happens very rarely.
240+
241+
### Directories and artifacts generated by x.py
242+
169243
The following tables indicate the outputs of various stage actions:
170244

171245
| Stage 0 Action | Output |
@@ -178,7 +252,7 @@ The following tables indicate the outputs of various stage actions:
178252
| copy `stage0-rustc (except executable)` | `build/HOST/stage0-sysroot/lib/rustlib/HOST` |
179253
| build `llvm` | `build/HOST/llvm` |
180254
| `stage0` builds `codegen` with `stage0-sysroot` | `build/HOST/stage0-codegen/HOST` |
181-
| `stage0` builds `rustdoc` with `stage0-sysroot` | `build/HOST/stage0-tools/HOST` |
255+
| `stage0` builds `rustdoc`, `clippy`, `miri`, with `stage0-sysroot` | `build/HOST/stage0-tools/HOST` |
182256

183257
`--stage=0` stops here.
184258

@@ -201,93 +275,19 @@ The following tables indicate the outputs of various stage actions:
201275
| copy (uplift) `stage1-sysroot` | `build/HOST/stage2/lib and build/HOST/stage2/lib/rustlib/HOST` |
202276
| `stage2` builds `test`/`std` (not HOST targets) | `build/HOST/stage2-std/TARGET` |
203277
| copy `stage2-std` (not HOST targets) | `build/HOST/stage2/lib/rustlib/TARGET` |
204-
| `stage2` builds `rustdoc` | `build/HOST/stage2-tools/HOST` |
278+
| `stage2` builds `rustdoc`, `clippy`, `miri` | `build/HOST/stage2-tools/HOST` |
205279
| copy `rustdoc` | `build/HOST/stage2/bin` |
206280

207281
`--stage=2` stops here.
208282

209-
Note that the convention `x.py` uses is that:
210-
- A "stage N artifact" is an artifact that is _produced_ by the stage N compiler.
211-
- The "stage (N+1) compiler" is assembled from "stage N artifacts".
212-
- A `--stage N` flag means build _with_ stage N.
213-
214-
In short, _stage 0 uses the stage0 compiler to create stage0 artifacts which
215-
will later be uplifted to stage1_.
216-
217-
Every time any of the main artifacts (`std` and `rustc`) are compiled, two
218-
steps are performed.
219-
When `std` is compiled by a stage N compiler, that `std` will be linked to
220-
programs built by the stage N compiler (including `rustc` built later
221-
on). It will also be used by the stage (N+1) compiler to link against itself.
222-
This is somewhat intuitive if one thinks of the stage (N+1) compiler as "just"
223-
another program we are building with the stage N compiler. In some ways, `rustc`
224-
(the binary, not the `rustbuild` step) could be thought of as one of the few
225-
`no_core` binaries out there.
226-
227-
So "stage0 std artifacts" are in fact the output of the downloaded stage0
228-
compiler, and are going to be used for anything built by the stage0 compiler:
229-
e.g. `rustc` artifacts. When it announces that it is "building stage1
230-
std artifacts" it has moved on to the next bootstrapping phase. This pattern
231-
continues in latter stages.
232-
233-
Also note that building host `std` and target `std` are different based on the
234-
stage (e.g. see in the table how stage2 only builds non-host `std` targets.
235-
This is because during stage2, the host `std` is uplifted from the "stage 1"
236-
`std` -- specifically, when "Building stage 1 artifacts" is announced, it is
237-
later copied into stage2 as well (both the compiler's `libdir` and the
238-
`sysroot`).
239-
240-
This `std` is pretty much necessary for any useful work with the compiler.
241-
Specifically, it's used as the `std` for programs compiled by the newly compiled
242-
compiler (so when you compile `fn main() { }` it is linked to the last `std`
243-
compiled with `x.py build library/std`).
244-
245-
The `rustc` generated by the stage0 compiler is linked to the freshly-built
246-
`std`, which means that for the most part only `std` needs to be cfg-gated,
247-
so that `rustc` can use featured added to std immediately after their addition,
248-
without need for them to get into the downloaded beta. The `std` built by the
249-
`stage1/bin/rustc` compiler, also known as "stage1 std artifacts", is not
250-
necessarily ABI-compatible with that compiler.
251-
That is, the `rustc` binary most likely could not use this `std` itself.
252-
It is however ABI-compatible with any programs that the `stage1/bin/rustc`
253-
binary builds (including itself), so in that sense they're paired.
254-
255-
This is also where `--keep-stage 1 library/std` comes into play. Since most
256-
changes to the compiler don't actually change the ABI, once you've produced a
257-
`std` in stage 1, you can probably just reuse it with a different compiler.
258-
If the ABI hasn't changed, you're good to go, no need to spend the time
259-
recompiling that `std`.
260-
`--keep-stage` simply assumes the previous compile is fine and copies those
261-
artifacts into the appropriate place, skipping the cargo invocation.
262-
263-
The reason we first build `std`, then `rustc`, is largely just
264-
because we want to minimize `cfg(stage0)` in the code for `rustc`.
265-
Currently `rustc` is always linked against a "new" `std` so it doesn't
266-
ever need to be concerned with differences in std; it can assume that the std is
267-
as fresh as possible.
268-
269-
The reason we need to build it twice is because of ABI compatibility.
270-
The beta compiler has it's own ABI, and then the `stage1/bin/rustc` compiler
271-
will produce programs/libraries with the new ABI.
272-
We used to build three times, but because we assume that the ABI is constant
273-
within a codebase, we presume that the libraries produced by the "stage2"
274-
compiler (produced by the `stage1/bin/rustc` compiler) is ABI-compatible with
275-
the `stage1/bin/rustc` compiler's produced libraries.
276-
What this means is that we can skip that final compilation -- and simply use the
277-
same libraries as the `stage2/bin/rustc` compiler uses itself for programs it
278-
links against.
279-
280-
This `stage2/bin/rustc` compiler is shipped to end-users, along with the
281-
`stage 1 {std,rustc}` artifacts.
282-
283283
## Passing stage-specific flags to `rustc`
284284

285285
`x.py` allows you to pass stage-specific flags to `rustc` when bootstrapping.
286286
The `RUSTFLAGS_STAGE_0`, `RUSTFLAGS_STAGE_1` and `RUSTFLAGS_STAGE_2`
287287
environment variables pass the given flags when building stage 0, 1, and 2
288288
artifacts respectively.
289289

290-
Additionally, the `RUSTFLAGS_STAGE_NOT_0` variable, as its name suggests, pass
290+
Additionally, the `RUSTFLAGS_STAGE_NOT_0` variable, as its name suggests, passes
291291
the given arguments if the stage is not 0.
292292

293293
## Environment Variables

0 commit comments

Comments
 (0)