diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 422af66787579..fd0c3f36e7299 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -39,11 +39,13 @@ use crate::json::{Json, ToJson}; use crate::spec::abi::{lookup as lookup_abi, Abi}; use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::symbol::{sym, Symbol}; use serde_json::Value; use std::borrow::Cow; use std::collections::BTreeMap; use std::convert::TryFrom; +use std::hash::{Hash, Hasher}; use std::iter::FromIterator; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; @@ -2183,7 +2185,7 @@ impl Target { TargetTriple::TargetTriple(ref target_triple) => { load_builtin(target_triple).expect("built-in target") } - TargetTriple::TargetPath(..) => { + TargetTriple::TargetJson { .. } => { panic!("built-in targets doens't support target-paths") } } @@ -2248,11 +2250,9 @@ impl Target { Err(format!("Could not find specification for target {:?}", target_triple)) } - TargetTriple::TargetPath(ref target_path) => { - if target_path.is_file() { - return load_file(&target_path); - } - Err(format!("Target path {:?} is not a valid file", target_path)) + TargetTriple::TargetJson { ref contents, .. } => { + let obj = serde_json::from_str(contents).map_err(|e| e.to_string())?; + Target::from_json(obj) } } } @@ -2421,10 +2421,77 @@ impl ToJson for Target { } /// Either a target triple string or a path to a JSON file. -#[derive(PartialEq, Clone, Debug, Hash, Encodable, Decodable)] +#[derive(Clone, Debug)] pub enum TargetTriple { TargetTriple(String), - TargetPath(PathBuf), + TargetJson { + /// Warning: This field may only be used by rustdoc. Using it anywhere else will lead to + /// inconsistencies as it is discarded during serialization. + path_for_rustdoc: PathBuf, + triple: String, + contents: String, + }, +} + +// Use a manual implementation to ignore the path field +impl PartialEq for TargetTriple { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::TargetTriple(l0), Self::TargetTriple(r0)) => l0 == r0, + ( + Self::TargetJson { path_for_rustdoc: _, triple: l_triple, contents: l_contents }, + Self::TargetJson { path_for_rustdoc: _, triple: r_triple, contents: r_contents }, + ) => l_triple == r_triple && l_contents == r_contents, + _ => false, + } + } +} + +// Use a manual implementation to ignore the path field +impl Hash for TargetTriple { + fn hash(&self, state: &mut H) -> () { + match self { + TargetTriple::TargetTriple(triple) => { + 0u8.hash(state); + triple.hash(state) + } + TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents } => { + 1u8.hash(state); + triple.hash(state); + contents.hash(state) + } + } + } +} + +// Use a manual implementation to prevent encoding the target json file path in the crate metadata +impl Encodable for TargetTriple { + fn encode(&self, s: &mut S) { + match self { + TargetTriple::TargetTriple(triple) => s.emit_enum_variant(0, |s| s.emit_str(triple)), + TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents } => s + .emit_enum_variant(1, |s| { + s.emit_str(triple); + s.emit_str(contents) + }), + } + } +} + +impl Decodable for TargetTriple { + fn decode(d: &mut D) -> Self { + match d.read_usize() { + 0 => TargetTriple::TargetTriple(d.read_str().to_owned()), + 1 => TargetTriple::TargetJson { + path_for_rustdoc: PathBuf::new(), + triple: d.read_str().to_owned(), + contents: d.read_str().to_owned(), + }, + _ => { + panic!("invalid enum variant tag while decoding `TargetTriple`, expected 0..2"); + } + } + } } impl TargetTriple { @@ -2436,7 +2503,19 @@ impl TargetTriple { /// Creates a target triple from the passed target path. pub fn from_path(path: &Path) -> Result { let canonicalized_path = path.canonicalize()?; - Ok(TargetTriple::TargetPath(canonicalized_path)) + let contents = std::fs::read_to_string(&canonicalized_path).map_err(|err| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("target path {:?} is not a valid file: {}", canonicalized_path, err), + ) + })?; + let triple = canonicalized_path + .file_stem() + .expect("target path must not be empty") + .to_str() + .expect("target path must be valid unicode") + .to_owned(); + Ok(TargetTriple::TargetJson { path_for_rustdoc: canonicalized_path, triple, contents }) } /// Returns a string triple for this target. @@ -2444,12 +2523,8 @@ impl TargetTriple { /// If this target is a path, the file name (without extension) is returned. pub fn triple(&self) -> &str { match *self { - TargetTriple::TargetTriple(ref triple) => triple, - TargetTriple::TargetPath(ref path) => path - .file_stem() - .expect("target path must not be empty") - .to_str() - .expect("target path must be valid unicode"), + TargetTriple::TargetTriple(ref triple) + | TargetTriple::TargetJson { ref triple, .. } => triple, } } @@ -2459,16 +2534,15 @@ impl TargetTriple { /// by `triple()`. pub fn debug_triple(&self) -> String { use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - - let triple = self.triple(); - if let TargetTriple::TargetPath(ref path) = *self { - let mut hasher = DefaultHasher::new(); - path.hash(&mut hasher); - let hash = hasher.finish(); - format!("{}-{}", triple, hash) - } else { - triple.into() + + match self { + TargetTriple::TargetTriple(triple) => triple.to_owned(), + TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents: content } => { + let mut hasher = DefaultHasher::new(); + content.hash(&mut hasher); + let hash = hasher.finish(); + format!("{}-{}", triple, hash) + } } } } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 86c58cd79dce0..ab72f4a3f502c 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -365,8 +365,8 @@ fn run_test( } compiler.arg("--target").arg(match target { TargetTriple::TargetTriple(s) => s, - TargetTriple::TargetPath(path) => { - path.to_str().expect("target path must be valid unicode").to_string() + TargetTriple::TargetJson { path_for_rustdoc, .. } => { + path_for_rustdoc.to_str().expect("target path must be valid unicode").to_string() } }); if let ErrorOutputType::HumanReadable(kind) = rustdoc_options.error_format {