diff --git a/Cargo.lock b/Cargo.lock index b5eec3599de..edad278e6e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -827,6 +827,7 @@ dependencies = [ "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1321f0e84ef..ebaee891792 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,9 @@ structopt = "0.2.15" config_proc_macro = { path = "config_proc_macro" } +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["errhandlingapi", "fileapi"] } + # A noop dependency that changes in the Rust repository, it's a bit of a hack. # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` # for more information. diff --git a/src/bin/main.rs b/src/bin/main.rs index 5b69a440b65..33bae11467f 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -15,8 +15,8 @@ use failure::err_msg; use getopts::{Matches, Options}; use crate::rustfmt::{ - load_config, CliOptions, Color, Config, Edition, EmitMode, ErrorKind, FileLines, FileName, - FormatReportFormatterBuilder, Input, Session, Verbosity, + absolute_path, load_config, CliOptions, Color, Config, Edition, EmitMode, ErrorKind, FileLines, + FileName, FormatReportFormatterBuilder, Input, Session, Verbosity, }; fn main() { @@ -432,9 +432,9 @@ fn determine_operation(matches: &Matches) -> Result { .iter() .map(|s| { let p = PathBuf::from(s); - // we will do comparison later, so here tries to canonicalize first - // to get the expected behavior. - p.canonicalize().unwrap_or(p) + // we will do comparison later, so here tries to get the absolute + // path first to get the expected behavior. + absolute_path(&p).unwrap_or(p) }) .collect(); diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 3c4d1c08b08..c1517ad9d4b 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -3,11 +3,11 @@ #![deny(warnings)] use cargo_metadata; +use rustfmt_nightly as rustfmt; use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet}; use std::env; -use std::fs; use std::hash::{Hash, Hasher}; use std::io::{self, Write}; use std::iter::FromIterator; @@ -51,6 +51,8 @@ pub struct Opts { format_all: bool, } +use crate::rustfmt::absolute_path; + fn main() { let exit_status = execute(); std::io::stdout().flush().unwrap(); @@ -169,10 +171,10 @@ pub struct Target { impl Target { pub fn from_target(target: &cargo_metadata::Target) -> Self { let path = PathBuf::from(&target.src_path); - let canonicalized = fs::canonicalize(&path).unwrap_or(path); + let path = absolute_path(&path).unwrap_or(path); Target { - path: canonicalized, + path, kind: target.kind[0].clone(), edition: target.edition.clone(), } @@ -247,9 +249,9 @@ fn get_targets(strategy: &CargoFmtStrategy) -> Result, io::Erro fn get_targets_root_only(targets: &mut BTreeSet) -> Result<(), io::Error> { let metadata = get_cargo_metadata(None)?; - let current_dir = env::current_dir()?.canonicalize()?; + let current_dir = absolute_path(env::current_dir()?)?; let current_dir_manifest = current_dir.join("Cargo.toml"); - let workspace_root_path = PathBuf::from(&metadata.workspace_root).canonicalize()?; + let workspace_root_path = absolute_path(PathBuf::from(&metadata.workspace_root))?; let in_workspace_root = workspace_root_path == current_dir; for package in metadata.packages { diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index f52f6dab339..fe1fc9fb969 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -7,9 +7,10 @@ use std::{cmp, fmt, iter, str}; use serde::{ser, Deserialize, Deserializer, Serialize, Serializer}; use serde_json as json; - use syntax::source_map::{self, SourceFile}; +use crate::utils::absolute_path; + /// A range of lines in a file, inclusive of both ends. pub struct LineRange { pub file: Rc, @@ -220,7 +221,7 @@ impl FileLines { Some(ref map) => map, }; - match canonicalize_path_string(file_name).and_then(|file| map.get(&file)) { + match absolute_path_string(file_name).and_then(|file| map.get(&file)) { Some(ranges) => ranges.iter().any(f), None => false, } @@ -259,9 +260,9 @@ impl<'a> iter::Iterator for Files<'a> { } } -fn canonicalize_path_string(file: &FileName) -> Option { +fn absolute_path_string(file: &FileName) -> Option { match *file { - FileName::Real(ref path) => path.canonicalize().ok().map(FileName::Real), + FileName::Real(ref path) => absolute_path(path).ok().map(FileName::Real), _ => Some(file.clone()), } } @@ -291,8 +292,8 @@ pub struct JsonSpan { impl JsonSpan { fn into_tuple(self) -> Result<(FileName, Range), String> { let (lo, hi) = self.range; - let canonical = canonicalize_path_string(&self.file) - .ok_or_else(|| format!("Can't canonicalize {}", &self.file))?; + let canonical = absolute_path_string(&self.file) + .ok_or_else(|| format!("Can't get absolute path {}", &self.file))?; Ok((canonical, Range::new(lo, hi))) } } diff --git a/src/lib.rs b/src/lib.rs index 88605079b7e..baff6925c19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,9 @@ extern crate lazy_static; #[macro_use] extern crate log; +#[cfg(windows)] +extern crate winapi; + use std::cell::RefCell; use std::collections::HashMap; use std::fmt; @@ -32,6 +35,7 @@ pub use crate::config::{ load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, Range, Verbosity, }; +pub use crate::utils::absolute_path; pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder}; diff --git a/src/utils.rs b/src/utils.rs index 9a0a29dc5c3..6ca45fd98a5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; +use std::io; +use std::path; use bytecount; - use rustc_target::spec::abi; use syntax::ast::{ self, Attribute, CrateSugar, MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind, @@ -615,6 +616,47 @@ pub(crate) fn unicode_str_width(s: &str) -> usize { s.width() } +#[cfg(windows)] +pub fn absolute_path>(p: P) -> io::Result { + use std::ffi::OsString; + use std::iter::once; + use std::os::windows::ffi::{OsStrExt, OsStringExt}; + use std::ptr::null_mut; + use winapi::um::errhandlingapi::GetLastError; + use winapi::um::fileapi::GetFullPathNameW; + + // FIXME: This `MAX_PATH` may be valid only from Windows 10, version 1607. + // https://docs.microsoft.com/ja-jp/windows/desktop/FileIO/naming-a-file#paths + const MAX_PATH: usize = 32767; + let wide: Vec = p + .as_ref() + .as_os_str() + .encode_wide() + .chain(once(0)) + .collect(); + let mut buffer: Vec = vec![0; MAX_PATH]; + unsafe { + let result = GetFullPathNameW( + wide.as_ptr(), + MAX_PATH as u32, + buffer.as_mut_ptr(), + null_mut(), + ); + if result == 0 { + Err(std::io::Error::from_raw_os_error(GetLastError() as i32)) + } else { + Ok(path::PathBuf::from(OsString::from_wide( + &buffer[..result as usize], + ))) + } + } +} + +#[cfg(not(windows))] +pub fn absolute_path>(p: P) -> io::Result { + std::fs::canonicalize(p) +} + pub(crate) fn get_skip_macro_names(attrs: &[ast::Attribute]) -> Vec { let mut skip_macro_names = vec![]; for attr in attrs {