diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 305ae23669bbf..31e293518567a 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -11,7 +11,7 @@ use rustc_session::config::{ }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; -use rustc_session::utils::NativeLibKind; +use rustc_session::utils::{CanonicalizedPath, NativeLibKind}; use rustc_session::{build_session, getopts, DiagnosticOutput, Session}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::symbol::sym; @@ -20,7 +20,7 @@ use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy} use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TlsModel}; use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; type CfgSpecs = FxHashSet<(String, Option)>; @@ -50,7 +50,8 @@ where S: Into, I: IntoIterator, { - let locations: BTreeSet<_> = locations.into_iter().map(|s| s.into()).collect(); + let locations: BTreeSet = + locations.into_iter().map(|s| CanonicalizedPath::new(Path::new(&s.into()))).collect(); ExternEntry { location: ExternLocation::ExactPaths(locations), diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index f203094fc73df..e3fbd1a2b29ea 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -28,7 +28,7 @@ use rustc_target::spec::{PanicStrategy, TargetTriple}; use proc_macro::bridge::client::ProcMacro; use std::path::Path; -use std::{cmp, env, fs}; +use std::{cmp, env}; use tracing::{debug, info}; #[derive(Clone)] @@ -252,10 +252,10 @@ impl<'a> CrateLoader<'a> { // Only use `--extern crate_name=path` here, not `--extern crate_name`. if let Some(mut files) = entry.files() { if files.any(|l| { - let l = fs::canonicalize(l).unwrap_or(l.clone().into()); - source.dylib.as_ref().map(|(p, _)| p) == Some(&l) - || source.rlib.as_ref().map(|(p, _)| p) == Some(&l) - || source.rmeta.as_ref().map(|(p, _)| p) == Some(&l) + let l = l.canonicalized(); + source.dylib.as_ref().map(|(p, _)| p) == Some(l) + || source.rlib.as_ref().map(|(p, _)| p) == Some(l) + || source.rmeta.as_ref().map(|(p, _)| p) == Some(l) }) { ret = Some(cnum); } diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index c4c025de8b3c4..b66c6cffb1b26 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -224,6 +224,7 @@ use rustc_middle::middle::cstore::{CrateSource, MetadataLoader}; use rustc_session::config::{self, CrateType}; use rustc_session::filesearch::{FileDoesntMatch, FileMatches, FileSearch}; use rustc_session::search_paths::PathKind; +use rustc_session::utils::CanonicalizedPath; use rustc_session::{CrateDisambiguator, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; @@ -244,7 +245,7 @@ crate struct CrateLocator<'a> { // Immutable per-search configuration. crate_name: Symbol, - exact_paths: Vec, + exact_paths: Vec, pub hash: Option, pub host_hash: Option, extra_filename: Option<&'a str>, @@ -315,7 +316,7 @@ impl<'a> CrateLocator<'a> { .into_iter() .filter_map(|entry| entry.files()) .flatten() - .map(PathBuf::from) + .cloned() .collect() } else { // SVH being specified means this is a transitive dependency, @@ -664,13 +665,19 @@ impl<'a> CrateLocator<'a> { let mut rmetas = FxHashMap::default(); let mut dylibs = FxHashMap::default(); for loc in &self.exact_paths { - if !loc.exists() { - return Err(CrateError::ExternLocationNotExist(self.crate_name, loc.clone())); + if !loc.canonicalized().exists() { + return Err(CrateError::ExternLocationNotExist( + self.crate_name, + loc.original().clone(), + )); } - let file = match loc.file_name().and_then(|s| s.to_str()) { + let file = match loc.original().file_name().and_then(|s| s.to_str()) { Some(file) => file, None => { - return Err(CrateError::ExternLocationNotFile(self.crate_name, loc.clone())); + return Err(CrateError::ExternLocationNotFile( + self.crate_name, + loc.original().clone(), + )); } }; @@ -685,7 +692,8 @@ impl<'a> CrateLocator<'a> { // e.g. symbolic links. If we canonicalize too early, we resolve // the symlink, the file type is lost and we might treat rlibs and // rmetas as dylibs. - let loc_canon = fs::canonicalize(&loc).unwrap_or_else(|_| loc.clone()); + let loc_canon = loc.canonicalized().clone(); + let loc = loc.original(); if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { rlibs.insert(loc_canon, PathKind::ExternFlag); } else if loc.file_name().unwrap().to_str().unwrap().ends_with(".rmeta") { @@ -695,7 +703,7 @@ impl<'a> CrateLocator<'a> { } } else { self.rejected_via_filename - .push(CrateMismatch { path: loc.clone(), got: String::new() }); + .push(CrateMismatch { path: loc.original().clone(), got: String::new() }); } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f9e40919149d9..9d73c3b4424cb 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -5,7 +5,7 @@ pub use crate::options::*; use crate::lint; use crate::search_paths::SearchPath; -use crate::utils::NativeLibKind; +use crate::utils::{CanonicalizedPath, NativeLibKind}; use crate::{early_error, early_warn, Session}; use rustc_data_structures::fx::FxHashSet; @@ -436,7 +436,7 @@ pub enum ExternLocation { /// which one to use. /// /// Added via `--extern prelude_name=some_file.rlib` - ExactPaths(BTreeSet), + ExactPaths(BTreeSet), } impl Externs { @@ -458,7 +458,7 @@ impl ExternEntry { ExternEntry { location, is_private_dep: false, add_prelude: false } } - pub fn files(&self) -> Option> { + pub fn files(&self) -> Option> { match &self.location { ExternLocation::ExactPaths(set) => Some(set.iter()), _ => None, @@ -1639,13 +1639,15 @@ pub fn parse_externs( for arg in matches.opt_strs("extern") { let (name, path) = match arg.split_once('=') { None => (arg, None), - Some((name, path)) => (name.to_string(), Some(path.to_string())), + Some((name, path)) => (name.to_string(), Some(Path::new(path))), }; let (options, name) = match name.split_once(':') { None => (None, name), Some((opts, name)) => (Some(opts), name.to_string()), }; + let path = path.map(|p| CanonicalizedPath::new(p)); + let entry = externs.entry(name.to_owned()); use std::collections::btree_map::Entry; diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 15447c01d1e55..f3d3330912464 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,5 +1,6 @@ use crate::session::Session; use rustc_data_structures::profiling::VerboseTimingGuard; +use std::path::{Path, PathBuf}; impl Session { pub fn timer<'a>(&'a self, what: &'static str) -> VerboseTimingGuard<'a> { @@ -30,3 +31,25 @@ pub enum NativeLibKind { } rustc_data_structures::impl_stable_hash_via_hash!(NativeLibKind); + +/// A path that has been canonicalized along with its original, non-canonicalized form +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct CanonicalizedPath { + // Optional since canonicalization can sometimes fail + canonicalized: Option, + original: PathBuf, +} + +impl CanonicalizedPath { + pub fn new(path: &Path) -> Self { + Self { original: path.to_owned(), canonicalized: std::fs::canonicalize(path).ok() } + } + + pub fn canonicalized(&self) -> &PathBuf { + self.canonicalized.as_ref().unwrap_or(self.original()) + } + + pub fn original(&self) -> &PathBuf { + &self.original + } +}