config.rs 38.1 KB
Newer Older
1 2
//! Serialized configuration of a build.
//!
3 4
//! This module implements parsing `config.toml` configuration files to tweak
//! how the build runs.
5

M
Mark Rousskov 已提交
6
use std::cmp;
7
use std::collections::{HashMap, HashSet};
A
Alex Crichton 已提交
8
use std::env;
A
Adam Perry 已提交
9
use std::ffi::OsString;
10
use std::fmt;
11
use std::fs;
12
use std::path::{Path, PathBuf};
13
use std::str::FromStr;
A
Alex Crichton 已提交
14

M
Mark Rousskov 已提交
15
use crate::cache::{Interned, INTERNER};
L
ljedrz 已提交
16
pub use crate::flags::Subcommand;
J
Joshua Nelson 已提交
17
use crate::flags::{Color, Flags};
18
use crate::util::exe;
M
Mark Rousskov 已提交
19
use build_helper::t;
20
use merge::Merge;
M
Mark Rousskov 已提交
21
use serde::Deserialize;
A
Alex Crichton 已提交
22

23 24 25 26 27 28 29 30 31 32
macro_rules! check_ci_llvm {
    ($name:expr) => {
        assert!(
            $name.is_none(),
            "setting {} is incompatible with download-ci-llvm.",
            stringify!($name)
        );
    };
}

A
Alex Crichton 已提交
33 34 35 36 37 38 39 40
/// Global configuration for the entire build and/or bootstrap.
///
/// This structure is derived from a combination of both `config.toml` and
/// `config.mk`. As of the time of this writing it's unlikely that `config.toml`
/// is used all that much, so this is primarily filled out by `config.mk` which
/// is generated from `./configure`.
///
/// Note that this structure is not decoded directly into, but rather it is
41 42
/// filled out from the decoded forms of the structs below. For documentation
/// each field, see the corresponding fields in
43
/// `config.toml.example`.
A
Alex Crichton 已提交
44 45
#[derive(Default)]
pub struct Config {
J
Joshua Nelson 已提交
46
    pub changelog_seen: Option<usize>,
A
Alex Crichton 已提交
47
    pub ccache: Option<String>,
48 49
    /// Call Build::ninja() instead of this.
    pub ninja_in_file: bool,
50
    pub verbose: usize,
A
Alex Crichton 已提交
51
    pub submodules: bool,
52
    pub fast_submodules: bool,
A
Alex Crichton 已提交
53 54
    pub compiler_docs: bool,
    pub docs: bool,
55
    pub locked_deps: bool,
56
    pub vendor: bool,
57
    pub target_config: HashMap<TargetSelection, Target>,
58
    pub full_bootstrap: bool,
59
    pub extended: bool,
60
    pub tools: Option<HashSet<String>>,
61
    pub sanitizers: bool,
62
    pub profiler: bool,
63
    pub ignore_git: bool,
64
    pub exclude: Vec<PathBuf>,
65
    pub include_default_paths: bool,
P
penpalperson 已提交
66
    pub rustc_error_format: Option<String>,
67
    pub json_output: bool,
68
    pub test_compare_mode: bool,
69
    pub llvm_libunwind: Option<LlvmLibunwind>,
J
Joshua Nelson 已提交
70
    pub color: Color,
A
Alex Crichton 已提交
71

M
Mark Simulacrum 已提交
72
    pub on_fail: Option<String>,
73
    pub stage: u32,
74
    pub keep_stage: Vec<u32>,
D
Dylan MacKenzie 已提交
75
    pub keep_stage_std: Vec<u32>,
M
Mark Simulacrum 已提交
76
    pub src: PathBuf,
J
Joshua Nelson 已提交
77 78
    // defaults to `config.toml`
    pub config: PathBuf,
M
Mark Simulacrum 已提交
79 80 81
    pub jobs: Option<u32>,
    pub cmd: Subcommand,
    pub incremental: bool,
82
    pub dry_run: bool,
M
Mark Simulacrum 已提交
83

84
    pub deny_warnings: bool,
J
John Kåre Alsaker 已提交
85
    pub backtrace_on_ice: bool,
86

A
Alex Crichton 已提交
87
    // llvm codegen options
88
    pub llvm_skip_rebuild: bool,
A
Alex Crichton 已提交
89 90
    pub llvm_assertions: bool,
    pub llvm_optimize: bool,
91
    pub llvm_thin_lto: bool,
92
    pub llvm_release_debuginfo: bool,
A
Alex Crichton 已提交
93 94
    pub llvm_version_check: bool,
    pub llvm_static_stdcpp: bool,
95
    pub llvm_link_shared: bool,
96
    pub llvm_clang_cl: Option<String>,
97
    pub llvm_targets: Option<String>,
98
    pub llvm_experimental_targets: Option<String>,
99
    pub llvm_link_jobs: Option<u32>,
100
    pub llvm_version_suffix: Option<String>,
101
    pub llvm_use_linker: Option<String>,
102
    pub llvm_allow_old_toolchain: Option<bool>,
103
    pub llvm_polly: Option<bool>,
104
    pub llvm_from_ci: bool,
A
Alex Crichton 已提交
105

106
    pub use_lld: bool,
107
    pub lld_enabled: bool,
108
    pub llvm_tools_enabled: bool,
109

110 111 112
    pub llvm_cflags: Option<String>,
    pub llvm_cxxflags: Option<String>,
    pub llvm_ldflags: Option<String>,
113 114
    pub llvm_use_libcxx: bool,

A
Alex Crichton 已提交
115 116
    // rust codegen options
    pub rust_optimize: bool,
117
    pub rust_codegen_units: Option<u32>,
118
    pub rust_codegen_units_std: Option<u32>,
A
Alex Crichton 已提交
119
    pub rust_debug_assertions: bool,
120
    pub rust_debug_assertions_std: bool,
G
Gus Wynn 已提交
121
    pub rust_debug_logging: bool,
122 123 124 125
    pub rust_debuginfo_level_rustc: u32,
    pub rust_debuginfo_level_std: u32,
    pub rust_debuginfo_level_tools: u32,
    pub rust_debuginfo_level_tests: u32,
126
    pub rust_run_dsymutil: bool,
A
Alex Crichton 已提交
127
    pub rust_rpath: bool,
128
    pub rustc_parallel: bool,
A
Alex Crichton 已提交
129
    pub rustc_default_linker: Option<String>,
130
    pub rust_optimize_tests: bool,
131
    pub rust_dist_src: bool,
132
    pub rust_codegen_backends: Vec<Interned<String>>,
133
    pub rust_verify_llvm_ir: bool,
134
    pub rust_thin_lto_import_instr_limit: Option<u32>,
135
    pub rust_remap_debuginfo: bool,
136
    pub rust_new_symbol_mangling: bool,
137 138
    pub rust_profile_use: Option<String>,
    pub rust_profile_generate: Option<String>,
A
Alex Crichton 已提交
139

140 141 142
    pub build: TargetSelection,
    pub hosts: Vec<TargetSelection>,
    pub targets: Vec<TargetSelection>,
143
    pub local_rebuild: bool,
144
    pub jemalloc: bool,
145
    pub control_flow_guard: bool,
A
Alex Crichton 已提交
146

147 148 149 150
    // dist misc
    pub dist_sign_folder: Option<PathBuf>,
    pub dist_upload_addr: Option<String>,
    pub dist_gpg_password_file: Option<PathBuf>,
151
    pub dist_compression_formats: Option<Vec<String>>,
152

A
Alex Crichton 已提交
153
    // libstd features
154
    pub backtrace: bool, // support for RUST_BACKTRACE
A
Alex Crichton 已提交
155 156

    // misc
157
    pub low_priority: bool,
A
Alex Crichton 已提交
158
    pub channel: String,
159
    pub description: Option<String>,
O
Oliver Schneider 已提交
160
    pub verbose_tests: bool,
161
    pub save_toolstates: Option<PathBuf>,
162
    pub print_step_timings: bool,
163
    pub missing_tools: bool,
164

J
Jorge Aparicio 已提交
165
    // Fallback musl-root for all targets
A
Alex Crichton 已提交
166
    pub musl_root: Option<PathBuf>,
167
    pub prefix: Option<PathBuf>,
168
    pub sysconfdir: Option<PathBuf>,
169
    pub datadir: Option<PathBuf>,
170
    pub docdir: Option<PathBuf>,
171
    pub bindir: PathBuf,
172 173
    pub libdir: Option<PathBuf>,
    pub mandir: Option<PathBuf>,
174
    pub codegen_tests: bool,
175
    pub nodejs: Option<PathBuf>,
176
    pub gdb: Option<PathBuf>,
177
    pub python: Option<PathBuf>,
178
    pub cargo_native_static: bool,
179
    pub configure_args: Vec<String>,
180 181 182 183

    // These are either the stage0 downloaded binaries or the locally installed ones.
    pub initial_cargo: PathBuf,
    pub initial_rustc: PathBuf,
A
Adam Perry 已提交
184
    pub initial_rustfmt: Option<PathBuf>,
185
    pub out: PathBuf,
A
Alex Crichton 已提交
186 187
}

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LlvmLibunwind {
    No,
    InTree,
    System,
}

impl Default for LlvmLibunwind {
    fn default() -> Self {
        Self::No
    }
}

impl FromStr for LlvmLibunwind {
    type Err = String;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        match value {
            "no" => Ok(Self::No),
            "in-tree" => Ok(Self::InTree),
            "system" => Ok(Self::System),
            invalid => Err(format!("Invalid value '{}' for rust.llvm-libunwind config.", invalid)),
        }
    }
}

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TargetSelection {
    pub triple: Interned<String>,
    file: Option<Interned<String>>,
}

impl TargetSelection {
    pub fn from_user(selection: &str) -> Self {
        let path = Path::new(selection);

        let (triple, file) = if path.exists() {
            let triple = path
                .file_stem()
                .expect("Target specification file has no file stem")
                .to_str()
                .expect("Target specification file stem is not UTF-8");

            (triple, Some(selection))
        } else {
            (selection, None)
        };

        let triple = INTERNER.intern_str(triple);
        let file = file.map(|f| INTERNER.intern_str(f));

        Self { triple, file }
    }

    pub fn rustc_target_arg(&self) -> &str {
        self.file.as_ref().unwrap_or(&self.triple)
    }

    pub fn contains(&self, needle: &str) -> bool {
        self.triple.contains(needle)
    }

    pub fn starts_with(&self, needle: &str) -> bool {
        self.triple.starts_with(needle)
    }

    pub fn ends_with(&self, needle: &str) -> bool {
        self.triple.ends_with(needle)
    }
}

impl fmt::Display for TargetSelection {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.triple)?;
        if let Some(file) = self.file {
            write!(f, "({})", file)?;
        }
        Ok(())
    }
}

impl PartialEq<&str> for TargetSelection {
    fn eq(&self, other: &&str) -> bool {
        self.triple == *other
    }
}

A
Alex Crichton 已提交
275 276 277
/// Per-target configuration stored in the global configuration structure.
#[derive(Default)]
pub struct Target {
278
    /// Some(path to llvm-config) if using an external LLVM.
A
Alex Crichton 已提交
279
    pub llvm_config: Option<PathBuf>,
280 281
    /// Some(path to FileCheck) if one was specified.
    pub llvm_filecheck: Option<PathBuf>,
A
Alex Crichton 已提交
282 283
    pub cc: Option<PathBuf>,
    pub cxx: Option<PathBuf>,
284
    pub ar: Option<PathBuf>,
285
    pub ranlib: Option<PathBuf>,
286
    pub linker: Option<PathBuf>,
A
Alex Crichton 已提交
287
    pub ndk: Option<PathBuf>,
288 289
    pub sanitizers: Option<bool>,
    pub profiler: Option<bool>,
290
    pub crt_static: Option<bool>,
J
Jorge Aparicio 已提交
291
    pub musl_root: Option<PathBuf>,
292
    pub musl_libdir: Option<PathBuf>,
293
    pub wasi_root: Option<PathBuf>,
294
    pub qemu_rootfs: Option<PathBuf>,
295
    pub no_std: bool,
A
Alex Crichton 已提交
296 297
}

298 299 300
impl Target {
    pub fn from_triple(triple: &str) -> Self {
        let mut target: Self = Default::default();
301
        if triple.contains("-none") || triple.contains("nvptx") {
302 303 304 305 306
            target.no_std = true;
        }
        target
    }
}
A
Alex Crichton 已提交
307 308 309 310 311
/// Structure of the `config.toml` file that configuration is read from.
///
/// This structure uses `Decodable` to automatically decode a TOML configuration
/// file into this format, and then this is traversed and written into the above
/// `Config` structure.
M
Mark Simulacrum 已提交
312
#[derive(Deserialize, Default)]
313
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
A
Alex Crichton 已提交
314
struct TomlConfig {
J
Joshua Nelson 已提交
315
    changelog_seen: Option<usize>,
A
Alex Crichton 已提交
316
    build: Option<Build>,
J
Jeremy Soller 已提交
317
    install: Option<Install>,
A
Alex Crichton 已提交
318 319 320
    llvm: Option<Llvm>,
    rust: Option<Rust>,
    target: Option<HashMap<String, TomlTarget>>,
321
    dist: Option<Dist>,
322 323 324 325
    profile: Option<String>,
}

impl Merge for TomlConfig {
J
Joshua Nelson 已提交
326 327 328 329
    fn merge(
        &mut self,
        TomlConfig { build, install, llvm, rust, dist, target, profile: _, changelog_seen: _ }: Self,
    ) {
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
        fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>) {
            if let Some(new) = y {
                if let Some(original) = x {
                    original.merge(new);
                } else {
                    *x = Some(new);
                }
            }
        };
        do_merge(&mut self.build, build);
        do_merge(&mut self.install, install);
        do_merge(&mut self.llvm, llvm);
        do_merge(&mut self.rust, rust);
        do_merge(&mut self.dist, dist);
        assert!(target.is_none(), "merging target-specific config is not currently supported");
    }
A
Alex Crichton 已提交
346 347 348
}

/// TOML representation of various global build decisions.
349
#[derive(Deserialize, Default, Clone, Merge)]
350
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
A
Alex Crichton 已提交
351 352
struct Build {
    build: Option<String>,
353 354
    host: Option<Vec<String>>,
    target: Option<Vec<String>>,
355 356
    // This is ignored, the rust code always gets the build directory from the `BUILD_DIR` env variable
    build_dir: Option<String>,
A
Alex Crichton 已提交
357 358
    cargo: Option<String>,
    rustc: Option<String>,
359
    rustfmt: Option<PathBuf>,
A
Alex Crichton 已提交
360
    docs: Option<bool>,
361
    compiler_docs: Option<bool>,
362
    submodules: Option<bool>,
363
    fast_submodules: Option<bool>,
364
    gdb: Option<String>,
365
    nodejs: Option<String>,
366
    python: Option<String>,
367 368
    locked_deps: Option<bool>,
    vendor: Option<bool>,
369
    full_bootstrap: Option<bool>,
370
    extended: Option<bool>,
371
    tools: Option<HashSet<String>>,
372
    verbose: Option<usize>,
373
    sanitizers: Option<bool>,
374
    profiler: Option<bool>,
375
    cargo_native_static: Option<bool>,
376
    low_priority: Option<bool>,
377 378
    configure_args: Option<Vec<String>>,
    local_rebuild: Option<bool>,
379
    print_step_timings: Option<bool>,
380 381 382 383 384 385
    doc_stage: Option<u32>,
    build_stage: Option<u32>,
    test_stage: Option<u32>,
    install_stage: Option<u32>,
    dist_stage: Option<u32>,
    bench_stage: Option<u32>,
A
Alex Crichton 已提交
386 387
}

J
Jeremy Soller 已提交
388
/// TOML representation of various global install decisions.
389
#[derive(Deserialize, Default, Clone, Merge)]
390
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
J
Jeremy Soller 已提交
391 392
struct Install {
    prefix: Option<String>,
393
    sysconfdir: Option<String>,
394
    docdir: Option<String>,
395
    bindir: Option<String>,
396
    libdir: Option<String>,
397
    mandir: Option<String>,
398
    datadir: Option<String>,
399 400 401 402

    // standard paths, currently unused
    infodir: Option<String>,
    localstatedir: Option<String>,
J
Jeremy Soller 已提交
403 404
}

A
Alex Crichton 已提交
405
/// TOML representation of how the LLVM build is configured.
406
#[derive(Deserialize, Default, Merge)]
407
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
A
Alex Crichton 已提交
408
struct Llvm {
409
    skip_rebuild: Option<bool>,
A
Alex Crichton 已提交
410
    optimize: Option<bool>,
411
    thin_lto: Option<bool>,
412
    release_debuginfo: Option<bool>,
413 414
    assertions: Option<bool>,
    ccache: Option<StringOrBool>,
A
Alex Crichton 已提交
415 416
    version_check: Option<bool>,
    static_libstdcpp: Option<bool>,
417
    ninja: Option<bool>,
418
    targets: Option<String>,
419
    experimental_targets: Option<String>,
420
    link_jobs: Option<u32>,
421
    link_shared: Option<bool>,
422
    version_suffix: Option<String>,
423
    clang_cl: Option<String>,
424 425 426
    cflags: Option<String>,
    cxxflags: Option<String>,
    ldflags: Option<String>,
427
    use_libcxx: Option<bool>,
428
    use_linker: Option<String>,
429
    allow_old_toolchain: Option<bool>,
430
    polly: Option<bool>,
431
    download_ci_llvm: Option<StringOrBool>,
A
Alex Crichton 已提交
432 433
}

434
#[derive(Deserialize, Default, Clone, Merge)]
435
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
436 437 438 439
struct Dist {
    sign_folder: Option<String>,
    gpg_password_file: Option<String>,
    upload_addr: Option<String>,
440
    src_tarball: Option<bool>,
441
    missing_tools: Option<bool>,
442
    compression_formats: Option<Vec<String>>,
443 444
}

M
Mark Simulacrum 已提交
445 446
#[derive(Deserialize)]
#[serde(untagged)]
A
Alex Crichton 已提交
447 448 449 450 451 452 453 454 455 456 457
enum StringOrBool {
    String(String),
    Bool(bool),
}

impl Default for StringOrBool {
    fn default() -> StringOrBool {
        StringOrBool::Bool(false)
    }
}

A
Alex Crichton 已提交
458
/// TOML representation of how the Rust build is configured.
459
#[derive(Deserialize, Default, Merge)]
460
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
A
Alex Crichton 已提交
461 462
struct Rust {
    optimize: Option<bool>,
463
    debug: Option<bool>,
A
Alex Crichton 已提交
464
    codegen_units: Option<u32>,
465
    codegen_units_std: Option<u32>,
A
Alex Crichton 已提交
466
    debug_assertions: Option<bool>,
467
    debug_assertions_std: Option<bool>,
G
Gus Wynn 已提交
468
    debug_logging: Option<bool>,
469 470 471 472 473
    debuginfo_level: Option<u32>,
    debuginfo_level_rustc: Option<u32>,
    debuginfo_level_std: Option<u32>,
    debuginfo_level_tools: Option<u32>,
    debuginfo_level_tests: Option<u32>,
474
    run_dsymutil: Option<bool>,
475
    backtrace: Option<bool>,
476 477
    incremental: Option<bool>,
    parallel_compiler: Option<bool>,
A
Alex Crichton 已提交
478 479
    default_linker: Option<String>,
    channel: Option<String>,
480
    description: Option<String>,
A
Alex Crichton 已提交
481 482
    musl_root: Option<String>,
    rpath: Option<bool>,
483
    verbose_tests: Option<bool>,
484
    optimize_tests: Option<bool>,
485
    codegen_tests: Option<bool>,
486
    ignore_git: Option<bool>,
487
    dist_src: Option<bool>,
488
    save_toolstates: Option<String>,
489
    codegen_backends: Option<Vec<String>>,
490
    lld: Option<bool>,
491
    use_lld: Option<bool>,
492
    llvm_tools: Option<bool>,
493
    deny_warnings: Option<bool>,
J
John Kåre Alsaker 已提交
494
    backtrace_on_ice: Option<bool>,
495
    verify_llvm_ir: Option<bool>,
496
    thin_lto_import_instr_limit: Option<u32>,
497
    remap_debuginfo: Option<bool>,
498
    jemalloc: Option<bool>,
499
    test_compare_mode: Option<bool>,
500
    llvm_libunwind: Option<String>,
501
    control_flow_guard: Option<bool>,
502
    new_symbol_mangling: Option<bool>,
503 504
    profile_generate: Option<String>,
    profile_use: Option<String>,
A
Alex Crichton 已提交
505 506 507
}

/// TOML representation of how each build target is configured.
508
#[derive(Deserialize, Default, Merge)]
509
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
A
Alex Crichton 已提交
510 511 512
struct TomlTarget {
    cc: Option<String>,
    cxx: Option<String>,
513
    ar: Option<String>,
514
    ranlib: Option<String>,
515
    linker: Option<String>,
516 517
    llvm_config: Option<String>,
    llvm_filecheck: Option<String>,
A
Alex Crichton 已提交
518
    android_ndk: Option<String>,
519 520
    sanitizers: Option<bool>,
    profiler: Option<bool>,
521
    crt_static: Option<bool>,
522
    musl_root: Option<String>,
523
    musl_libdir: Option<String>,
524
    wasi_root: Option<String>,
525
    qemu_rootfs: Option<String>,
526
    no_std: Option<bool>,
A
Alex Crichton 已提交
527 528 529
}

impl Config {
530 531
    fn path_from_python(var_key: &str) -> PathBuf {
        match env::var_os(var_key) {
A
Adam Perry 已提交
532
            Some(var_val) => Self::normalize_python_path(var_val),
533 534 535 536
            _ => panic!("expected '{}' to be set", var_key),
        }
    }

A
Adam Perry 已提交
537 538 539 540 541
    /// Normalizes paths from Python slightly. We don't trust paths from Python (#49785).
    fn normalize_python_path(path: OsString) -> PathBuf {
        Path::new(&path).components().collect()
    }

542
    pub fn default_opts() -> Config {
A
Alex Crichton 已提交
543 544
        let mut config = Config::default();
        config.llvm_optimize = true;
545
        config.ninja_in_file = true;
546
        config.llvm_version_check = true;
547
        config.backtrace = true;
A
Alex Crichton 已提交
548
        config.rust_optimize = true;
549
        config.rust_optimize_tests = true;
A
Alex Crichton 已提交
550
        config.submodules = true;
551
        config.fast_submodules = true;
A
Alex Crichton 已提交
552 553 554
        config.docs = true;
        config.rust_rpath = true;
        config.channel = "dev".to_string();
555
        config.codegen_tests = true;
556
        config.ignore_git = false;
557
        config.rust_dist_src = true;
558
        config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")];
559
        config.deny_warnings = true;
560
        config.missing_tools = false;
A
Alex Crichton 已提交
561

N
nooberfsh 已提交
562
        // set by build.rs
563
        config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
564 565 566
        let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        // Undo `src/bootstrap`
        config.src = manifest_dir.parent().unwrap().parent().unwrap().to_owned();
567
        config.out = Config::path_from_python("BUILD_DIR");
568

569 570
        config.initial_cargo = PathBuf::from(env!("CARGO"));
        config.initial_rustc = PathBuf::from(env!("RUSTC"));
571 572 573 574 575 576

        config
    }

    pub fn parse(args: &[String]) -> Config {
        let flags = Flags::parse(&args);
577

578 579
        let mut config = Config::default_opts();
        config.exclude = flags.exclude;
580
        config.include_default_paths = flags.include_default_paths;
P
penpalperson 已提交
581
        config.rustc_error_format = flags.rustc_error_format;
582
        config.json_output = flags.json_output;
M
Mark Simulacrum 已提交
583
        config.on_fail = flags.on_fail;
584
        config.jobs = flags.jobs.map(threads_from_config);
M
Mark Simulacrum 已提交
585 586
        config.cmd = flags.cmd;
        config.incremental = flags.incremental;
587
        config.dry_run = flags.dry_run;
M
Mark Simulacrum 已提交
588
        config.keep_stage = flags.keep_stage;
D
Dylan MacKenzie 已提交
589
        config.keep_stage_std = flags.keep_stage_std;
590
        config.bindir = "bin".into(); // default
J
Joshua Nelson 已提交
591
        config.color = flags.color;
592
        if let Some(value) = flags.deny_warnings {
593 594
            config.deny_warnings = value;
        }
M
Mark Simulacrum 已提交
595

596 597 598 599 600 601
        if config.dry_run {
            let dir = config.out.join("tmp-dry-run");
            t!(fs::create_dir_all(&dir));
            config.out = dir;
        }

602
        #[cfg(test)]
603
        let get_toml = |_| TomlConfig::default();
604
        #[cfg(not(test))]
605
        let get_toml = |file: &Path| {
606 607
            use std::process;

608
            let contents = t!(fs::read_to_string(file), "`include` config not found");
609 610 611 612 613
            match toml::from_str(&contents) {
                Ok(table) => table,
                Err(err) => {
                    println!("failed to parse TOML configuration '{}': {}", file.display(), err);
                    process::exit(2);
A
Alex Crichton 已提交
614
                }
615 616 617
            }
        };

618
        let mut toml = flags.config.as_deref().map(get_toml).unwrap_or_else(TomlConfig::default);
619 620 621 622 623
        if let Some(include) = &toml.profile {
            let mut include_path = config.src.clone();
            include_path.push("src");
            include_path.push("bootstrap");
            include_path.push("defaults");
624
            include_path.push(format!("config.{}.toml", include));
625
            let included_toml = get_toml(&include_path);
626 627
            toml.merge(included_toml);
        }
A
Alex Crichton 已提交
628

J
Joshua Nelson 已提交
629
        config.changelog_seen = toml.changelog_seen;
630 631 632
        if let Some(cfg) = flags.config {
            config.config = cfg;
        }
J
Joshua Nelson 已提交
633

634
        let build = toml.build.unwrap_or_default();
635

636
        config.hosts = if let Some(arg_host) = flags.host {
637 638 639 640 641 642
            arg_host
        } else if let Some(file_host) = build.host {
            file_host.iter().map(|h| TargetSelection::from_user(h)).collect()
        } else {
            vec![config.build]
        };
643
        config.targets = if let Some(arg_target) = flags.target {
644 645 646 647 648 649 650 651
            arg_target
        } else if let Some(file_target) = build.target {
            file_target.iter().map(|h| TargetSelection::from_user(h)).collect()
        } else {
            // If target is *not* configured, then default to the host
            // toolchains.
            config.hosts.clone()
        };
652

653
        config.nodejs = build.nodejs.map(PathBuf::from);
654
        config.gdb = build.gdb.map(PathBuf::from);
655
        config.python = build.python.map(PathBuf::from);
656
        set(&mut config.low_priority, build.low_priority);
A
Alex Crichton 已提交
657 658
        set(&mut config.compiler_docs, build.compiler_docs);
        set(&mut config.docs, build.docs);
659
        set(&mut config.submodules, build.submodules);
660
        set(&mut config.fast_submodules, build.fast_submodules);
661
        set(&mut config.locked_deps, build.locked_deps);
662
        set(&mut config.vendor, build.vendor);
663
        set(&mut config.full_bootstrap, build.full_bootstrap);
664
        set(&mut config.extended, build.extended);
665
        config.tools = build.tools;
666 667 668
        if build.rustfmt.is_some() {
            config.initial_rustfmt = build.rustfmt;
        }
669
        set(&mut config.verbose, build.verbose);
670
        set(&mut config.sanitizers, build.sanitizers);
671
        set(&mut config.profiler, build.profiler);
672
        set(&mut config.cargo_native_static, build.cargo_native_static);
673 674
        set(&mut config.configure_args, build.configure_args);
        set(&mut config.local_rebuild, build.local_rebuild);
675
        set(&mut config.print_step_timings, build.print_step_timings);
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691

        // See https://github.com/rust-lang/compiler-team/issues/326
        config.stage = match config.cmd {
            Subcommand::Doc { .. } => flags.stage.or(build.doc_stage).unwrap_or(0),
            Subcommand::Build { .. } => flags.stage.or(build.build_stage).unwrap_or(1),
            Subcommand::Test { .. } => flags.stage.or(build.test_stage).unwrap_or(1),
            Subcommand::Bench { .. } => flags.stage.or(build.bench_stage).unwrap_or(2),
            Subcommand::Dist { .. } => flags.stage.or(build.dist_stage).unwrap_or(2),
            Subcommand::Install { .. } => flags.stage.or(build.install_stage).unwrap_or(2),
            // These are all bootstrap tools, which don't depend on the compiler.
            // The stage we pass shouldn't matter, but use 0 just in case.
            Subcommand::Clean { .. }
            | Subcommand::Check { .. }
            | Subcommand::Clippy { .. }
            | Subcommand::Fix { .. }
            | Subcommand::Run { .. }
J
Joshua Nelson 已提交
692
            | Subcommand::Setup { .. }
693 694 695 696 697 698 699 700 701 702 703 704
            | Subcommand::Format { .. } => flags.stage.unwrap_or(0),
        };

        // CI should always run stage 2 builds, unless it specifically states otherwise
        #[cfg(not(test))]
        if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None {
            match config.cmd {
                Subcommand::Test { .. }
                | Subcommand::Doc { .. }
                | Subcommand::Build { .. }
                | Subcommand::Bench { .. }
                | Subcommand::Dist { .. }
705 706 707 708 709 710 711
                | Subcommand::Install { .. } => {
                    assert_eq!(
                        config.stage, 2,
                        "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`",
                        config.stage,
                    );
                }
712 713 714 715 716
                Subcommand::Clean { .. }
                | Subcommand::Check { .. }
                | Subcommand::Clippy { .. }
                | Subcommand::Fix { .. }
                | Subcommand::Run { .. }
J
Joshua Nelson 已提交
717
                | Subcommand::Setup { .. }
718 719 720 721
                | Subcommand::Format { .. } => {}
            }
        }

M
Mark Simulacrum 已提交
722
        config.verbose = cmp::max(config.verbose, flags.verbose);
A
Alex Crichton 已提交
723

724 725 726 727 728 729 730 731
        if let Some(install) = toml.install {
            config.prefix = install.prefix.map(PathBuf::from);
            config.sysconfdir = install.sysconfdir.map(PathBuf::from);
            config.datadir = install.datadir.map(PathBuf::from);
            config.docdir = install.docdir.map(PathBuf::from);
            set(&mut config.bindir, install.bindir.map(PathBuf::from));
            config.libdir = install.libdir.map(PathBuf::from);
            config.mandir = install.mandir.map(PathBuf::from);
J
Jeremy Soller 已提交
732 733
        }

734 735 736 737 738
        // We want the llvm-skip-rebuild flag to take precedence over the
        // skip-rebuild config.toml option so we store it separately
        // so that we can infer the right value
        let mut llvm_skip_rebuild = flags.llvm_skip_rebuild;

739 740 741 742 743
        // Store off these values as options because if they're not provided
        // we'll infer default values for them later
        let mut llvm_assertions = None;
        let mut debug = None;
        let mut debug_assertions = None;
744
        let mut debug_assertions_std = None;
G
Gus Wynn 已提交
745
        let mut debug_logging = None;
746 747 748 749 750
        let mut debuginfo_level = None;
        let mut debuginfo_level_rustc = None;
        let mut debuginfo_level_std = None;
        let mut debuginfo_level_tools = None;
        let mut debuginfo_level_tests = None;
751
        let mut optimize = None;
752
        let mut ignore_git = None;
753

754
        if let Some(llvm) = toml.llvm {
A
Alex Crichton 已提交
755
            match llvm.ccache {
M
Mark Rousskov 已提交
756
                Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()),
A
Alex Crichton 已提交
757 758 759 760 761
                Some(StringOrBool::Bool(true)) => {
                    config.ccache = Some("ccache".to_string());
                }
                Some(StringOrBool::Bool(false)) | None => {}
            }
762
            set(&mut config.ninja_in_file, llvm.ninja);
763
            llvm_assertions = llvm.assertions;
764
            llvm_skip_rebuild = llvm_skip_rebuild.or(llvm.skip_rebuild);
A
Alex Crichton 已提交
765
            set(&mut config.llvm_optimize, llvm.optimize);
766
            set(&mut config.llvm_thin_lto, llvm.thin_lto);
767
            set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
A
Alex Crichton 已提交
768 769
            set(&mut config.llvm_version_check, llvm.version_check);
            set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
770
            set(&mut config.llvm_link_shared, llvm.link_shared);
771
            config.llvm_targets = llvm.targets.clone();
772
            config.llvm_experimental_targets = llvm.experimental_targets.clone();
773
            config.llvm_link_jobs = llvm.link_jobs;
774
            config.llvm_version_suffix = llvm.version_suffix.clone();
775
            config.llvm_clang_cl = llvm.clang_cl.clone();
776 777 778 779

            config.llvm_cflags = llvm.cflags.clone();
            config.llvm_cxxflags = llvm.cxxflags.clone();
            config.llvm_ldflags = llvm.ldflags.clone();
780
            set(&mut config.llvm_use_libcxx, llvm.use_libcxx);
781
            config.llvm_use_linker = llvm.use_linker.clone();
782
            config.llvm_allow_old_toolchain = llvm.allow_old_toolchain;
783
            config.llvm_polly = llvm.polly;
784 785 786 787 788 789 790 791
            config.llvm_from_ci = match llvm.download_ci_llvm {
                Some(StringOrBool::String(s)) => {
                    assert!(s == "if-available", "unknown option `{}` for download-ci-llvm", s);
                    config.build.triple == "x86_64-unknown-linux-gnu"
                }
                Some(StringOrBool::Bool(b)) => b,
                None => false,
            };
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

            if config.llvm_from_ci {
                // None of the LLVM options, except assertions, are supported
                // when using downloaded LLVM. We could just ignore these but
                // that's potentially confusing, so force them to not be
                // explicitly set. The defaults and CI defaults don't
                // necessarily match but forcing people to match (somewhat
                // arbitrary) CI configuration locally seems bad/hard.
                check_ci_llvm!(llvm.optimize);
                check_ci_llvm!(llvm.thin_lto);
                check_ci_llvm!(llvm.release_debuginfo);
                check_ci_llvm!(llvm.link_shared);
                check_ci_llvm!(llvm.static_libstdcpp);
                check_ci_llvm!(llvm.targets);
                check_ci_llvm!(llvm.experimental_targets);
                check_ci_llvm!(llvm.link_jobs);
                check_ci_llvm!(llvm.link_shared);
                check_ci_llvm!(llvm.clang_cl);
                check_ci_llvm!(llvm.version_suffix);
                check_ci_llvm!(llvm.cflags);
                check_ci_llvm!(llvm.cxxflags);
                check_ci_llvm!(llvm.ldflags);
                check_ci_llvm!(llvm.use_libcxx);
                check_ci_llvm!(llvm.use_linker);
                check_ci_llvm!(llvm.allow_old_toolchain);
817
                check_ci_llvm!(llvm.polly);
818 819 820

                // CI-built LLVM is shared
                config.llvm_link_shared = true;
821 822 823 824 825 826 827
            }

            if config.llvm_thin_lto {
                // If we're building with ThinLTO on, we want to link to LLVM
                // shared, to avoid re-doing ThinLTO (which happens in the link
                // step) with each stage.
                config.llvm_link_shared = true;
828
            }
A
Alex Crichton 已提交
829
        }
J
Jeremy Soller 已提交
830

831
        if let Some(rust) = toml.rust {
832 833
            debug = rust.debug;
            debug_assertions = rust.debug_assertions;
834
            debug_assertions_std = rust.debug_assertions_std;
G
Gus Wynn 已提交
835
            debug_logging = rust.debug_logging;
836 837 838 839 840
            debuginfo_level = rust.debuginfo_level;
            debuginfo_level_rustc = rust.debuginfo_level_rustc;
            debuginfo_level_std = rust.debuginfo_level_std;
            debuginfo_level_tools = rust.debuginfo_level_tools;
            debuginfo_level_tests = rust.debuginfo_level_tests;
841
            config.rust_run_dsymutil = rust.run_dsymutil.unwrap_or(false);
842
            optimize = rust.optimize;
843
            ignore_git = rust.ignore_git;
844
            set(&mut config.rust_new_symbol_mangling, rust.new_symbol_mangling);
845
            set(&mut config.rust_optimize_tests, rust.optimize_tests);
846
            set(&mut config.codegen_tests, rust.codegen_tests);
A
Alex Crichton 已提交
847
            set(&mut config.rust_rpath, rust.rpath);
848
            set(&mut config.jemalloc, rust.jemalloc);
849
            set(&mut config.test_compare_mode, rust.test_compare_mode);
850 851 852
            config.llvm_libunwind = rust
                .llvm_libunwind
                .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
853
            set(&mut config.backtrace, rust.backtrace);
854
            set(&mut config.channel, rust.channel);
855
            config.description = rust.description;
856
            set(&mut config.rust_dist_src, rust.dist_src);
O
Oliver Schneider 已提交
857
            set(&mut config.verbose_tests, rust.verbose_tests);
858 859 860 861
            // in the case "false" is set explicitly, do not overwrite the command line args
            if let Some(true) = rust.incremental {
                config.incremental = true;
            }
862
            set(&mut config.use_lld, rust.use_lld);
863
            set(&mut config.lld_enabled, rust.lld);
864
            set(&mut config.llvm_tools_enabled, rust.llvm_tools);
865
            config.rustc_parallel = rust.parallel_compiler.unwrap_or(false);
866 867 868
            config.rustc_default_linker = rust.default_linker;
            config.musl_root = rust.musl_root.map(PathBuf::from);
            config.save_toolstates = rust.save_toolstates.map(PathBuf::from);
869
            set(&mut config.deny_warnings, flags.deny_warnings.or(rust.deny_warnings));
J
John Kåre Alsaker 已提交
870
            set(&mut config.backtrace_on_ice, rust.backtrace_on_ice);
871
            set(&mut config.rust_verify_llvm_ir, rust.verify_llvm_ir);
872
            config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit;
873
            set(&mut config.rust_remap_debuginfo, rust.remap_debuginfo);
874
            set(&mut config.control_flow_guard, rust.control_flow_guard);
A
Alex Crichton 已提交
875

876
            if let Some(ref backends) = rust.codegen_backends {
M
Mark Rousskov 已提交
877 878
                config.rust_codegen_backends =
                    backends.iter().map(|s| INTERNER.intern_str(s)).collect();
879 880
            }

881 882
            config.rust_codegen_units = rust.codegen_units.map(threads_from_config);
            config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config);
883 884 885 886 887
            config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use);
            config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate);
        } else {
            config.rust_profile_use = flags.rust_profile_use;
            config.rust_profile_generate = flags.rust_profile_generate;
A
Alex Crichton 已提交
888 889
        }

890
        if let Some(t) = toml.target {
A
Alex Crichton 已提交
891
            for (triple, cfg) in t {
892
                let mut target = Target::from_triple(&triple);
A
Alex Crichton 已提交
893 894

                if let Some(ref s) = cfg.llvm_config {
895
                    target.llvm_config = Some(config.src.join(s));
A
Alex Crichton 已提交
896
                }
897 898 899
                if let Some(ref s) = cfg.llvm_filecheck {
                    target.llvm_filecheck = Some(config.src.join(s));
                }
A
Alex Crichton 已提交
900
                if let Some(ref s) = cfg.android_ndk {
901
                    target.ndk = Some(config.src.join(s));
A
Alex Crichton 已提交
902
                }
903 904 905
                if let Some(s) = cfg.no_std {
                    target.no_std = s;
                }
906 907 908 909 910
                target.cc = cfg.cc.map(PathBuf::from);
                target.cxx = cfg.cxx.map(PathBuf::from);
                target.ar = cfg.ar.map(PathBuf::from);
                target.ranlib = cfg.ranlib.map(PathBuf::from);
                target.linker = cfg.linker.map(PathBuf::from);
911
                target.crt_static = cfg.crt_static;
912 913 914 915
                target.musl_root = cfg.musl_root.map(PathBuf::from);
                target.musl_libdir = cfg.musl_libdir.map(PathBuf::from);
                target.wasi_root = cfg.wasi_root.map(PathBuf::from);
                target.qemu_rootfs = cfg.qemu_rootfs.map(PathBuf::from);
916 917
                target.sanitizers = cfg.sanitizers;
                target.profiler = cfg.profiler;
A
Alex Crichton 已提交
918

919
                config.target_config.insert(TargetSelection::from_user(&triple), target);
A
Alex Crichton 已提交
920 921 922
            }
        }

923 924 925 926 927 928 929 930 931 932 933 934 935 936
        if config.llvm_from_ci {
            let triple = &config.build.triple;
            let mut build_target = config
                .target_config
                .entry(config.build)
                .or_insert_with(|| Target::from_triple(&triple));

            check_ci_llvm!(build_target.llvm_config);
            check_ci_llvm!(build_target.llvm_filecheck);
            let ci_llvm_bin = config.out.join(&*config.build.triple).join("ci-llvm/bin");
            build_target.llvm_config = Some(ci_llvm_bin.join(exe("llvm-config", config.build)));
            build_target.llvm_filecheck = Some(ci_llvm_bin.join(exe("FileCheck", config.build)));
        }

937 938 939 940
        if let Some(t) = toml.dist {
            config.dist_sign_folder = t.sign_folder.map(PathBuf::from);
            config.dist_gpg_password_file = t.gpg_password_file.map(PathBuf::from);
            config.dist_upload_addr = t.upload_addr;
941
            config.dist_compression_formats = t.compression_formats;
942
            set(&mut config.rust_dist_src, t.src_tarball);
943
            set(&mut config.missing_tools, t.missing_tools);
944 945
        }

946 947 948 949 950 951 952 953 954 955 956 957
        config.initial_rustfmt = config.initial_rustfmt.or_else({
            let build = config.build;
            let initial_rustc = &config.initial_rustc;

            move || {
                // Cargo does not provide a RUSTFMT environment variable, so we
                // synthesize it manually.
                let rustfmt = initial_rustc.with_file_name(exe("rustfmt", build));

                if rustfmt.exists() { Some(rustfmt) } else { None }
            }
        });
958

959 960
        // Now that we've reached the end of our configuration, infer the
        // default values for all options that we haven't otherwise stored yet.
961

962 963
        config.llvm_skip_rebuild = llvm_skip_rebuild.unwrap_or(false);

964
        let default = false;
965
        config.llvm_assertions = llvm_assertions.unwrap_or(default);
966

967 968 969
        let default = true;
        config.rust_optimize = optimize.unwrap_or(default);

970 971
        let default = debug == Some(true);
        config.rust_debug_assertions = debug_assertions.unwrap_or(default);
972 973
        config.rust_debug_assertions_std =
            debug_assertions_std.unwrap_or(config.rust_debug_assertions);
974

G
Gus Wynn 已提交
975 976
        config.rust_debug_logging = debug_logging.unwrap_or(config.rust_debug_assertions);

977
        let with_defaults = |debuginfo_level_specific: Option<u32>| {
M
Mark Rousskov 已提交
978
            debuginfo_level_specific.or(debuginfo_level).unwrap_or(if debug == Some(true) {
979
                1
M
Mark Rousskov 已提交
980 981 982
            } else {
                0
            })
983 984 985 986
        };
        config.rust_debuginfo_level_rustc = with_defaults(debuginfo_level_rustc);
        config.rust_debuginfo_level_std = with_defaults(debuginfo_level_std);
        config.rust_debuginfo_level_tools = with_defaults(debuginfo_level_tools);
987
        config.rust_debuginfo_level_tests = debuginfo_level_tests.unwrap_or(0);
988

989 990 991
        let default = config.channel == "dev";
        config.ignore_git = ignore_git.unwrap_or(default);

992
        config
993
    }
994

995 996
    /// Try to find the relative path of `bindir`, otherwise return it in full.
    pub fn bindir_relative(&self) -> &Path {
997
        let bindir = &self.bindir;
998
        if bindir.is_absolute() {
999
            // Try to make it relative to the prefix.
1000 1001 1002 1003 1004
            if let Some(prefix) = &self.prefix {
                if let Ok(stripped) = bindir.strip_prefix(prefix) {
                    return stripped;
                }
            }
1005
        }
1006
        bindir
1007 1008
    }

1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
    /// Try to find the relative path of `libdir`.
    pub fn libdir_relative(&self) -> Option<&Path> {
        let libdir = self.libdir.as_ref()?;
        if libdir.is_relative() {
            Some(libdir)
        } else {
            // Try to make it relative to the prefix.
            libdir.strip_prefix(self.prefix.as_ref()?).ok()
        }
    }

1020 1021 1022 1023 1024 1025 1026
    pub fn verbose(&self) -> bool {
        self.verbose > 0
    }

    pub fn very_verbose(&self) -> bool {
        self.verbose > 1
    }
B
bjorn3 已提交
1027

1028
    pub fn sanitizers_enabled(&self, target: TargetSelection) -> bool {
1029
        self.target_config.get(&target).map(|t| t.sanitizers).flatten().unwrap_or(self.sanitizers)
1030 1031 1032
    }

    pub fn any_sanitizers_enabled(&self) -> bool {
1033
        self.target_config.values().any(|t| t.sanitizers == Some(true)) || self.sanitizers
1034 1035 1036
    }

    pub fn profiler_enabled(&self, target: TargetSelection) -> bool {
1037
        self.target_config.get(&target).map(|t| t.profiler).flatten().unwrap_or(self.profiler)
1038 1039 1040
    }

    pub fn any_profiler_enabled(&self) -> bool {
1041
        self.target_config.values().any(|t| t.profiler == Some(true)) || self.profiler
1042 1043
    }

B
bjorn3 已提交
1044 1045 1046
    pub fn llvm_enabled(&self) -> bool {
        self.rust_codegen_backends.contains(&INTERNER.intern_str("llvm"))
    }
A
Alex Crichton 已提交
1047 1048 1049 1050 1051 1052 1053
}

fn set<T>(field: &mut T, val: Option<T>) {
    if let Some(v) = val {
        *field = v;
    }
}
1054 1055 1056 1057 1058 1059 1060

fn threads_from_config(v: u32) -> u32 {
    match v {
        0 => num_cpus::get() as u32,
        n => n,
    }
}