Skip to content

Commit 94ae2a2

Browse files
committed
Auto merge of #38072 - nikomatsakis:bootstrap-incremental, r=acrichto
add preliminary support for incremental compilation to rustbuild.py This implements the integration described in #37929. It requires the use of a local nightly as your bootstrap compiler. The setup is described in `src/bootstrap/README.md`. This does NOT implement the "copy stage0 libs to stage1" optimization described in #37929, just because that seems orthogonal to me. In local testing, I do not yet see any incremental re-use when building rustc. I'm not sure why that is, more investigation needed. (For these reasons, this is not marked as fixing the relevant issue.) r? @alexcrichton -- I included one random cleanup (`Step::noop()`) that turned out to not be especially relevant. Feel free to tell me you liked it better the old way.
2 parents 3f9823d + 83453bc commit 94ae2a2

File tree

8 files changed

+136
-23
lines changed

8 files changed

+136
-23
lines changed

src/bootstrap/README.md

+36
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,42 @@ compiler. What actually happens when you invoke rustbuild is:
116116
The goal of each stage is to (a) leverage Cargo as much as possible and failing
117117
that (b) leverage Rust as much as possible!
118118

119+
## Incremental builds
120+
121+
You can configure rustbuild to use incremental compilation. Because
122+
incremental is new and evolving rapidly, if you want to use it, it is
123+
recommended that you replace the snapshot with a locally installed
124+
nightly build of rustc. You will want to keep this up to date.
125+
126+
To follow this course of action, first thing you will want to do is to
127+
install a nightly, presumably using `rustup`. You will then want to
128+
configure your directory to use this build, like so:
129+
130+
```
131+
# configure to use local rust instead of downloding a beta.
132+
# `--local-rust-root` is optional here. If elided, we will
133+
# use whatever rustc we find on your PATH.
134+
> configure --enable-rustbuild --local-rust-root=~/.cargo/ --enable-local-rebuild
135+
```
136+
137+
After that, you can use the `--incremental` flag to actually do
138+
incremental builds:
139+
140+
```
141+
> ../x.py build --incremental
142+
```
143+
144+
The `--incremental` flag will store incremental compilation artifacts
145+
in `build/stage0-incremental`. Note that we only use incremental
146+
compilation for the stage0 -> stage1 compilation -- this is because
147+
the stage1 compiler is changing, and we don't try to cache and reuse
148+
incremental artifacts across different versions of the compiler. For
149+
this reason, `--incremental` defaults to `--stage 1` (though you can
150+
manually select a higher stage, if you prefer).
151+
152+
You can always drop the `--incremental` to build as normal (but you
153+
will still be using the local nightly as your bootstrap).
154+
119155
## Directory Layout
120156

121157
This build system houses all output under the `build` directory, which looks

src/bootstrap/bin/rustc.rs

+27
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ extern crate bootstrap;
2929

3030
use std::env;
3131
use std::ffi::OsString;
32+
use std::io;
33+
use std::io::prelude::*;
34+
use std::str::FromStr;
3235
use std::path::PathBuf;
3336
use std::process::{Command, ExitStatus};
3437

@@ -41,6 +44,11 @@ fn main() {
4144
.and_then(|w| w[1].to_str());
4245
let version = args.iter().find(|w| &**w == "-vV");
4346

47+
let verbose = match env::var("RUSTC_VERBOSE") {
48+
Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
49+
Err(_) => 0,
50+
};
51+
4452
// Build scripts always use the snapshot compiler which is guaranteed to be
4553
// able to produce an executable, whereas intermediate compilers may not
4654
// have the standard library built yet and may not be able to produce an
@@ -95,6 +103,15 @@ fn main() {
95103
cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
96104
}
97105

106+
// Pass down incremental directory, if any.
107+
if let Ok(dir) = env::var("RUSTC_INCREMENTAL") {
108+
cmd.arg(format!("-Zincremental={}", dir));
109+
110+
if verbose > 0 {
111+
cmd.arg("-Zincremental-info");
112+
}
113+
}
114+
98115
// If we're compiling specifically the `panic_abort` crate then we pass
99116
// the `-C panic=abort` option. Note that we do not do this for any
100117
// other crate intentionally as this is the only crate for now that we
@@ -176,9 +193,19 @@ fn main() {
176193
if let Some(rpath) = rpath {
177194
cmd.arg("-C").arg(format!("link-args={}", rpath));
178195
}
196+
197+
if let Ok(s) = env::var("RUSTFLAGS") {
198+
for flag in s.split_whitespace() {
199+
cmd.arg(flag);
200+
}
201+
}
179202
}
180203
}
181204

205+
if verbose > 1 {
206+
writeln!(&mut io::stderr(), "rustc command: {:?}", cmd).unwrap();
207+
}
208+
182209
// Actually run the compiler!
183210
std::process::exit(match exec_cmd(&mut cmd) {
184211
Ok(s) => s.code().unwrap_or(0xfe),

src/bootstrap/bootstrap.py

+2
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ def build_bootstrap(self):
294294
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib")
295295
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
296296
os.pathsep + env["PATH"]
297+
if not os.path.isfile(self.cargo()):
298+
raise Exception("no cargo executable found at `%s`" % self.cargo())
297299
args = [self.cargo(), "build", "--manifest-path",
298300
os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")]
299301
if self.use_vendored_sources:

src/bootstrap/check.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ pub fn compiletest(build: &Build,
190190

191191
cmd.args(&build.flags.cmd.test_args());
192192

193-
if build.config.verbose || build.flags.verbose {
193+
if build.config.verbose() || build.flags.verbose() {
194194
cmd.arg("--verbose");
195195
}
196196

src/bootstrap/config.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use util::push_exe_path;
4040
pub struct Config {
4141
pub ccache: Option<String>,
4242
pub ninja: bool,
43-
pub verbose: bool,
43+
pub verbose: usize,
4444
pub submodules: bool,
4545
pub compiler_docs: bool,
4646
pub docs: bool,
@@ -504,6 +504,14 @@ impl Config {
504504
}
505505
}
506506
}
507+
508+
pub fn verbose(&self) -> bool {
509+
self.verbose > 0
510+
}
511+
512+
pub fn very_verbose(&self) -> bool {
513+
self.verbose > 1
514+
}
507515
}
508516

509517
#[cfg(not(windows))]

src/bootstrap/flags.rs

+26-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use step;
2727

2828
/// Deserialized version of all flags for this compile.
2929
pub struct Flags {
30-
pub verbose: bool,
30+
pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
3131
pub stage: Option<u32>,
3232
pub keep_stage: Option<u32>,
3333
pub build: String,
@@ -37,6 +37,17 @@ pub struct Flags {
3737
pub src: Option<PathBuf>,
3838
pub jobs: Option<u32>,
3939
pub cmd: Subcommand,
40+
pub incremental: bool,
41+
}
42+
43+
impl Flags {
44+
pub fn verbose(&self) -> bool {
45+
self.verbose > 0
46+
}
47+
48+
pub fn very_verbose(&self) -> bool {
49+
self.verbose > 1
50+
}
4051
}
4152

4253
pub enum Subcommand {
@@ -63,7 +74,8 @@ pub enum Subcommand {
6374
impl Flags {
6475
pub fn parse(args: &[String]) -> Flags {
6576
let mut opts = Options::new();
66-
opts.optflag("v", "verbose", "use verbose output");
77+
opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
78+
opts.optflag("i", "incremental", "use incremental compilation");
6779
opts.optopt("", "config", "TOML configuration file for build", "FILE");
6880
opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
6981
opts.optmulti("", "host", "host targets to build", "HOST");
@@ -256,8 +268,18 @@ To learn more about a subcommand, run `./x.py <command> -h`
256268
}
257269
});
258270

271+
let mut stage = m.opt_str("stage").map(|j| j.parse().unwrap());
272+
273+
let incremental = m.opt_present("i");
274+
275+
if incremental {
276+
if stage.is_none() {
277+
stage = Some(1);
278+
}
279+
}
280+
259281
Flags {
260-
verbose: m.opt_present("v"),
282+
verbose: m.opt_count("v"),
261283
stage: m.opt_str("stage").map(|j| j.parse().unwrap()),
262284
keep_stage: m.opt_str("keep-stage").map(|j| j.parse().unwrap()),
263285
build: m.opt_str("build").unwrap_or_else(|| {
@@ -269,6 +291,7 @@ To learn more about a subcommand, run `./x.py <command> -h`
269291
src: m.opt_str("src").map(PathBuf::from),
270292
jobs: m.opt_str("jobs").map(|j| j.parse().unwrap()),
271293
cmd: cmd,
294+
incremental: incremental,
272295
}
273296
}
274297
}

src/bootstrap/lib.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ extern crate rustc_serialize;
7474
extern crate toml;
7575

7676
use std::collections::HashMap;
77+
use std::cmp;
7778
use std::env;
7879
use std::ffi::OsString;
7980
use std::fs::{self, File};
@@ -497,6 +498,17 @@ impl Build {
497498
cargo.env("RUSTC_BOOTSTRAP", "1");
498499
self.add_rust_test_threads(&mut cargo);
499500

501+
// Ignore incremental modes except for stage0, since we're
502+
// not guaranteeing correctness acros builds if the compiler
503+
// is changing under your feet.`
504+
if self.flags.incremental && compiler.stage == 0 {
505+
let incr_dir = self.incremental_dir(compiler);
506+
cargo.env("RUSTC_INCREMENTAL", incr_dir);
507+
}
508+
509+
let verbose = cmp::max(self.config.verbose, self.flags.verbose);
510+
cargo.env("RUSTC_VERBOSE", format!("{}", verbose));
511+
500512
// Specify some various options for build scripts used throughout
501513
// the build.
502514
//
@@ -516,7 +528,7 @@ impl Build {
516528
// FIXME: should update code to not require this env var
517529
cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
518530

519-
if self.config.verbose || self.flags.verbose {
531+
if self.config.verbose() || self.flags.verbose() {
520532
cargo.arg("-v");
521533
}
522534
// FIXME: cargo bench does not accept `--release`
@@ -630,6 +642,12 @@ impl Build {
630642
}
631643
}
632644

645+
/// Get the directory for incremental by-products when using the
646+
/// given compiler.
647+
fn incremental_dir(&self, compiler: &Compiler) -> PathBuf {
648+
self.out.join(compiler.host).join(format!("stage{}-incremental", compiler.stage))
649+
}
650+
633651
/// Returns the libdir where the standard library and other artifacts are
634652
/// found for a compiler's sysroot.
635653
fn sysroot_libdir(&self, compiler: &Compiler, target: &str) -> PathBuf {
@@ -768,7 +786,7 @@ impl Build {
768786

769787
/// Prints a message if this build is configured in verbose mode.
770788
fn verbose(&self, msg: &str) {
771-
if self.flags.verbose || self.config.verbose {
789+
if self.flags.verbose() || self.config.verbose() {
772790
println!("{}", msg);
773791
}
774792
}

src/bootstrap/step.rs

+15-16
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub fn build_rules(build: &Build) -> Rules {
8686
//
8787
// To handle this we do a bit of dynamic dispatch to see what the dependency
8888
// is. If we're building a LLVM for the build triple, then we don't actually
89-
// have any dependencies! To do that we return a dependency on the "dummy"
89+
// have any dependencies! To do that we return a dependency on the `Step::noop()`
9090
// target which does nothing.
9191
//
9292
// If we're build a cross-compiled LLVM, however, we need to assemble the
@@ -104,7 +104,7 @@ pub fn build_rules(build: &Build) -> Rules {
104104
.host(true)
105105
.dep(move |s| {
106106
if s.target == build.config.build {
107-
dummy(s, build)
107+
Step::noop()
108108
} else {
109109
s.target(&build.config.build)
110110
}
@@ -115,14 +115,11 @@ pub fn build_rules(build: &Build) -> Rules {
115115
// going on here. You can check out the API docs below and also see a bunch
116116
// more examples of rules directly below as well.
117117

118-
// dummy rule to do nothing, useful when a dep maps to no deps
119-
rules.build("dummy", "path/to/nowhere");
120-
121118
// the compiler with no target libraries ready to go
122119
rules.build("rustc", "src/rustc")
123120
.dep(move |s| {
124121
if s.stage == 0 {
125-
dummy(s, build)
122+
Step::noop()
126123
} else {
127124
s.name("librustc")
128125
.host(&build.config.build)
@@ -165,7 +162,7 @@ pub fn build_rules(build: &Build) -> Rules {
165162
.dep(move |s| s.name("rustc").host(&build.config.build).target(s.host))
166163
.dep(move |s| {
167164
if s.host == build.config.build {
168-
dummy(s, build)
165+
Step::noop()
169166
} else {
170167
s.host(&build.config.build)
171168
}
@@ -183,7 +180,7 @@ pub fn build_rules(build: &Build) -> Rules {
183180
.dep(|s| s.name("libstd"))
184181
.dep(move |s| {
185182
if s.host == build.config.build {
186-
dummy(s, build)
183+
Step::noop()
187184
} else {
188185
s.host(&build.config.build)
189186
}
@@ -203,7 +200,7 @@ pub fn build_rules(build: &Build) -> Rules {
203200
.dep(move |s| s.name("llvm").host(&build.config.build).stage(0))
204201
.dep(move |s| {
205202
if s.host == build.config.build {
206-
dummy(s, build)
203+
Step::noop()
207204
} else {
208205
s.host(&build.config.build)
209206
}
@@ -233,7 +230,7 @@ pub fn build_rules(build: &Build) -> Rules {
233230
if s.target.contains("android") {
234231
s.name("android-copy-libs")
235232
} else {
236-
dummy(s, build)
233+
Step::noop()
237234
}
238235
})
239236
.default(true)
@@ -514,12 +511,6 @@ pub fn build_rules(build: &Build) -> Rules {
514511

515512
rules.verify();
516513
return rules;
517-
518-
fn dummy<'a>(s: &Step<'a>, build: &'a Build) -> Step<'a> {
519-
s.name("dummy").stage(0)
520-
.target(&build.config.build)
521-
.host(&build.config.build)
522-
}
523514
}
524515

525516
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
@@ -543,6 +534,10 @@ struct Step<'a> {
543534
}
544535

545536
impl<'a> Step<'a> {
537+
fn noop() -> Step<'a> {
538+
Step { name: "", stage: 0, host: "", target: "" }
539+
}
540+
546541
/// Creates a new step which is the same as this, except has a new name.
547542
fn name(&self, name: &'a str) -> Step<'a> {
548543
Step { name: name, ..*self }
@@ -738,6 +733,9 @@ impl<'a> Rules<'a> {
738733
if self.rules.contains_key(&dep.name) || dep.name.starts_with("default:") {
739734
continue
740735
}
736+
if dep == Step::noop() {
737+
continue
738+
}
741739
panic!("\
742740
743741
invalid rule dependency graph detected, was a rule added and maybe typo'd?
@@ -864,6 +862,7 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd?
864862
// of what we need to do.
865863
let mut order = Vec::new();
866864
let mut added = HashSet::new();
865+
added.insert(Step::noop());
867866
for step in steps.iter().cloned() {
868867
self.fill(step, &mut order, &mut added);
869868
}

0 commit comments

Comments
 (0)