diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 818f3d6f08d26..77137bae3cbd8 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -475,6 +475,7 @@ impl<'a> TestRunner<'a> { cmd.arg("-Zunstable-options"); cmd.arg("--check-cfg=cfg(no_unstable_features)"); cmd.arg("--check-cfg=cfg(jit)"); + cmd.arg("--emit=metadata,link"); cmd.args(args); cmd } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index b29f71bfb9553..5268185d4ee7e 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -316,8 +316,11 @@ fn link_rlib<'a>( let trailing_metadata = match flavor { RlibFlavor::Normal => { - let (metadata, metadata_position) = - create_wrapper_file(sess, b".rmeta".to_vec(), codegen_results.metadata.raw_data()); + let (metadata, metadata_position) = create_wrapper_file( + sess, + b".rmeta".to_vec(), + codegen_results.metadata.maybe_reference(), + ); let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME); match metadata_position { MetadataPosition::First => { diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index b683e1b45a8d1..c6cd430103bf8 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -528,8 +528,8 @@ pub fn create_compressed_metadata_file( symbol_name: &str, ) -> Vec { let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec(); - packed_metadata.write_all(&(metadata.raw_data().len() as u64).to_le_bytes()).unwrap(); - packed_metadata.extend(metadata.raw_data()); + packed_metadata.write_all(&(metadata.maybe_reference().len() as u64).to_le_bytes()).unwrap(); + packed_metadata.extend(metadata.maybe_reference()); let Some(mut file) = create_object_file(sess) else { return packed_metadata.to_vec(); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 60f11b1bdd490..d5eb22029ea74 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -677,6 +677,7 @@ fn list_metadata(early_dcx: &EarlyDiagCtxt, sess: &Session, metadata_loader: &dy metadata_loader, &mut v, &sess.opts.unstable_opts.ls, + sess.cfg_version, ) .unwrap(); safe_println!("{}", String::from_utf8(v).unwrap()); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index bfc4fc07d4cc7..2c4ac7cb7c925 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -702,6 +702,7 @@ fn test_unstable_options_tracking_hash() { untracked!(shell_argfiles, true); untracked!(span_debug, true); untracked!(span_free_formats, true); + untracked!(split_metadata, true); untracked!(temps_dir, Some(String::from("abc"))); untracked!(threads, 99); untracked!(time_llvm_passes, true); diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index a0abda53eb3e7..52b35aafe691c 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -50,7 +50,8 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new(""))) .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err })); let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); - let metadata_filename = metadata_tmpdir.as_ref().join(METADATA_FILENAME); + let metadata_filename = metadata_tmpdir.as_ref().join("full.rmeta"); + let metadata_reference_filename = metadata_tmpdir.as_ref().join("ref.rmeta"); // Always create a file at `metadata_filename`, even if we have nothing to write to it. // This simplifies the creation of the output `out_filename` when requested. @@ -60,9 +61,12 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { std::fs::File::create(&metadata_filename).unwrap_or_else(|err| { tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_filename, err }); }); + std::fs::File::create(&metadata_reference_filename).unwrap_or_else(|err| { + tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_filename, err }); + }); } MetadataKind::Uncompressed | MetadataKind::Compressed => { - encode_metadata(tcx, &metadata_filename); + encode_metadata(tcx, &metadata_filename, &metadata_reference_filename) } }; @@ -100,9 +104,10 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // Load metadata back to memory: codegen may need to include it in object files. let metadata = - EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| { - tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err }); - }); + EncodedMetadata::from_path(metadata_filename, metadata_reference_filename, metadata_tmpdir) + .unwrap_or_else(|err| { + tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err }); + }); let need_metadata_module = metadata_kind == MetadataKind::Compressed; diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 1ab965e28769f..cf0c54ee2d773 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -243,7 +243,6 @@ use std::{cmp, fmt}; pub(crate) struct CrateLocator<'a> { // Immutable per-session configuration. only_needs_metadata: bool, - sysroot: &'a Path, metadata_loader: &'a dyn MetadataLoader, cfg_version: &'static str, @@ -319,7 +318,6 @@ impl<'a> CrateLocator<'a> { CrateLocator { only_needs_metadata, - sysroot: &sess.sysroot, metadata_loader, cfg_version: sess.cfg_version, crate_name, @@ -569,31 +567,47 @@ impl<'a> CrateLocator<'a> { debug!("skipping empty file"); continue; } - let (hash, metadata) = - match get_metadata_section(self.target, flavor, &lib, self.metadata_loader) { - Ok(blob) => { - if let Some(h) = self.crate_matches(&blob, &lib) { - (h, blob) - } else { - info!("metadata mismatch"); - continue; - } - } - Err(MetadataError::LoadFailure(err)) => { - info!("no metadata found: {}", err); - // The file was present and created by the same compiler version, but we - // couldn't load it for some reason. Give a hard error instead of silently - // ignoring it, but only if we would have given an error anyway. - self.crate_rejections - .via_invalid - .push(CrateMismatch { path: lib, got: err }); - continue; - } - Err(err @ MetadataError::NotPresent(_)) => { - info!("no metadata found: {}", err); + let (hash, metadata) = match get_metadata_section( + self.target, + flavor, + &lib, + self.metadata_loader, + self.cfg_version, + ) { + Ok(blob) => { + if let Some(h) = self.crate_matches(&blob, &lib) { + (h, blob) + } else { + info!("metadata mismatch"); continue; } - }; + } + Err(MetadataError::VersionMismatch { expected_version, found_version }) => { + // The file was present and created by the same compiler version, but we + // couldn't load it for some reason. Give a hard error instead of silently + // ignoring it, but only if we would have given an error anyway. + info!( + "Rejecting via version: expected {} got {}", + expected_version, found_version + ); + self.crate_rejections + .via_version + .push(CrateMismatch { path: lib, got: found_version }); + continue; + } + Err(MetadataError::LoadFailure(err)) => { + info!("no metadata found: {}", err); + // The file was present and created by the same compiler version, but we + // couldn't load it for some reason. Give a hard error instead of silently + // ignoring it, but only if we would have given an error anyway. + self.crate_rejections.via_invalid.push(CrateMismatch { path: lib, got: err }); + continue; + } + Err(err @ MetadataError::NotPresent(_)) => { + info!("no metadata found: {}", err); + continue; + } + }; // If we see multiple hashes, emit an error about duplicate candidates. if slot.as_ref().is_some_and(|s| s.0 != hash) { if let Some(candidates) = err_data { @@ -610,33 +624,11 @@ impl<'a> CrateLocator<'a> { continue; } - // Ok so at this point we've determined that `(lib, kind)` above is - // a candidate crate to load, and that `slot` is either none (this - // is the first crate of its kind) or if some the previous path has - // the exact same hash (e.g., it's the exact same crate). - // - // In principle these two candidate crates are exactly the same so - // we can choose either of them to link. As a stupidly gross hack, - // however, we favor crate in the sysroot. - // - // You can find more info in rust-lang/rust#39518 and various linked - // issues, but the general gist is that during testing libstd the - // compilers has two candidates to choose from: one in the sysroot - // and one in the deps folder. These two crates are the exact same - // crate but if the compiler chooses the one in the deps folder - // it'll cause spurious errors on Windows. - // - // As a result, we favor the sysroot crate here. Note that the - // candidates are all canonicalized, so we canonicalize the sysroot - // as well. - if let Some((prev, _)) = &ret { - let sysroot = self.sysroot; - let sysroot = try_canonicalize(sysroot).unwrap_or_else(|_| sysroot.to_path_buf()); - if prev.starts_with(&sysroot) { - continue; - } + if !metadata.get_header().is_reference { + // FIXME nicer error when only an rlib or dylib with is_reference is found + // and no .rmeta? + *slot = Some((hash, metadata, lib.clone())); } - *slot = Some((hash, metadata, lib.clone())); ret = Some((lib, kind)); } @@ -648,16 +640,6 @@ impl<'a> CrateLocator<'a> { } fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option { - let rustc_version = rustc_version(self.cfg_version); - let found_version = metadata.get_rustc_version(); - if found_version != rustc_version { - info!("Rejecting via version: expected {} got {}", rustc_version, found_version); - self.crate_rejections - .via_version - .push(CrateMismatch { path: libpath.to_path_buf(), got: found_version }); - return None; - } - let header = metadata.get_header(); if header.is_proc_macro_crate != self.is_proc_macro { info!( @@ -736,10 +718,12 @@ impl<'a> CrateLocator<'a> { let loc_canon = loc.canonicalized().clone(); let loc = loc.original(); if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { + rmetas.insert(loc_canon.with_extension("rmeta"), PathKind::ExternFlag); rlibs.insert(loc_canon, PathKind::ExternFlag); } else if loc.file_name().unwrap().to_str().unwrap().ends_with(".rmeta") { rmetas.insert(loc_canon, PathKind::ExternFlag); } else { + rmetas.insert(loc_canon.with_extension("rmeta"), PathKind::ExternFlag); dylibs.insert(loc_canon, PathKind::ExternFlag); } } else { @@ -770,6 +754,7 @@ fn get_metadata_section<'p>( flavor: CrateFlavor, filename: &'p Path, loader: &dyn MetadataLoader, + cfg_version: &'static str, ) -> Result> { if !filename.exists() { return Err(MetadataError::NotPresent(filename)); @@ -847,13 +832,12 @@ fn get_metadata_section<'p>( } }; let blob = MetadataBlob(raw_bytes); - if blob.is_compatible() { - Ok(blob) - } else { - Err(MetadataError::LoadFailure(format!( - "invalid metadata version found: {}", - filename.display() - ))) + match blob.check_compatibility(cfg_version) { + Ok(()) => Ok(blob), + Err(version) => Err(MetadataError::VersionMismatch { + expected_version: cfg_version, + found_version: version, + }), } } @@ -864,9 +848,10 @@ pub fn list_file_metadata( metadata_loader: &dyn MetadataLoader, out: &mut dyn Write, ls_kinds: &[String], + cfg_version: &'static str, ) -> IoResult<()> { let flavor = get_flavor_from_path(path); - match get_metadata_section(target, flavor, path, metadata_loader) { + match get_metadata_section(target, flavor, path, metadata_loader, cfg_version) { Ok(metadata) => metadata.list_crate_metadata(out, ls_kinds), Err(msg) => write!(out, "{msg}\n"), } @@ -932,6 +917,8 @@ enum MetadataError<'a> { NotPresent(&'a Path), /// The file was present and invalid. LoadFailure(String), + /// The file was present, but compiled with a different rustc version. + VersionMismatch { expected_version: &'static str, found_version: String }, } impl fmt::Display for MetadataError<'_> { @@ -941,6 +928,12 @@ impl fmt::Display for MetadataError<'_> { f.write_str(&format!("no such file: '{}'", filename.display())) } MetadataError::LoadFailure(msg) => f.write_str(msg), + MetadataError::VersionMismatch { expected_version, found_version } => { + f.write_str(&format!( + "rustc version mismatch. expected {}, found {}", + expected_version, found_version, + )) + } } } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 11cb1bb6d9e6e..86f9cc0bda511 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -680,13 +680,23 @@ impl<'a, 'tcx, I: Idx, T> Decodable> for LazyTable implement_ty_decoder!(DecodeContext<'a, 'tcx>); impl MetadataBlob { - pub(crate) fn is_compatible(&self) -> bool { - self.blob().starts_with(METADATA_HEADER) - } + pub(crate) fn check_compatibility(&self, cfg_version: &'static str) -> Result<(), String> { + if !self.blob().starts_with(METADATA_HEADER) { + if self.blob().starts_with(b"rust") { + return Err("".to_string()); + } + return Err("".to_string()); + } - pub(crate) fn get_rustc_version(&self) -> String { - LazyValue::::from_position(NonZeroUsize::new(METADATA_HEADER.len() + 8).unwrap()) - .decode(self) + let found_version = LazyValue::::from_position( + NonZeroUsize::new(METADATA_HEADER.len() + 8).unwrap(), + ) + .decode(self); + if rustc_version(cfg_version) != found_version { + return Err(found_version); + } + + Ok(()) } fn root_pos(&self) -> NonZeroUsize { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 79aa9a547f7c6..69d58b966e93e 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1,4 +1,4 @@ -use crate::errors::{FailCreateFileEncoder, FailWriteFile}; +use crate::errors::{FailCreateFileEncoder, FailWriteFile, FailedCreateFile}; use crate::rmeta::*; use rustc_ast::Attribute; @@ -692,6 +692,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { triple: tcx.sess.opts.target_triple.clone(), hash: tcx.crate_hash(LOCAL_CRATE), is_proc_macro_crate: proc_macro_data.is_some(), + is_reference: false, }, extra_filename: tcx.sess.opts.cg.extra_filename.clone(), stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(), @@ -2133,8 +2134,10 @@ fn prefetch_mir(tcx: TyCtxt<'_>) { // generated regardless of trailing bytes that end up in it. pub struct EncodedMetadata { - // The declaration order matters because `mmap` should be dropped before `_temp_dir`. - mmap: Option, + // The declaration order matters because `full_mmap` should be dropped + // before `_temp_dir`. + full_mmap: Option, + reference: Option>, // We need to carry MaybeTempDir to avoid deleting the temporary // directory while accessing the Mmap. _temp_dir: Option, @@ -2142,33 +2145,50 @@ pub struct EncodedMetadata { impl EncodedMetadata { #[inline] - pub fn from_path(path: PathBuf, temp_dir: Option) -> std::io::Result { + pub fn from_path( + path: PathBuf, + reference_path: PathBuf, + temp_dir: Option, + ) -> std::io::Result { let file = std::fs::File::open(&path)?; let file_metadata = file.metadata()?; if file_metadata.len() == 0 { - return Ok(Self { mmap: None, _temp_dir: None }); + return Ok(Self { full_mmap: None, reference: None, _temp_dir: None }); } - let mmap = unsafe { Some(Mmap::map(file)?) }; - Ok(Self { mmap, _temp_dir: temp_dir }) + let full_mmap = unsafe { Some(Mmap::map(file)?) }; + + let reference = std::fs::read(reference_path)?; + let reference = if reference.is_empty() { None } else { Some(reference) }; + + Ok(Self { full_mmap, reference, _temp_dir: temp_dir }) + } + + #[inline] + pub fn full(&self) -> &[u8] { + &self.full_mmap.as_deref().unwrap_or_default() } #[inline] - pub fn raw_data(&self) -> &[u8] { - self.mmap.as_deref().unwrap_or_default() + pub fn maybe_reference(&self) -> &[u8] { + self.reference.as_deref().unwrap_or(self.full()) } } impl Encodable for EncodedMetadata { fn encode(&self, s: &mut S) { - let slice = self.raw_data(); + self.reference.encode(s); + + let slice = self.full(); slice.encode(s) } } impl Decodable for EncodedMetadata { fn decode(d: &mut D) -> Self { + let reference = >>::decode(d); + let len = d.read_usize(); - let mmap = if len > 0 { + let full_mmap = if len > 0 { let mut mmap = MmapMut::map_anon(len).unwrap(); for _ in 0..len { (&mut mmap[..]).write(&[d.read_u8()]).unwrap(); @@ -2179,11 +2199,11 @@ impl Decodable for EncodedMetadata { None }; - Self { mmap, _temp_dir: None } + Self { full_mmap, reference, _temp_dir: None } } } -pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { +pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: &Path) { let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata"); // Since encoding metadata is not in a query, and nothing is cached, @@ -2197,6 +2217,48 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE)); } + with_encode_metadata_header(tcx, path, |ecx| { + // Encode all the entries and extra information in the crate, + // culminating in the `CrateRoot` which points to all of it. + let root = ecx.encode_crate_root(); + + // Flush buffer to ensure backing file has the correct size. + ecx.opaque.flush(); + // Record metadata size for self-profiling + tcx.prof.artifact_size( + "crate_metadata", + "crate_metadata", + ecx.opaque.file().metadata().unwrap().len(), + ); + + root.position.get() + }); + + if tcx.sess.opts.unstable_opts.split_metadata + && !tcx.crate_types().contains(&CrateType::ProcMacro) + { + with_encode_metadata_header(tcx, ref_path, |ecx| { + let header: LazyValue = ecx.lazy(CrateHeader { + name: tcx.crate_name(LOCAL_CRATE), + triple: tcx.sess.opts.target_triple.clone(), + hash: tcx.crate_hash(LOCAL_CRATE), + is_proc_macro_crate: false, + is_reference: true, + }); + header.position.get() + }); + } else { + std::fs::File::create(&ref_path).unwrap_or_else(|err| { + tcx.dcx().emit_fatal(FailedCreateFile { filename: &ref_path, err }); + }); + } +} + +pub fn with_encode_metadata_header( + tcx: TyCtxt<'_>, + path: &Path, + f: impl FnOnce(&mut EncodeContext<'_, '_>) -> usize, +) { let mut encoder = opaque::FileEncoder::new(path) .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailCreateFileEncoder { err })); encoder.emit_raw_bytes(METADATA_HEADER); @@ -2231,9 +2293,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { // Encode the rustc version string in a predictable location. rustc_version(tcx.sess.cfg_version).encode(&mut ecx); - // Encode all the entries and extra information in the crate, - // culminating in the `CrateRoot` which points to all of it. - let root = ecx.encode_crate_root(); + let root_position = f(&mut ecx); // Make sure we report any errors from writing to the file. // If we forget this, compilation can succeed with an incomplete rmeta file, @@ -2243,12 +2303,9 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { } let file = ecx.opaque.file(); - if let Err(err) = encode_root_position(file, root.position.get()) { + if let Err(err) = encode_root_position(file, root_position) { tcx.dcx().emit_fatal(FailWriteFile { path: ecx.opaque.path(), err }); } - - // Record metadata size for self-profiling - tcx.prof.artifact_size("crate_metadata", "crate_metadata", file.metadata().unwrap().len()); } fn encode_root_position(mut file: &File, pos: usize) -> Result<(), std::io::Error> { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2f77588269345..ba0d4435a4f2f 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -222,6 +222,10 @@ pub(crate) struct CrateHeader { /// This is separate from [`ProcMacroData`] to avoid having to update [`METADATA_VERSION`] every /// time ProcMacroData changes. pub(crate) is_proc_macro_crate: bool, + /// Whether this header is a reference to a separate rmeta file. + /// + /// This is used inside rlibs and dylibs when using `-Zsplit-metadata`. + pub(crate) is_reference: bool, } /// Serialized `.rmeta` data for a crate. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index ea93ac5841fe9..465acc2049c15 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1892,6 +1892,8 @@ written to standard error output)"), by the linker"), split_lto_unit: Option = (None, parse_opt_bool, [TRACKED], "enable LTO unit splitting (default: no)"), + split_metadata: bool = (false, parse_bool, [TRACKED], + "split metadata out of libraries into .rmeta files"), src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), #[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")] diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 38c55b2034496..661ad63d1f1bc 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -113,6 +113,12 @@ fn main() { } } + if orig_args.iter().any(|arg| arg == "-Zsplit-metadata") { + if orig_args.windows(2).any(|args| args[0] == "--crate-type" && args[1] == "dylib") { + cmd.arg("--emit").arg("metadata"); + } + } + // Print backtrace in case of ICE if env::var("RUSTC_BACKTRACE_ON_ICE").is_ok() && env::var("RUST_BACKTRACE").is_err() { cmd.env("RUST_BACKTRACE", "1"); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 27b1d311864f3..f0ebd7e29d1ee 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1894,14 +1894,12 @@ pub fn run_cargo( || filename.ends_with(".a") || is_debug_info(&filename) || is_dylib(&filename) + || filename.ends_with(".rmeta") { - // Always keep native libraries, rust dylibs and debuginfo + // Always keep native libraries, rust dylibs, debuginfo and crate metadata keep = true; } - if is_check && filename.ends_with(".rmeta") { - // During check builds we need to keep crate metadata - keep = true; - } else if rlib_only_metadata { + if !is_check && rlib_only_metadata { if filename.contains("jemalloc_sys") || filename.contains("rustc_smir") || filename.contains("stable_mir") @@ -1913,7 +1911,6 @@ pub fn run_cargo( // Distribute the rest of the rustc crates as rmeta files only to reduce // the tarball sizes by about 50%. The object files are linked into // librustc_driver.so, so it is still possible to link against them. - keep |= filename.ends_with(".rmeta"); } } else { // In all other cases keep all rlibs @@ -1959,7 +1956,12 @@ pub fn run_cargo( let file_stem = parts.next().unwrap().to_owned(); let extension = parts.next().unwrap().to_owned(); - toplevel.push((file_stem, extension, expected_len)); + if extension == "so" || extension == "dylib" { + // FIXME workaround for the fact that cargo doesn't understand `-Zsplit-metadata` + toplevel.push((file_stem.clone(), "rmeta".to_owned(), None)); + } + + toplevel.push((file_stem, extension, Some(expected_len))); } }); @@ -1980,7 +1982,7 @@ pub fn run_cargo( .collect::>(); for (prefix, extension, expected_len) in toplevel { let candidates = contents.iter().filter(|&&(_, ref filename, ref meta)| { - meta.len() == expected_len + expected_len.map_or(true, |expected_len| meta.len() == expected_len) && filename .strip_prefix(&prefix[..]) .map(|s| s.starts_with('-') && s.ends_with(&extension[..])) @@ -1991,6 +1993,7 @@ pub fn run_cargo( }); let path_to_add = match max { Some(triple) => triple.0.to_str().unwrap(), + None if extension == "rmeta" => continue, // cfg(not(bootstrap)) remove this once -Zsplit-metadata is passed for all stages None => panic!("no output generated for {prefix:?} {extension:?}"), }; if is_dylib(path_to_add) { diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 83485abfa5618..16a32d41142a8 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1484,6 +1484,12 @@ impl<'a> Builder<'a> { hostflags.arg("-Zunstable-options"); hostflags.arg("--check-cfg=cfg(bootstrap)"); + // cfg(bootstrap) unconditionally pass this once the bootstrap compiler understands it + if stage != 0 { + // FIXME remove once cargo enables this by default + rustflags.arg("-Zsplit-metadata"); + } + // 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. diff --git a/tests/ui/duplicate_entry_error.rs b/tests/ui/duplicate_entry_error.rs index 776ecedea7e7e..be86fc9af8e8a 100644 --- a/tests/ui/duplicate_entry_error.rs +++ b/tests/ui/duplicate_entry_error.rs @@ -1,4 +1,4 @@ -// normalize-stderr-test "loaded from .*libstd-.*.rlib" -> "loaded from SYSROOT/libstd-*.rlib" +// normalize-stderr-test "loaded from .*libstd-.*.rmeta" -> "loaded from SYSROOT/libstd-*.rmeta" // note-pattern: first defined in crate `std`. // Test for issue #31788 and E0152 diff --git a/tests/ui/duplicate_entry_error.stderr b/tests/ui/duplicate_entry_error.stderr index 3b5998df353c4..d310b46dd2432 100644 --- a/tests/ui/duplicate_entry_error.stderr +++ b/tests/ui/duplicate_entry_error.stderr @@ -8,7 +8,7 @@ LL | | } | |_^ | = note: the lang item is first defined in crate `std` (which `duplicate_entry_error` depends on) - = note: first definition in `std` loaded from SYSROOT/libstd-*.rlib + = note: first definition in `std` loaded from SYSROOT/libstd-*.rmeta = note: second definition in the local crate (`duplicate_entry_error`) error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0152.rs b/tests/ui/error-codes/E0152.rs index ee8e5e6dffee3..c3958d9d73aee 100644 --- a/tests/ui/error-codes/E0152.rs +++ b/tests/ui/error-codes/E0152.rs @@ -1,4 +1,4 @@ -// normalize-stderr-test "loaded from .*liballoc-.*.rlib" -> "loaded from SYSROOT/liballoc-*.rlib" +// normalize-stderr-test "loaded from .*liballoc-.*.rmeta" -> "loaded from SYSROOT/liballoc-*.rmeta" #![feature(lang_items)] #[lang = "owned_box"] diff --git a/tests/ui/error-codes/E0152.stderr b/tests/ui/error-codes/E0152.stderr index dbea7e6d27fbe..73df5803e839a 100644 --- a/tests/ui/error-codes/E0152.stderr +++ b/tests/ui/error-codes/E0152.stderr @@ -5,7 +5,7 @@ LL | struct Foo(T); | ^^^^^^^^^^^^^^^^^ | = note: the lang item is first defined in crate `alloc` (which `std` depends on) - = note: first definition in `alloc` loaded from SYSROOT/liballoc-*.rlib + = note: first definition in `alloc` loaded from SYSROOT/liballoc-*.rmeta = note: second definition in the local crate (`E0152`) error: aborting due to 1 previous error diff --git a/tests/ui/lang-items/duplicate.rs b/tests/ui/lang-items/duplicate.rs index f88d23544145a..ba4e1dd72db74 100644 --- a/tests/ui/lang-items/duplicate.rs +++ b/tests/ui/lang-items/duplicate.rs @@ -1,4 +1,4 @@ -// normalize-stderr-test "loaded from .*libcore-.*.rlib" -> "loaded from SYSROOT/libcore-*.rlib" +// normalize-stderr-test "loaded from .*libcore-.*.rmeta" -> "loaded from SYSROOT/libcore-*.rmeta" #![feature(lang_items)] #[lang = "sized"] diff --git a/tests/ui/lang-items/duplicate.stderr b/tests/ui/lang-items/duplicate.stderr index aaa8f5e605afa..5639bcc838d81 100644 --- a/tests/ui/lang-items/duplicate.stderr +++ b/tests/ui/lang-items/duplicate.stderr @@ -5,7 +5,7 @@ LL | trait Sized {} | ^^^^^^^^^^^^^^ | = note: the lang item is first defined in crate `core` (which `std` depends on) - = note: first definition in `core` loaded from SYSROOT/libcore-*.rlib + = note: first definition in `core` loaded from SYSROOT/libcore-*.rmeta = note: second definition in the local crate (`duplicate`) error: aborting due to 1 previous error diff --git a/tests/ui/panic-handler/panic-handler-std.rs b/tests/ui/panic-handler/panic-handler-std.rs index 6183c886cfac7..60e26a2f8cbe3 100644 --- a/tests/ui/panic-handler/panic-handler-std.rs +++ b/tests/ui/panic-handler/panic-handler-std.rs @@ -1,4 +1,4 @@ -// normalize-stderr-test "loaded from .*libstd-.*.rlib" -> "loaded from SYSROOT/libstd-*.rlib" +// normalize-stderr-test "loaded from .*libstd-.*.rmeta" -> "loaded from SYSROOT/libstd-*.rmeta" // error-pattern: found duplicate lang item `panic_impl` diff --git a/tests/ui/panic-handler/panic-handler-std.stderr b/tests/ui/panic-handler/panic-handler-std.stderr index 48c216ce27ec4..3c44267822337 100644 --- a/tests/ui/panic-handler/panic-handler-std.stderr +++ b/tests/ui/panic-handler/panic-handler-std.stderr @@ -7,7 +7,7 @@ LL | | } | |_^ | = note: the lang item is first defined in crate `std` (which `panic_handler_std` depends on) - = note: first definition in `std` loaded from SYSROOT/libstd-*.rlib + = note: first definition in `std` loaded from SYSROOT/libstd-*.rmeta = note: second definition in the local crate (`panic_handler_std`) error: aborting due to 1 previous error