diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 9c2b7500a20f7..4a5d768961ff9 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -98,9 +98,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.73" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" [[package]] name = "cfg-if" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index e9675e204523f..ca0d1fa5bd0c0 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -36,7 +36,7 @@ test = false # Most of the time updating these dependencies requires modifications to the # bootstrap codebase(e.g., https://github.com/rust-lang/rust/issues/124565); # otherwise, some targets will fail. That's why these dependencies are explicitly pinned. -cc = "=1.0.73" +cc = "=1.0.97" cmake = "=0.1.48" build_helper = { path = "../tools/build_helper" } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index d4473e240399f..3af1a05caa829 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -20,7 +20,9 @@ use std::sync::OnceLock; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::{Config, TargetSelection}; use crate::utils::channel; -use crate::utils::helpers::{self, exe, get_clang_cl_resource_dir, output, t, up_to_date}; +use crate::utils::helpers::{ + self, exe, get_clang_cl_resource_dir, output, t, unhashed_basename, up_to_date, +}; use crate::{generate_smart_stamp_hash, CLang, GitRepo, Kind}; use build_helper::ci::CiEnv; @@ -506,7 +508,7 @@ impl Step for Llvm { cfg.define("LLVM_VERSION_SUFFIX", suffix); } - configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); + configure_cmake(builder, target, &mut cfg, true, ldflags, &[], &[]); configure_llvm(builder, target, &mut cfg); for (key, val) in &builder.config.llvm_build_config { @@ -596,6 +598,7 @@ fn configure_cmake( use_compiler_launcher: bool, mut ldflags: LdFlags, extra_compiler_flags: &[&str], + suppressed_compiler_flag_prefixes: &[&str], ) { // Do not print installation messages for up-to-date files. // LLVM and LLD builds can produce a lot of those and hit CI limits on log size. @@ -729,7 +732,17 @@ fn configure_cmake( } cfg.build_arg("-j").build_arg(builder.jobs().to_string()); - let mut cflags: OsString = builder.cflags(target, GitRepo::Llvm, CLang::C).join(" ").into(); + let mut cflags: OsString = builder + .cflags(target, GitRepo::Llvm, CLang::C) + .into_iter() + .filter(|flag| { + !suppressed_compiler_flag_prefixes + .iter() + .any(|suppressed_prefix| flag.starts_with(suppressed_prefix)) + }) + .collect::>() + .join(" ") + .into(); if let Some(ref s) = builder.config.llvm_cflags { cflags.push(" "); cflags.push(s); @@ -742,7 +755,17 @@ fn configure_cmake( cflags.push(&format!(" {flag}")); } cfg.define("CMAKE_C_FLAGS", cflags); - let mut cxxflags: OsString = builder.cflags(target, GitRepo::Llvm, CLang::Cxx).join(" ").into(); + let mut cxxflags: OsString = builder + .cflags(target, GitRepo::Llvm, CLang::Cxx) + .into_iter() + .filter(|flag| { + !suppressed_compiler_flag_prefixes + .iter() + .any(|suppressed_prefix| flag.starts_with(suppressed_prefix)) + }) + .collect::>() + .join(" ") + .into(); if let Some(ref s) = builder.config.llvm_cxxflags { cxxflags.push(" "); cxxflags.push(s); @@ -921,7 +944,7 @@ impl Step for Lld { ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'"); } - configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); + configure_cmake(builder, target, &mut cfg, true, ldflags, &[], &[]); configure_llvm(builder, target, &mut cfg); // Re-use the same flags as llvm to control the level of debug information @@ -1022,6 +1045,12 @@ impl Step for Sanitizers { let use_compiler_launcher = !self.target.contains("apple-darwin"); let extra_compiler_flags: &[&str] = if self.target.contains("apple") { &["-fembed-bitcode=off"] } else { &[] }; + // Since v1.0.86, the cc crate adds -mmacosx-version-min to the default + // flags on MacOS. A long-standing bug in the CMake rules for compiler-rt + // causes architecture detection to be skipped when this flag is present, + // and compilation fails. https://github.com/llvm/llvm-project/issues/88780 + let suppressed_compiler_flag_prefixes: &[&str] = + if self.target.contains("apple-darwin") { &["-mmacosx-version-min="] } else { &[] }; configure_cmake( builder, self.target, @@ -1029,6 +1058,7 @@ impl Step for Sanitizers { use_compiler_launcher, LdFlags::default(), extra_compiler_flags, + suppressed_compiler_flag_prefixes, ); t!(fs::create_dir_all(&out_dir)); @@ -1190,7 +1220,7 @@ impl Step for CrtBeginEnd { let crtbegin_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtbegin.c"); let crtend_src = builder.src.join("src/llvm-project/compiler-rt/lib/builtins/crtend.c"); - if up_to_date(&crtbegin_src, &out_dir.join("crtbegin.o")) + if up_to_date(&crtbegin_src, &out_dir.join("crtbeginS.o")) && up_to_date(&crtend_src, &out_dir.join("crtendS.o")) { return out_dir; @@ -1222,10 +1252,15 @@ impl Step for CrtBeginEnd { .define("CRT_HAS_INITFINI_ARRAY", None) .define("EH_USE_FRAME_REGISTRY", None); - cfg.compile("crt"); + let objs = cfg.compile_intermediates(); + assert_eq!(objs.len(), 2); + for obj in objs { + let base_name = unhashed_basename(&obj); + assert!(base_name == "crtbegin" || base_name == "crtend"); + t!(fs::copy(&obj, out_dir.join(format!("{}S.o", base_name)))); + t!(fs::rename(&obj, out_dir.join(format!("{}.o", base_name)))); + } - t!(fs::copy(out_dir.join("crtbegin.o"), out_dir.join("crtbeginS.o"))); - t!(fs::copy(out_dir.join("crtend.o"), out_dir.join("crtendS.o"))); out_dir } } @@ -1372,9 +1407,9 @@ impl Step for Libunwind { for entry in fs::read_dir(&out_dir).unwrap() { let file = entry.unwrap().path().canonicalize().unwrap(); if file.is_file() && file.extension() == Some(OsStr::new("o")) { - // file name starts with "Unwind-EHABI", "Unwind-seh" or "libunwind" - let file_name = file.file_name().unwrap().to_str().expect("UTF-8 file name"); - if cpp_sources.iter().any(|f| file_name.starts_with(&f[..f.len() - 4])) { + // Object file name without the hash prefix is "Unwind-EHABI", "Unwind-seh" or "libunwind". + let base_name = unhashed_basename(&file); + if cpp_sources.iter().any(|f| *base_name == f[..f.len() - 4]) { cc_cfg.object(&file); count += 1; } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index caec46366dde0..9898d495c023d 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -32,7 +32,7 @@ fn configure_with_args(cmd: &[String], host: &[&str], target: &[&str]) -> Config .join(&thread::current().name().unwrap_or("unknown").replace(":", "-")); t!(fs::create_dir_all(&dir)); config.out = dir; - config.build = TargetSelection::from_user("A"); + config.build = TargetSelection::from_user("A-A"); config.hosts = host.iter().map(|s| TargetSelection::from_user(s)).collect(); config.targets = target.iter().map(|s| TargetSelection::from_user(s)).collect(); config @@ -53,27 +53,27 @@ fn run_build(paths: &[PathBuf], config: Config) -> Cache { fn check_cli(paths: [&str; N]) { run_build( &paths.map(PathBuf::from), - configure_with_args(&paths.map(String::from), &["A"], &["A"]), + configure_with_args(&paths.map(String::from), &["A-A"], &["A-A"]), ); } macro_rules! std { ($host:ident => $target:ident, stage = $stage:literal) => { compile::Std::new( - Compiler { host: TargetSelection::from_user(stringify!($host)), stage: $stage }, - TargetSelection::from_user(stringify!($target)), + Compiler { host: TargetSelection::from_user(concat!(stringify!($host), "-", stringify!($host))), stage: $stage }, + TargetSelection::from_user(concat!(stringify!($target), "-", stringify!($target))), ) }; } macro_rules! doc_std { ($host:ident => $target:ident, stage = $stage:literal) => {{ - let config = configure("doc", &["A"], &["A"]); + let config = configure("doc", &["A-A"], &["A-A"]); let build = Build::new(config); let builder = Builder::new(&build); doc::Std::new( $stage, - TargetSelection::from_user(stringify!($target)), + TargetSelection::from_user(concat!(stringify!($target), "-", stringify!($target))), &builder, DocumentationFormat::Html, ) @@ -83,8 +83,8 @@ macro_rules! doc_std { macro_rules! rustc { ($host:ident => $target:ident, stage = $stage:literal) => { compile::Rustc::new( - Compiler { host: TargetSelection::from_user(stringify!($host)), stage: $stage }, - TargetSelection::from_user(stringify!($target)), + Compiler { host: TargetSelection::from_user(concat!(stringify!($host), "-", stringify!($host))), stage: $stage }, + TargetSelection::from_user(concat!(stringify!($target), "-", stringify!($target))), ) }; } @@ -117,7 +117,7 @@ fn test_intersection() { #[test] fn validate_path_remap() { - let build = Build::new(configure("test", &["A"], &["A"])); + let build = Build::new(configure("test", &["A-A"], &["A-A"])); PATH_REMAP .iter() @@ -130,7 +130,7 @@ fn validate_path_remap() { #[test] fn test_exclude() { - let mut config = configure("test", &["A"], &["A"]); + let mut config = configure("test", &["A-A"], &["A-A"]); config.skip = vec!["src/tools/tidy".into()]; let cache = run_build(&[], config); @@ -145,7 +145,7 @@ fn test_exclude() { fn test_exclude_kind() { let path = PathBuf::from("compiler/rustc_data_structures"); - let mut config = configure("test", &["A"], &["A"]); + let mut config = configure("test", &["A-A"], &["A-A"]); // Ensure our test is valid, and `test::Rustc` would be run without the exclude. assert!(run_build(&[], config.clone()).contains::()); // Ensure tests for rustc are not skipped. @@ -159,13 +159,13 @@ fn test_exclude_kind() { #[test] fn alias_and_path_for_library() { let mut cache = - run_build(&["library".into(), "core".into()], configure("build", &["A"], &["A"])); + run_build(&["library".into(), "core".into()], configure("build", &["A-A"], &["A-A"])); assert_eq!( first(cache.all::()), &[std!(A => A, stage = 0), std!(A => A, stage = 1)] ); - let mut cache = run_build(&["library".into(), "core".into()], configure("doc", &["A"], &["A"])); + let mut cache = run_build(&["library".into(), "core".into()], configure("doc", &["A-A"], &["A-A"])); assert_eq!(first(cache.all::()), &[doc_std!(A => A, stage = 0)]); } @@ -177,9 +177,9 @@ mod defaults { #[test] fn build_default() { - let mut cache = run_build(&[], configure("build", &["A"], &["A"])); + let mut cache = run_build(&[], configure("build", &["A-A"], &["A-A"])); - let a = TargetSelection::from_user("A"); + let a = TargetSelection::from_user("A-A"); assert_eq!( first(cache.all::()), &[std!(A => A, stage = 0), std!(A => A, stage = 1),] @@ -197,10 +197,10 @@ mod defaults { #[test] fn build_stage_0() { - let config = Config { stage: 0, ..configure("build", &["A"], &["A"]) }; + let config = Config { stage: 0, ..configure("build", &["A-A"], &["A-A"]) }; let mut cache = run_build(&[], config); - let a = TargetSelection::from_user("A"); + let a = TargetSelection::from_user("A-A"); assert_eq!(first(cache.all::()), &[std!(A => A, stage = 0)]); assert!(!cache.all::().is_empty()); assert_eq!( @@ -214,11 +214,11 @@ mod defaults { #[test] fn build_cross_compile() { - let config = Config { stage: 1, ..configure("build", &["A", "B"], &["A", "B"]) }; + let config = Config { stage: 1, ..configure("build", &["A-A", "B-B"], &["A-A", "B-B"]) }; let mut cache = run_build(&[], config); - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); + let a = TargetSelection::from_user("A-A"); + let b = TargetSelection::from_user("B-B"); // Ideally, this build wouldn't actually have `target: a` // rustdoc/rustcc/std here (the user only requested a host=B build, so @@ -257,11 +257,11 @@ mod defaults { #[test] fn doc_default() { - let mut config = configure("doc", &["A"], &["A"]); + let mut config = configure("doc", &["A-A"], &["A-A"]); config.compiler_docs = true; config.cmd = Subcommand::Doc { open: false, json: false }; let mut cache = run_build(&[], config); - let a = TargetSelection::from_user("A"); + let a = TargetSelection::from_user("A-A"); // error_index_generator uses stage 0 to share rustdoc artifacts with the // rustdoc tool. @@ -291,9 +291,9 @@ mod dist { #[test] fn dist_baseline() { - let mut cache = run_build(&[], configure(&["A"], &["A"])); + let mut cache = run_build(&[], configure(&["A-A"], &["A-A"])); - let a = TargetSelection::from_user("A"); + let a = TargetSelection::from_user("A-A"); assert_eq!(first(cache.all::()), &[dist::Docs { host: a },]); assert_eq!(first(cache.all::()), &[dist::Mingw { host: a },]); @@ -315,10 +315,10 @@ mod dist { #[test] fn dist_with_targets() { - let mut cache = run_build(&[], configure(&["A"], &["A", "B"])); + let mut cache = run_build(&[], configure(&["A-A"], &["A-A", "B-B"])); - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); + let a = TargetSelection::from_user("A-A"); + let b = TargetSelection::from_user("B-B"); assert_eq!( first(cache.all::()), @@ -344,10 +344,10 @@ mod dist { #[test] fn dist_with_hosts() { - let mut cache = run_build(&[], configure(&["A", "B"], &["A", "B"])); + let mut cache = run_build(&[], configure(&["A-A", "B-B"], &["A-A", "B-B"])); - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); + let a = TargetSelection::from_user("A-A"); + let b = TargetSelection::from_user("B-B"); assert_eq!( first(cache.all::()), @@ -386,8 +386,8 @@ mod dist { #[test] fn dist_only_cross_host() { - let b = TargetSelection::from_user("B"); - let mut config = configure(&["A", "B"], &["A", "B"]); + let b = TargetSelection::from_user("B-B"); + let mut config = configure(&["A-A", "B-B"], &["A-A", "B-B"]); config.docs = false; config.extended = true; config.hosts = vec![b]; @@ -405,11 +405,11 @@ mod dist { #[test] fn dist_with_targets_and_hosts() { - let mut cache = run_build(&[], configure(&["A", "B"], &["A", "B", "C"])); + let mut cache = run_build(&[], configure(&["A-A", "B-B"], &["A-A", "B-B", "C-C"])); - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); - let c = TargetSelection::from_user("C"); + let a = TargetSelection::from_user("A-A"); + let b = TargetSelection::from_user("B-B"); + let c = TargetSelection::from_user("C-C"); assert_eq!( first(cache.all::()), @@ -439,11 +439,11 @@ mod dist { #[test] fn dist_with_empty_host() { - let config = configure(&[], &["C"]); + let config = configure(&[], &["C-C"]); let mut cache = run_build(&[], config); - let a = TargetSelection::from_user("A"); - let c = TargetSelection::from_user("C"); + let a = TargetSelection::from_user("A-A"); + let c = TargetSelection::from_user("C-C"); assert_eq!(first(cache.all::()), &[dist::Docs { host: c },]); assert_eq!(first(cache.all::()), &[dist::Mingw { host: c },]); @@ -455,10 +455,10 @@ mod dist { #[test] fn dist_with_same_targets_and_hosts() { - let mut cache = run_build(&[], configure(&["A", "B"], &["A", "B"])); + let mut cache = run_build(&[], configure(&["A-A", "B-B"], &["A-A", "B-B"])); - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); + let a = TargetSelection::from_user("A-A"); + let b = TargetSelection::from_user("B-B"); assert_eq!( first(cache.all::()), @@ -506,7 +506,7 @@ mod dist { #[test] fn build_all() { - let build = Build::new(configure(&["A", "B"], &["A", "B", "C"])); + let build = Build::new(configure(&["A-A", "B-B"], &["A-A", "B-B", "C-C"])); let mut builder = Builder::new(&build); builder.run_step_descriptions( &Builder::get_step_descriptions(Kind::Build), @@ -539,29 +539,29 @@ mod dist { #[test] fn llvm_out_behaviour() { - let mut config = configure(&["A"], &["B"]); + let mut config = configure(&["A-A"], &["B-B"]); config.llvm_from_ci = true; let build = Build::new(config.clone()); - let target = TargetSelection::from_user("A"); + let target = TargetSelection::from_user("A-A"); assert!(build.llvm_out(target).ends_with("ci-llvm")); - let target = TargetSelection::from_user("B"); + let target = TargetSelection::from_user("B-B"); assert!(build.llvm_out(target).ends_with("llvm")); config.llvm_from_ci = false; let build = Build::new(config.clone()); - let target = TargetSelection::from_user("A"); + let target = TargetSelection::from_user("A-A"); assert!(build.llvm_out(target).ends_with("llvm")); } #[test] fn build_with_empty_host() { - let config = configure(&[], &["C"]); + let config = configure(&[], &["C-C"]); let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); - let a = TargetSelection::from_user("A"); + let a = TargetSelection::from_user("A-A"); assert_eq!( first(builder.cache.all::()), @@ -583,7 +583,7 @@ mod dist { #[test] fn test_with_no_doc_stage0() { - let mut config = configure(&["A"], &["A"]); + let mut config = configure(&["A-A"], &["A-A"]); config.stage = 0; config.paths = vec!["library/std".into()]; config.cmd = Subcommand::Test { @@ -605,7 +605,7 @@ mod dist { let build = Build::new(config); let mut builder = Builder::new(&build); - let host = TargetSelection::from_user("A"); + let host = TargetSelection::from_user("A-A"); builder.run_step_descriptions( &[StepDescription::from::(Kind::Test)], @@ -627,13 +627,13 @@ mod dist { #[test] fn doc_ci() { - let mut config = configure(&["A"], &["A"]); + let mut config = configure(&["A-A"], &["A-A"]); config.compiler_docs = true; config.cmd = Subcommand::Doc { open: false, json: false }; let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]); - let a = TargetSelection::from_user("A"); + let a = TargetSelection::from_user("A-A"); // error_index_generator uses stage 1 to share rustdoc artifacts with the // rustdoc tool. @@ -656,7 +656,7 @@ mod dist { #[test] fn test_docs() { // Behavior of `x.py test` doing various documentation tests. - let mut config = configure(&["A"], &["A"]); + let mut config = configure(&["A-A"], &["A-A"]); config.cmd = Subcommand::Test { test_args: vec![], rustc_args: vec![], @@ -678,7 +678,7 @@ mod dist { let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]); - let a = TargetSelection::from_user("A"); + let a = TargetSelection::from_user("A-A"); // error_index_generator uses stage 1 to share rustdoc artifacts with the // rustdoc tool. diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 0c069a540696f..493ad99cc705b 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -192,7 +192,7 @@ than building it. let target_str = target.to_string(); // Ignore fake targets that are only used for unit tests in bootstrap. - if !["A", "B", "C"].contains(&target_str.as_str()) { + if !["A-A", "B-B", "C-C"].contains(&target_str.as_str()) { let mut has_target = false; let supported_target_list = diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index c599709a32279..47b03d411cb0d 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1723,7 +1723,7 @@ impl Build { return; } let _ = fs::remove_file(dst); - let metadata = t!(src.symlink_metadata()); + let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display())); let mut src = src.to_path_buf(); if metadata.file_type().is_symlink() { if dereference_symlinks { diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 928c9aa4dff44..0d2ff4f951b61 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -296,6 +296,15 @@ pub fn up_to_date(src: &Path, dst: &Path) -> bool { } } +/// Returns the filename without the hash prefix added by the cc crate. +/// +/// Since v1.0.78 of the cc crate, object files are prefixed with a 16-character hash +/// to avoid filename collisions. +pub fn unhashed_basename(obj: &Path) -> &str { + let basename = obj.file_stem().unwrap().to_str().expect("UTF-8 file name"); + basename.split_once('-').unwrap().1 +} + fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool { t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| { let meta = t!(e.metadata());