builder.rs 85.6 KB
Newer Older
1
use std::any::{type_name, Any};
2
use std::cell::{Cell, RefCell};
3 4
use std::collections::BTreeSet;
use std::env;
5
use std::ffi::{OsStr, OsString};
6
use std::fmt::{Debug, Write};
7
use std::fs::{self, File};
8
use std::hash::Hash;
9
use std::io::{BufRead, BufReader, ErrorKind};
10
use std::ops::Deref;
11
use std::path::{Component, Path, PathBuf};
12
use std::process::{Command, Stdio};
S
Santiago Pastorino 已提交
13
use std::time::{Duration, Instant};
14

L
ljedrz 已提交
15 16
use crate::cache::{Cache, Interned, INTERNER};
use crate::compile;
17
use crate::config::{SplitDebuginfo, TargetSelection};
L
ljedrz 已提交
18 19
use crate::dist;
use crate::doc;
J
Joshua Nelson 已提交
20
use crate::flags::{Color, Subcommand};
L
ljedrz 已提交
21 22
use crate::install;
use crate::native;
23
use crate::run;
L
ljedrz 已提交
24
use crate::test;
25
use crate::tool::{self, SourceType};
B
bjorn3 已提交
26
use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
27
use crate::EXTRA_CHECK_CFGS;
28
use crate::{check, Config};
29
use crate::{Build, CLang, DocTests, GitRepo, Mode};
L
ljedrz 已提交
30 31

pub use crate::Compiler;
32
// FIXME: replace with std::lazy after it gets stabilized and reaches beta
33 34
use once_cell::sync::{Lazy, OnceCell};
use xz2::bufread::XzDecoder;
35

36 37 38 39 40
pub struct Builder<'a> {
    pub build: &'a Build,
    pub top_stage: u32,
    pub kind: Kind,
    cache: Cache,
41
    stack: RefCell<Vec<Box<dyn Any>>>,
42
    time_spent_on_dependencies: Cell<Duration>,
43
    pub paths: Vec<PathBuf>,
44 45 46 47 48 49 50 51 52 53
}

impl<'a> Deref for Builder<'a> {
    type Target = Build;

    fn deref(&self) -> &Self::Target {
        self.build
    }
}

54
pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
55 56
    /// `PathBuf` when directories are created or to return a `Compiler` once
    /// it's been assembled.
57
    type Output: Clone;
58

R
Ralf Jung 已提交
59 60
    /// Whether this step is run by default as part of its respective phase.
    /// `true` here can still be overwritten by `should_run` calling `default_condition`.
61 62
    const DEFAULT: bool = false;

63
    /// If true, then this rule should be skipped if --target was specified, but --host was not
64 65
    const ONLY_HOSTS: bool = false;

A
Alexander Regueiro 已提交
66
    /// Primary function to execute this rule. Can call `builder.ensure()`
67
    /// with other steps to run those.
T
Taiki Endo 已提交
68
    fn run(self, builder: &Builder<'_>) -> Self::Output;
69

70 71
    /// When bootstrap is passed a set of paths, this controls whether this rule
    /// will execute. However, it does not get called in a "default" context
A
Alexander Regueiro 已提交
72
    /// when we are not passed any paths; in that case, `make_run` is called
73
    /// directly.
T
Taiki Endo 已提交
74
    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_>;
75

A
Alexander Regueiro 已提交
76
    /// Builds up a "root" rule, either as a default rule or from a path passed
77 78 79 80 81
    /// to us.
    ///
    /// When path is `None`, we are executing in a context where no paths were
    /// passed. When `./x.py build` is run, for example, this rule could get
    /// called if it is in the correct list below with a path of `None`.
T
Taiki Endo 已提交
82
    fn make_run(_run: RunConfig<'_>) {
83 84 85 86 87 88
        // It is reasonable to not have an implementation of make_run for rules
        // who do not want to get called from the root context. This means that
        // they are likely dependencies (e.g., sysroot creation) or similar, and
        // as such calling them from ./x.py isn't logical.
        unimplemented!()
    }
89 90
}

91 92
pub struct RunConfig<'a> {
    pub builder: &'a Builder<'a>,
93
    pub target: TargetSelection,
94
    pub path: PathBuf,
95 96
}

97 98 99 100 101 102
impl RunConfig<'_> {
    pub fn build_triple(&self) -> TargetSelection {
        self.builder.build.build
    }
}

103 104 105
struct StepDescription {
    default: bool,
    only_hosts: bool,
T
Taiki Endo 已提交
106 107
    should_run: fn(ShouldRun<'_>) -> ShouldRun<'_>,
    make_run: fn(RunConfig<'_>),
108
    name: &'static str,
109
    kind: Kind,
110 111
}

112
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
113 114
pub struct TaskPath {
    pub path: PathBuf,
115
    pub kind: Option<Kind>,
116 117 118 119
}

impl TaskPath {
    pub fn parse(path: impl Into<PathBuf>) -> TaskPath {
120 121 122 123 124 125 126 127 128 129
        let mut kind = None;
        let mut path = path.into();

        let mut components = path.components();
        if let Some(Component::Normal(os_str)) = components.next() {
            if let Some(str) = os_str.to_str() {
                if let Some((found_kind, found_prefix)) = str.split_once("::") {
                    if found_kind.is_empty() {
                        panic!("empty kind in task path {}", path.display());
                    }
130 131
                    kind = Kind::parse(found_kind);
                    assert!(kind.is_some());
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
                    path = Path::new(found_prefix).join(components.as_path());
                }
            }
        }

        TaskPath { path, kind }
    }
}

impl Debug for TaskPath {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(kind) = &self.kind {
            write!(f, "{}::", kind.as_str())?;
        }
        write!(f, "{}", self.path.display())
147 148 149
    }
}

150
/// Collection of paths used to match a task rule.
151
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
152
pub enum PathSet {
153 154 155 156 157
    /// A collection of individual paths.
    ///
    /// These are generally matched as a path suffix. For example, a
    /// command-line value of `libstd` will match if `src/libstd` is in the
    /// set.
158
    Set(BTreeSet<TaskPath>),
159 160 161 162 163 164
    /// A "suite" of paths.
    ///
    /// These can match as a path suffix (like `Set`), or as a prefix. For
    /// example, a command-line value of `src/test/ui/abi/variadic-ffi.rs`
    /// will match `src/test/ui`. A command-line value of `ui` would also
    /// match `src/test/ui`.
165
    Suite(TaskPath),
166 167 168
}

impl PathSet {
169
    fn empty() -> PathSet {
170
        PathSet::Set(BTreeSet::new())
171 172
    }

173
    fn one<P: Into<PathBuf>>(path: P, kind: Kind) -> PathSet {
174
        let mut set = BTreeSet::new();
175
        set.insert(TaskPath { path: path.into(), kind: Some(kind) });
176
        PathSet::Set(set)
177 178
    }

179 180 181 182 183 184 185 186 187
    fn has(&self, needle: &Path, module: Option<Kind>) -> bool {
        let check = |p: &TaskPath| {
            if let (Some(p_kind), Some(kind)) = (&p.kind, module) {
                p.path.ends_with(needle) && *p_kind == kind
            } else {
                p.path.ends_with(needle)
            }
        };

188
        match self {
189 190
            PathSet::Set(set) => set.iter().any(check),
            PathSet::Suite(suite) => check(suite),
191
        }
192 193
    }

T
Taiki Endo 已提交
194
    fn path(&self, builder: &Builder<'_>) -> PathBuf {
195
        match self {
196 197 198 199
            PathSet::Set(set) => {
                set.iter().next().map(|p| &p.path).unwrap_or(&builder.build.src).clone()
            }
            PathSet::Suite(path) => path.path.clone(),
200
        }
201
    }
202 203 204
}

impl StepDescription {
205
    fn from<S: Step>(kind: Kind) -> StepDescription {
206 207 208 209 210
        StepDescription {
            default: S::DEFAULT,
            only_hosts: S::ONLY_HOSTS,
            should_run: S::should_run,
            make_run: S::make_run,
M
Mark Rousskov 已提交
211
            name: std::any::type_name::<S>(),
212
            kind,
213 214 215
        }
    }

T
Taiki Endo 已提交
216
    fn maybe_run(&self, builder: &Builder<'_>, pathset: &PathSet) {
217
        if self.is_excluded(builder, pathset) {
218
            return;
219
        }
220

M
Mark Simulacrum 已提交
221
        // Determine the targets participating in this rule.
T
Tyler Mandry 已提交
222
        let targets = if self.only_hosts { &builder.hosts } else { &builder.targets };
223

224 225 226
        for target in targets {
            let run = RunConfig { builder, path: pathset.path(builder), target: *target };
            (self.make_run)(run);
227 228 229
        }
    }

230
    fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool {
231
        if builder.config.exclude.iter().any(|e| pathset.has(&e.path, e.kind)) {
232
            println!("Skipping {:?} because it is excluded", pathset);
233 234 235 236
            return true;
        }

        if !builder.config.exclude.is_empty() {
237
            builder.verbose(&format!(
238 239
                "{:?} not skipped for {:?} -- not in {:?}",
                pathset, self.name, builder.config.exclude
240
            ));
241 242 243 244
        }
        false
    }

T
Taiki Endo 已提交
245
    fn run(v: &[StepDescription], builder: &Builder<'_>, paths: &[PathBuf]) {
246 247 248 249
        let should_runs = v
            .iter()
            .map(|desc| (desc.should_run)(ShouldRun::new(builder, desc.kind)))
            .collect::<Vec<_>>();
250 251 252

        // sanity checks on rules
        for (desc, should_run) in v.iter().zip(&should_runs) {
S
Santiago Pastorino 已提交
253 254 255 256 257
            assert!(
                !should_run.paths.is_empty(),
                "{:?} should have at least one pathset",
                desc.name
            );
258 259
        }

260 261
        if paths.is_empty() || builder.config.include_default_paths {
            for (desc, should_run) in v.iter().zip(&should_runs) {
262
                if desc.default && should_run.is_really_default() {
263 264 265
                    for pathset in &should_run.paths {
                        desc.maybe_run(builder, pathset);
                    }
266 267
                }
            }
268
        }
269

270 271 272 273 274 275 276 277 278 279 280 281
        for path in paths {
            // strip CurDir prefix if present
            let path = match path.strip_prefix(".") {
                Ok(p) => p,
                Err(_) => path,
            };

            let mut attempted_run = false;
            for (desc, should_run) in v.iter().zip(&should_runs) {
                if let Some(suite) = should_run.is_suite_path(path) {
                    attempted_run = true;
                    desc.maybe_run(builder, suite);
282
                } else if let Some(pathset) = should_run.pathset_for_path(path, desc.kind) {
283 284
                    attempted_run = true;
                    desc.maybe_run(builder, pathset);
285 286
                }
            }
287 288

            if !attempted_run {
289 290 291 292 293 294 295 296 297 298 299 300 301
                eprintln!(
                    "error: no `{}` rules matched '{}'",
                    builder.kind.as_str(),
                    path.display()
                );
                eprintln!(
                    "help: run `x.py {} --help --verbose` to show a list of available paths",
                    builder.kind.as_str()
                );
                eprintln!(
                    "note: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`"
                );
                std::process::exit(1);
302
            }
303 304 305 306
        }
    }
}

307 308 309 310 311
enum ReallyDefault<'a> {
    Bool(bool),
    Lazy(Lazy<bool, Box<dyn Fn() -> bool + 'a>>),
}

312
pub struct ShouldRun<'a> {
313
    pub builder: &'a Builder<'a>,
314 315
    kind: Kind,

316
    // use a BTreeSet to maintain sort order
317
    paths: BTreeSet<PathSet>,
318 319

    // If this is a default rule, this is an additional constraint placed on
320
    // its run. Generally something like compiler docs being enabled.
321
    is_really_default: ReallyDefault<'a>,
322 323 324
}

impl<'a> ShouldRun<'a> {
325
    fn new(builder: &'a Builder<'_>, kind: Kind) -> ShouldRun<'a> {
326
        ShouldRun {
327
            builder,
328
            kind,
329
            paths: BTreeSet::new(),
330
            is_really_default: ReallyDefault::Bool(true), // by default no additional conditions
331 332 333
        }
    }

334
    pub fn default_condition(mut self, cond: bool) -> Self {
335 336 337 338 339 340
        self.is_really_default = ReallyDefault::Bool(cond);
        self
    }

    pub fn lazy_default_condition(mut self, lazy_cond: Box<dyn Fn() -> bool + 'a>) -> Self {
        self.is_really_default = ReallyDefault::Lazy(Lazy::new(lazy_cond));
341 342 343
        self
    }

344 345 346 347 348 349 350
    pub fn is_really_default(&self) -> bool {
        match &self.is_really_default {
            ReallyDefault::Bool(val) => *val,
            ReallyDefault::Lazy(lazy) => *lazy.deref(),
        }
    }

351 352 353 354 355 356 357 358 359
    /// Indicates it should run if the command-line selects the given crate or
    /// any of its (local) dependencies.
    ///
    /// Compared to `krate`, this treats the dependencies as aliases for the
    /// same job. Generally it is preferred to use `krate`, and treat each
    /// individual path separately. For example `./x.py test src/liballoc`
    /// (which uses `krate`) will test just `liballoc`. However, `./x.py check
    /// src/liballoc` (which uses `all_krates`) will check all of `libtest`.
    /// `all_krates` should probably be removed at some point.
360 361
    pub fn all_krates(mut self, name: &str) -> Self {
        let mut set = BTreeSet::new();
362
        for krate in self.builder.in_tree_crates(name, None) {
363
            let path = krate.local_path(self.builder);
364
            set.insert(TaskPath { path, kind: Some(self.kind) });
365
        }
366
        self.paths.insert(PathSet::Set(set));
367 368 369
        self
    }

370 371 372 373
    /// Indicates it should run if the command-line selects the given crate or
    /// any of its (local) dependencies.
    ///
    /// `make_run` will be called separately for each matching command-line path.
374
    pub fn krate(mut self, name: &str) -> Self {
375
        for krate in self.builder.in_tree_crates(name, None) {
376
            let path = krate.local_path(self.builder);
377
            self.paths.insert(PathSet::one(path, self.kind));
378 379 380 381
        }
        self
    }

382 383 384 385 386 387 388 389 390 391 392 393 394
    // single alias, which does not correspond to any on-disk path
    pub fn alias(mut self, alias: &str) -> Self {
        assert!(
            !self.builder.src.join(alias).exists(),
            "use `builder.path()` for real paths: {}",
            alias
        );
        self.paths.insert(PathSet::Set(
            std::iter::once(TaskPath { path: alias.into(), kind: Some(self.kind) }).collect(),
        ));
        self
    }

395 396 397 398 399 400 401
    // single, non-aliased path
    pub fn path(self, path: &str) -> Self {
        self.paths(&[path])
    }

    // multiple aliases for the same job
    pub fn paths(mut self, paths: &[&str]) -> Self {
402
        self.paths.insert(PathSet::Set(
403 404 405
            paths
                .iter()
                .map(|p| {
406 407 408 409 410 411 412
                    // FIXME(#96188): make sure this is actually a path.
                    // This currently breaks for paths within submodules.
                    //assert!(
                    //    self.builder.src.join(p).exists(),
                    //    "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {}",
                    //    p
                    //);
413 414 415
                    TaskPath { path: p.into(), kind: Some(self.kind) }
                })
                .collect(),
416
        ));
417 418 419 420
        self
    }

    pub fn is_suite_path(&self, path: &Path) -> Option<&PathSet> {
S
Santiago Pastorino 已提交
421
        self.paths.iter().find(|pathset| match pathset {
422
            PathSet::Suite(p) => path.starts_with(&p.path),
S
Santiago Pastorino 已提交
423
            PathSet::Set(_) => false,
424 425 426 427
        })
    }

    pub fn suite_path(mut self, suite: &str) -> Self {
428
        self.paths.insert(PathSet::Suite(TaskPath { path: suite.into(), kind: Some(self.kind) }));
429 430 431 432
        self
    }

    // allows being more explicit about why should_run in Step returns the value passed to it
433 434
    pub fn never(mut self) -> ShouldRun<'a> {
        self.paths.insert(PathSet::empty());
435 436 437
        self
    }

438 439
    fn pathset_for_path(&self, path: &Path, kind: Kind) -> Option<&PathSet> {
        self.paths.iter().find(|pathset| pathset.has(path, Some(kind)))
440 441 442
    }
}

443
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
444 445
pub enum Kind {
    Build,
446
    Check,
L
ljedrz 已提交
447 448
    Clippy,
    Fix,
449
    Format,
450 451 452
    Test,
    Bench,
    Doc,
453 454
    Clean,
    Dist,
455
    Install,
456
    Run,
457
    Setup,
458 459
}

460
impl Kind {
461 462 463 464 465
    pub fn parse(string: &str) -> Option<Kind> {
        // these strings, including the one-letter aliases, must match the x.py help text
        Some(match string {
            "build" | "b" => Kind::Build,
            "check" | "c" => Kind::Check,
466 467
            "clippy" => Kind::Clippy,
            "fix" => Kind::Fix,
468 469
            "fmt" => Kind::Format,
            "test" | "t" => Kind::Test,
470
            "bench" => Kind::Bench,
471 472
            "doc" | "d" => Kind::Doc,
            "clean" => Kind::Clean,
473 474
            "dist" => Kind::Dist,
            "install" => Kind::Install,
475 476 477 478
            "run" | "r" => Kind::Run,
            "setup" => Kind::Setup,
            _ => return None,
        })
479 480
    }

481
    pub fn as_str(&self) -> &'static str {
482 483 484 485 486
        match self {
            Kind::Build => "build",
            Kind::Check => "check",
            Kind::Clippy => "clippy",
            Kind::Fix => "fix",
487
            Kind::Format => "fmt",
488 489 490
            Kind::Test => "test",
            Kind::Bench => "bench",
            Kind::Doc => "doc",
491 492
            Kind::Clean => "clean",
            Kind::Dist => "dist",
493 494
            Kind::Install => "install",
            Kind::Run => "run",
495
            Kind::Setup => "setup",
496 497 498 499
        }
    }
}

500 501 502
impl<'a> Builder<'a> {
    fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> {
        macro_rules! describe {
T
Taiki Endo 已提交
503
            ($($rule:ty),+ $(,)?) => {{
504
                vec![$(StepDescription::from::<$rule>(kind)),+]
505
            }};
506
        }
507
        match kind {
S
Santiago Pastorino 已提交
508 509
            Kind::Build => describe!(
                compile::Std,
510
                compile::Assemble,
511
                compile::CodegenBackend,
S
Santiago Pastorino 已提交
512 513 514 515 516 517 518 519 520 521 522 523 524 525
                compile::StartupObjects,
                tool::BuildManifest,
                tool::Rustbook,
                tool::ErrorIndex,
                tool::UnstableBookGen,
                tool::Tidy,
                tool::Linkchecker,
                tool::CargoTest,
                tool::Compiletest,
                tool::RemoteTestServer,
                tool::RemoteTestClient,
                tool::RustInstaller,
                tool::Cargo,
                tool::Rls,
A
Aleksey Kladov 已提交
526
                tool::RustAnalyzer,
R
Rich Kadel 已提交
527
                tool::RustDemangler,
S
Santiago Pastorino 已提交
528 529
                tool::Rustdoc,
                tool::Clippy,
530
                tool::CargoClippy,
S
Santiago Pastorino 已提交
531
                native::Llvm,
532
                native::Sanitizers,
S
Santiago Pastorino 已提交
533 534
                tool::Rustfmt,
                tool::Miri,
535
                tool::CargoMiri,
536 537
                native::Lld,
                native::CrtBeginEnd
S
Santiago Pastorino 已提交
538
            ),
Y
Yuki Okushi 已提交
539
            Kind::Check | Kind::Clippy | Kind::Fix => describe!(
540 541 542 543 544
                check::Std,
                check::Rustc,
                check::Rustdoc,
                check::CodegenBackend,
                check::Clippy,
545 546
                check::Miri,
                check::Rls,
547
                check::Rustfmt,
548 549
                check::Bootstrap
            ),
S
Santiago Pastorino 已提交
550
            Kind::Test => describe!(
551
                crate::toolstate::ToolStateCheck,
552
                test::ExpandYamlAnchors,
S
Santiago Pastorino 已提交
553 554 555 556 557 558
                test::Tidy,
                test::Ui,
                test::RunPassValgrind,
                test::MirOpt,
                test::Codegen,
                test::CodegenUnits,
559
                test::Assembly,
S
Santiago Pastorino 已提交
560 561 562 563 564 565 566 567
                test::Incremental,
                test::Debuginfo,
                test::UiFullDeps,
                test::Rustdoc,
                test::Pretty,
                test::Crate,
                test::CrateLibrustc,
                test::CrateRustdoc,
568
                test::CrateRustdocJsonTypes,
S
Santiago Pastorino 已提交
569
                test::Linkcheck,
570
                test::TierCheck,
S
Santiago Pastorino 已提交
571 572 573 574 575
                test::Cargotest,
                test::Cargo,
                test::Rls,
                test::ErrorIndex,
                test::Distcheck,
576
                test::RunMakeFullDeps,
S
Santiago Pastorino 已提交
577 578 579 580 581 582 583
                test::Nomicon,
                test::Reference,
                test::RustdocBook,
                test::RustByExample,
                test::TheBook,
                test::UnstableBook,
                test::RustcBook,
584
                test::LintDocs,
585
                test::RustcGuide,
586
                test::EmbeddedBook,
E
Eric Huss 已提交
587
                test::EditionGuide,
S
Santiago Pastorino 已提交
588 589 590
                test::Rustfmt,
                test::Miri,
                test::Clippy,
R
Rich Kadel 已提交
591
                test::RustDemangler,
592
                test::CompiletestTest,
G
Guillaume Gomez 已提交
593
                test::RustdocJSStd,
G
Guillaume Gomez 已提交
594
                test::RustdocJSNotStd,
G
Guillaume Gomez 已提交
595
                test::RustdocGUI,
S
Santiago Pastorino 已提交
596
                test::RustdocTheme,
J
John Kåre Alsaker 已提交
597
                test::RustdocUi,
N
Nixon Enraght-Moony 已提交
598
                test::RustdocJson,
G
Guillaume Gomez 已提交
599
                test::HtmlCheck,
600 601
                // Run bootstrap close to the end as it's unlikely to fail
                test::Bootstrap,
602
                // Run run-make last, since these won't pass without make on Windows
S
Santiago Pastorino 已提交
603 604
                test::RunMake,
            ),
605
            Kind::Bench => describe!(test::Crate, test::CrateLibrustc),
S
Santiago Pastorino 已提交
606 607 608 609 610 611 612 613
            Kind::Doc => describe!(
                doc::UnstableBook,
                doc::UnstableBookGen,
                doc::TheBook,
                doc::Standalone,
                doc::Std,
                doc::Rustc,
                doc::Rustdoc,
614
                doc::Rustfmt,
S
Santiago Pastorino 已提交
615 616 617 618 619 620
                doc::ErrorIndex,
                doc::Nomicon,
                doc::Reference,
                doc::RustdocBook,
                doc::RustByExample,
                doc::RustcBook,
S
Steve Klabnik 已提交
621
                doc::CargoBook,
X
xFrednet 已提交
622
                doc::Clippy,
J
James Munns 已提交
623
                doc::EmbeddedBook,
S
Steve Klabnik 已提交
624
                doc::EditionGuide,
S
Santiago Pastorino 已提交
625 626 627 628 629 630 631
            ),
            Kind::Dist => describe!(
                dist::Docs,
                dist::RustcDocs,
                dist::Mingw,
                dist::Rustc,
                dist::Std,
J
Josh Stone 已提交
632
                dist::RustcDev,
S
Santiago Pastorino 已提交
633 634 635 636
                dist::Analysis,
                dist::Src,
                dist::Cargo,
                dist::Rls,
A
Aleksey Kladov 已提交
637
                dist::RustAnalyzer,
S
Santiago Pastorino 已提交
638
                dist::Rustfmt,
R
Rich Kadel 已提交
639
                dist::RustDemangler,
640
                dist::Clippy,
641
                dist::Miri,
642
                dist::LlvmTools,
643
                dist::RustDev,
S
Santiago Pastorino 已提交
644
                dist::Extended,
645
                // It seems that PlainSourceTarball somehow changes how some of the tools
Y
Yuri Astrakhan 已提交
646
                // perceive their dependencies (see #93033) which would invalidate fingerprints
647 648 649
                // and force us to rebuild tools after vendoring dependencies.
                // To work around this, create the Tarball after building all the tools.
                dist::PlainSourceTarball,
650
                dist::BuildManifest,
651
                dist::ReproducibleArtifacts,
S
Santiago Pastorino 已提交
652 653 654 655 656 657
            ),
            Kind::Install => describe!(
                install::Docs,
                install::Std,
                install::Cargo,
                install::Rls,
A
Aleksey Kladov 已提交
658
                install::RustAnalyzer,
S
Santiago Pastorino 已提交
659
                install::Rustfmt,
R
Rich Kadel 已提交
660
                install::RustDemangler,
661
                install::Clippy,
662
                install::Miri,
S
Santiago Pastorino 已提交
663 664 665 666
                install::Analysis,
                install::Src,
                install::Rustc
            ),
667
            Kind::Run => describe!(run::ExpandYamlAnchors, run::BuildManifest, run::BumpStage0),
668
            // These commands either don't use paths, or they're special-cased in Build::build()
Y
Yuki Okushi 已提交
669
            Kind::Clean | Kind::Format | Kind::Setup => vec![],
670 671
        }
    }
672

673 674 675 676 677
    pub fn get_help(build: &Build, kind: Kind) -> Option<String> {
        let step_descriptions = Builder::get_step_descriptions(kind);
        if step_descriptions.is_empty() {
            return None;
        }
678

J
Joshua Nelson 已提交
679
        let builder = Self::new_internal(build, kind, vec![]);
680
        let builder = &builder;
681 682 683
        // The "build" kind here is just a placeholder, it will be replaced with something else in
        // the following statement.
        let mut should_run = ShouldRun::new(builder, Kind::Build);
684
        for desc in step_descriptions {
685
            should_run.kind = desc.kind;
686
            should_run = (desc.should_run)(should_run);
687 688
        }
        let mut help = String::from("Available paths:\n");
689
        let mut add_path = |path: &Path| {
690
            t!(write!(help, "    ./x.py {} {}\n", kind.as_str(), path.display()));
691
        };
692
        for pathset in should_run.paths {
693 694 695
            match pathset {
                PathSet::Set(set) => {
                    for path in set {
696
                        add_path(&path.path);
697 698 699
                    }
                }
                PathSet::Suite(path) => {
700
                    add_path(&path.path.join("..."));
701
                }
702
            }
703 704 705 706
        }
        Some(help)
    }

J
Joshua Nelson 已提交
707 708 709
    fn new_internal(build: &Build, kind: Kind, paths: Vec<PathBuf>) -> Builder<'_> {
        Builder {
            build,
710
            top_stage: build.config.stage,
J
Joshua Nelson 已提交
711 712 713 714 715 716 717 718
            kind,
            cache: Cache::new(),
            stack: RefCell::new(Vec::new()),
            time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
            paths,
        }
    }

T
Taiki Endo 已提交
719
    pub fn new(build: &Build) -> Builder<'_> {
M
Mark Simulacrum 已提交
720
        let (kind, paths) = match build.config.cmd {
721
            Subcommand::Build { ref paths } => (Kind::Build, &paths[..]),
722
            Subcommand::Check { ref paths } => (Kind::Check, &paths[..]),
J
Joshua Nelson 已提交
723
            Subcommand::Clippy { ref paths, .. } => (Kind::Clippy, &paths[..]),
L
ljedrz 已提交
724
            Subcommand::Fix { ref paths } => (Kind::Fix, &paths[..]),
725
            Subcommand::Doc { ref paths, .. } => (Kind::Doc, &paths[..]),
726 727 728 729
            Subcommand::Test { ref paths, .. } => (Kind::Test, &paths[..]),
            Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]),
            Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]),
            Subcommand::Install { ref paths } => (Kind::Install, &paths[..]),
730
            Subcommand::Run { ref paths } => (Kind::Run, &paths[..]),
J
Joshua Nelson 已提交
731 732
            Subcommand::Format { .. } | Subcommand::Clean { .. } | Subcommand::Setup { .. } => {
                panic!()
733
            }
734
        };
735

736
        Self::new_internal(build, kind, paths.to_owned())
737 738
    }

739
    pub fn execute_cli(&self) {
M
Mark Simulacrum 已提交
740
        self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths);
741 742
    }

743
    pub fn default_doc(&self, paths: &[PathBuf]) {
M
Mark Simulacrum 已提交
744 745 746
        self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), paths);
    }

747 748 749 750 751 752 753 754 755 756 757 758
    /// NOTE: keep this in sync with `rustdoc::clean::utils::doc_rust_lang_org_channel`, or tests will fail on beta/stable.
    pub fn doc_rust_lang_org_channel(&self) -> String {
        let channel = match &*self.config.channel {
            "stable" => &self.version,
            "beta" => "beta",
            "nightly" | "dev" => "nightly",
            // custom build of rustdoc maybe? link to the latest stable docs just in case
            _ => "stable",
        };
        "https://doc.rust-lang.org/".to_owned() + channel
    }

M
Mark Simulacrum 已提交
759 760
    fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) {
        StepDescription::run(v, self, paths);
761 762
    }

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
    /// Modifies the interpreter section of 'fname' to fix the dynamic linker,
    /// or the RPATH section, to fix the dynamic library search path
    ///
    /// This is only required on NixOS and uses the PatchELF utility to
    /// change the interpreter/RPATH of ELF executables.
    ///
    /// Please see https://nixos.org/patchelf.html for more information
    pub(crate) fn fix_bin_or_dylib(&self, fname: &Path) {
        // FIXME: cache NixOS detection?
        match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() {
            Err(_) => return,
            Ok(output) if !output.status.success() => return,
            Ok(output) => {
                let mut s = output.stdout;
                if s.last() == Some(&b'\n') {
                    s.pop();
                }
                if s != b"Linux" {
                    return;
                }
            }
        }

        // If the user has asked binaries to be patched for Nix, then
        // don't check for NixOS or `/lib`, just continue to the patching.
788 789 790
        // NOTE: this intentionally comes after the Linux check:
        // - patchelf only works with ELF files, so no need to run it on Mac or Windows
        // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc.
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
        if !self.config.patch_binaries_for_nix {
            // Use `/etc/os-release` instead of `/etc/NIXOS`.
            // The latter one does not exist on NixOS when using tmpfs as root.
            const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""];
            let os_release = match File::open("/etc/os-release") {
                Err(e) if e.kind() == ErrorKind::NotFound => return,
                Err(e) => panic!("failed to access /etc/os-release: {}", e),
                Ok(f) => f,
            };
            if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) {
                return;
            }
            if Path::new("/lib").exists() {
                return;
            }
        }

        // At this point we're pretty sure the user is running NixOS or using Nix
        println!("info: you seem to be using Nix. Attempting to patch {}", fname.display());

        // Only build `.nix-deps` once.
        static NIX_DEPS_DIR: OnceCell<PathBuf> = OnceCell::new();
        let mut nix_build_succeeded = true;
        let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| {
            // Run `nix-build` to "build" each dependency (which will likely reuse
            // the existing `/nix/store` copy, or at most download a pre-built copy).
            //
            // Importantly, we create a gc-root called `.nix-deps` in the `build/`
            // directory, but still reference the actual `/nix/store` path in the rpath
            // as it makes it significantly more robust against changes to the location of
            // the `.nix-deps` location.
            //
            // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`).
            // zlib: Needed as a system dependency of `libLLVM-*.so`.
            // patchelf: Needed for patching ELF binaries (see doc comment above).
            let nix_deps_dir = self.out.join(".nix-deps");
            const NIX_EXPR: &str = "
            with (import <nixpkgs> {});
            symlinkJoin {
                name = \"rust-stage0-dependencies\";
                paths = [
                    zlib
                    patchelf
                    stdenv.cc.bintools
                ];
            }
            ";
            nix_build_succeeded = self.try_run(Command::new("nix-build").args(&[
                Path::new("-E"),
                Path::new(NIX_EXPR),
                Path::new("-o"),
                &nix_deps_dir,
            ]));
            nix_deps_dir
        });
        if !nix_build_succeeded {
            return;
        }

        let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf"));
        let rpath_entries = {
            // ORIGIN is a relative default, all binary and dynamic libraries we ship
            // appear to have this (even when `../lib` is redundant).
            // NOTE: there are only two paths here, delimited by a `:`
            let mut entries = OsString::from("$ORIGIN/../lib:");
            entries.push(t!(fs::canonicalize(nix_deps_dir)));
            entries.push("/lib");
            entries
        };
        patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
        if !fname.extension().map_or(false, |ext| ext == "so") {
            // Finally, set the corret .interp for binaries
            let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
            // FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
            let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path))));
            patchelf.args(&["--set-interpreter", dynamic_linker.trim_end()]);
        }

        self.try_run(patchelf.arg(fname));
    }

872 873 874 875 876 877 878
    pub(crate) fn download_component(
        &self,
        base: &str,
        url: &str,
        dest_path: &Path,
        help_on_error: &str,
    ) {
879 880 881
        // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.
        let tempfile = self.tempdir().join(dest_path.file_name().unwrap());
        // FIXME: support `do_verify` (only really needed for nightly rustfmt)
882
        self.download_with_retries(&tempfile, &format!("{}/{}", base, url), help_on_error);
883 884 885
        t!(std::fs::rename(&tempfile, dest_path));
    }

886
    fn download_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
887 888
        println!("downloading {}", url);
        // Try curl. If that fails and we are on windows, fallback to PowerShell.
889 890
        let mut curl = Command::new("curl");
        curl.args(&[
891 892 893 894 895 896 897 898 899 900 901
            "-#",
            "-y",
            "30",
            "-Y",
            "10", // timeout if speed is < 10 bytes/sec for > 30 seconds
            "--connect-timeout",
            "30", // timeout if cannot connect within 30 seconds
            "--retry",
            "3",
            "-Sf",
            "-o",
902 903 904 905
        ]);
        curl.arg(tempfile);
        curl.arg(url);
        if !self.check_run(&mut curl) {
906 907 908 909 910 911 912 913 914
            if self.build.build.contains("windows-msvc") {
                println!("Fallback to PowerShell");
                for _ in 0..3 {
                    if self.try_run(Command::new("PowerShell.exe").args(&[
                        "/nologo",
                        "-Command",
                        "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
                        &format!(
                            "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
915
                            url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"),
916 917 918 919 920 921 922
                        ),
                    ])) {
                        return;
                    }
                    println!("\nspurious failure, trying again");
                }
            }
923 924 925
            if !help_on_error.is_empty() {
                eprintln!("{}", help_on_error);
            }
926 927 928 929
            std::process::exit(1);
        }
    }

930
    pub(crate) fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) {
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
        println!("extracting {} to {}", tarball.display(), dst.display());
        if !dst.exists() {
            t!(fs::create_dir_all(dst));
        }

        // `tarball` ends with `.tar.xz`; strip that suffix
        // example: `rust-dev-nightly-x86_64-unknown-linux-gnu`
        let uncompressed_filename =
            Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap();
        let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap());

        // decompress the file
        let data = t!(File::open(tarball));
        let decompressor = XzDecoder::new(BufReader::new(data));

        let mut tar = tar::Archive::new(decompressor);
        for member in t!(tar.entries()) {
            let mut member = t!(member);
            let original_path = t!(member.path()).into_owned();
            // skip the top-level directory
            if original_path == directory_prefix {
                continue;
            }
            let mut short_path = t!(original_path.strip_prefix(directory_prefix));
955
            if !short_path.starts_with(pattern) {
956 957
                continue;
            }
958
            short_path = t!(short_path.strip_prefix(pattern));
959 960 961 962 963 964 965 966 967 968 969 970 971 972
            let dst_path = dst.join(short_path);
            self.verbose(&format!("extracting {} to {}", original_path.display(), dst.display()));
            if !t!(member.unpack_in(dst)) {
                panic!("path traversal attack ??");
            }
            let src_path = dst.join(original_path);
            if src_path.is_dir() && dst_path.exists() {
                continue;
            }
            t!(fs::rename(src_path, dst_path));
        }
        t!(fs::remove_dir_all(dst.join(directory_prefix)));
    }

B
Bastien Orivel 已提交
973
    /// Obtain a compiler at a given stage and for a given host. Explicitly does
974 975 976
    /// not take `Compiler` since all `Compiler` instances are meant to be
    /// obtained through this function, since it ensures that they are valid
    /// (i.e., built and assembled).
977
    pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler {
M
Mark Rousskov 已提交
978
        self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } })
979 980
    }

981 982 983 984 985 986 987 988 989 990 991
    /// Similar to `compiler`, except handles the full-bootstrap option to
    /// silently use the stage1 compiler instead of a stage2 compiler if one is
    /// requested.
    ///
    /// Note that this does *not* have the side effect of creating
    /// `compiler(stage, host)`, unlike `compiler` above which does have such
    /// a side effect. The returned compiler here can only be used to compile
    /// new artifacts, it can't be used to rely on the presence of a particular
    /// sysroot.
    ///
    /// See `force_use_stage1` for documentation on what each argument is.
A
Alex Crichton 已提交
992 993 994
    pub fn compiler_for(
        &self,
        stage: u32,
995 996
        host: TargetSelection,
        target: TargetSelection,
A
Alex Crichton 已提交
997
    ) -> Compiler {
998 999 1000 1001 1002 1003 1004
        if self.build.force_use_stage1(Compiler { stage, host }, target) {
            self.compiler(1, self.config.build)
        } else {
            self.compiler(stage, host)
        }
    }

1005
    pub fn sysroot(&self, compiler: Compiler) -> Interned<PathBuf> {
1006 1007 1008 1009 1010
        self.ensure(compile::Sysroot { compiler })
    }

    /// Returns the libdir where the standard library and other artifacts are
    /// found for a compiler's sysroot.
1011
    pub fn sysroot_libdir(&self, compiler: Compiler, target: TargetSelection) -> Interned<PathBuf> {
1012 1013 1014
        #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
        struct Libdir {
            compiler: Compiler,
1015
            target: TargetSelection,
1016
        }
1017 1018
        impl Step for Libdir {
            type Output = Interned<PathBuf>;
1019

T
Taiki Endo 已提交
1020
            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1021
                run.never()
1022 1023
            }

T
Taiki Endo 已提交
1024
            fn run(self, builder: &Builder<'_>) -> Interned<PathBuf> {
O
O01eg 已提交
1025
                let lib = builder.sysroot_libdir_relative(self.compiler);
S
Santiago Pastorino 已提交
1026 1027 1028 1029
                let sysroot = builder
                    .sysroot(self.compiler)
                    .join(lib)
                    .join("rustlib")
1030
                    .join(self.target.triple)
S
Santiago Pastorino 已提交
1031
                    .join("lib");
1032 1033
                // Avoid deleting the rustlib/ directory we just copied
                // (in `impl Step for Sysroot`).
1034
                if !builder.download_rustc() {
1035 1036 1037
                    let _ = fs::remove_dir_all(&sysroot);
                    t!(fs::create_dir_all(&sysroot));
                }
1038
                INTERNER.intern_path(sysroot)
1039 1040 1041 1042 1043
            }
        }
        self.ensure(Libdir { compiler, target })
    }

1044 1045 1046 1047
    pub fn sysroot_codegen_backends(&self, compiler: Compiler) -> PathBuf {
        self.sysroot_libdir(compiler, compiler.host).with_file_name("codegen-backends")
    }

1048 1049 1050 1051 1052 1053 1054
    /// Returns the compiler's libdir where it stores the dynamic libraries that
    /// it itself links against.
    ///
    /// For example this returns `<sysroot>/lib` on Unix and `<sysroot>/bin` on
    /// Windows.
    pub fn rustc_libdir(&self, compiler: Compiler) -> PathBuf {
        if compiler.is_snapshot(self) {
1055
            self.rustc_snapshot_libdir()
1056
        } else {
O
O01eg 已提交
1057
            match self.config.libdir_relative() {
M
Mark Rousskov 已提交
1058 1059 1060
                Some(relative_libdir) if compiler.stage >= 1 => {
                    self.sysroot(compiler).join(relative_libdir)
                }
1061
                _ => self.sysroot(compiler).join(libdir(compiler.host)),
O
O01eg 已提交
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
            }
        }
    }

    /// Returns the compiler's relative libdir where it stores the dynamic libraries that
    /// it itself links against.
    ///
    /// For example this returns `lib` on Unix and `bin` on
    /// Windows.
    pub fn libdir_relative(&self, compiler: Compiler) -> &Path {
        if compiler.is_snapshot(self) {
1073
            libdir(self.config.build).as_ref()
O
O01eg 已提交
1074 1075
        } else {
            match self.config.libdir_relative() {
M
Mark Rousskov 已提交
1076
                Some(relative_libdir) if compiler.stage >= 1 => relative_libdir,
1077
                _ => libdir(compiler.host).as_ref(),
O
O01eg 已提交
1078
            }
1079 1080 1081
        }
    }

O
O01eg 已提交
1082 1083 1084 1085 1086 1087
    /// Returns the compiler's relative libdir where the standard library and other artifacts are
    /// found for a compiler's sysroot.
    ///
    /// For example this returns `lib` on Unix and Windows.
    pub fn sysroot_libdir_relative(&self, compiler: Compiler) -> &Path {
        match self.config.libdir_relative() {
M
Mark Rousskov 已提交
1088
            Some(relative_libdir) if compiler.stage >= 1 => relative_libdir,
O
O01eg 已提交
1089
            _ if compiler.stage == 0 => &self.build.initial_libdir,
M
Mark Rousskov 已提交
1090
            _ => Path::new("lib"),
O
O01eg 已提交
1091 1092 1093
        }
    }

1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
    pub fn rustc_lib_paths(&self, compiler: Compiler) -> Vec<PathBuf> {
        let mut dylib_dirs = vec![self.rustc_libdir(compiler)];

        // Ensure that the downloaded LLVM libraries can be found.
        if self.config.llvm_from_ci {
            let ci_llvm_lib = self.out.join(&*compiler.host.triple).join("ci-llvm").join("lib");
            dylib_dirs.push(ci_llvm_lib);
        }

        dylib_dirs
    }

1106 1107
    /// Adds the compiler's directory of dynamic libraries to `cmd`'s dynamic
    /// library lookup path.
1108
    pub fn add_rustc_lib_path(&self, compiler: Compiler, cmd: &mut Command) {
1109 1110 1111 1112
        // Windows doesn't need dylib path munging because the dlls for the
        // compiler live next to the compiler and the system will find them
        // automatically.
        if cfg!(windows) {
S
Santiago Pastorino 已提交
1113
            return;
1114 1115
        }

1116
        add_dylib_path(self.rustc_lib_paths(compiler), cmd);
1117 1118
    }

A
Alexander Regueiro 已提交
1119
    /// Gets a path to the compiler specified.
1120 1121 1122 1123
    pub fn rustc(&self, compiler: Compiler) -> PathBuf {
        if compiler.is_snapshot(self) {
            self.initial_rustc.clone()
        } else {
1124
            self.sysroot(compiler).join("bin").join(exe("rustc", compiler.host))
1125 1126 1127
        }
    }

1128 1129 1130 1131 1132 1133 1134 1135 1136
    /// Gets the paths to all of the compiler's codegen backends.
    fn codegen_backends(&self, compiler: Compiler) -> impl Iterator<Item = PathBuf> {
        fs::read_dir(self.sysroot_codegen_backends(compiler))
            .into_iter()
            .flatten()
            .filter_map(Result::ok)
            .map(|entry| entry.path())
    }

M
Mark Rousskov 已提交
1137 1138
    pub fn rustdoc(&self, compiler: Compiler) -> PathBuf {
        self.ensure(tool::Rustdoc { compiler })
M
Mark Simulacrum 已提交
1139 1140
    }

M
Mark Rousskov 已提交
1141
    pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command {
1142
        let mut cmd = Command::new(&self.bootstrap_out.join("rustdoc"));
O
Oliver Schneider 已提交
1143
        cmd.env("RUSTC_STAGE", compiler.stage.to_string())
S
Santiago Pastorino 已提交
1144
            .env("RUSTC_SYSROOT", self.sysroot(compiler))
M
Mark Rousskov 已提交
1145 1146 1147
            // Note that this is *not* the sysroot_libdir because rustdoc must be linked
            // equivalently to rustc.
            .env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler))
S
Santiago Pastorino 已提交
1148
            .env("CFG_RELEASE_CHANNEL", &self.config.channel)
M
Mark Rousskov 已提交
1149
            .env("RUSTDOC_REAL", self.rustdoc(compiler))
1150 1151
            .env("RUSTC_BOOTSTRAP", "1");

M
Mark Rousskov 已提交
1152
        cmd.arg("-Wrustdoc::invalid_codeblock_attributes");
1153

G
Guillaume Gomez 已提交
1154 1155 1156
        if self.config.deny_warnings {
            cmd.arg("-Dwarnings");
        }
1157
        cmd.arg("-Znormalize-docs");
1158 1159 1160 1161 1162

        // Remove make-related flags that can cause jobserver problems.
        cmd.env_remove("MAKEFLAGS");
        cmd.env_remove("MFLAGS");

1163
        if let Some(linker) = self.linker(compiler.host) {
1164 1165
            cmd.env("RUSTDOC_LINKER", linker);
        }
1166
        if self.is_fuse_ld_lld(compiler.host) {
1167
            cmd.env("RUSTDOC_FUSE_LD_LLD", "1");
O
Oliver Schneider 已提交
1168
        }
M
Mark Simulacrum 已提交
1169
        cmd
1170 1171
    }

1172 1173 1174 1175
    /// Return the path to `llvm-config` for the target, if it exists.
    ///
    /// Note that this returns `None` if LLVM is disabled, or if we're in a
    /// check build or dry-run, where there's no need to build all of LLVM.
1176
    fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
1177 1178 1179 1180 1181 1182 1183 1184 1185
        if self.config.llvm_enabled() && self.kind != Kind::Check && !self.config.dry_run {
            let llvm_config = self.ensure(native::Llvm { target });
            if llvm_config.is_file() {
                return Some(llvm_config);
            }
        }
        None
    }

1186 1187 1188 1189 1190
    /// Convenience wrapper to allow `builder.llvm_link_shared()` instead of `builder.config.llvm_link_shared(&builder)`.
    pub(crate) fn llvm_link_shared(&self) -> bool {
        Config::llvm_link_shared(self)
    }

1191 1192 1193 1194
    pub(crate) fn download_rustc(&self) -> bool {
        Config::download_rustc(self)
    }

1195 1196 1197 1198 1199 1200 1201
    /// Prepares an invocation of `cargo` to be run.
    ///
    /// This will create a `Command` that represents a pending execution of
    /// Cargo. This cargo will be configured to use `compiler` as the actual
    /// rustc compiler, its output will be scoped by `mode`'s output directory,
    /// it will pass the `--target` flag for the specified `target`, and will be
    /// executing the Cargo command `cmd`.
S
Santiago Pastorino 已提交
1202 1203 1204 1205
    pub fn cargo(
        &self,
        compiler: Compiler,
        mode: Mode,
1206
        source_type: SourceType,
1207
        target: TargetSelection,
S
Santiago Pastorino 已提交
1208
        cmd: &str,
1209
    ) -> Cargo {
M
Mark Simulacrum 已提交
1210 1211
        let mut cargo = Command::new(&self.initial_cargo);
        let out_dir = self.stage_out(compiler, mode);
1212

1213 1214 1215 1216 1217 1218
        // Codegen backends are not yet tracked by -Zbinary-dep-depinfo,
        // so we need to explicitly clear out if they've been updated.
        for backend in self.codegen_backends(compiler) {
            self.clear_if_dirty(&out_dir, &backend);
        }

1219
        if cmd == "doc" || cmd == "rustdoc" {
1220
            let my_out = match mode {
1221
                // This is the intended out directory for compiler documentation.
1222
                Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target),
1223
                Mode::Std => out_dir.join(target.triple).join("doc"),
E
Eric Huss 已提交
1224
                _ => panic!("doc mode {:?} not expected", mode),
1225
            };
M
Mark Rousskov 已提交
1226
            let rustdoc = self.rustdoc(compiler);
1227 1228 1229
            self.clear_if_dirty(&my_out, &rustdoc);
        }

J
Jonas Schievink 已提交
1230
        cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd);
1231 1232

        let profile_var = |name: &str| {
M
Mark Rousskov 已提交
1233
            let profile = if self.config.rust_optimize { "RELEASE" } else { "DEV" };
1234 1235
            format!("CARGO_PROFILE_{}_{}", profile, name)
        };
1236

1237
        // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config
1238 1239 1240 1241 1242 1243
        // needs to not accidentally link to libLLVM in stage0/lib.
        cargo.env("REAL_LIBRARY_PATH_VAR", &util::dylib_path_var());
        if let Some(e) = env::var_os(util::dylib_path_var()) {
            cargo.env("REAL_LIBRARY_PATH", e);
        }

1244 1245 1246 1247
        // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger`
        // from out of tree it shouldn't matter, since x.py is only used for
        // building in-tree.
        let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"];
J
Joshua Nelson 已提交
1248 1249 1250
        match self.build.config.color {
            Color::Always => {
                cargo.arg("--color=always");
1251 1252 1253
                for log in &color_logs {
                    cargo.env(log, "always");
                }
J
Joshua Nelson 已提交
1254 1255 1256
            }
            Color::Never => {
                cargo.arg("--color=never");
1257 1258 1259
                for log in &color_logs {
                    cargo.env(log, "never");
                }
J
Joshua Nelson 已提交
1260 1261 1262 1263
            }
            Color::Auto => {} // nothing to do
        }

1264
        if cmd != "install" {
1265
            cargo.arg("--target").arg(target.rustc_target_arg());
1266 1267 1268
        } else {
            assert_eq!(target, compiler.host);
        }
1269

L
ljedrz 已提交
1270
        // Set a flag for `check`/`clippy`/`fix`, so that certain build
1271
        // scripts can do less work (i.e. not building/requiring LLVM).
L
ljedrz 已提交
1272
        if cmd == "check" || cmd == "clippy" || cmd == "fix" {
1273
            // If we've not yet built LLVM, or it's stale, then bust
1274
            // the rustc_llvm cache. That will always work, even though it
1275
            // may mean that on the next non-check build we'll need to rebuild
1276
            // rustc_llvm. But if LLVM is stale, that'll be a tiny amount
1277
            // of work comparatively, and we'd likely need to rebuild it anyway,
1278 1279 1280 1281
            // so that's okay.
            if crate::native::prebuilt_llvm_config(self, target).is_err() {
                cargo.env("RUST_CHECK", "1");
            }
V
varkor 已提交
1282 1283
        }

1284
        let stage = if compiler.stage == 0 && self.local_rebuild {
1285
            // Assume the local-rebuild rustc already has stage1 features.
1286
            1
1287
        } else {
1288 1289
            compiler.stage
        };
1290

1291
        let mut rustflags = Rustflags::new(target);
1292
        if stage != 0 {
1293 1294 1295
            if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
                cargo.args(s.split_whitespace());
            }
1296 1297
            rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP");
        } else {
1298 1299 1300
            if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") {
                cargo.args(s.split_whitespace());
            }
1301
            rustflags.env("RUSTFLAGS_BOOTSTRAP");
J
Joshua Nelson 已提交
1302
            if cmd == "clippy" {
1303 1304
                // clippy overwrites sysroot if we pass it to cargo.
                // Pass it directly to clippy instead.
J
Joshua Nelson 已提交
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317
                // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
                // so it has no way of knowing the sysroot.
                rustflags.arg("--sysroot");
                rustflags.arg(
                    self.sysroot(compiler)
                        .as_os_str()
                        .to_str()
                        .expect("sysroot must be valid UTF-8"),
                );
                // Only run clippy on a very limited subset of crates (in particular, not build scripts).
                cargo.arg("-Zunstable-options");
                // Explicitly does *not* set `--cfg=bootstrap`, since we're using a nightly clippy.
                let host_version = Command::new("rustc").arg("--version").output().map_err(|_| ());
1318
                let output = host_version.and_then(|output| {
1319
                    if output.status.success() {
J
Joshua Nelson 已提交
1320 1321 1322 1323
                        Ok(output)
                    } else {
                        Err(())
                    }
1324
                }).unwrap_or_else(|_| {
J
Joshua Nelson 已提交
1325
                    eprintln!(
1326
                        "error: `x.py clippy` requires a host `rustc` toolchain with the `clippy` component"
J
Joshua Nelson 已提交
1327
                    );
1328
                    eprintln!("help: try `rustup component add clippy`");
J
Joshua Nelson 已提交
1329
                    std::process::exit(1);
1330 1331 1332
                });
                if !t!(std::str::from_utf8(&output.stdout)).contains("nightly") {
                    rustflags.arg("--cfg=bootstrap");
J
Joshua Nelson 已提交
1333 1334 1335 1336
                }
            } else {
                rustflags.arg("--cfg=bootstrap");
            }
1337 1338
        }

1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
        let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling {
            Some(setting) => {
                // If an explicit setting is given, use that
                setting
            }
            None => {
                if mode == Mode::Std {
                    // The standard library defaults to the legacy scheme
                    false
                } else {
                    // The compiler and tools default to the new scheme
                    true
                }
            }
        };

P
Pietro Albini 已提交
1355 1356
        if use_new_symbol_mangling {
            rustflags.arg("-Csymbol-mangling-version=v0");
1357
        } else {
P
Pietro Albini 已提交
1358 1359
            rustflags.arg("-Csymbol-mangling-version=legacy");
            rustflags.arg("-Zunstable-options");
1360 1361
        }

1362 1363 1364 1365
        // FIXME(Urgau): This a hack as it shouldn't be gated on stage 0 but until `rustc_llvm`
        // is made to work with `--check-cfg` which is currently not easly possible until cargo
        // get some support for setting `--check-cfg` within build script, it's the least invasive
        // hack that still let's us have cfg checking for the vast majority of the codebase.
1366
        if stage != 0 {
1367 1368
            // Enable cfg checking of cargo features for everything but std and also enable cfg
            // checking of names and values.
1369 1370
            //
            // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like
1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381
            // backtrace, core_simd, std_float, ...), those dependencies have their own
            // features but cargo isn't involved in the #[path] process and so cannot pass the
            // complete list of features, so for that reason we don't enable checking of
            // features for std crates.
            cargo.arg(if mode != Mode::Std {
                "-Zcheck-cfg=names,values,features"
            } else {
                "-Zcheck-cfg=names,values"
            });

            // Add extra cfg not defined in/by rustc
U
Urgau 已提交
1382
            //
1383 1384 1385 1386
            // Note: Altrough it would seems that "-Zunstable-options" to `rustflags` is useless as
            // cargo would implicitly add it, it was discover that sometimes bootstrap only use
            // `rustflags` without `cargo` making it required.
            rustflags.arg("-Zunstable-options");
1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403
            for (restricted_mode, name, values) in EXTRA_CHECK_CFGS {
                if *restricted_mode == None || *restricted_mode == Some(mode) {
                    // Creating a string of the values by concatenating each value:
                    // ',"tvos","watchos"' or '' (nothing) when there are no values
                    let values = match values {
                        Some(values) => values
                            .iter()
                            .map(|val| [",", "\"", val, "\""])
                            .flatten()
                            .collect::<String>(),
                        None => String::new(),
                    };
                    rustflags.arg(&format!("--check-cfg=values({name}{values})"));
                }
            }
        }

1404 1405 1406
        // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`,
        // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See
        // #71458.
1407
        let mut rustdocflags = rustflags.clone();
1408 1409 1410 1411 1412 1413
        rustdocflags.propagate_cargo_env("RUSTDOCFLAGS");
        if stage == 0 {
            rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP");
        } else {
            rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP");
        }
1414

1415 1416 1417 1418
        if let Ok(s) = env::var("CARGOFLAGS") {
            cargo.args(s.split_whitespace());
        }

J
John Kåre Alsaker 已提交
1419
        match mode {
M
Mark Rousskov 已提交
1420
            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}
1421
            Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
J
John Kåre Alsaker 已提交
1422 1423 1424
                // Build proc macros both for the host and the target
                if target != compiler.host && cmd != "check" {
                    cargo.arg("-Zdual-proc-macros");
1425
                    rustflags.arg("-Zdual-proc-macros");
J
John Kåre Alsaker 已提交
1426
                }
M
Mark Rousskov 已提交
1427
            }
J
John Kåre Alsaker 已提交
1428 1429
        }

1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441
        // This tells Cargo (and in turn, rustc) to output more complete
        // dependency information.  Most importantly for rustbuild, this
        // includes sysroot artifacts, like libstd, which means that we don't
        // need to track those in rustbuild (an error prone process!). This
        // feature is currently unstable as there may be some bugs and such, but
        // it represents a big improvement in rustbuild's reliability on
        // rebuilds, so we're using it here.
        //
        // For some additional context, see #63470 (the PR originally adding
        // this), as well as #63012 which is the tracking issue for this
        // feature on the rustc side.
        cargo.arg("-Zbinary-dep-depinfo");
1442 1443 1444 1445 1446 1447
        match mode {
            Mode::ToolBootstrap => {
                // Restrict the allowed features to those passed by rustbuild, so we don't depend on nightly accidentally.
                // HACK: because anyhow does feature detection in build.rs, we need to allow the backtrace feature too.
                rustflags.arg("-Zallow-features=binary-dep-depinfo,backtrace");
            }
1448 1449 1450 1451 1452 1453
            Mode::ToolStd => {
                // Right now this is just compiletest and a few other tools that build on stable.
                // Allow them to use `feature(test)`, but nothing else.
                rustflags.arg("-Zallow-features=binary-dep-depinfo,test,backtrace");
            }
            Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {}
1454
        }
1455

1456 1457 1458 1459
        cargo.arg("-j").arg(self.jobs().to_string());
        // Remove make-related flags to ensure Cargo can correctly set things up
        cargo.env_remove("MAKEFLAGS");
        cargo.env_remove("MFLAGS");
1460

1461 1462
        // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
        // Force cargo to output binaries with disambiguating hashes in the name
1463 1464
        let mut metadata = if compiler.stage == 0 {
            // Treat stage0 like a special channel, whether it's a normal prior-
1465 1466
            // release rustc or a local rebuild with the same version, so we
            // never mix these libraries by accident.
1467
            "bootstrap".to_string()
1468
        } else {
1469
            self.config.channel.to_string()
1470
        };
1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483
        // We want to make sure that none of the dependencies between
        // std/test/rustc unify with one another. This is done for weird linkage
        // reasons but the gist of the problem is that if librustc, libtest, and
        // libstd all depend on libc from crates.io (which they actually do) we
        // want to make sure they all get distinct versions. Things get really
        // weird if we try to unify all these dependencies right now, namely
        // around how many times the library is linked in dynamic libraries and
        // such. If rustc were a static executable or if we didn't ship dylibs
        // this wouldn't be a problem, but we do, so it is. This is in general
        // just here to make sure things build right. If you can remove this and
        // things still build right, please do!
        match mode {
            Mode::Std => metadata.push_str("std"),
M
Mark Rousskov 已提交
1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
            // When we're building rustc tools, they're built with a search path
            // that contains things built during the rustc build. For example,
            // bitflags is built during the rustc build, and is a dependency of
            // rustdoc as well. We're building rustdoc in a different target
            // directory, though, which means that Cargo will rebuild the
            // dependency. When we go on to build rustdoc, we'll look for
            // bitflags, and find two different copies: one built during the
            // rustc step and one that we just built. This isn't always a
            // problem, somehow -- not really clear why -- but we know that this
            // fixes things.
            Mode::ToolRustc => metadata.push_str("tool-rustc"),
1495 1496
            // Same for codegen backends.
            Mode::Codegen => metadata.push_str("codegen"),
M
Mark Rousskov 已提交
1497
            _ => {}
1498
        }
1499
        cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata);
1500

L
ljedrz 已提交
1501
        if cmd == "clippy" {
1502
            rustflags.arg("-Zforce-unstable-if-unmarked");
1503 1504
        }

J
Jonas Schievink 已提交
1505
        rustflags.arg("-Zmacro-backtrace");
1506

K
kennytm 已提交
1507
        let want_rustdoc = self.doc_tests != DocTests::No;
K
kennytm 已提交
1508

1509 1510 1511 1512 1513 1514
        // We synthetically interpret a stage0 compiler used to build tools as a
        // "raw" compiler in that it's the exact snapshot we download. Normally
        // the stage0 build means it uses libraries build by the stage0
        // compiler, but for tools we just use the precompiled libraries that
        // we've downloaded
        let use_snapshot = mode == Mode::ToolBootstrap;
1515
        assert!(!use_snapshot || stage == 0 || self.local_rebuild);
1516 1517

        let maybe_sysroot = self.sysroot(compiler);
M
Mark Rousskov 已提交
1518
        let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
M
Mark Rousskov 已提交
1519
        let libdir = self.rustc_libdir(compiler);
1520

1521 1522 1523 1524 1525 1526 1527 1528
        // Clear the output directory if the real rustc we're using has changed;
        // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc.
        //
        // Avoid doing this during dry run as that usually means the relevant
        // compiler is not yet linked/copied properly.
        //
        // Only clear out the directory if we're compiling std; otherwise, we
        // should let Cargo take care of things for us (via depdep info)
M
Mark Rousskov 已提交
1529
        if !self.config.dry_run && mode == Mode::Std && cmd == "build" {
1530 1531 1532
            self.clear_if_dirty(&out_dir, &self.rustc(compiler));
        }

1533 1534
        // Customize the compiler we're running. Specify the compiler to cargo
        // as our shim and then pass it some various options used to configure
M
Mark Simulacrum 已提交
1535
        // how the actual compiler itself is called.
1536 1537 1538
        //
        // These variables are primarily all read by
        // src/bootstrap/bin/{rustc.rs,rustdoc.rs}
S
Santiago Pastorino 已提交
1539 1540 1541 1542
        cargo
            .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
            .env("RUSTC_REAL", self.rustc(compiler))
            .env("RUSTC_STAGE", stage.to_string())
1543 1544
            .env("RUSTC_SYSROOT", &sysroot)
            .env("RUSTC_LIBDIR", &libdir)
1545
            .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
S
Santiago Pastorino 已提交
1546 1547
            .env(
                "RUSTDOC_REAL",
1548
                if cmd == "doc" || cmd == "rustdoc" || (cmd == "test" && want_rustdoc) {
M
Mark Rousskov 已提交
1549
                    self.rustdoc(compiler)
S
Santiago Pastorino 已提交
1550 1551 1552 1553
                } else {
                    PathBuf::from("/path/to/nowhere/rustdoc/not/required")
                },
            )
1554 1555
            .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
            .env("RUSTC_BREAK_ON_ICE", "1");
J
Joshua Nelson 已提交
1556 1557 1558
        // Clippy support is a hack and uses the default `cargo-clippy` in path.
        // Don't override RUSTC so that the `cargo-clippy` in path will be run.
        if cmd != "clippy" {
1559
            cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
J
Joshua Nelson 已提交
1560
        }
1561

1562 1563 1564 1565 1566 1567 1568 1569 1570
        // Dealing with rpath here is a little special, so let's go into some
        // detail. First off, `-rpath` is a linker option on Unix platforms
        // which adds to the runtime dynamic loader path when looking for
        // dynamic libraries. We use this by default on Unix platforms to ensure
        // that our nightlies behave the same on Windows, that is they work out
        // of the box. This can be disabled, of course, but basically that's why
        // we're gated on RUSTC_RPATH here.
        //
        // Ok, so the astute might be wondering "why isn't `-C rpath` used
K
KRAAI, MATTHEW [VISUS] 已提交
1571
        // here?" and that is indeed a good question to ask. This codegen
1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587
        // option is the compiler's current interface to generating an rpath.
        // Unfortunately it doesn't quite suffice for us. The flag currently
        // takes no value as an argument, so the compiler calculates what it
        // should pass to the linker as `-rpath`. This unfortunately is based on
        // the **compile time** directory structure which when building with
        // Cargo will be very different than the runtime directory structure.
        //
        // All that's a really long winded way of saying that if we use
        // `-Crpath` then the executables generated have the wrong rpath of
        // something like `$ORIGIN/deps` when in fact the way we distribute
        // rustc requires the rpath to be `$ORIGIN/../lib`.
        //
        // So, all in all, to set up the correct rpath we pass the linker
        // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it
        // fun to pass a flag to a tool to pass a flag to pass a flag to a tool
        // to change a flag in a binary?
1588
        if self.config.rust_rpath && util::use_host_linker(target) {
1589 1590 1591 1592 1593 1594 1595 1596
            let rpath = if target.contains("apple") {
                // Note that we need to take one extra step on macOS to also pass
                // `-Wl,-instal_name,@rpath/...` to get things to work right. To
                // do that we pass a weird flag to the compiler to get it to do
                // so. Note that this is definitely a hack, and we should likely
                // flesh out rpath support more fully in the future.
                rustflags.arg("-Zosx-rpath-install-name");
                Some("-Wl,-rpath,@loader_path/../lib")
1597
            } else if !target.contains("windows") {
1598
                rustflags.arg("-Clink-args=-Wl,-z,origin");
1599 1600 1601 1602 1603 1604 1605 1606 1607
                Some("-Wl,-rpath,$ORIGIN/../lib")
            } else {
                None
            };
            if let Some(rpath) = rpath {
                rustflags.arg(&format!("-Clink-args={}", rpath));
            }
        }

1608
        if let Some(host_linker) = self.linker(compiler.host) {
O
Oliver Schneider 已提交
1609 1610
            cargo.env("RUSTC_HOST_LINKER", host_linker);
        }
1611
        if self.is_fuse_ld_lld(compiler.host) {
1612
            cargo.env("RUSTC_HOST_FUSE_LD_LLD", "1");
1613
            cargo.env("RUSTDOC_FUSE_LD_LLD", "1");
1614
        }
1615

1616
        if let Some(target_linker) = self.linker(target) {
1617
            let target = crate::envify(&target.triple);
1618
            cargo.env(&format!("CARGO_TARGET_{}_LINKER", target), target_linker);
O
Oliver Schneider 已提交
1619
        }
1620
        if self.is_fuse_ld_lld(target) {
1621 1622
            rustflags.arg("-Clink-args=-fuse-ld=lld");
        }
1623 1624 1625
        self.lld_flags(target).for_each(|flag| {
            rustdocflags.arg(&flag);
        });
1626

L
ljedrz 已提交
1627
        if !(["build", "check", "clippy", "fix", "rustc"].contains(&cmd)) && want_rustdoc {
M
Mark Rousskov 已提交
1628
            cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
1629
        }
1630

1631
        let debuginfo_level = match mode {
1632
            Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
1633
            Mode::Std => self.config.rust_debuginfo_level_std,
M
Mark Rousskov 已提交
1634 1635 1636
            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => {
                self.config.rust_debuginfo_level_tools
            }
1637
        };
1638
        cargo.env(profile_var("DEBUG"), debuginfo_level.to_string());
1639 1640 1641 1642 1643 1644 1645 1646
        cargo.env(
            profile_var("DEBUG_ASSERTIONS"),
            if mode == Mode::Std {
                self.config.rust_debug_assertions_std.to_string()
            } else {
                self.config.rust_debug_assertions.to_string()
            },
        );
1647 1648 1649 1650 1651 1652 1653 1654
        cargo.env(
            profile_var("OVERFLOW_CHECKS"),
            if mode == Mode::Std {
                self.config.rust_overflow_checks_std.to_string()
            } else {
                self.config.rust_overflow_checks.to_string()
            },
        );
1655

M
Mark Rousskov 已提交
1656
        if !target.contains("windows") {
1657 1658 1659 1660 1661 1662
            let needs_unstable_opts = target.contains("linux")
                || target.contains("windows")
                || target.contains("bsd")
                || target.contains("dragonfly");

            if needs_unstable_opts {
1663
                rustflags.arg("-Zunstable-options");
1664
            }
1665 1666 1667 1668 1669
            match self.config.rust_split_debuginfo {
                SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"),
                SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"),
                SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"),
            };
1670 1671
        }

1672 1673 1674 1675 1676
        if self.config.cmd.bless() {
            // Bless `expect!` tests.
            cargo.env("UPDATE_EXPECT", "1");
        }

1677
        if !mode.is_tool() {
O
Oliver Schneider 已提交
1678
            cargo.env("RUSTC_FORCE_UNSTABLE", "1");
1679 1680
        }

1681
        if let Some(x) = self.crt_static(target) {
1682 1683 1684 1685 1686
            if x {
                rustflags.arg("-Ctarget-feature=+crt-static");
            } else {
                rustflags.arg("-Ctarget-feature=-crt-static");
            }
1687 1688
        }

1689 1690 1691 1692
        if let Some(x) = self.crt_static(compiler.host) {
            cargo.env("RUSTC_HOST_CRT_STATIC", x.to_string());
        }

1693 1694
        if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) {
            let map = format!("{}={}", self.build.src.display(), map_to);
1695
            cargo.env("RUSTC_DEBUGINFO_MAP", map);
1696 1697 1698 1699

            // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to,
            // in order to opportunistically reverse it later.
            cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to);
1700 1701
        }

1702 1703
        // Enable usage of unstable features
        cargo.env("RUSTC_BOOTSTRAP", "1");
M
Mark Simulacrum 已提交
1704
        self.add_rust_test_threads(&mut cargo);
1705 1706 1707

        // Almost all of the crates that we compile as part of the bootstrap may
        // have a build script, including the standard library. To compile a
M
Mark Simulacrum 已提交
1708
        // build script, however, it itself needs a standard library! This
1709
        // introduces a bit of a pickle when we're compiling the standard
M
Mark Simulacrum 已提交
1710
        // library itself.
1711 1712
        //
        // To work around this we actually end up using the snapshot compiler
M
Mark Simulacrum 已提交
1713
        // (stage0) for compiling build scripts of the standard library itself.
1714 1715 1716 1717 1718
        // The stage0 compiler is guaranteed to have a libstd available for use.
        //
        // For other crates, however, we know that we've already got a standard
        // library up and running, so we can use the normal compiler to compile
        // build scripts in that situation.
B
bjorn3 已提交
1719
        if mode == Mode::Std {
S
Santiago Pastorino 已提交
1720 1721 1722
            cargo
                .env("RUSTC_SNAPSHOT", &self.initial_rustc)
                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
1723
        } else {
S
Santiago Pastorino 已提交
1724 1725 1726
            cargo
                .env("RUSTC_SNAPSHOT", self.rustc(compiler))
                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler));
1727 1728
        }

1729 1730 1731 1732
        // Tools that use compiler libraries may inherit the `-lLLVM` link
        // requirement, but the `-L` library path is not propagated across
        // separate Cargo projects. We can add LLVM's library path to the
        // platform-specific environment variable as a workaround.
1733
        if mode == Mode::ToolRustc || mode == Mode::Codegen {
1734 1735 1736 1737
            if let Some(llvm_config) = self.llvm_config(target) {
                let llvm_libdir = output(Command::new(&llvm_config).arg("--libdir"));
                add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo);
            }
1738 1739
        }

1740 1741 1742 1743
        // Compile everything except libraries and proc macros with the more
        // efficient initial-exec TLS model. This doesn't work with `dlopen`,
        // so we can't use it by default in general, but we can use it for tools
        // and our own internal libraries.
1744
        if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") {
1745 1746 1747
            rustflags.arg("-Ztls-model=initial-exec");
        }

1748
        if self.config.incremental {
1749
            cargo.env("CARGO_INCREMENTAL", "1");
1750 1751 1752
        } else {
            // Don't rely on any default setting for incr. comp. in Cargo
            cargo.env("CARGO_INCREMENTAL", "0");
1753 1754
        }

M
Mark Simulacrum 已提交
1755
        if let Some(ref on_fail) = self.config.on_fail {
1756 1757 1758
            cargo.env("RUSTC_ON_FAIL", on_fail);
        }

1759 1760 1761 1762
        if self.config.print_step_timings {
            cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1");
        }

1763 1764 1765 1766
        if self.config.print_step_rusage {
            cargo.env("RUSTC_PRINT_STEP_RUSAGE", "1");
        }

J
John Kåre Alsaker 已提交
1767 1768 1769 1770
        if self.config.backtrace_on_ice {
            cargo.env("RUSTC_BACKTRACE_ON_ICE", "1");
        }

L
ljedrz 已提交
1771
        cargo.env("RUSTC_VERBOSE", self.verbosity.to_string());
1772

1773
        if source_type == SourceType::InTree {
1774
            let mut lint_flags = Vec::new();
1775 1776 1777
            // When extending this list, add the new lints to the RUSTFLAGS of the
            // build_bootstrap function of src/bootstrap/bootstrap.py as well as
            // some code doesn't go through this `rustc` wrapper.
1778 1779
            lint_flags.push("-Wrust_2018_idioms");
            lint_flags.push("-Wunused_lifetimes");
1780
            lint_flags.push("-Wsemicolon_in_expressions_from_macros");
1781 1782

            if self.config.deny_warnings {
1783
                lint_flags.push("-Dwarnings");
1784
                rustdocflags.arg("-Dwarnings");
1785
            }
1786

1787 1788 1789 1790 1791 1792 1793 1794 1795
            // This does not use RUSTFLAGS due to caching issues with Cargo.
            // Clippy is treated as an "in tree" tool, but shares the same
            // cache as other "submodule" tools. With these options set in
            // RUSTFLAGS, that causes *every* shared dependency to be rebuilt.
            // By injecting this into the rustc wrapper, this circumvents
            // Cargo's fingerprint detection. This is fine because lint flags
            // are always ignored in dependencies. Eventually this should be
            // fixed via better support from Cargo.
            cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" "));
G
Guillaume Gomez 已提交
1796

M
Mark Rousskov 已提交
1797
            rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes");
1798 1799
        }

1800
        if mode == Mode::Rustc {
A
Alex Crichton 已提交
1801 1802
            rustflags.arg("-Zunstable-options");
            rustflags.arg("-Wrustc::internal");
1803 1804
        }

O
Oliver Schneider 已提交
1805 1806 1807 1808 1809
        // Throughout the build Cargo can execute a number of build scripts
        // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
        // obtained previously to those build scripts.
        // Build scripts use either the `cc` crate or `configure/make` so we pass
        // the options through environment variables that are fetched and understood by both.
1810 1811
        //
        // FIXME: the guard against msvc shouldn't need to be here
1812 1813 1814 1815 1816
        if target.contains("msvc") {
            if let Some(ref cl) = self.config.llvm_clang_cl {
                cargo.env("CC", cl).env("CXX", cl);
            }
        } else {
1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833
            let ccache = self.config.ccache.as_ref();
            let ccacheify = |s: &Path| {
                let ccache = match ccache {
                    Some(ref s) => s,
                    None => return s.display().to_string(),
                };
                // FIXME: the cc-rs crate only recognizes the literal strings
                // `ccache` and `sccache` when doing caching compilations, so we
                // mirror that here. It should probably be fixed upstream to
                // accept a new env var or otherwise work with custom ccache
                // vars.
                match &ccache[..] {
                    "ccache" | "sccache" => format!("{} {}", ccache, s.display()),
                    _ => s.display().to_string(),
                }
            };
            let cc = ccacheify(&self.cc(target));
1834
            cargo.env(format!("CC_{}", target.triple), &cc);
O
Oliver Schneider 已提交
1835

1836
            let cflags = self.cflags(target, GitRepo::Rustc, CLang::C).join(" ");
1837
            cargo.env(format!("CFLAGS_{}", target.triple), &cflags);
O
Oliver Schneider 已提交
1838 1839 1840

            if let Some(ar) = self.ar(target) {
                let ranlib = format!("{} s", ar.display());
1841 1842 1843
                cargo
                    .env(format!("AR_{}", target.triple), ar)
                    .env(format!("RANLIB_{}", target.triple), ranlib);
O
Oliver Schneider 已提交
1844
            }
1845

M
Mark Simulacrum 已提交
1846
            if let Ok(cxx) = self.cxx(target) {
1847
                let cxx = ccacheify(&cxx);
1848
                let cxxflags = self.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ");
S
Santiago Pastorino 已提交
1849
                cargo
1850
                    .env(format!("CXX_{}", target.triple), &cxx)
1851
                    .env(format!("CXXFLAGS_{}", target.triple), cxxflags);
1852 1853 1854
            }
        }

M
Mark Rousskov 已提交
1855
        if mode == Mode::Std && self.config.extended && compiler.is_final_stage(self) {
1856
            rustflags.arg("-Zsave-analysis");
M
Mark Rousskov 已提交
1857 1858 1859
            cargo.env(
                "RUST_SAVE_ANALYSIS_CONFIG",
                "{\"output_file\": null,\"full_docs\": false,\
1860
                       \"pub_only\": true,\"reachable_only\": false,\
M
Mark Rousskov 已提交
1861 1862
                       \"distro_crate\": true,\"signatures\": false,\"borrow_data\": false}",
            );
1863 1864
        }

A
Andrew Paverd 已提交
1865
        // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc
1866 1867 1868 1869 1870 1871 1872 1873 1874 1875
        // when compiling the standard library, since this might be linked into the final outputs
        // produced by rustc. Since this mitigation is only available on Windows, only enable it
        // for the standard library in case the compiler is run on a non-Windows platform.
        // This is not needed for stage 0 artifacts because these will only be used for building
        // the stage 1 compiler.
        if cfg!(windows)
            && mode == Mode::Std
            && self.config.control_flow_guard
            && compiler.stage >= 1
        {
1876
            rustflags.arg("-Ccontrol-flow-guard");
1877 1878
        }

O
Oliver Schneider 已提交
1879
        // For `cargo doc` invocations, make rustdoc print the Rust version into the docs
1880 1881 1882 1883 1884
        // This replaces spaces with newlines because RUSTDOCFLAGS does not
        // support arguments with regular spaces. Hopefully someday Cargo will
        // have space support.
        let rust_version = self.rust_version().replace(' ', "\n");
        rustdocflags.arg("--crate-version").arg(&rust_version);
O
Oliver Schneider 已提交
1885

1886 1887 1888
        // Environment variables *required* throughout the build
        //
        // FIXME: should update code to not require this env var
1889
        cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple);
1890

1891
        // Set this for all builds to make sure doc builds also get it.
1892
        cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel);
1893

1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911
        // This one's a bit tricky. As of the time of this writing the compiler
        // links to the `winapi` crate on crates.io. This crate provides raw
        // bindings to Windows system functions, sort of like libc does for
        // Unix. This crate also, however, provides "import libraries" for the
        // MinGW targets. There's an import library per dll in the windows
        // distribution which is what's linked to. These custom import libraries
        // are used because the winapi crate can reference Windows functions not
        // present in the MinGW import libraries.
        //
        // For example MinGW may ship libdbghelp.a, but it may not have
        // references to all the functions in the dbghelp dll. Instead the
        // custom import library for dbghelp in the winapi crates has all this
        // information.
        //
        // Unfortunately for us though the import libraries are linked by
        // default via `-ldylib=winapi_foo`. That is, they're linked with the
        // `dylib` type with a `winapi_` prefix (so the winapi ones don't
        // conflict with the system MinGW ones). This consequently means that
I
Irina Popa 已提交
1912
        // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm
1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924
        // DLL) when linked against *again*, for example with procedural macros
        // or plugins, will trigger the propagation logic of `-ldylib`, passing
        // `-lwinapi_foo` to the linker again. This isn't actually available in
        // our distribution, however, so the link fails.
        //
        // To solve this problem we tell winapi to not use its bundled import
        // libraries. This means that it will link to the system MinGW import
        // libraries by default, and the `-ldylib=foo` directives will still get
        // passed to the final linker, but they'll look like `-lfoo` which can
        // be resolved because MinGW has the import library. The downside is we
        // don't get newer functions from Windows, but we don't use any of them
        // anyway.
C
Collins Abitekaniza 已提交
1925
        if !mode.is_tool() {
A
Alex Crichton 已提交
1926 1927
            cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1");
        }
1928

X
Ximin Luo 已提交
1929
        for _ in 0..self.verbosity {
1930 1931
            cargo.arg("-v");
        }
1932

1933
        match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) {
M
Mark Rousskov 已提交
1934
            (Mode::Std, Some(n), _) | (_, _, Some(n)) => {
1935
                cargo.env(profile_var("CODEGEN_UNITS"), n.to_string());
1936 1937 1938 1939
            }
            _ => {
                // Don't set anything
            }
1940 1941
        }

O
Oliver Schneider 已提交
1942
        if self.config.rust_optimize {
1943 1944
            // FIXME: cargo bench/install do not accept `--release`
            if cmd != "bench" && cmd != "install" {
O
Oliver Schneider 已提交
1945 1946
                cargo.arg("--release");
            }
1947
        }
1948

M
Mark Simulacrum 已提交
1949
        if self.config.locked_deps {
1950 1951
            cargo.arg("--locked");
        }
M
Mark Simulacrum 已提交
1952
        if self.config.vendor || self.is_sudo {
1953 1954 1955
            cargo.arg("--frozen");
        }

1956
        // Try to use a sysroot-relative bindir, in case it was configured absolutely.
1957
        cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative());
1958

M
Mark Simulacrum 已提交
1959
        self.ci_env.force_coloring_in_ci(&mut cargo);
1960

1961 1962 1963
        // When we build Rust dylibs they're all intended for intermediate
        // usage, so make sure we pass the -Cprefer-dynamic flag instead of
        // linking all deps statically into the dylib.
1964
        if matches!(mode, Mode::Std | Mode::Rustc) {
A
Alex Crichton 已提交
1965
            rustflags.arg("-Cprefer-dynamic");
1966 1967
        }

1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982
        // When building incrementally we default to a lower ThinLTO import limit
        // (unless explicitly specified otherwise). This will produce a somewhat
        // slower code but give way better compile times.
        {
            let limit = match self.config.rust_thin_lto_import_instr_limit {
                Some(limit) => Some(limit),
                None if self.config.incremental => Some(10),
                _ => None,
            };

            if let Some(limit) = limit {
                rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={}", limit));
            }
        }

1983
        Cargo { command: cargo, rustflags, rustdocflags }
1984 1985
    }

V
varkor 已提交
1986
    /// Ensure that a given step is built, returning its output. This will
1987 1988
    /// cache the step, so it is safe (and good!) to call this as often as
    /// needed to ensure that all dependencies are built.
1989
    pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
1990 1991
        {
            let mut stack = self.stack.borrow_mut();
1992 1993
            for stack_step in stack.iter() {
                // should skip
M
Mark Rousskov 已提交
1994
                if stack_step.downcast_ref::<S>().map_or(true, |stack_step| *stack_step != step) {
1995
                    continue;
1996
                }
1997
                let mut out = String::new();
1998
                out += &format!("\n\nCycle in build detected when adding {:?}\n", step);
1999 2000 2001
                for el in stack.iter().rev() {
                    out += &format!("\t{:?}\n", el);
                }
2002
                panic!("{}", out);
2003
            }
2004
            if let Some(out) = self.cache.get(&step) {
2005
                self.verbose_than(1, &format!("{}c {:?}", "  ".repeat(stack.len()), step));
2006 2007 2008

                return out;
            }
2009
            self.verbose_than(1, &format!("{}> {:?}", "  ".repeat(stack.len()), step));
2010
            stack.push(Box::new(step.clone()));
2011
        }
2012

2013 2014 2015
        #[cfg(feature = "build-metrics")]
        self.metrics.enter_step(&step);

2016 2017 2018 2019 2020 2021 2022 2023 2024 2025
        let (out, dur) = {
            let start = Instant::now();
            let zero = Duration::new(0, 0);
            let parent = self.time_spent_on_dependencies.replace(zero);
            let out = step.clone().run(self);
            let dur = start.elapsed();
            let deps = self.time_spent_on_dependencies.replace(parent + dur);
            (out, dur - deps)
        };

M
Mark Rousskov 已提交
2026
        if self.config.print_step_timings && !self.config.dry_run {
2027 2028 2029 2030 2031 2032 2033 2034 2035 2036
            let step_string = format!("{:?}", step);
            let brace_index = step_string.find("{").unwrap_or(0);
            let type_string = type_name::<S>();
            println!(
                "[TIMING] {} {} -- {}.{:03}",
                &type_string.strip_prefix("bootstrap::").unwrap_or(type_string),
                &step_string[brace_index..],
                dur.as_secs(),
                dur.subsec_millis()
            );
2037 2038
        }

2039 2040 2041
        #[cfg(feature = "build-metrics")]
        self.metrics.exit_step();

2042 2043
        {
            let mut stack = self.stack.borrow_mut();
2044 2045
            let cur_step = stack.pop().expect("step stack empty");
            assert_eq!(cur_step.downcast_ref(), Some(&step));
2046
        }
2047
        self.verbose_than(1, &format!("{}< {:?}", "  ".repeat(self.stack.borrow().len()), step));
2048 2049
        self.cache.put(step, out.clone());
        out
2050
    }
2051 2052 2053 2054 2055 2056 2057

    /// Ensure that a given step is built *only if it's supposed to be built by default*, returning
    /// its output. This will cache the step, so it's safe (and good!) to call this as often as
    /// needed to ensure that all dependencies are build.
    pub(crate) fn ensure_if_default<T, S: Step<Output = Option<T>>>(
        &'a self,
        step: S,
2058
        kind: Kind,
2059
    ) -> S::Output {
2060 2061
        let desc = StepDescription::from::<S>(kind);
        let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind));
2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072

        // Avoid running steps contained in --exclude
        for pathset in &should_run.paths {
            if desc.is_excluded(self, pathset) {
                return None;
            }
        }

        // Only execute if it's supposed to run as default
        if desc.default && should_run.is_really_default() { self.ensure(step) } else { None }
    }
2073 2074

    /// Checks if any of the "should_run" paths is in the `Builder` paths.
2075 2076 2077
    pub(crate) fn was_invoked_explicitly<S: Step>(&'a self, kind: Kind) -> bool {
        let desc = StepDescription::from::<S>(kind);
        let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind));
2078 2079

        for path in &self.paths {
2080 2081 2082
            if should_run.paths.iter().any(|s| s.has(path, Some(desc.kind)))
                && !desc.is_excluded(
                    self,
2083
                    &PathSet::Suite(TaskPath { path: path.clone(), kind: Some(desc.kind) }),
2084
                )
2085 2086 2087 2088 2089 2090 2091
            {
                return true;
            }
        }

        false
    }
2092
}
M
Mark Simulacrum 已提交
2093 2094

#[cfg(test)]
C
chansuke 已提交
2095
mod tests;
2096

2097
#[derive(Debug, Clone)]
2098
struct Rustflags(String, TargetSelection);
2099 2100

impl Rustflags {
2101
    fn new(target: TargetSelection) -> Rustflags {
2102 2103 2104 2105
        let mut ret = Rustflags(String::new(), target);
        ret.propagate_cargo_env("RUSTFLAGS");
        ret
    }
2106

2107
    /// By default, cargo will pick up on various variables in the environment. However, bootstrap
D
Dylan DPC 已提交
2108
    /// reuses those variables to pass additional flags to rustdoc, so by default they get overridden.
2109 2110 2111 2112
    /// Explicitly add back any previous value in the environment.
    ///
    /// `prefix` is usually `RUSTFLAGS` or `RUSTDOCFLAGS`.
    fn propagate_cargo_env(&mut self, prefix: &str) {
A
Alex Crichton 已提交
2113
        // Inherit `RUSTFLAGS` by default ...
2114
        self.env(prefix);
2115

2116 2117 2118
        // ... and also handle target-specific env RUSTFLAGS if they're configured.
        let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix);
        self.env(&target_specific);
2119 2120 2121 2122
    }

    fn env(&mut self, env: &str) {
        if let Ok(s) = env::var(env) {
2123
            for part in s.split(' ') {
2124 2125 2126 2127 2128 2129
                self.arg(part);
            }
        }
    }

    fn arg(&mut self, arg: &str) -> &mut Self {
2130
        assert_eq!(arg.split(' ').count(), 1);
2131
        if !self.0.is_empty() {
M
Matthias Krüger 已提交
2132
            self.0.push(' ');
2133 2134 2135 2136 2137
        }
        self.0.push_str(arg);
        self
    }
}
2138 2139 2140 2141 2142

#[derive(Debug)]
pub struct Cargo {
    command: Command,
    rustflags: Rustflags,
2143
    rustdocflags: Rustflags,
2144 2145 2146
}

impl Cargo {
M
Mark Rousskov 已提交
2147 2148 2149 2150
    pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo {
        self.rustdocflags.arg(arg);
        self
    }
2151 2152 2153 2154 2155
    pub fn rustflag(&mut self, arg: &str) -> &mut Cargo {
        self.rustflags.arg(arg);
        self
    }

2156 2157 2158 2159 2160 2161
    pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Cargo {
        self.command.arg(arg.as_ref());
        self
    }

    pub fn args<I, S>(&mut self, args: I) -> &mut Cargo
M
Mark Rousskov 已提交
2162 2163 2164
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
2165 2166 2167 2168 2169 2170 2171 2172
    {
        for arg in args {
            self.arg(arg.as_ref());
        }
        self
    }

    pub fn env(&mut self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) -> &mut Cargo {
M
Mark Rousskov 已提交
2173 2174 2175
        // These are managed through rustflag/rustdocflag interfaces.
        assert_ne!(key.as_ref(), "RUSTFLAGS");
        assert_ne!(key.as_ref(), "RUSTDOCFLAGS");
2176 2177 2178
        self.command.env(key.as_ref(), value.as_ref());
        self
    }
2179 2180 2181 2182

    pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>, compiler: Compiler) {
        builder.add_rustc_lib_path(compiler, &mut self.command);
    }
2183 2184 2185 2186 2187

    pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo {
        self.command.current_dir(dir);
        self
    }
2188 2189 2190 2191
}

impl From<Cargo> for Command {
    fn from(mut cargo: Cargo) -> Command {
2192 2193 2194 2195 2196 2197 2198 2199 2200 2201
        let rustflags = &cargo.rustflags.0;
        if !rustflags.is_empty() {
            cargo.command.env("RUSTFLAGS", rustflags);
        }

        let rustdocflags = &cargo.rustdocflags.0;
        if !rustdocflags.is_empty() {
            cargo.command.env("RUSTDOCFLAGS", rustdocflags);
        }

2202 2203 2204
        cargo.command
    }
}