Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 124 additions & 26 deletions src/compiler/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2006,6 +2006,83 @@ fn test_can_trim_this() {
assert!(!can_trim_this(&rlib_file));
}

/// Extract the crate name and extension from a library file path in a crate
/// link directory.
///
/// Filenames are normally `lib<crate>-<metadata-hash>.<ext>`, but cdylib and
/// staticlib outputs can have no metadata hash (e.g. `libcrc_fast.rmeta` from a
/// crate with `crate-type = ["lib", "cdylib", "staticlib"]`). In that case
/// `rsplitn(2, '-')` yields a single element, so the whole filename is the
/// libname.
#[cfg(feature = "dist-client")]
fn crate_name_and_ext_from_lib_path(path: &Path) -> Option<(&str, &str)> {
let ext = path.extension()?.to_str()?;
// file_stem strips the extension, giving e.g. `libcrc_fast` from
// `libcrc_fast.rmeta`. rsplitn on the stem then correctly handles both
// `libfoo-HASH` (normal) and `libcrc_fast` (cdylib, no hash) cases.
let stem = path.file_stem()?.to_str()?;
let mut rev_name_split = stem.rsplitn(2, '-');
let _extra_filename_and_ext = rev_name_split.next();
let libname = rev_name_split.next().unwrap_or(stem);
if libname.starts_with(DLL_PREFIX) && ext == DLL_EXTENSION {
Some((&libname[DLL_PREFIX.len()..], ext))
} else if libname.starts_with(RLIB_PREFIX) && (ext == RLIB_EXTENSION || ext == RMETA_EXTENSION) {
Some((&libname[RLIB_PREFIX.len()..], ext))
} else {
None
}
}

#[test]
#[cfg(feature = "dist-client")]
fn test_crate_name_and_ext_from_lib_path() {
use std::path::Path;

// Normal rlib with metadata hash suffix
let p = Path::new("libfoo-abc123.rlib");
assert_eq!(crate_name_and_ext_from_lib_path(p), Some(("foo", "rlib")));

// Normal rmeta with metadata hash suffix
let p = Path::new("libfoo-abc123.rmeta");
assert_eq!(crate_name_and_ext_from_lib_path(p), Some(("foo", "rmeta")));

// cdylib output with no metadata hash suffix (crate-type includes cdylib)
let p = Path::new("libcrc_fast.rmeta");
assert_eq!(
crate_name_and_ext_from_lib_path(p),
Some(("crc_fast", "rmeta"))
);

// cdylib output with no metadata hash suffix, rlib variant
let p = Path::new("libcrc_fast.rlib");
assert_eq!(
crate_name_and_ext_from_lib_path(p),
Some(("crc_fast", "rlib"))
);

// Dynamic library with metadata hash
let p = Path::new("libfoo-abc123.so");
assert_eq!(crate_name_and_ext_from_lib_path(p), Some(("foo", "so")));

// Dynamic library without metadata hash (cdylib)
let p = Path::new("libcrc_fast.so");
assert_eq!(
crate_name_and_ext_from_lib_path(p),
Some(("crc_fast", "so"))
);

// Not a library file
let p = Path::new("foo.txt");
assert_eq!(crate_name_and_ext_from_lib_path(p), None);

// Path with multiple dashes in crate name
let p = Path::new("libxai_file_utils-abc123.rmeta");
assert_eq!(
crate_name_and_ext_from_lib_path(p),
Some(("xai_file_utils", "rmeta"))
);
}

#[cfg(feature = "dist-client")]
fn maybe_add_cargo_toml(input_path: &Path, verify: bool) -> Option<PathBuf> {
let lib_rs = PathBuf::new().join("src").join("lib.rs");
Expand Down Expand Up @@ -2158,32 +2235,11 @@ impl pkg::InputsPackager for RustInputsPackager {

{
// Take a look at the path and see if it's something we care about
let libname: &str = match path.file_name().and_then(|s| s.to_str()) {
Some(name) => {
let mut rev_name_split = name.rsplitn(2, '-');
let _extra_filename_and_ext = rev_name_split.next();
let libname = if let Some(libname) = rev_name_split.next() {
libname
} else {
continue;
};
assert!(rev_name_split.next().is_none());
libname
}
None => continue,
};
let (crate_name, ext): (&str, _) = match path.extension() {
Some(ext) if libname.starts_with(DLL_PREFIX) && ext == DLL_EXTENSION => {
(&libname[DLL_PREFIX.len()..], ext)
}
Some(ext) if libname.starts_with(RLIB_PREFIX) && ext == RLIB_EXTENSION => {
(&libname[RLIB_PREFIX.len()..], ext)
}
Some(ext) if libname.starts_with(RLIB_PREFIX) && ext == RMETA_EXTENSION => {
(&libname[RLIB_PREFIX.len()..], ext)
}
_ => continue,
};
let (crate_name, ext): (&str, &str) =
match crate_name_and_ext_from_lib_path(&path) {
Some(c) => c,
None => continue,
};
if let Some((_, ref dep_crate_names)) = rlib_dep_reader_and_names {
// We have a list of crate names we care about, see if this lib is a candidate
if !dep_crate_names.contains(crate_name) {
Expand Down Expand Up @@ -2676,6 +2732,15 @@ fn parse_rustc_z_ls(stdout: &str) -> Result<Vec<&str>> {
let libstring = line_splits
.next()
.context("No lib string on line from rustc -Z ls")?;
// The libstring may contain additional metadata after the crate name
// (e.g., "crc_fast hash 05bce6... host_hash None kind Unconditional public"
// when the crate has no -HASH suffix). Take only the first
// whitespace-delimited token so rsplitn(-) below operates on just
// the crate name, not the entire trailing metadata.
let libstring = libstring
.split_whitespace()
.next()
.context("No lib string on line from rustc -Z ls")?;
if num != dep_names.len() + 1 {
bail!(
"Unexpected numbering of {} in rustc -Z ls output",
Expand Down Expand Up @@ -3359,6 +3424,39 @@ proc_macro false
assert_eq!(res[2], "lucet_runtime_macros");
}

#[cfg(feature = "dist-client")]
#[test]
fn test_parse_rustc_z_ls_modern_no_hash_suffix() {
// Modern rustc (1.75+) prints extended metadata per dep line:
// N libname[-hash] hash HASH host_hash ... kind ... public
// Crates without a -HASH suffix (e.g. crc_fast, a proc-macro-style
// library built with a deterministic hash) appear as:
// N crc_fast hash 05bce6... host_hash None kind Unconditional public
// The parser must extract just "crc_fast", not the entire trailing
// metadata, so that RustInputsPackager can match it against rmeta
// files in crate_link_paths.
let output = "Crate info:
name xai_file_utils
hash 42fac6f0 stable_crate_id StableCrateId(12206970385906972588)
=External Dependencies=
1 std-453218b5e9634890 hash c76be37888b32288681053863554e618 host_hash None kind Unconditional public
2 core-5f5c0031517c19c4 hash 4e0d60221dfd8f9efa10e2a33c921b61 host_hash None kind Unconditional public
3 crc_fast hash 05bce60290e56777e3ae6d3ddcc01e6c host_hash None kind Unconditional public
4 crc-8c7d86e779319534 hash 49e85fac7c830ee1def0dd1674e740b0 host_hash None kind Unconditional public
5 aws_sdk_s3-c49a342c963fe6e9 hash 119191ef3d4a8059aee3abebc4c4b5bf host_hash None kind Unconditional public

";
let res = parse_rustc_z_ls(output);
assert!(res.is_ok());
let res = res.unwrap();
assert_eq!(res.len(), 5);
assert_eq!(res[0], "std");
assert_eq!(res[1], "core");
assert_eq!(res[2], "crc_fast");
assert_eq!(res[3], "crc");
assert_eq!(res[4], "aws_sdk_s3");
}

#[cfg(feature = "dist-client")]
#[test]
fn test_rlib_dep_reader_call() {
Expand Down