From 2b43fac2b98fd9b2c12decca18a086f96d068b35 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 19 Oct 2016 09:34:19 +0000 Subject: Remove `CrateReader`, use `CrateLoader` instead. --- src/librustc_driver/driver.rs | 3 ++- src/librustc_metadata/creader.rs | 48 ++++++++++------------------------- src/librustc_metadata/macro_import.rs | 4 +-- src/librustc_plugin/load.rs | 6 ++--- 4 files changed, 21 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index e8ab2f3a240..9b27f7a29e9 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -645,7 +645,8 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, // its contents but the results of name resolution on those contents. Hopefully we'll push // this back at some point. let _ignore = sess.dep_graph.in_ignore(); - let mut crate_loader = CrateLoader::new(sess, &cstore, &krate, crate_name); + let mut crate_loader = CrateLoader::new(sess, &cstore, crate_name, krate.config.clone()); + crate_loader.preprocess(&krate); let resolver_arenas = Resolver::arenas(); let mut resolver = Resolver::new(sess, &krate, make_glob_map, &mut crate_loader, &resolver_arenas); diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index f97df67c445..e3a589c6c03 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -42,12 +42,6 @@ use log; pub struct CrateLoader<'a> { pub sess: &'a Session, - pub creader: CrateReader<'a>, - cstore: &'a CStore, -} - -pub struct CrateReader<'a> { - sess: &'a Session, cstore: &'a CStore, next_crate_num: CrateNum, foreign_item_map: FnvHashMap>, @@ -159,13 +153,13 @@ pub struct Macros { pub dylib: Option, } -impl<'a> CrateReader<'a> { +impl<'a> CrateLoader<'a> { pub fn new(sess: &'a Session, cstore: &'a CStore, local_crate_name: &str, local_crate_config: ast::CrateConfig) - -> CrateReader<'a> { - CrateReader { + -> Self { + CrateLoader { sess: sess, cstore: cstore, next_crate_num: cstore.next_crate_num(), @@ -890,7 +884,7 @@ impl<'a> CrateReader<'a> { } impl ExtensionCrate { - fn register(self, creader: &mut CrateReader) { + fn register(self, loader: &mut CrateLoader) { if !self.should_link { return } @@ -901,31 +895,17 @@ impl ExtensionCrate { }; // Register crate now to avoid double-reading metadata - creader.register_crate(&None, - &self.ident, - &self.name, - self.span, - library, - true); + loader.register_crate(&None, &self.ident, &self.name, self.span, library, true); } } impl<'a> CrateLoader<'a> { - pub fn new(sess: &'a Session, cstore: &'a CStore, krate: &ast::Crate, crate_name: &str) - -> Self { - let loader = CrateLoader { - sess: sess, - cstore: cstore, - creader: CrateReader::new(sess, cstore, crate_name, krate.config.clone()), - }; - + pub fn preprocess(&mut self, krate: &ast::Crate) { for attr in krate.attrs.iter().filter(|m| m.name() == "link_args") { if let Some(ref linkarg) = attr.value_str() { - loader.cstore.add_used_link_args(&linkarg); + self.cstore.add_used_link_args(&linkarg); } } - - loader } fn process_foreign_mod(&mut self, i: &ast::Item, fm: &ast::ForeignMod) { @@ -982,7 +962,7 @@ impl<'a> CrateLoader<'a> { Some(name) => name, None => continue, }; - let list = self.creader.foreign_item_map.entry(lib_name.to_string()) + let list = self.foreign_item_map.entry(lib_name.to_string()) .or_insert(Vec::new()); list.extend(fm.items.iter().map(|it| it.id)); } @@ -991,8 +971,8 @@ impl<'a> CrateLoader<'a> { impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> { fn postprocess(&mut self, krate: &ast::Crate) { - self.creader.inject_allocator_crate(); - self.creader.inject_panic_runtime(krate); + self.inject_allocator_crate(); + self.inject_panic_runtime(krate); if log_enabled!(log::INFO) { dump_crates(&self.cstore); @@ -1001,7 +981,7 @@ impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> { for &(ref name, kind) in &self.sess.opts.libs { register_native_lib(self.sess, self.cstore, None, name.clone(), kind); } - self.creader.register_statically_included_foreign_items(); + self.register_statically_included_foreign_items(); } fn process_item(&mut self, item: &ast::Item, definitions: &hir_map::Definitions) { @@ -1024,12 +1004,12 @@ impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> { } } - if let Some(info) = self.creader.extract_crate_info(item) { + if let Some(info) = self.extract_crate_info(item) { if !info.should_link { return; } - let (cnum, ..) = self.creader.resolve_crate( + let (cnum, ..) = self.resolve_crate( &None, &info.ident, &info.name, None, item.span, PathKind::Crate, true, ); @@ -1038,7 +1018,7 @@ impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> { let extern_crate = ExternCrate { def_id: def_id, span: item.span, direct: true, path_len: len }; - self.creader.update_extern_crate(cnum, extern_crate, &mut FnvHashSet()); + self.update_extern_crate(cnum, extern_crate, &mut FnvHashSet()); self.cstore.add_extern_mod_stmt_cnum(info.id, cnum); } diff --git a/src/librustc_metadata/macro_import.rs b/src/librustc_metadata/macro_import.rs index 41e14ea9f40..ddc254a16d9 100644 --- a/src/librustc_metadata/macro_import.rs +++ b/src/librustc_metadata/macro_import.rs @@ -110,7 +110,7 @@ impl<'a> CrateLoader<'a> { if sel.is_empty() && reexport.is_empty() { // Make sure we can read macros from `#[no_link]` crates. if no_link { - self.creader.read_macros(vi); + self.read_macros(vi); } return Vec::new(); } @@ -122,7 +122,7 @@ impl<'a> CrateLoader<'a> { return Vec::new(); } - let mut macros = self.creader.read_macros(vi); + let mut macros = self.read_macros(vi); let mut ret = Vec::new(); let mut seen = HashSet::new(); diff --git a/src/librustc_plugin/load.rs b/src/librustc_plugin/load.rs index 9e56397bc99..669df3ad950 100644 --- a/src/librustc_plugin/load.rs +++ b/src/librustc_plugin/load.rs @@ -11,7 +11,7 @@ //! Used by `rustc` when loading a plugin. use rustc::session::Session; -use rustc_metadata::creader::CrateReader; +use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::CStore; use registry::Registry; @@ -33,7 +33,7 @@ pub struct PluginRegistrar { struct PluginLoader<'a> { sess: &'a Session, - reader: CrateReader<'a>, + reader: CrateLoader<'a>, plugins: Vec, } @@ -96,7 +96,7 @@ impl<'a> PluginLoader<'a> { -> PluginLoader<'a> { PluginLoader { sess: sess, - reader: CrateReader::new(sess, cstore, crate_name, crate_config), + reader: CrateLoader::new(sess, cstore, crate_name, crate_config), plugins: vec![], } } -- cgit 1.4.1-3-g733a5 From 0e99b83f528cf37117f100069c4b64a3cd592d4d Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 19 Oct 2016 09:56:39 +0000 Subject: Rename `csearch.rs` -> `cstore_impl.rs`. --- src/librustc_metadata/csearch.rs | 591 ----------------------------------- src/librustc_metadata/cstore_impl.rs | 591 +++++++++++++++++++++++++++++++++++ src/librustc_metadata/lib.rs | 2 +- 3 files changed, 592 insertions(+), 592 deletions(-) delete mode 100644 src/librustc_metadata/csearch.rs create mode 100644 src/librustc_metadata/cstore_impl.rs (limited to 'src') diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs deleted file mode 100644 index 54664b9c040..00000000000 --- a/src/librustc_metadata/csearch.rs +++ /dev/null @@ -1,591 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use cstore; -use encoder; -use loader; -use schema; - -use rustc::middle::cstore::{InlinedItem, CrateStore, CrateSource, ExternCrate}; -use rustc::middle::cstore::{NativeLibraryKind, LinkMeta, LinkagePreference}; -use rustc::hir::def::{self, Def}; -use rustc::middle::lang_items; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX}; - -use rustc::dep_graph::DepNode; -use rustc::hir::map as hir_map; -use rustc::hir::map::DefKey; -use rustc::mir::repr::Mir; -use rustc::mir::mir_map::MirMap; -use rustc::util::nodemap::{NodeSet, DefIdMap}; -use rustc_back::PanicStrategy; - -use std::path::PathBuf; -use syntax::ast; -use syntax::attr; -use syntax::parse::token; -use rustc::hir::svh::Svh; -use rustc_back::target::Target; -use rustc::hir; - -impl<'tcx> CrateStore<'tcx> for cstore::CStore { - fn describe_def(&self, def: DefId) -> Option { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_def(def.index) - } - - fn stability(&self, def: DefId) -> Option { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_stability(def.index) - } - - fn deprecation(&self, def: DefId) -> Option { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_deprecation(def.index) - } - - fn visibility(&self, def: DefId) -> ty::Visibility { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_visibility(def.index) - } - - fn closure_kind(&self, def_id: DefId) -> ty::ClosureKind - { - assert!(!def_id.is_local()); - self.dep_graph.read(DepNode::MetaData(def_id)); - self.get_crate_data(def_id.krate).closure_kind(def_id.index) - } - - fn closure_ty<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::ClosureTy<'tcx> { - assert!(!def_id.is_local()); - self.dep_graph.read(DepNode::MetaData(def_id)); - self.get_crate_data(def_id.krate).closure_ty(def_id.index, tcx) - } - - fn item_variances(&self, def: DefId) -> Vec { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_item_variances(def.index) - } - - fn item_type<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> Ty<'tcx> - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_type(def.index, tcx) - } - - fn item_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> ty::GenericPredicates<'tcx> - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_predicates(def.index, tcx) - } - - fn item_super_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> ty::GenericPredicates<'tcx> - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_super_predicates(def.index, tcx) - } - - fn item_generics<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> ty::Generics<'tcx> - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_generics(def.index, tcx) - } - - fn item_attrs(&self, def_id: DefId) -> Vec - { - self.dep_graph.read(DepNode::MetaData(def_id)); - self.get_crate_data(def_id.krate).get_item_attrs(def_id.index) - } - - fn trait_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::TraitDef<'tcx> - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_trait_def(def.index, tcx) - } - - fn adt_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::AdtDefMaster<'tcx> - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_adt_def(def.index, tcx) - } - - fn fn_arg_names(&self, did: DefId) -> Vec - { - self.dep_graph.read(DepNode::MetaData(did)); - self.get_crate_data(did.krate).get_fn_arg_names(did.index) - } - - fn inherent_implementations_for_type(&self, def_id: DefId) -> Vec - { - self.dep_graph.read(DepNode::MetaData(def_id)); - self.get_crate_data(def_id.krate).get_inherent_implementations_for_type(def_id.index) - } - - fn implementations_of_trait(&self, filter: Option) -> Vec - { - if let Some(def_id) = filter { - self.dep_graph.read(DepNode::MetaData(def_id)); - } - let mut result = vec![]; - self.iter_crate_data(|_, cdata| { - cdata.get_implementations_for_trait(filter, &mut result) - }); - result - } - - fn impl_or_trait_items(&self, def_id: DefId) -> Vec { - self.dep_graph.read(DepNode::MetaData(def_id)); - let mut result = vec![]; - self.get_crate_data(def_id.krate) - .each_child_of_item(def_id.index, |child| result.push(child.def.def_id())); - result - } - - fn impl_polarity(&self, def: DefId) -> hir::ImplPolarity - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_impl_polarity(def.index) - } - - fn impl_trait_ref<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> Option> - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_impl_trait(def.index, tcx) - } - - fn custom_coerce_unsized_kind(&self, def: DefId) - -> Option - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_custom_coerce_unsized_kind(def.index) - } - - fn impl_parent(&self, impl_def: DefId) -> Option { - self.dep_graph.read(DepNode::MetaData(impl_def)); - self.get_crate_data(impl_def.krate).get_parent_impl(impl_def.index) - } - - fn trait_of_item(&self, def_id: DefId) -> Option { - self.dep_graph.read(DepNode::MetaData(def_id)); - self.get_crate_data(def_id.krate).get_trait_of_item(def_id.index) - } - - fn impl_or_trait_item<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> Option> - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_impl_or_trait_item(def.index, tcx) - } - - fn is_const_fn(&self, did: DefId) -> bool - { - self.dep_graph.read(DepNode::MetaData(did)); - self.get_crate_data(did.krate).is_const_fn(did.index) - } - - fn is_defaulted_trait(&self, trait_def_id: DefId) -> bool - { - self.dep_graph.read(DepNode::MetaData(trait_def_id)); - self.get_crate_data(trait_def_id.krate).is_defaulted_trait(trait_def_id.index) - } - - fn is_default_impl(&self, impl_did: DefId) -> bool { - self.dep_graph.read(DepNode::MetaData(impl_did)); - self.get_crate_data(impl_did.krate).is_default_impl(impl_did.index) - } - - fn is_extern_item<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, did: DefId) -> bool { - self.dep_graph.read(DepNode::MetaData(did)); - self.get_crate_data(did.krate).is_extern_item(did.index, tcx) - } - - fn is_foreign_item(&self, did: DefId) -> bool { - self.get_crate_data(did.krate).is_foreign_item(did.index) - } - - fn is_statically_included_foreign_item(&self, id: ast::NodeId) -> bool - { - self.do_is_statically_included_foreign_item(id) - } - - fn dylib_dependency_formats(&self, cnum: CrateNum) - -> Vec<(CrateNum, LinkagePreference)> - { - self.get_crate_data(cnum).get_dylib_dependency_formats() - } - - fn lang_items(&self, cnum: CrateNum) -> Vec<(DefIndex, usize)> - { - self.get_crate_data(cnum).get_lang_items() - } - - fn missing_lang_items(&self, cnum: CrateNum) - -> Vec - { - self.get_crate_data(cnum).get_missing_lang_items() - } - - fn is_staged_api(&self, cnum: CrateNum) -> bool - { - self.get_crate_data(cnum).is_staged_api() - } - - fn is_explicitly_linked(&self, cnum: CrateNum) -> bool - { - self.get_crate_data(cnum).explicitly_linked.get() - } - - fn is_allocator(&self, cnum: CrateNum) -> bool - { - self.get_crate_data(cnum).is_allocator() - } - - fn is_panic_runtime(&self, cnum: CrateNum) -> bool - { - self.get_crate_data(cnum).is_panic_runtime() - } - - fn is_compiler_builtins(&self, cnum: CrateNum) -> bool { - self.get_crate_data(cnum).is_compiler_builtins() - } - - fn panic_strategy(&self, cnum: CrateNum) -> PanicStrategy { - self.get_crate_data(cnum).panic_strategy() - } - - fn crate_name(&self, cnum: CrateNum) -> token::InternedString - { - token::intern_and_get_ident(&self.get_crate_data(cnum).name[..]) - } - - fn original_crate_name(&self, cnum: CrateNum) -> token::InternedString - { - token::intern_and_get_ident(&self.get_crate_data(cnum).name()) - } - - fn extern_crate(&self, cnum: CrateNum) -> Option - { - self.get_crate_data(cnum).extern_crate.get() - } - - fn crate_hash(&self, cnum: CrateNum) -> Svh - { - self.get_crate_hash(cnum) - } - - fn crate_disambiguator(&self, cnum: CrateNum) -> token::InternedString - { - token::intern_and_get_ident(&self.get_crate_data(cnum).disambiguator()) - } - - fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option - { - self.get_crate_data(cnum).root.plugin_registrar_fn.map(|index| DefId { - krate: cnum, - index: index - }) - } - - fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)> - { - self.get_crate_data(cnum).get_native_libraries() - } - - fn reachable_ids(&self, cnum: CrateNum) -> Vec - { - self.get_crate_data(cnum).get_reachable_ids() - } - - fn is_no_builtins(&self, cnum: CrateNum) -> bool { - self.get_crate_data(cnum).is_no_builtins() - } - - fn def_index_for_def_key(&self, - cnum: CrateNum, - def: DefKey) - -> Option { - let cdata = self.get_crate_data(cnum); - cdata.key_map.get(&def).cloned() - } - - /// Returns the `DefKey` for a given `DefId`. This indicates the - /// parent `DefId` as well as some idea of what kind of data the - /// `DefId` refers to. - fn def_key(&self, def: DefId) -> hir_map::DefKey { - // Note: loading the def-key (or def-path) for a def-id is not - // a *read* of its metadata. This is because the def-id is - // really just an interned shorthand for a def-path, which is the - // canonical name for an item. - // - // self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).def_key(def.index) - } - - fn relative_def_path(&self, def: DefId) -> Option { - // See `Note` above in `def_key()` for why this read is - // commented out: - // - // self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).def_path(def.index) - } - - fn struct_field_names(&self, def: DefId) -> Vec - { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).get_struct_field_names(def.index) - } - - fn item_children(&self, def_id: DefId) -> Vec - { - self.dep_graph.read(DepNode::MetaData(def_id)); - let mut result = vec![]; - self.get_crate_data(def_id.krate) - .each_child_of_item(def_id.index, |child| result.push(child)); - result - } - - fn maybe_get_item_ast<'a>(&'tcx self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> Option<(&'tcx InlinedItem, ast::NodeId)> - { - self.dep_graph.read(DepNode::MetaData(def_id)); - - match self.inlined_item_cache.borrow().get(&def_id) { - Some(&None) => { - return None; // Not inlinable - } - Some(&Some(ref cached_inlined_item)) => { - // Already inline - debug!("maybe_get_item_ast({}): already inline as node id {}", - tcx.item_path_str(def_id), cached_inlined_item.item_id); - return Some((tcx.map.expect_inlined_item(cached_inlined_item.inlined_root), - cached_inlined_item.item_id)); - } - None => { - // Not seen yet - } - } - - debug!("maybe_get_item_ast({}): inlining item", tcx.item_path_str(def_id)); - - let inlined = self.get_crate_data(def_id.krate).maybe_get_item_ast(tcx, def_id.index); - - let cache_inlined_item = |original_def_id, inlined_item_id, inlined_root_node_id| { - let cache_entry = cstore::CachedInlinedItem { - inlined_root: inlined_root_node_id, - item_id: inlined_item_id, - }; - self.inlined_item_cache - .borrow_mut() - .insert(original_def_id, Some(cache_entry)); - self.defid_for_inlined_node - .borrow_mut() - .insert(inlined_item_id, original_def_id); - }; - - let find_inlined_item_root = |inlined_item_id| { - let mut node = inlined_item_id; - let mut path = Vec::with_capacity(10); - - // If we can't find the inline root after a thousand hops, we can - // be pretty sure there's something wrong with the HIR map. - for _ in 0 .. 1000 { - path.push(node); - let parent_node = tcx.map.get_parent_node(node); - if parent_node == node { - return node; - } - node = parent_node; - } - bug!("cycle in HIR map parent chain") - }; - - match inlined { - None => { - self.inlined_item_cache - .borrow_mut() - .insert(def_id, None); - } - Some(&InlinedItem::Item(d, ref item)) => { - assert_eq!(d, def_id); - let inlined_root_node_id = find_inlined_item_root(item.id); - cache_inlined_item(def_id, item.id, inlined_root_node_id); - } - Some(&InlinedItem::TraitItem(_, ref trait_item)) => { - let inlined_root_node_id = find_inlined_item_root(trait_item.id); - cache_inlined_item(def_id, trait_item.id, inlined_root_node_id); - - // Associated consts already have to be evaluated in `typeck`, so - // the logic to do that already exists in `middle`. In order to - // reuse that code, it needs to be able to look up the traits for - // inlined items. - let ty_trait_item = tcx.impl_or_trait_item(def_id).clone(); - let trait_item_def_id = tcx.map.local_def_id(trait_item.id); - tcx.impl_or_trait_items.borrow_mut() - .insert(trait_item_def_id, ty_trait_item); - } - Some(&InlinedItem::ImplItem(_, ref impl_item)) => { - let inlined_root_node_id = find_inlined_item_root(impl_item.id); - cache_inlined_item(def_id, impl_item.id, inlined_root_node_id); - } - } - - // We can be sure to hit the cache now - return self.maybe_get_item_ast(tcx, def_id); - } - - fn local_node_for_inlined_defid(&'tcx self, def_id: DefId) -> Option { - assert!(!def_id.is_local()); - match self.inlined_item_cache.borrow().get(&def_id) { - Some(&Some(ref cached_inlined_item)) => { - Some(cached_inlined_item.item_id) - } - Some(&None) => { - None - } - _ => { - bug!("Trying to lookup inlined NodeId for unexpected item"); - } - } - } - - fn defid_for_inlined_node(&'tcx self, node_id: ast::NodeId) -> Option { - self.defid_for_inlined_node.borrow().get(&node_id).map(|x| *x) - } - - fn maybe_get_item_mir<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> Option> { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).maybe_get_item_mir(tcx, def.index) - } - - fn is_item_mir_available(&self, def: DefId) -> bool { - self.dep_graph.read(DepNode::MetaData(def)); - self.get_crate_data(def.krate).is_item_mir_available(def.index) - } - - fn crates(&self) -> Vec - { - let mut result = vec![]; - self.iter_crate_data(|cnum, _| result.push(cnum)); - result - } - - fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)> - { - self.get_used_libraries().borrow().clone() - } - - fn used_link_args(&self) -> Vec - { - self.get_used_link_args().borrow().clone() - } - - fn metadata_filename(&self) -> &str - { - loader::METADATA_FILENAME - } - - fn metadata_section_name(&self, target: &Target) -> &str - { - loader::meta_section_name(target) - } - - fn used_crates(&self, prefer: LinkagePreference) -> Vec<(CrateNum, Option)> - { - self.do_get_used_crates(prefer) - } - - fn used_crate_source(&self, cnum: CrateNum) -> CrateSource - { - self.opt_used_crate_source(cnum).unwrap() - } - - fn extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option - { - self.do_extern_mod_stmt_cnum(emod_id) - } - - fn encode_metadata<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, - reexports: &def::ExportMap, - link_meta: &LinkMeta, - reachable: &NodeSet, - mir_map: &MirMap<'tcx>) -> Vec - { - encoder::encode_metadata(tcx, self, reexports, link_meta, reachable, mir_map) - } - - fn metadata_encoding_version(&self) -> &[u8] - { - schema::METADATA_HEADER - } - - /// Returns a map from a sufficiently visible external item (i.e. an external item that is - /// visible from at least one local module) to a sufficiently visible parent (considering - /// modules that re-export the external item to be parents). - fn visible_parent_map<'a>(&'a self) -> ::std::cell::RefMut<'a, DefIdMap> { - let mut visible_parent_map = self.visible_parent_map.borrow_mut(); - if !visible_parent_map.is_empty() { return visible_parent_map; } - - use std::collections::vec_deque::VecDeque; - use std::collections::hash_map::Entry; - for cnum in (1 .. self.next_crate_num().as_usize()).map(CrateNum::new) { - let cdata = self.get_crate_data(cnum); - - match cdata.extern_crate.get() { - // Ignore crates without a corresponding local `extern crate` item. - Some(extern_crate) if !extern_crate.direct => continue, - _ => {}, - } - - let mut bfs_queue = &mut VecDeque::new(); - let mut add_child = |bfs_queue: &mut VecDeque<_>, child: def::Export, parent: DefId| { - let child = child.def.def_id(); - - if self.visibility(child) != ty::Visibility::Public { - return; - } - - match visible_parent_map.entry(child) { - Entry::Occupied(mut entry) => { - // If `child` is defined in crate `cnum`, ensure - // that it is mapped to a parent in `cnum`. - if child.krate == cnum && entry.get().krate != cnum { - entry.insert(parent); - } - } - Entry::Vacant(entry) => { - entry.insert(parent); - bfs_queue.push_back(child); - } - } - }; - - bfs_queue.push_back(DefId { - krate: cnum, - index: CRATE_DEF_INDEX - }); - while let Some(def) = bfs_queue.pop_front() { - for child in self.item_children(def) { - add_child(bfs_queue, child, def); - } - } - } - - visible_parent_map - } -} diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs new file mode 100644 index 00000000000..54664b9c040 --- /dev/null +++ b/src/librustc_metadata/cstore_impl.rs @@ -0,0 +1,591 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cstore; +use encoder; +use loader; +use schema; + +use rustc::middle::cstore::{InlinedItem, CrateStore, CrateSource, ExternCrate}; +use rustc::middle::cstore::{NativeLibraryKind, LinkMeta, LinkagePreference}; +use rustc::hir::def::{self, Def}; +use rustc::middle::lang_items; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX}; + +use rustc::dep_graph::DepNode; +use rustc::hir::map as hir_map; +use rustc::hir::map::DefKey; +use rustc::mir::repr::Mir; +use rustc::mir::mir_map::MirMap; +use rustc::util::nodemap::{NodeSet, DefIdMap}; +use rustc_back::PanicStrategy; + +use std::path::PathBuf; +use syntax::ast; +use syntax::attr; +use syntax::parse::token; +use rustc::hir::svh::Svh; +use rustc_back::target::Target; +use rustc::hir; + +impl<'tcx> CrateStore<'tcx> for cstore::CStore { + fn describe_def(&self, def: DefId) -> Option { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_def(def.index) + } + + fn stability(&self, def: DefId) -> Option { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_stability(def.index) + } + + fn deprecation(&self, def: DefId) -> Option { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_deprecation(def.index) + } + + fn visibility(&self, def: DefId) -> ty::Visibility { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_visibility(def.index) + } + + fn closure_kind(&self, def_id: DefId) -> ty::ClosureKind + { + assert!(!def_id.is_local()); + self.dep_graph.read(DepNode::MetaData(def_id)); + self.get_crate_data(def_id.krate).closure_kind(def_id.index) + } + + fn closure_ty<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::ClosureTy<'tcx> { + assert!(!def_id.is_local()); + self.dep_graph.read(DepNode::MetaData(def_id)); + self.get_crate_data(def_id.krate).closure_ty(def_id.index, tcx) + } + + fn item_variances(&self, def: DefId) -> Vec { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_item_variances(def.index) + } + + fn item_type<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) + -> Ty<'tcx> + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_type(def.index, tcx) + } + + fn item_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) + -> ty::GenericPredicates<'tcx> + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_predicates(def.index, tcx) + } + + fn item_super_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) + -> ty::GenericPredicates<'tcx> + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_super_predicates(def.index, tcx) + } + + fn item_generics<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) + -> ty::Generics<'tcx> + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_generics(def.index, tcx) + } + + fn item_attrs(&self, def_id: DefId) -> Vec + { + self.dep_graph.read(DepNode::MetaData(def_id)); + self.get_crate_data(def_id.krate).get_item_attrs(def_id.index) + } + + fn trait_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::TraitDef<'tcx> + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_trait_def(def.index, tcx) + } + + fn adt_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::AdtDefMaster<'tcx> + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_adt_def(def.index, tcx) + } + + fn fn_arg_names(&self, did: DefId) -> Vec + { + self.dep_graph.read(DepNode::MetaData(did)); + self.get_crate_data(did.krate).get_fn_arg_names(did.index) + } + + fn inherent_implementations_for_type(&self, def_id: DefId) -> Vec + { + self.dep_graph.read(DepNode::MetaData(def_id)); + self.get_crate_data(def_id.krate).get_inherent_implementations_for_type(def_id.index) + } + + fn implementations_of_trait(&self, filter: Option) -> Vec + { + if let Some(def_id) = filter { + self.dep_graph.read(DepNode::MetaData(def_id)); + } + let mut result = vec![]; + self.iter_crate_data(|_, cdata| { + cdata.get_implementations_for_trait(filter, &mut result) + }); + result + } + + fn impl_or_trait_items(&self, def_id: DefId) -> Vec { + self.dep_graph.read(DepNode::MetaData(def_id)); + let mut result = vec![]; + self.get_crate_data(def_id.krate) + .each_child_of_item(def_id.index, |child| result.push(child.def.def_id())); + result + } + + fn impl_polarity(&self, def: DefId) -> hir::ImplPolarity + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_impl_polarity(def.index) + } + + fn impl_trait_ref<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) + -> Option> + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_impl_trait(def.index, tcx) + } + + fn custom_coerce_unsized_kind(&self, def: DefId) + -> Option + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_custom_coerce_unsized_kind(def.index) + } + + fn impl_parent(&self, impl_def: DefId) -> Option { + self.dep_graph.read(DepNode::MetaData(impl_def)); + self.get_crate_data(impl_def.krate).get_parent_impl(impl_def.index) + } + + fn trait_of_item(&self, def_id: DefId) -> Option { + self.dep_graph.read(DepNode::MetaData(def_id)); + self.get_crate_data(def_id.krate).get_trait_of_item(def_id.index) + } + + fn impl_or_trait_item<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) + -> Option> + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_impl_or_trait_item(def.index, tcx) + } + + fn is_const_fn(&self, did: DefId) -> bool + { + self.dep_graph.read(DepNode::MetaData(did)); + self.get_crate_data(did.krate).is_const_fn(did.index) + } + + fn is_defaulted_trait(&self, trait_def_id: DefId) -> bool + { + self.dep_graph.read(DepNode::MetaData(trait_def_id)); + self.get_crate_data(trait_def_id.krate).is_defaulted_trait(trait_def_id.index) + } + + fn is_default_impl(&self, impl_did: DefId) -> bool { + self.dep_graph.read(DepNode::MetaData(impl_did)); + self.get_crate_data(impl_did.krate).is_default_impl(impl_did.index) + } + + fn is_extern_item<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, did: DefId) -> bool { + self.dep_graph.read(DepNode::MetaData(did)); + self.get_crate_data(did.krate).is_extern_item(did.index, tcx) + } + + fn is_foreign_item(&self, did: DefId) -> bool { + self.get_crate_data(did.krate).is_foreign_item(did.index) + } + + fn is_statically_included_foreign_item(&self, id: ast::NodeId) -> bool + { + self.do_is_statically_included_foreign_item(id) + } + + fn dylib_dependency_formats(&self, cnum: CrateNum) + -> Vec<(CrateNum, LinkagePreference)> + { + self.get_crate_data(cnum).get_dylib_dependency_formats() + } + + fn lang_items(&self, cnum: CrateNum) -> Vec<(DefIndex, usize)> + { + self.get_crate_data(cnum).get_lang_items() + } + + fn missing_lang_items(&self, cnum: CrateNum) + -> Vec + { + self.get_crate_data(cnum).get_missing_lang_items() + } + + fn is_staged_api(&self, cnum: CrateNum) -> bool + { + self.get_crate_data(cnum).is_staged_api() + } + + fn is_explicitly_linked(&self, cnum: CrateNum) -> bool + { + self.get_crate_data(cnum).explicitly_linked.get() + } + + fn is_allocator(&self, cnum: CrateNum) -> bool + { + self.get_crate_data(cnum).is_allocator() + } + + fn is_panic_runtime(&self, cnum: CrateNum) -> bool + { + self.get_crate_data(cnum).is_panic_runtime() + } + + fn is_compiler_builtins(&self, cnum: CrateNum) -> bool { + self.get_crate_data(cnum).is_compiler_builtins() + } + + fn panic_strategy(&self, cnum: CrateNum) -> PanicStrategy { + self.get_crate_data(cnum).panic_strategy() + } + + fn crate_name(&self, cnum: CrateNum) -> token::InternedString + { + token::intern_and_get_ident(&self.get_crate_data(cnum).name[..]) + } + + fn original_crate_name(&self, cnum: CrateNum) -> token::InternedString + { + token::intern_and_get_ident(&self.get_crate_data(cnum).name()) + } + + fn extern_crate(&self, cnum: CrateNum) -> Option + { + self.get_crate_data(cnum).extern_crate.get() + } + + fn crate_hash(&self, cnum: CrateNum) -> Svh + { + self.get_crate_hash(cnum) + } + + fn crate_disambiguator(&self, cnum: CrateNum) -> token::InternedString + { + token::intern_and_get_ident(&self.get_crate_data(cnum).disambiguator()) + } + + fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option + { + self.get_crate_data(cnum).root.plugin_registrar_fn.map(|index| DefId { + krate: cnum, + index: index + }) + } + + fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)> + { + self.get_crate_data(cnum).get_native_libraries() + } + + fn reachable_ids(&self, cnum: CrateNum) -> Vec + { + self.get_crate_data(cnum).get_reachable_ids() + } + + fn is_no_builtins(&self, cnum: CrateNum) -> bool { + self.get_crate_data(cnum).is_no_builtins() + } + + fn def_index_for_def_key(&self, + cnum: CrateNum, + def: DefKey) + -> Option { + let cdata = self.get_crate_data(cnum); + cdata.key_map.get(&def).cloned() + } + + /// Returns the `DefKey` for a given `DefId`. This indicates the + /// parent `DefId` as well as some idea of what kind of data the + /// `DefId` refers to. + fn def_key(&self, def: DefId) -> hir_map::DefKey { + // Note: loading the def-key (or def-path) for a def-id is not + // a *read* of its metadata. This is because the def-id is + // really just an interned shorthand for a def-path, which is the + // canonical name for an item. + // + // self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).def_key(def.index) + } + + fn relative_def_path(&self, def: DefId) -> Option { + // See `Note` above in `def_key()` for why this read is + // commented out: + // + // self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).def_path(def.index) + } + + fn struct_field_names(&self, def: DefId) -> Vec + { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).get_struct_field_names(def.index) + } + + fn item_children(&self, def_id: DefId) -> Vec + { + self.dep_graph.read(DepNode::MetaData(def_id)); + let mut result = vec![]; + self.get_crate_data(def_id.krate) + .each_child_of_item(def_id.index, |child| result.push(child)); + result + } + + fn maybe_get_item_ast<'a>(&'tcx self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> Option<(&'tcx InlinedItem, ast::NodeId)> + { + self.dep_graph.read(DepNode::MetaData(def_id)); + + match self.inlined_item_cache.borrow().get(&def_id) { + Some(&None) => { + return None; // Not inlinable + } + Some(&Some(ref cached_inlined_item)) => { + // Already inline + debug!("maybe_get_item_ast({}): already inline as node id {}", + tcx.item_path_str(def_id), cached_inlined_item.item_id); + return Some((tcx.map.expect_inlined_item(cached_inlined_item.inlined_root), + cached_inlined_item.item_id)); + } + None => { + // Not seen yet + } + } + + debug!("maybe_get_item_ast({}): inlining item", tcx.item_path_str(def_id)); + + let inlined = self.get_crate_data(def_id.krate).maybe_get_item_ast(tcx, def_id.index); + + let cache_inlined_item = |original_def_id, inlined_item_id, inlined_root_node_id| { + let cache_entry = cstore::CachedInlinedItem { + inlined_root: inlined_root_node_id, + item_id: inlined_item_id, + }; + self.inlined_item_cache + .borrow_mut() + .insert(original_def_id, Some(cache_entry)); + self.defid_for_inlined_node + .borrow_mut() + .insert(inlined_item_id, original_def_id); + }; + + let find_inlined_item_root = |inlined_item_id| { + let mut node = inlined_item_id; + let mut path = Vec::with_capacity(10); + + // If we can't find the inline root after a thousand hops, we can + // be pretty sure there's something wrong with the HIR map. + for _ in 0 .. 1000 { + path.push(node); + let parent_node = tcx.map.get_parent_node(node); + if parent_node == node { + return node; + } + node = parent_node; + } + bug!("cycle in HIR map parent chain") + }; + + match inlined { + None => { + self.inlined_item_cache + .borrow_mut() + .insert(def_id, None); + } + Some(&InlinedItem::Item(d, ref item)) => { + assert_eq!(d, def_id); + let inlined_root_node_id = find_inlined_item_root(item.id); + cache_inlined_item(def_id, item.id, inlined_root_node_id); + } + Some(&InlinedItem::TraitItem(_, ref trait_item)) => { + let inlined_root_node_id = find_inlined_item_root(trait_item.id); + cache_inlined_item(def_id, trait_item.id, inlined_root_node_id); + + // Associated consts already have to be evaluated in `typeck`, so + // the logic to do that already exists in `middle`. In order to + // reuse that code, it needs to be able to look up the traits for + // inlined items. + let ty_trait_item = tcx.impl_or_trait_item(def_id).clone(); + let trait_item_def_id = tcx.map.local_def_id(trait_item.id); + tcx.impl_or_trait_items.borrow_mut() + .insert(trait_item_def_id, ty_trait_item); + } + Some(&InlinedItem::ImplItem(_, ref impl_item)) => { + let inlined_root_node_id = find_inlined_item_root(impl_item.id); + cache_inlined_item(def_id, impl_item.id, inlined_root_node_id); + } + } + + // We can be sure to hit the cache now + return self.maybe_get_item_ast(tcx, def_id); + } + + fn local_node_for_inlined_defid(&'tcx self, def_id: DefId) -> Option { + assert!(!def_id.is_local()); + match self.inlined_item_cache.borrow().get(&def_id) { + Some(&Some(ref cached_inlined_item)) => { + Some(cached_inlined_item.item_id) + } + Some(&None) => { + None + } + _ => { + bug!("Trying to lookup inlined NodeId for unexpected item"); + } + } + } + + fn defid_for_inlined_node(&'tcx self, node_id: ast::NodeId) -> Option { + self.defid_for_inlined_node.borrow().get(&node_id).map(|x| *x) + } + + fn maybe_get_item_mir<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) + -> Option> { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).maybe_get_item_mir(tcx, def.index) + } + + fn is_item_mir_available(&self, def: DefId) -> bool { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).is_item_mir_available(def.index) + } + + fn crates(&self) -> Vec + { + let mut result = vec![]; + self.iter_crate_data(|cnum, _| result.push(cnum)); + result + } + + fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)> + { + self.get_used_libraries().borrow().clone() + } + + fn used_link_args(&self) -> Vec + { + self.get_used_link_args().borrow().clone() + } + + fn metadata_filename(&self) -> &str + { + loader::METADATA_FILENAME + } + + fn metadata_section_name(&self, target: &Target) -> &str + { + loader::meta_section_name(target) + } + + fn used_crates(&self, prefer: LinkagePreference) -> Vec<(CrateNum, Option)> + { + self.do_get_used_crates(prefer) + } + + fn used_crate_source(&self, cnum: CrateNum) -> CrateSource + { + self.opt_used_crate_source(cnum).unwrap() + } + + fn extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option + { + self.do_extern_mod_stmt_cnum(emod_id) + } + + fn encode_metadata<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + reexports: &def::ExportMap, + link_meta: &LinkMeta, + reachable: &NodeSet, + mir_map: &MirMap<'tcx>) -> Vec + { + encoder::encode_metadata(tcx, self, reexports, link_meta, reachable, mir_map) + } + + fn metadata_encoding_version(&self) -> &[u8] + { + schema::METADATA_HEADER + } + + /// Returns a map from a sufficiently visible external item (i.e. an external item that is + /// visible from at least one local module) to a sufficiently visible parent (considering + /// modules that re-export the external item to be parents). + fn visible_parent_map<'a>(&'a self) -> ::std::cell::RefMut<'a, DefIdMap> { + let mut visible_parent_map = self.visible_parent_map.borrow_mut(); + if !visible_parent_map.is_empty() { return visible_parent_map; } + + use std::collections::vec_deque::VecDeque; + use std::collections::hash_map::Entry; + for cnum in (1 .. self.next_crate_num().as_usize()).map(CrateNum::new) { + let cdata = self.get_crate_data(cnum); + + match cdata.extern_crate.get() { + // Ignore crates without a corresponding local `extern crate` item. + Some(extern_crate) if !extern_crate.direct => continue, + _ => {}, + } + + let mut bfs_queue = &mut VecDeque::new(); + let mut add_child = |bfs_queue: &mut VecDeque<_>, child: def::Export, parent: DefId| { + let child = child.def.def_id(); + + if self.visibility(child) != ty::Visibility::Public { + return; + } + + match visible_parent_map.entry(child) { + Entry::Occupied(mut entry) => { + // If `child` is defined in crate `cnum`, ensure + // that it is mapped to a parent in `cnum`. + if child.krate == cnum && entry.get().krate != cnum { + entry.insert(parent); + } + } + Entry::Vacant(entry) => { + entry.insert(parent); + bfs_queue.push_back(child); + } + } + }; + + bfs_queue.push_back(DefId { + krate: cnum, + index: CRATE_DEF_INDEX + }); + while let Some(def) = bfs_queue.pop_front() { + for child in self.item_children(def) { + add_child(bfs_queue, child, def); + } + } + } + + visible_parent_map + } +} diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 3d8a10f6c31..cc78dafd7ed 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -53,7 +53,7 @@ mod index_builder; mod index; mod encoder; mod decoder; -mod csearch; +mod cstore_impl; mod schema; pub mod creader; -- cgit 1.4.1-3-g733a5 From 92413c9cf4508fb40c7563dabcc156a6528c4913 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 20 Oct 2016 04:21:25 +0000 Subject: Move `Library` into `creader.rs`. --- src/librustc_metadata/creader.rs | 14 ++++++++++---- src/librustc_metadata/loader.rs | 7 +------ 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index e3a589c6c03..c016f01b505 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -40,6 +40,12 @@ use syntax::parse::token::InternedString; use syntax_pos::{self, Span, mk_sp}; use log; +pub struct Library { + pub dylib: Option<(PathBuf, PathKind)>, + pub rlib: Option<(PathBuf, PathKind)>, + pub metadata: MetadataBlob, +} + pub struct CrateLoader<'a> { pub sess: &'a Session, cstore: &'a CStore, @@ -123,7 +129,7 @@ struct ExtensionCrate { enum PMDSource { Registered(Rc), - Owned(loader::Library), + Owned(Library), } impl Deref for PMDSource { @@ -139,7 +145,7 @@ impl Deref for PMDSource { enum LoadResult { Previous(CrateNum), - Loaded(loader::Library), + Loaded(Library), } pub struct Macros { @@ -275,7 +281,7 @@ impl<'a> CrateLoader<'a> { ident: &str, name: &str, span: Span, - lib: loader::Library, + lib: Library, explicitly_linked: bool) -> (CrateNum, Rc, cstore::CrateSource) { @@ -300,7 +306,7 @@ impl<'a> CrateLoader<'a> { // Maintain a reference to the top most crate. let root = if root.is_some() { root } else { &crate_paths }; - let loader::Library { dylib, rlib, metadata } = lib; + let Library { dylib, rlib, metadata } = lib; let cnum_map = self.resolve_crate_deps(root, &crate_root, &metadata, cnum, span); diff --git a/src/librustc_metadata/loader.rs b/src/librustc_metadata/loader.rs index 75242fc36db..a5e08c77eaa 100644 --- a/src/librustc_metadata/loader.rs +++ b/src/librustc_metadata/loader.rs @@ -213,6 +213,7 @@ //! metadata::loader or metadata::creader for all the juicy details! use cstore::MetadataBlob; +use creader::Library; use schema::{METADATA_HEADER, rustc_version}; use rustc::hir::svh::Svh; @@ -263,12 +264,6 @@ pub struct Context<'a> { pub should_match_name: bool, } -pub struct Library { - pub dylib: Option<(PathBuf, PathKind)>, - pub rlib: Option<(PathBuf, PathKind)>, - pub metadata: MetadataBlob, -} - pub struct ArchiveMetadata { _archive: ArchiveRO, // points into self._archive -- cgit 1.4.1-3-g733a5 From f3993d1a7f9176140f233ff93ea68f76d80d8250 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 20 Oct 2016 04:31:14 +0000 Subject: Rename `loader.rs` -> `locator.rs`. --- src/librustc_driver/lib.rs | 5 +- src/librustc_metadata/creader.rs | 30 +- src/librustc_metadata/cstore.rs | 4 +- src/librustc_metadata/cstore_impl.rs | 6 +- src/librustc_metadata/lib.rs | 2 +- src/librustc_metadata/loader.rs | 892 ----------------------------------- src/librustc_metadata/locator.rs | 892 +++++++++++++++++++++++++++++++++++ 7 files changed, 915 insertions(+), 916 deletions(-) delete mode 100644 src/librustc_metadata/loader.rs create mode 100644 src/librustc_metadata/locator.rs (limited to 'src') diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 6972bdac5e1..cb001688da2 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -77,7 +77,7 @@ use rustc::session::config::nightly_options; use rustc::session::early_error; use rustc::lint::Lint; use rustc::lint; -use rustc_metadata::loader; +use rustc_metadata::locator; use rustc_metadata::cstore::CStore; use rustc::util::common::time; @@ -578,8 +578,7 @@ impl RustcDefaultCalls { &Input::File(ref ifile) => { let path = &(*ifile); let mut v = Vec::new(); - loader::list_file_metadata(&sess.target.target, path, &mut v) - .unwrap(); + locator::list_file_metadata(&sess.target.target, path, &mut v).unwrap(); println!("{}", String::from_utf8(v).unwrap()); } &Input::Str { .. } => { diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index c016f01b505..7ae3f6f8107 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -11,7 +11,7 @@ //! Validates all used crates and extern libraries and loads their metadata use cstore::{self, CStore, CrateSource, MetadataBlob}; -use loader::{self, CratePaths}; +use locator::{self, CratePaths}; use macro_import; use schema::CrateRoot; @@ -352,7 +352,7 @@ impl<'a> CrateLoader<'a> { Some(cnum) => LoadResult::Previous(cnum), None => { info!("falling back to a load"); - let mut load_ctxt = loader::Context { + let mut locate_ctxt = locator::Context { sess: self.sess, span: span, ident: ident, @@ -368,9 +368,9 @@ impl<'a> CrateLoader<'a> { rejected_via_version: vec!(), should_match_name: true, }; - match self.load(&mut load_ctxt) { + match self.load(&mut locate_ctxt) { Some(result) => result, - None => load_ctxt.report_load_errs(), + None => locate_ctxt.report_errs(), } } }; @@ -390,8 +390,8 @@ impl<'a> CrateLoader<'a> { } } - fn load(&mut self, loader: &mut loader::Context) -> Option { - let library = match loader.maybe_load_library_crate() { + fn load(&mut self, locate_ctxt: &mut locator::Context) -> Option { + let library = match locate_ctxt.maybe_load_library_crate() { Some(lib) => lib, None => return None, }; @@ -405,11 +405,11 @@ impl<'a> CrateLoader<'a> { // don't want to match a host crate against an equivalent target one // already loaded. let root = library.metadata.get_root(); - if loader.triple == self.sess.opts.target_triple { + if locate_ctxt.triple == self.sess.opts.target_triple { let mut result = LoadResult::Loaded(library); self.cstore.iter_crate_data(|cnum, data| { if data.name() == root.name && root.hash == data.hash() { - assert!(loader.hash.is_none()); + assert!(locate_ctxt.hash.is_none()); info!("load success, going to previous cnum: {}", cnum); result = LoadResult::Previous(cnum); } @@ -494,7 +494,7 @@ impl<'a> CrateLoader<'a> { let mut target_only = false; let ident = info.ident.clone(); let name = info.name.clone(); - let mut load_ctxt = loader::Context { + let mut locate_ctxt = locator::Context { sess: self.sess, span: span, ident: &ident[..], @@ -510,7 +510,7 @@ impl<'a> CrateLoader<'a> { rejected_via_version: vec!(), should_match_name: true, }; - let library = self.load(&mut load_ctxt).or_else(|| { + let library = self.load(&mut locate_ctxt).or_else(|| { if !is_cross { return None } @@ -519,15 +519,15 @@ impl<'a> CrateLoader<'a> { target_only = true; should_link = info.should_link; - load_ctxt.target = &self.sess.target.target; - load_ctxt.triple = target_triple; - load_ctxt.filesearch = self.sess.target_filesearch(PathKind::Crate); + locate_ctxt.target = &self.sess.target.target; + locate_ctxt.triple = target_triple; + locate_ctxt.filesearch = self.sess.target_filesearch(PathKind::Crate); - self.load(&mut load_ctxt) + self.load(&mut locate_ctxt) }); let library = match library { Some(l) => l, - None => load_ctxt.report_load_errs(), + None => locate_ctxt.report_errs(), }; let (dylib, metadata) = match library { diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 038d0f73d5c..a87e61c4c94 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -11,7 +11,7 @@ // The crate store - a central repo for information collected about external // crates and libraries -use loader; +use locator; use schema; use rustc::dep_graph::DepGraph; @@ -43,7 +43,7 @@ pub type CrateNumMap = IndexVec; pub enum MetadataBlob { Inflated(Bytes), - Archive(loader::ArchiveMetadata), + Archive(locator::ArchiveMetadata), } /// Holds information about a syntax_pos::FileMap imported from another crate. diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 54664b9c040..7637b769f93 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -10,7 +10,7 @@ use cstore; use encoder; -use loader; +use locator; use schema; use rustc::middle::cstore::{InlinedItem, CrateStore, CrateSource, ExternCrate}; @@ -497,12 +497,12 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { fn metadata_filename(&self) -> &str { - loader::METADATA_FILENAME + locator::METADATA_FILENAME } fn metadata_section_name(&self, target: &Target) -> &str { - loader::meta_section_name(target) + locator::meta_section_name(target) } fn used_crates(&self, prefer: LinkagePreference) -> Vec<(CrateNum, Option)> diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index cc78dafd7ed..c0fc1a7065c 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -58,7 +58,7 @@ mod schema; pub mod creader; pub mod cstore; -pub mod loader; +pub mod locator; pub mod macro_import; __build_diagnostic_array! { librustc_metadata, DIAGNOSTICS } diff --git a/src/librustc_metadata/loader.rs b/src/librustc_metadata/loader.rs deleted file mode 100644 index a5e08c77eaa..00000000000 --- a/src/librustc_metadata/loader.rs +++ /dev/null @@ -1,892 +0,0 @@ -// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Finds crate binaries and loads their metadata -//! -//! Might I be the first to welcome you to a world of platform differences, -//! version requirements, dependency graphs, conflicting desires, and fun! This -//! is the major guts (along with metadata::creader) of the compiler for loading -//! crates and resolving dependencies. Let's take a tour! -//! -//! # The problem -//! -//! Each invocation of the compiler is immediately concerned with one primary -//! problem, to connect a set of crates to resolved crates on the filesystem. -//! Concretely speaking, the compiler follows roughly these steps to get here: -//! -//! 1. Discover a set of `extern crate` statements. -//! 2. Transform these directives into crate names. If the directive does not -//! have an explicit name, then the identifier is the name. -//! 3. For each of these crate names, find a corresponding crate on the -//! filesystem. -//! -//! Sounds easy, right? Let's walk into some of the nuances. -//! -//! ## Transitive Dependencies -//! -//! Let's say we've got three crates: A, B, and C. A depends on B, and B depends -//! on C. When we're compiling A, we primarily need to find and locate B, but we -//! also end up needing to find and locate C as well. -//! -//! The reason for this is that any of B's types could be composed of C's types, -//! any function in B could return a type from C, etc. To be able to guarantee -//! that we can always typecheck/translate any function, we have to have -//! complete knowledge of the whole ecosystem, not just our immediate -//! dependencies. -//! -//! So now as part of the "find a corresponding crate on the filesystem" step -//! above, this involves also finding all crates for *all upstream -//! dependencies*. This includes all dependencies transitively. -//! -//! ## Rlibs and Dylibs -//! -//! The compiler has two forms of intermediate dependencies. These are dubbed -//! rlibs and dylibs for the static and dynamic variants, respectively. An rlib -//! is a rustc-defined file format (currently just an ar archive) while a dylib -//! is a platform-defined dynamic library. Each library has a metadata somewhere -//! inside of it. -//! -//! When translating a crate name to a crate on the filesystem, we all of a -//! sudden need to take into account both rlibs and dylibs! Linkage later on may -//! use either one of these files, as each has their pros/cons. The job of crate -//! loading is to discover what's possible by finding all candidates. -//! -//! Most parts of this loading systems keep the dylib/rlib as just separate -//! variables. -//! -//! ## Where to look? -//! -//! We can't exactly scan your whole hard drive when looking for dependencies, -//! so we need to places to look. Currently the compiler will implicitly add the -//! target lib search path ($prefix/lib/rustlib/$target/lib) to any compilation, -//! and otherwise all -L flags are added to the search paths. -//! -//! ## What criterion to select on? -//! -//! This a pretty tricky area of loading crates. Given a file, how do we know -//! whether it's the right crate? Currently, the rules look along these lines: -//! -//! 1. Does the filename match an rlib/dylib pattern? That is to say, does the -//! filename have the right prefix/suffix? -//! 2. Does the filename have the right prefix for the crate name being queried? -//! This is filtering for files like `libfoo*.rlib` and such. -//! 3. Is the file an actual rust library? This is done by loading the metadata -//! from the library and making sure it's actually there. -//! 4. Does the name in the metadata agree with the name of the library? -//! 5. Does the target in the metadata agree with the current target? -//! 6. Does the SVH match? (more on this later) -//! -//! If the file answers `yes` to all these questions, then the file is -//! considered as being *candidate* for being accepted. It is illegal to have -//! more than two candidates as the compiler has no method by which to resolve -//! this conflict. Additionally, rlib/dylib candidates are considered -//! separately. -//! -//! After all this has happened, we have 1 or two files as candidates. These -//! represent the rlib/dylib file found for a library, and they're returned as -//! being found. -//! -//! ### What about versions? -//! -//! A lot of effort has been put forth to remove versioning from the compiler. -//! There have been forays in the past to have versioning baked in, but it was -//! largely always deemed insufficient to the point that it was recognized that -//! it's probably something the compiler shouldn't do anyway due to its -//! complicated nature and the state of the half-baked solutions. -//! -//! With a departure from versioning, the primary criterion for loading crates -//! is just the name of a crate. If we stopped here, it would imply that you -//! could never link two crates of the same name from different sources -//! together, which is clearly a bad state to be in. -//! -//! To resolve this problem, we come to the next section! -//! -//! # Expert Mode -//! -//! A number of flags have been added to the compiler to solve the "version -//! problem" in the previous section, as well as generally enabling more -//! powerful usage of the crate loading system of the compiler. The goal of -//! these flags and options are to enable third-party tools to drive the -//! compiler with prior knowledge about how the world should look. -//! -//! ## The `--extern` flag -//! -//! The compiler accepts a flag of this form a number of times: -//! -//! ```text -//! --extern crate-name=path/to/the/crate.rlib -//! ``` -//! -//! This flag is basically the following letter to the compiler: -//! -//! > Dear rustc, -//! > -//! > When you are attempting to load the immediate dependency `crate-name`, I -//! > would like you to assume that the library is located at -//! > `path/to/the/crate.rlib`, and look nowhere else. Also, please do not -//! > assume that the path I specified has the name `crate-name`. -//! -//! This flag basically overrides most matching logic except for validating that -//! the file is indeed a rust library. The same `crate-name` can be specified -//! twice to specify the rlib/dylib pair. -//! -//! ## Enabling "multiple versions" -//! -//! This basically boils down to the ability to specify arbitrary packages to -//! the compiler. For example, if crate A wanted to use Bv1 and Bv2, then it -//! would look something like: -//! -//! ```ignore -//! extern crate b1; -//! extern crate b2; -//! -//! fn main() {} -//! ``` -//! -//! and the compiler would be invoked as: -//! -//! ```text -//! rustc a.rs --extern b1=path/to/libb1.rlib --extern b2=path/to/libb2.rlib -//! ``` -//! -//! In this scenario there are two crates named `b` and the compiler must be -//! manually driven to be informed where each crate is. -//! -//! ## Frobbing symbols -//! -//! One of the immediate problems with linking the same library together twice -//! in the same problem is dealing with duplicate symbols. The primary way to -//! deal with this in rustc is to add hashes to the end of each symbol. -//! -//! In order to force hashes to change between versions of a library, if -//! desired, the compiler exposes an option `-C metadata=foo`, which is used to -//! initially seed each symbol hash. The string `foo` is prepended to each -//! string-to-hash to ensure that symbols change over time. -//! -//! ## Loading transitive dependencies -//! -//! Dealing with same-named-but-distinct crates is not just a local problem, but -//! one that also needs to be dealt with for transitive dependencies. Note that -//! in the letter above `--extern` flags only apply to the *local* set of -//! dependencies, not the upstream transitive dependencies. Consider this -//! dependency graph: -//! -//! ```text -//! A.1 A.2 -//! | | -//! | | -//! B C -//! \ / -//! \ / -//! D -//! ``` -//! -//! In this scenario, when we compile `D`, we need to be able to distinctly -//! resolve `A.1` and `A.2`, but an `--extern` flag cannot apply to these -//! transitive dependencies. -//! -//! Note that the key idea here is that `B` and `C` are both *already compiled*. -//! That is, they have already resolved their dependencies. Due to unrelated -//! technical reasons, when a library is compiled, it is only compatible with -//! the *exact same* version of the upstream libraries it was compiled against. -//! We use the "Strict Version Hash" to identify the exact copy of an upstream -//! library. -//! -//! With this knowledge, we know that `B` and `C` will depend on `A` with -//! different SVH values, so we crawl the normal `-L` paths looking for -//! `liba*.rlib` and filter based on the contained SVH. -//! -//! In the end, this ends up not needing `--extern` to specify upstream -//! transitive dependencies. -//! -//! # Wrapping up -//! -//! That's the general overview of loading crates in the compiler, but it's by -//! no means all of the necessary details. Take a look at the rest of -//! metadata::loader or metadata::creader for all the juicy details! - -use cstore::MetadataBlob; -use creader::Library; -use schema::{METADATA_HEADER, rustc_version}; - -use rustc::hir::svh::Svh; -use rustc::session::Session; -use rustc::session::filesearch::{FileSearch, FileMatches, FileDoesntMatch}; -use rustc::session::search_paths::PathKind; -use rustc::util::common; -use rustc::util::nodemap::FnvHashMap; - -use rustc_llvm as llvm; -use rustc_llvm::{False, ObjectFile, mk_section_iter}; -use rustc_llvm::archive_ro::ArchiveRO; -use errors::DiagnosticBuilder; -use syntax_pos::Span; -use rustc_back::target::Target; - -use std::cmp; -use std::fmt; -use std::fs; -use std::io; -use std::path::{Path, PathBuf}; -use std::ptr; -use std::slice; -use std::time::Instant; - -use flate; - -pub struct CrateMismatch { - path: PathBuf, - got: String, -} - -pub struct Context<'a> { - pub sess: &'a Session, - pub span: Span, - pub ident: &'a str, - pub crate_name: &'a str, - pub hash: Option<&'a Svh>, - // points to either self.sess.target.target or self.sess.host, must match triple - pub target: &'a Target, - pub triple: &'a str, - pub filesearch: FileSearch<'a>, - pub root: &'a Option, - pub rejected_via_hash: Vec, - pub rejected_via_triple: Vec, - pub rejected_via_kind: Vec, - pub rejected_via_version: Vec, - pub should_match_name: bool, -} - -pub struct ArchiveMetadata { - _archive: ArchiveRO, - // points into self._archive - data: *const [u8], -} - -pub struct CratePaths { - pub ident: String, - pub dylib: Option, - pub rlib: Option -} - -pub const METADATA_FILENAME: &'static str = "rust.metadata.bin"; - -#[derive(Copy, Clone, PartialEq)] -enum CrateFlavor { - Rlib, - Dylib -} - -impl fmt::Display for CrateFlavor { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match *self { - CrateFlavor::Rlib => "rlib", - CrateFlavor::Dylib => "dylib" - }) - } -} - -impl CratePaths { - fn paths(&self) -> Vec { - match (&self.dylib, &self.rlib) { - (&None, &None) => vec!(), - (&Some(ref p), &None) | - (&None, &Some(ref p)) => vec!(p.clone()), - (&Some(ref p1), &Some(ref p2)) => vec!(p1.clone(), p2.clone()), - } - } -} - -impl<'a> Context<'a> { - pub fn maybe_load_library_crate(&mut self) -> Option { - self.find_library_crate() - } - - pub fn load_library_crate(&mut self) -> Library { - self.find_library_crate().unwrap_or_else(|| self.report_load_errs()) - } - - pub fn report_load_errs(&mut self) -> ! { - let add = match self.root { - &None => String::new(), - &Some(ref r) => format!(" which `{}` depends on", - r.ident) - }; - let mut err = if !self.rejected_via_hash.is_empty() { - struct_span_err!(self.sess, self.span, E0460, - "found possibly newer version of crate `{}`{}", - self.ident, add) - } else if !self.rejected_via_triple.is_empty() { - struct_span_err!(self.sess, self.span, E0461, - "couldn't find crate `{}` with expected target triple {}{}", - self.ident, self.triple, add) - } else if !self.rejected_via_kind.is_empty() { - struct_span_err!(self.sess, self.span, E0462, - "found staticlib `{}` instead of rlib or dylib{}", - self.ident, add) - } else if !self.rejected_via_version.is_empty() { - struct_span_err!(self.sess, self.span, E0514, - "found crate `{}` compiled by an incompatible version of rustc{}", - self.ident, add) - } else { - let mut err = struct_span_err!(self.sess, self.span, E0463, - "can't find crate for `{}`{}", - self.ident, add); - err.span_label(self.span, &format!("can't find crate")); - err - }; - - if !self.rejected_via_triple.is_empty() { - let mismatches = self.rejected_via_triple.iter(); - for (i, &CrateMismatch{ ref path, ref got }) in mismatches.enumerate() { - err.note(&format!("crate `{}`, path #{}, triple {}: {}", - self.ident, i+1, got, path.display())); - } - } - if !self.rejected_via_hash.is_empty() { - err.note("perhaps that crate needs to be recompiled?"); - let mismatches = self.rejected_via_hash.iter(); - for (i, &CrateMismatch{ ref path, .. }) in mismatches.enumerate() { - err.note(&format!("crate `{}` path #{}: {}", - self.ident, i+1, path.display())); - } - match self.root { - &None => {} - &Some(ref r) => { - for (i, path) in r.paths().iter().enumerate() { - err.note(&format!("crate `{}` path #{}: {}", - r.ident, i+1, path.display())); - } - } - } - } - if !self.rejected_via_kind.is_empty() { - err.help("please recompile that crate using --crate-type lib"); - let mismatches = self.rejected_via_kind.iter(); - for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() { - err.note(&format!("crate `{}` path #{}: {}", - self.ident, i+1, path.display())); - } - } - if !self.rejected_via_version.is_empty() { - err.help(&format!("please recompile that crate using this compiler ({})", - rustc_version())); - let mismatches = self.rejected_via_version.iter(); - for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() { - err.note(&format!("crate `{}` path #{}: {} compiled by {:?}", - self.ident, i+1, path.display(), got)); - } - } - - err.emit(); - self.sess.abort_if_errors(); - unreachable!(); - } - - fn find_library_crate(&mut self) -> Option { - // If an SVH is specified, then this is a transitive dependency that - // must be loaded via -L plus some filtering. - if self.hash.is_none() { - self.should_match_name = false; - if let Some(s) = self.sess.opts.externs.get(self.crate_name) { - return self.find_commandline_library(s.iter()); - } - self.should_match_name = true; - } - - let dypair = self.dylibname(); - let staticpair = self.staticlibname(); - - // want: crate_name.dir_part() + prefix + crate_name.file_part + "-" - let dylib_prefix = format!("{}{}", dypair.0, self.crate_name); - let rlib_prefix = format!("lib{}", self.crate_name); - let staticlib_prefix = format!("{}{}", staticpair.0, self.crate_name); - - let mut candidates = FnvHashMap(); - let mut staticlibs = vec!(); - - // First, find all possible candidate rlibs and dylibs purely based on - // the name of the files themselves. We're trying to match against an - // exact crate name and a possibly an exact hash. - // - // During this step, we can filter all found libraries based on the - // name and id found in the crate id (we ignore the path portion for - // filename matching), as well as the exact hash (if specified). If we - // end up having many candidates, we must look at the metadata to - // perform exact matches against hashes/crate ids. Note that opening up - // the metadata is where we do an exact match against the full contents - // of the crate id (path/name/id). - // - // The goal of this step is to look at as little metadata as possible. - self.filesearch.search(|path, kind| { - let file = match path.file_name().and_then(|s| s.to_str()) { - None => return FileDoesntMatch, - Some(file) => file, - }; - let (hash, rlib) = if file.starts_with(&rlib_prefix[..]) && - file.ends_with(".rlib") { - (&file[(rlib_prefix.len()) .. (file.len() - ".rlib".len())], - true) - } else if file.starts_with(&dylib_prefix) && - file.ends_with(&dypair.1) { - (&file[(dylib_prefix.len()) .. (file.len() - dypair.1.len())], - false) - } else { - if file.starts_with(&staticlib_prefix[..]) && - file.ends_with(&staticpair.1) { - staticlibs.push(CrateMismatch { - path: path.to_path_buf(), - got: "static".to_string() - }); - } - return FileDoesntMatch - }; - info!("lib candidate: {}", path.display()); - - let hash_str = hash.to_string(); - let slot = candidates.entry(hash_str) - .or_insert_with(|| (FnvHashMap(), FnvHashMap())); - let (ref mut rlibs, ref mut dylibs) = *slot; - fs::canonicalize(path).map(|p| { - if rlib { - rlibs.insert(p, kind); - } else { - dylibs.insert(p, kind); - } - FileMatches - }).unwrap_or(FileDoesntMatch) - }); - self.rejected_via_kind.extend(staticlibs); - - // We have now collected all known libraries into a set of candidates - // keyed of the filename hash listed. For each filename, we also have a - // list of rlibs/dylibs that apply. Here, we map each of these lists - // (per hash), to a Library candidate for returning. - // - // A Library candidate is created if the metadata for the set of - // libraries corresponds to the crate id and hash criteria that this - // search is being performed for. - let mut libraries = FnvHashMap(); - for (_hash, (rlibs, dylibs)) in candidates { - let mut slot = None; - let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot); - let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot); - if let Some((h, m)) = slot { - libraries.insert(h, Library { - dylib: dylib, - rlib: rlib, - metadata: m, - }); - } - } - - // Having now translated all relevant found hashes into libraries, see - // what we've got and figure out if we found multiple candidates for - // libraries or not. - match libraries.len() { - 0 => None, - 1 => Some(libraries.into_iter().next().unwrap().1), - _ => { - let mut err = struct_span_err!(self.sess, self.span, E0464, - "multiple matching crates for `{}`", - self.crate_name); - err.note("candidates:"); - for (_, lib) in libraries { - if let Some((ref p, _)) = lib.dylib { - err.note(&format!("path: {}", p.display())); - } - if let Some((ref p, _)) = lib.rlib { - err.note(&format!("path: {}", p.display())); - } - note_crate_name(&mut err, &lib.metadata.get_root().name); - } - err.emit(); - None - } - } - } - - // Attempts to extract *one* library from the set `m`. If the set has no - // elements, `None` is returned. If the set has more than one element, then - // the errors and notes are emitted about the set of libraries. - // - // With only one library in the set, this function will extract it, and then - // read the metadata from it if `*slot` is `None`. If the metadata couldn't - // be read, it is assumed that the file isn't a valid rust library (no - // errors are emitted). - fn extract_one(&mut self, m: FnvHashMap, flavor: CrateFlavor, - slot: &mut Option<(Svh, MetadataBlob)>) -> Option<(PathBuf, PathKind)> { - let mut ret: Option<(PathBuf, PathKind)> = None; - let mut error = 0; - - if slot.is_some() { - // FIXME(#10786): for an optimization, we only read one of the - // libraries' metadata sections. In theory we should - // read both, but reading dylib metadata is quite - // slow. - if m.is_empty() { - return None - } else if m.len() == 1 { - return Some(m.into_iter().next().unwrap()) - } - } - - let mut err: Option = None; - for (lib, kind) in m { - info!("{} reading metadata from: {}", flavor, lib.display()); - let (hash, metadata) = match get_metadata_section(self.target, flavor, &lib) { - Ok(blob) => { - if let Some(h) = self.crate_matches(&blob, &lib) { - (h, blob) - } else { - info!("metadata mismatch"); - continue - } - } - Err(err) => { - info!("no metadata found: {}", err); - continue - } - }; - // If we see multiple hashes, emit an error about duplicate candidates. - if slot.as_ref().map_or(false, |s| s.0 != hash) { - let mut e = struct_span_err!(self.sess, self.span, E0465, - "multiple {} candidates for `{}` found", - flavor, self.crate_name); - e.span_note(self.span, - &format!(r"candidate #1: {}", - ret.as_ref().unwrap().0 - .display())); - if let Some(ref mut e) = err { - e.emit(); - } - err = Some(e); - error = 1; - *slot = None; - } - if error > 0 { - error += 1; - err.as_mut().unwrap().span_note(self.span, - &format!(r"candidate #{}: {}", error, - lib.display())); - continue - } - *slot = Some((hash, metadata)); - ret = Some((lib, kind)); - } - - if error > 0 { - err.unwrap().emit(); - None - } else { - ret - } - } - - fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option { - let root = metadata.get_root(); - let rustc_version = rustc_version(); - if root.rustc_version != rustc_version { - info!("Rejecting via version: expected {} got {}", - rustc_version, root.rustc_version); - self.rejected_via_version.push(CrateMismatch { - path: libpath.to_path_buf(), - got: root.rustc_version - }); - return None; - } - - if self.should_match_name { - if self.crate_name != root.name { - info!("Rejecting via crate name"); return None; - } - } - - if root.triple != self.triple { - info!("Rejecting via crate triple: expected {} got {}", - self.triple, root.triple); - self.rejected_via_triple.push(CrateMismatch { - path: libpath.to_path_buf(), - got: root.triple - }); - return None; - } - - if let Some(myhash) = self.hash { - if *myhash != root.hash { - info!("Rejecting via hash: expected {} got {}", - *myhash, root.hash); - self.rejected_via_hash.push(CrateMismatch { - path: libpath.to_path_buf(), - got: myhash.to_string() - }); - return None; - } - } - - Some(root.hash) - } - - - // Returns the corresponding (prefix, suffix) that files need to have for - // dynamic libraries - fn dylibname(&self) -> (String, String) { - let t = &self.target; - (t.options.dll_prefix.clone(), t.options.dll_suffix.clone()) - } - - // Returns the corresponding (prefix, suffix) that files need to have for - // static libraries - fn staticlibname(&self) -> (String, String) { - let t = &self.target; - (t.options.staticlib_prefix.clone(), t.options.staticlib_suffix.clone()) - } - - fn find_commandline_library<'b, LOCS> (&mut self, locs: LOCS) -> Option - where LOCS: Iterator - { - // First, filter out all libraries that look suspicious. We only accept - // files which actually exist that have the correct naming scheme for - // rlibs/dylibs. - let sess = self.sess; - let dylibname = self.dylibname(); - let mut rlibs = FnvHashMap(); - let mut dylibs = FnvHashMap(); - { - let locs = locs.map(|l| PathBuf::from(l)).filter(|loc| { - if !loc.exists() { - sess.err(&format!("extern location for {} does not exist: {}", - self.crate_name, loc.display())); - return false; - } - let file = match loc.file_name().and_then(|s| s.to_str()) { - Some(file) => file, - None => { - sess.err(&format!("extern location for {} is not a file: {}", - self.crate_name, loc.display())); - return false; - } - }; - if file.starts_with("lib") && file.ends_with(".rlib") { - return true - } else { - let (ref prefix, ref suffix) = dylibname; - if file.starts_with(&prefix[..]) && - file.ends_with(&suffix[..]) { - return true - } - } - sess.struct_err(&format!("extern location for {} is of an unknown type: {}", - self.crate_name, loc.display())) - .help(&format!("file name should be lib*.rlib or {}*.{}", - dylibname.0, dylibname.1)) - .emit(); - false - }); - - // Now that we have an iterator of good candidates, make sure - // there's at most one rlib and at most one dylib. - for loc in locs { - if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { - rlibs.insert(fs::canonicalize(&loc).unwrap(), - PathKind::ExternFlag); - } else { - dylibs.insert(fs::canonicalize(&loc).unwrap(), - PathKind::ExternFlag); - } - } - }; - - // Extract the rlib/dylib pair. - let mut slot = None; - let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot); - let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot); - - if rlib.is_none() && dylib.is_none() { return None } - match slot { - Some((_, metadata)) => Some(Library { - dylib: dylib, - rlib: rlib, - metadata: metadata, - }), - None => None, - } - } -} - -pub fn note_crate_name(err: &mut DiagnosticBuilder, name: &str) { - err.note(&format!("crate name: {}", name)); -} - -impl ArchiveMetadata { - fn new(ar: ArchiveRO) -> Option { - let data = { - let section = ar.iter().filter_map(|s| s.ok()).find(|sect| { - sect.name() == Some(METADATA_FILENAME) - }); - match section { - Some(s) => s.data() as *const [u8], - None => { - debug!("didn't find '{}' in the archive", METADATA_FILENAME); - return None; - } - } - }; - - Some(ArchiveMetadata { - _archive: ar, - data: data, - }) - } - - pub fn as_slice<'a>(&'a self) -> &'a [u8] { unsafe { &*self.data } } -} - -fn verify_decompressed_encoding_version(blob: &MetadataBlob, filename: &Path) - -> Result<(), String> -{ - if !blob.is_compatible() { - Err((format!("incompatible metadata version found: '{}'", - filename.display()))) - } else { - Ok(()) - } -} - -// Just a small wrapper to time how long reading metadata takes. -fn get_metadata_section(target: &Target, flavor: CrateFlavor, filename: &Path) - -> Result { - let start = Instant::now(); - let ret = get_metadata_section_imp(target, flavor, filename); - info!("reading {:?} => {:?}", filename.file_name().unwrap(), - start.elapsed()); - return ret -} - -fn get_metadata_section_imp(target: &Target, flavor: CrateFlavor, filename: &Path) - -> Result { - if !filename.exists() { - return Err(format!("no such file: '{}'", filename.display())); - } - if flavor == CrateFlavor::Rlib { - // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap - // internally to read the file. We also avoid even using a memcpy by - // just keeping the archive along while the metadata is in use. - let archive = match ArchiveRO::open(filename) { - Some(ar) => ar, - None => { - debug!("llvm didn't like `{}`", filename.display()); - return Err(format!("failed to read rlib metadata: '{}'", - filename.display())); - } - }; - return match ArchiveMetadata::new(archive).map(|ar| MetadataBlob::Archive(ar)) { - None => Err(format!("failed to read rlib metadata: '{}'", - filename.display())), - Some(blob) => { - verify_decompressed_encoding_version(&blob, filename)?; - Ok(blob) - } - }; - } - unsafe { - let buf = common::path2cstr(filename); - let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr()); - if mb as isize == 0 { - return Err(format!("error reading library: '{}'", - filename.display())) - } - let of = match ObjectFile::new(mb) { - Some(of) => of, - _ => { - return Err((format!("provided path not an object file: '{}'", - filename.display()))) - } - }; - let si = mk_section_iter(of.llof); - while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { - let mut name_buf = ptr::null(); - let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf); - let name = slice::from_raw_parts(name_buf as *const u8, - name_len as usize).to_vec(); - let name = String::from_utf8(name).unwrap(); - debug!("get_metadata_section: name {}", name); - if read_meta_section_name(target) == name { - let cbuf = llvm::LLVMGetSectionContents(si.llsi); - let csz = llvm::LLVMGetSectionSize(si.llsi) as usize; - let cvbuf: *const u8 = cbuf as *const u8; - let vlen = METADATA_HEADER.len(); - debug!("checking {} bytes of metadata-version stamp", - vlen); - let minsz = cmp::min(vlen, csz); - let buf0 = slice::from_raw_parts(cvbuf, minsz); - let version_ok = buf0 == METADATA_HEADER; - if !version_ok { - return Err((format!("incompatible metadata version found: '{}'", - filename.display()))); - } - - let cvbuf1 = cvbuf.offset(vlen as isize); - debug!("inflating {} bytes of compressed metadata", - csz - vlen); - let bytes = slice::from_raw_parts(cvbuf1, csz - vlen); - match flate::inflate_bytes(bytes) { - Ok(inflated) => { - let blob = MetadataBlob::Inflated(inflated); - verify_decompressed_encoding_version(&blob, filename)?; - return Ok(blob); - } - Err(_) => {} - } - } - llvm::LLVMMoveToNextSection(si.llsi); - } - Err(format!("metadata not found: '{}'", filename.display())) - } -} - -pub fn meta_section_name(target: &Target) -> &'static str { - // Historical note: - // - // When using link.exe it was seen that the section name `.note.rustc` - // was getting shortened to `.note.ru`, and according to the PE and COFF - // specification: - // - // > Executable images do not use a string table and do not support - // > section names longer than 8 characters - // - // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx - // - // As a result, we choose a slightly shorter name! As to why - // `.note.rustc` works on MinGW, that's another good question... - - if target.options.is_like_osx { - "__DATA,.rustc" - } else { - ".rustc" - } -} - -pub fn read_meta_section_name(_target: &Target) -> &'static str { - ".rustc" -} - -// A diagnostic function for dumping crate metadata to an output stream -pub fn list_file_metadata(target: &Target, path: &Path, - out: &mut io::Write) -> io::Result<()> { - let filename = path.file_name().unwrap().to_str().unwrap(); - let flavor = if filename.ends_with(".rlib") { CrateFlavor::Rlib } else { CrateFlavor::Dylib }; - match get_metadata_section(target, flavor, path) { - Ok(metadata) => metadata.list_crate_metadata(out), - Err(msg) => { - write!(out, "{}\n", msg) - } - } -} diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs new file mode 100644 index 00000000000..e684cd16366 --- /dev/null +++ b/src/librustc_metadata/locator.rs @@ -0,0 +1,892 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Finds crate binaries and loads their metadata +//! +//! Might I be the first to welcome you to a world of platform differences, +//! version requirements, dependency graphs, conflicting desires, and fun! This +//! is the major guts (along with metadata::creader) of the compiler for loading +//! crates and resolving dependencies. Let's take a tour! +//! +//! # The problem +//! +//! Each invocation of the compiler is immediately concerned with one primary +//! problem, to connect a set of crates to resolved crates on the filesystem. +//! Concretely speaking, the compiler follows roughly these steps to get here: +//! +//! 1. Discover a set of `extern crate` statements. +//! 2. Transform these directives into crate names. If the directive does not +//! have an explicit name, then the identifier is the name. +//! 3. For each of these crate names, find a corresponding crate on the +//! filesystem. +//! +//! Sounds easy, right? Let's walk into some of the nuances. +//! +//! ## Transitive Dependencies +//! +//! Let's say we've got three crates: A, B, and C. A depends on B, and B depends +//! on C. When we're compiling A, we primarily need to find and locate B, but we +//! also end up needing to find and locate C as well. +//! +//! The reason for this is that any of B's types could be composed of C's types, +//! any function in B could return a type from C, etc. To be able to guarantee +//! that we can always typecheck/translate any function, we have to have +//! complete knowledge of the whole ecosystem, not just our immediate +//! dependencies. +//! +//! So now as part of the "find a corresponding crate on the filesystem" step +//! above, this involves also finding all crates for *all upstream +//! dependencies*. This includes all dependencies transitively. +//! +//! ## Rlibs and Dylibs +//! +//! The compiler has two forms of intermediate dependencies. These are dubbed +//! rlibs and dylibs for the static and dynamic variants, respectively. An rlib +//! is a rustc-defined file format (currently just an ar archive) while a dylib +//! is a platform-defined dynamic library. Each library has a metadata somewhere +//! inside of it. +//! +//! When translating a crate name to a crate on the filesystem, we all of a +//! sudden need to take into account both rlibs and dylibs! Linkage later on may +//! use either one of these files, as each has their pros/cons. The job of crate +//! loading is to discover what's possible by finding all candidates. +//! +//! Most parts of this loading systems keep the dylib/rlib as just separate +//! variables. +//! +//! ## Where to look? +//! +//! We can't exactly scan your whole hard drive when looking for dependencies, +//! so we need to places to look. Currently the compiler will implicitly add the +//! target lib search path ($prefix/lib/rustlib/$target/lib) to any compilation, +//! and otherwise all -L flags are added to the search paths. +//! +//! ## What criterion to select on? +//! +//! This a pretty tricky area of loading crates. Given a file, how do we know +//! whether it's the right crate? Currently, the rules look along these lines: +//! +//! 1. Does the filename match an rlib/dylib pattern? That is to say, does the +//! filename have the right prefix/suffix? +//! 2. Does the filename have the right prefix for the crate name being queried? +//! This is filtering for files like `libfoo*.rlib` and such. +//! 3. Is the file an actual rust library? This is done by loading the metadata +//! from the library and making sure it's actually there. +//! 4. Does the name in the metadata agree with the name of the library? +//! 5. Does the target in the metadata agree with the current target? +//! 6. Does the SVH match? (more on this later) +//! +//! If the file answers `yes` to all these questions, then the file is +//! considered as being *candidate* for being accepted. It is illegal to have +//! more than two candidates as the compiler has no method by which to resolve +//! this conflict. Additionally, rlib/dylib candidates are considered +//! separately. +//! +//! After all this has happened, we have 1 or two files as candidates. These +//! represent the rlib/dylib file found for a library, and they're returned as +//! being found. +//! +//! ### What about versions? +//! +//! A lot of effort has been put forth to remove versioning from the compiler. +//! There have been forays in the past to have versioning baked in, but it was +//! largely always deemed insufficient to the point that it was recognized that +//! it's probably something the compiler shouldn't do anyway due to its +//! complicated nature and the state of the half-baked solutions. +//! +//! With a departure from versioning, the primary criterion for loading crates +//! is just the name of a crate. If we stopped here, it would imply that you +//! could never link two crates of the same name from different sources +//! together, which is clearly a bad state to be in. +//! +//! To resolve this problem, we come to the next section! +//! +//! # Expert Mode +//! +//! A number of flags have been added to the compiler to solve the "version +//! problem" in the previous section, as well as generally enabling more +//! powerful usage of the crate loading system of the compiler. The goal of +//! these flags and options are to enable third-party tools to drive the +//! compiler with prior knowledge about how the world should look. +//! +//! ## The `--extern` flag +//! +//! The compiler accepts a flag of this form a number of times: +//! +//! ```text +//! --extern crate-name=path/to/the/crate.rlib +//! ``` +//! +//! This flag is basically the following letter to the compiler: +//! +//! > Dear rustc, +//! > +//! > When you are attempting to load the immediate dependency `crate-name`, I +//! > would like you to assume that the library is located at +//! > `path/to/the/crate.rlib`, and look nowhere else. Also, please do not +//! > assume that the path I specified has the name `crate-name`. +//! +//! This flag basically overrides most matching logic except for validating that +//! the file is indeed a rust library. The same `crate-name` can be specified +//! twice to specify the rlib/dylib pair. +//! +//! ## Enabling "multiple versions" +//! +//! This basically boils down to the ability to specify arbitrary packages to +//! the compiler. For example, if crate A wanted to use Bv1 and Bv2, then it +//! would look something like: +//! +//! ```ignore +//! extern crate b1; +//! extern crate b2; +//! +//! fn main() {} +//! ``` +//! +//! and the compiler would be invoked as: +//! +//! ```text +//! rustc a.rs --extern b1=path/to/libb1.rlib --extern b2=path/to/libb2.rlib +//! ``` +//! +//! In this scenario there are two crates named `b` and the compiler must be +//! manually driven to be informed where each crate is. +//! +//! ## Frobbing symbols +//! +//! One of the immediate problems with linking the same library together twice +//! in the same problem is dealing with duplicate symbols. The primary way to +//! deal with this in rustc is to add hashes to the end of each symbol. +//! +//! In order to force hashes to change between versions of a library, if +//! desired, the compiler exposes an option `-C metadata=foo`, which is used to +//! initially seed each symbol hash. The string `foo` is prepended to each +//! string-to-hash to ensure that symbols change over time. +//! +//! ## Loading transitive dependencies +//! +//! Dealing with same-named-but-distinct crates is not just a local problem, but +//! one that also needs to be dealt with for transitive dependencies. Note that +//! in the letter above `--extern` flags only apply to the *local* set of +//! dependencies, not the upstream transitive dependencies. Consider this +//! dependency graph: +//! +//! ```text +//! A.1 A.2 +//! | | +//! | | +//! B C +//! \ / +//! \ / +//! D +//! ``` +//! +//! In this scenario, when we compile `D`, we need to be able to distinctly +//! resolve `A.1` and `A.2`, but an `--extern` flag cannot apply to these +//! transitive dependencies. +//! +//! Note that the key idea here is that `B` and `C` are both *already compiled*. +//! That is, they have already resolved their dependencies. Due to unrelated +//! technical reasons, when a library is compiled, it is only compatible with +//! the *exact same* version of the upstream libraries it was compiled against. +//! We use the "Strict Version Hash" to identify the exact copy of an upstream +//! library. +//! +//! With this knowledge, we know that `B` and `C` will depend on `A` with +//! different SVH values, so we crawl the normal `-L` paths looking for +//! `liba*.rlib` and filter based on the contained SVH. +//! +//! In the end, this ends up not needing `--extern` to specify upstream +//! transitive dependencies. +//! +//! # Wrapping up +//! +//! That's the general overview of loading crates in the compiler, but it's by +//! no means all of the necessary details. Take a look at the rest of +//! metadata::locator or metadata::creader for all the juicy details! + +use cstore::MetadataBlob; +use creader::Library; +use schema::{METADATA_HEADER, rustc_version}; + +use rustc::hir::svh::Svh; +use rustc::session::Session; +use rustc::session::filesearch::{FileSearch, FileMatches, FileDoesntMatch}; +use rustc::session::search_paths::PathKind; +use rustc::util::common; +use rustc::util::nodemap::FnvHashMap; + +use rustc_llvm as llvm; +use rustc_llvm::{False, ObjectFile, mk_section_iter}; +use rustc_llvm::archive_ro::ArchiveRO; +use errors::DiagnosticBuilder; +use syntax_pos::Span; +use rustc_back::target::Target; + +use std::cmp; +use std::fmt; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; +use std::ptr; +use std::slice; +use std::time::Instant; + +use flate; + +pub struct CrateMismatch { + path: PathBuf, + got: String, +} + +pub struct Context<'a> { + pub sess: &'a Session, + pub span: Span, + pub ident: &'a str, + pub crate_name: &'a str, + pub hash: Option<&'a Svh>, + // points to either self.sess.target.target or self.sess.host, must match triple + pub target: &'a Target, + pub triple: &'a str, + pub filesearch: FileSearch<'a>, + pub root: &'a Option, + pub rejected_via_hash: Vec, + pub rejected_via_triple: Vec, + pub rejected_via_kind: Vec, + pub rejected_via_version: Vec, + pub should_match_name: bool, +} + +pub struct ArchiveMetadata { + _archive: ArchiveRO, + // points into self._archive + data: *const [u8], +} + +pub struct CratePaths { + pub ident: String, + pub dylib: Option, + pub rlib: Option +} + +pub const METADATA_FILENAME: &'static str = "rust.metadata.bin"; + +#[derive(Copy, Clone, PartialEq)] +enum CrateFlavor { + Rlib, + Dylib +} + +impl fmt::Display for CrateFlavor { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match *self { + CrateFlavor::Rlib => "rlib", + CrateFlavor::Dylib => "dylib" + }) + } +} + +impl CratePaths { + fn paths(&self) -> Vec { + match (&self.dylib, &self.rlib) { + (&None, &None) => vec!(), + (&Some(ref p), &None) | + (&None, &Some(ref p)) => vec!(p.clone()), + (&Some(ref p1), &Some(ref p2)) => vec!(p1.clone(), p2.clone()), + } + } +} + +impl<'a> Context<'a> { + pub fn maybe_load_library_crate(&mut self) -> Option { + self.find_library_crate() + } + + pub fn load_library_crate(&mut self) -> Library { + self.find_library_crate().unwrap_or_else(|| self.report_errs()) + } + + pub fn report_errs(&mut self) -> ! { + let add = match self.root { + &None => String::new(), + &Some(ref r) => format!(" which `{}` depends on", + r.ident) + }; + let mut err = if !self.rejected_via_hash.is_empty() { + struct_span_err!(self.sess, self.span, E0460, + "found possibly newer version of crate `{}`{}", + self.ident, add) + } else if !self.rejected_via_triple.is_empty() { + struct_span_err!(self.sess, self.span, E0461, + "couldn't find crate `{}` with expected target triple {}{}", + self.ident, self.triple, add) + } else if !self.rejected_via_kind.is_empty() { + struct_span_err!(self.sess, self.span, E0462, + "found staticlib `{}` instead of rlib or dylib{}", + self.ident, add) + } else if !self.rejected_via_version.is_empty() { + struct_span_err!(self.sess, self.span, E0514, + "found crate `{}` compiled by an incompatible version of rustc{}", + self.ident, add) + } else { + let mut err = struct_span_err!(self.sess, self.span, E0463, + "can't find crate for `{}`{}", + self.ident, add); + err.span_label(self.span, &format!("can't find crate")); + err + }; + + if !self.rejected_via_triple.is_empty() { + let mismatches = self.rejected_via_triple.iter(); + for (i, &CrateMismatch{ ref path, ref got }) in mismatches.enumerate() { + err.note(&format!("crate `{}`, path #{}, triple {}: {}", + self.ident, i+1, got, path.display())); + } + } + if !self.rejected_via_hash.is_empty() { + err.note("perhaps that crate needs to be recompiled?"); + let mismatches = self.rejected_via_hash.iter(); + for (i, &CrateMismatch{ ref path, .. }) in mismatches.enumerate() { + err.note(&format!("crate `{}` path #{}: {}", + self.ident, i+1, path.display())); + } + match self.root { + &None => {} + &Some(ref r) => { + for (i, path) in r.paths().iter().enumerate() { + err.note(&format!("crate `{}` path #{}: {}", + r.ident, i+1, path.display())); + } + } + } + } + if !self.rejected_via_kind.is_empty() { + err.help("please recompile that crate using --crate-type lib"); + let mismatches = self.rejected_via_kind.iter(); + for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() { + err.note(&format!("crate `{}` path #{}: {}", + self.ident, i+1, path.display())); + } + } + if !self.rejected_via_version.is_empty() { + err.help(&format!("please recompile that crate using this compiler ({})", + rustc_version())); + let mismatches = self.rejected_via_version.iter(); + for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() { + err.note(&format!("crate `{}` path #{}: {} compiled by {:?}", + self.ident, i+1, path.display(), got)); + } + } + + err.emit(); + self.sess.abort_if_errors(); + unreachable!(); + } + + fn find_library_crate(&mut self) -> Option { + // If an SVH is specified, then this is a transitive dependency that + // must be loaded via -L plus some filtering. + if self.hash.is_none() { + self.should_match_name = false; + if let Some(s) = self.sess.opts.externs.get(self.crate_name) { + return self.find_commandline_library(s.iter()); + } + self.should_match_name = true; + } + + let dypair = self.dylibname(); + let staticpair = self.staticlibname(); + + // want: crate_name.dir_part() + prefix + crate_name.file_part + "-" + let dylib_prefix = format!("{}{}", dypair.0, self.crate_name); + let rlib_prefix = format!("lib{}", self.crate_name); + let staticlib_prefix = format!("{}{}", staticpair.0, self.crate_name); + + let mut candidates = FnvHashMap(); + let mut staticlibs = vec!(); + + // First, find all possible candidate rlibs and dylibs purely based on + // the name of the files themselves. We're trying to match against an + // exact crate name and a possibly an exact hash. + // + // During this step, we can filter all found libraries based on the + // name and id found in the crate id (we ignore the path portion for + // filename matching), as well as the exact hash (if specified). If we + // end up having many candidates, we must look at the metadata to + // perform exact matches against hashes/crate ids. Note that opening up + // the metadata is where we do an exact match against the full contents + // of the crate id (path/name/id). + // + // The goal of this step is to look at as little metadata as possible. + self.filesearch.search(|path, kind| { + let file = match path.file_name().and_then(|s| s.to_str()) { + None => return FileDoesntMatch, + Some(file) => file, + }; + let (hash, rlib) = if file.starts_with(&rlib_prefix[..]) && + file.ends_with(".rlib") { + (&file[(rlib_prefix.len()) .. (file.len() - ".rlib".len())], + true) + } else if file.starts_with(&dylib_prefix) && + file.ends_with(&dypair.1) { + (&file[(dylib_prefix.len()) .. (file.len() - dypair.1.len())], + false) + } else { + if file.starts_with(&staticlib_prefix[..]) && + file.ends_with(&staticpair.1) { + staticlibs.push(CrateMismatch { + path: path.to_path_buf(), + got: "static".to_string() + }); + } + return FileDoesntMatch + }; + info!("lib candidate: {}", path.display()); + + let hash_str = hash.to_string(); + let slot = candidates.entry(hash_str) + .or_insert_with(|| (FnvHashMap(), FnvHashMap())); + let (ref mut rlibs, ref mut dylibs) = *slot; + fs::canonicalize(path).map(|p| { + if rlib { + rlibs.insert(p, kind); + } else { + dylibs.insert(p, kind); + } + FileMatches + }).unwrap_or(FileDoesntMatch) + }); + self.rejected_via_kind.extend(staticlibs); + + // We have now collected all known libraries into a set of candidates + // keyed of the filename hash listed. For each filename, we also have a + // list of rlibs/dylibs that apply. Here, we map each of these lists + // (per hash), to a Library candidate for returning. + // + // A Library candidate is created if the metadata for the set of + // libraries corresponds to the crate id and hash criteria that this + // search is being performed for. + let mut libraries = FnvHashMap(); + for (_hash, (rlibs, dylibs)) in candidates { + let mut slot = None; + let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot); + let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot); + if let Some((h, m)) = slot { + libraries.insert(h, Library { + dylib: dylib, + rlib: rlib, + metadata: m, + }); + } + } + + // Having now translated all relevant found hashes into libraries, see + // what we've got and figure out if we found multiple candidates for + // libraries or not. + match libraries.len() { + 0 => None, + 1 => Some(libraries.into_iter().next().unwrap().1), + _ => { + let mut err = struct_span_err!(self.sess, self.span, E0464, + "multiple matching crates for `{}`", + self.crate_name); + err.note("candidates:"); + for (_, lib) in libraries { + if let Some((ref p, _)) = lib.dylib { + err.note(&format!("path: {}", p.display())); + } + if let Some((ref p, _)) = lib.rlib { + err.note(&format!("path: {}", p.display())); + } + note_crate_name(&mut err, &lib.metadata.get_root().name); + } + err.emit(); + None + } + } + } + + // Attempts to extract *one* library from the set `m`. If the set has no + // elements, `None` is returned. If the set has more than one element, then + // the errors and notes are emitted about the set of libraries. + // + // With only one library in the set, this function will extract it, and then + // read the metadata from it if `*slot` is `None`. If the metadata couldn't + // be read, it is assumed that the file isn't a valid rust library (no + // errors are emitted). + fn extract_one(&mut self, m: FnvHashMap, flavor: CrateFlavor, + slot: &mut Option<(Svh, MetadataBlob)>) -> Option<(PathBuf, PathKind)> { + let mut ret: Option<(PathBuf, PathKind)> = None; + let mut error = 0; + + if slot.is_some() { + // FIXME(#10786): for an optimization, we only read one of the + // libraries' metadata sections. In theory we should + // read both, but reading dylib metadata is quite + // slow. + if m.is_empty() { + return None + } else if m.len() == 1 { + return Some(m.into_iter().next().unwrap()) + } + } + + let mut err: Option = None; + for (lib, kind) in m { + info!("{} reading metadata from: {}", flavor, lib.display()); + let (hash, metadata) = match get_metadata_section(self.target, flavor, &lib) { + Ok(blob) => { + if let Some(h) = self.crate_matches(&blob, &lib) { + (h, blob) + } else { + info!("metadata mismatch"); + continue + } + } + Err(err) => { + info!("no metadata found: {}", err); + continue + } + }; + // If we see multiple hashes, emit an error about duplicate candidates. + if slot.as_ref().map_or(false, |s| s.0 != hash) { + let mut e = struct_span_err!(self.sess, self.span, E0465, + "multiple {} candidates for `{}` found", + flavor, self.crate_name); + e.span_note(self.span, + &format!(r"candidate #1: {}", + ret.as_ref().unwrap().0 + .display())); + if let Some(ref mut e) = err { + e.emit(); + } + err = Some(e); + error = 1; + *slot = None; + } + if error > 0 { + error += 1; + err.as_mut().unwrap().span_note(self.span, + &format!(r"candidate #{}: {}", error, + lib.display())); + continue + } + *slot = Some((hash, metadata)); + ret = Some((lib, kind)); + } + + if error > 0 { + err.unwrap().emit(); + None + } else { + ret + } + } + + fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option { + let root = metadata.get_root(); + let rustc_version = rustc_version(); + if root.rustc_version != rustc_version { + info!("Rejecting via version: expected {} got {}", + rustc_version, root.rustc_version); + self.rejected_via_version.push(CrateMismatch { + path: libpath.to_path_buf(), + got: root.rustc_version + }); + return None; + } + + if self.should_match_name { + if self.crate_name != root.name { + info!("Rejecting via crate name"); return None; + } + } + + if root.triple != self.triple { + info!("Rejecting via crate triple: expected {} got {}", + self.triple, root.triple); + self.rejected_via_triple.push(CrateMismatch { + path: libpath.to_path_buf(), + got: root.triple + }); + return None; + } + + if let Some(myhash) = self.hash { + if *myhash != root.hash { + info!("Rejecting via hash: expected {} got {}", + *myhash, root.hash); + self.rejected_via_hash.push(CrateMismatch { + path: libpath.to_path_buf(), + got: myhash.to_string() + }); + return None; + } + } + + Some(root.hash) + } + + + // Returns the corresponding (prefix, suffix) that files need to have for + // dynamic libraries + fn dylibname(&self) -> (String, String) { + let t = &self.target; + (t.options.dll_prefix.clone(), t.options.dll_suffix.clone()) + } + + // Returns the corresponding (prefix, suffix) that files need to have for + // static libraries + fn staticlibname(&self) -> (String, String) { + let t = &self.target; + (t.options.staticlib_prefix.clone(), t.options.staticlib_suffix.clone()) + } + + fn find_commandline_library<'b, LOCS> (&mut self, locs: LOCS) -> Option + where LOCS: Iterator + { + // First, filter out all libraries that look suspicious. We only accept + // files which actually exist that have the correct naming scheme for + // rlibs/dylibs. + let sess = self.sess; + let dylibname = self.dylibname(); + let mut rlibs = FnvHashMap(); + let mut dylibs = FnvHashMap(); + { + let locs = locs.map(|l| PathBuf::from(l)).filter(|loc| { + if !loc.exists() { + sess.err(&format!("extern location for {} does not exist: {}", + self.crate_name, loc.display())); + return false; + } + let file = match loc.file_name().and_then(|s| s.to_str()) { + Some(file) => file, + None => { + sess.err(&format!("extern location for {} is not a file: {}", + self.crate_name, loc.display())); + return false; + } + }; + if file.starts_with("lib") && file.ends_with(".rlib") { + return true + } else { + let (ref prefix, ref suffix) = dylibname; + if file.starts_with(&prefix[..]) && + file.ends_with(&suffix[..]) { + return true + } + } + sess.struct_err(&format!("extern location for {} is of an unknown type: {}", + self.crate_name, loc.display())) + .help(&format!("file name should be lib*.rlib or {}*.{}", + dylibname.0, dylibname.1)) + .emit(); + false + }); + + // Now that we have an iterator of good candidates, make sure + // there's at most one rlib and at most one dylib. + for loc in locs { + if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { + rlibs.insert(fs::canonicalize(&loc).unwrap(), + PathKind::ExternFlag); + } else { + dylibs.insert(fs::canonicalize(&loc).unwrap(), + PathKind::ExternFlag); + } + } + }; + + // Extract the rlib/dylib pair. + let mut slot = None; + let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot); + let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot); + + if rlib.is_none() && dylib.is_none() { return None } + match slot { + Some((_, metadata)) => Some(Library { + dylib: dylib, + rlib: rlib, + metadata: metadata, + }), + None => None, + } + } +} + +pub fn note_crate_name(err: &mut DiagnosticBuilder, name: &str) { + err.note(&format!("crate name: {}", name)); +} + +impl ArchiveMetadata { + fn new(ar: ArchiveRO) -> Option { + let data = { + let section = ar.iter().filter_map(|s| s.ok()).find(|sect| { + sect.name() == Some(METADATA_FILENAME) + }); + match section { + Some(s) => s.data() as *const [u8], + None => { + debug!("didn't find '{}' in the archive", METADATA_FILENAME); + return None; + } + } + }; + + Some(ArchiveMetadata { + _archive: ar, + data: data, + }) + } + + pub fn as_slice<'a>(&'a self) -> &'a [u8] { unsafe { &*self.data } } +} + +fn verify_decompressed_encoding_version(blob: &MetadataBlob, filename: &Path) + -> Result<(), String> +{ + if !blob.is_compatible() { + Err((format!("incompatible metadata version found: '{}'", + filename.display()))) + } else { + Ok(()) + } +} + +// Just a small wrapper to time how long reading metadata takes. +fn get_metadata_section(target: &Target, flavor: CrateFlavor, filename: &Path) + -> Result { + let start = Instant::now(); + let ret = get_metadata_section_imp(target, flavor, filename); + info!("reading {:?} => {:?}", filename.file_name().unwrap(), + start.elapsed()); + return ret +} + +fn get_metadata_section_imp(target: &Target, flavor: CrateFlavor, filename: &Path) + -> Result { + if !filename.exists() { + return Err(format!("no such file: '{}'", filename.display())); + } + if flavor == CrateFlavor::Rlib { + // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap + // internally to read the file. We also avoid even using a memcpy by + // just keeping the archive along while the metadata is in use. + let archive = match ArchiveRO::open(filename) { + Some(ar) => ar, + None => { + debug!("llvm didn't like `{}`", filename.display()); + return Err(format!("failed to read rlib metadata: '{}'", + filename.display())); + } + }; + return match ArchiveMetadata::new(archive).map(|ar| MetadataBlob::Archive(ar)) { + None => Err(format!("failed to read rlib metadata: '{}'", + filename.display())), + Some(blob) => { + verify_decompressed_encoding_version(&blob, filename)?; + Ok(blob) + } + }; + } + unsafe { + let buf = common::path2cstr(filename); + let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr()); + if mb as isize == 0 { + return Err(format!("error reading library: '{}'", + filename.display())) + } + let of = match ObjectFile::new(mb) { + Some(of) => of, + _ => { + return Err((format!("provided path not an object file: '{}'", + filename.display()))) + } + }; + let si = mk_section_iter(of.llof); + while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { + let mut name_buf = ptr::null(); + let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf); + let name = slice::from_raw_parts(name_buf as *const u8, + name_len as usize).to_vec(); + let name = String::from_utf8(name).unwrap(); + debug!("get_metadata_section: name {}", name); + if read_meta_section_name(target) == name { + let cbuf = llvm::LLVMGetSectionContents(si.llsi); + let csz = llvm::LLVMGetSectionSize(si.llsi) as usize; + let cvbuf: *const u8 = cbuf as *const u8; + let vlen = METADATA_HEADER.len(); + debug!("checking {} bytes of metadata-version stamp", + vlen); + let minsz = cmp::min(vlen, csz); + let buf0 = slice::from_raw_parts(cvbuf, minsz); + let version_ok = buf0 == METADATA_HEADER; + if !version_ok { + return Err((format!("incompatible metadata version found: '{}'", + filename.display()))); + } + + let cvbuf1 = cvbuf.offset(vlen as isize); + debug!("inflating {} bytes of compressed metadata", + csz - vlen); + let bytes = slice::from_raw_parts(cvbuf1, csz - vlen); + match flate::inflate_bytes(bytes) { + Ok(inflated) => { + let blob = MetadataBlob::Inflated(inflated); + verify_decompressed_encoding_version(&blob, filename)?; + return Ok(blob); + } + Err(_) => {} + } + } + llvm::LLVMMoveToNextSection(si.llsi); + } + Err(format!("metadata not found: '{}'", filename.display())) + } +} + +pub fn meta_section_name(target: &Target) -> &'static str { + // Historical note: + // + // When using link.exe it was seen that the section name `.note.rustc` + // was getting shortened to `.note.ru`, and according to the PE and COFF + // specification: + // + // > Executable images do not use a string table and do not support + // > section names longer than 8 characters + // + // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx + // + // As a result, we choose a slightly shorter name! As to why + // `.note.rustc` works on MinGW, that's another good question... + + if target.options.is_like_osx { + "__DATA,.rustc" + } else { + ".rustc" + } +} + +pub fn read_meta_section_name(_target: &Target) -> &'static str { + ".rustc" +} + +// A diagnostic function for dumping crate metadata to an output stream +pub fn list_file_metadata(target: &Target, path: &Path, + out: &mut io::Write) -> io::Result<()> { + let filename = path.file_name().unwrap().to_str().unwrap(); + let flavor = if filename.ends_with(".rlib") { CrateFlavor::Rlib } else { CrateFlavor::Dylib }; + match get_metadata_section(target, flavor, path) { + Ok(metadata) => metadata.list_crate_metadata(out), + Err(msg) => { + write!(out, "{}\n", msg) + } + } +} -- cgit 1.4.1-3-g733a5