rust/src/librustc/metadata/creader.rs

338 lines
11 KiB
Rust
Raw Normal View History

// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Validates all used crates and extern libraries and loads their metadata
use metadata::cstore;
use metadata::decoder;
use metadata::filesearch::FileSearch;
use metadata::loader;
use std::hashmap::HashMap;
2012-09-04 13:54:36 -05:00
use syntax::attr;
use syntax::attr::AttrMetaMethods;
2013-01-30 11:56:33 -06:00
use syntax::codemap::{span, dummy_sp};
use syntax::diagnostic::span_handler;
use syntax::parse::token;
2012-09-04 13:54:36 -05:00
use syntax::parse::token::ident_interner;
use syntax::visit;
2013-03-26 15:38:07 -05:00
use syntax::ast;
// Traverses an AST, reading all the information about use'd crates and extern
2011-07-07 23:03:09 -05:00
// libraries necessary for later resolving, typechecking, linking, etc.
pub fn read_crates(diag: @span_handler,
crate: &ast::Crate,
cstore: @mut cstore::CStore,
filesearch: @FileSearch,
os: loader::os,
statik: bool,
intr: @ident_interner) {
let e = @mut Env {
diag: diag,
filesearch: filesearch,
cstore: cstore,
os: os,
statik: statik,
crate_cache: @mut ~[],
next_crate_num: 1,
intr: intr
};
2011-07-27 07:19:39 -05:00
let v =
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_view_item: |a| visit_view_item(e, a),
visit_item: |a| visit_item(e, a),
.. *visit::default_simple_visitor()});
visit_crate(e, crate);
visit::visit_crate(crate, ((), v));
dump_crates(*e.crate_cache);
warn_if_multiple_versions(e, diag, *e.crate_cache);
}
struct cache_entry {
cnum: int,
span: span,
hash: @str,
metas: @~[@ast::MetaItem]
}
fn dump_crates(crate_cache: &[cache_entry]) {
2012-08-22 19:24:52 -05:00
debug!("resolved crates:");
2013-08-01 02:16:42 -05:00
foreach entry in crate_cache.iter() {
2012-08-22 19:24:52 -05:00
debug!("cnum: %?", entry.cnum);
debug!("span: %?", entry.span);
debug!("hash: %?", entry.hash);
2012-04-09 17:06:38 -05:00
}
}
fn warn_if_multiple_versions(e: @mut Env,
diag: @span_handler,
crate_cache: &[cache_entry]) {
use std::either::*;
2012-06-04 10:03:14 -05:00
if crate_cache.len() != 0u {
let name = loader::crate_name_from_metas(
*crate_cache[crate_cache.len() - 1].metas
);
let vec: ~[Either<cache_entry, cache_entry>] = crate_cache.iter().transform(|&entry| {
2013-07-02 14:47:32 -05:00
let othername = loader::crate_name_from_metas(*entry.metas);
if name == othername {
Left(entry)
} else {
Right(entry)
}
}).collect();
let (matches, non_matches) = partition(vec);
2013-03-28 20:39:09 -05:00
assert!(!matches.is_empty());
if matches.len() != 1u {
diag.handler().warn(
fmt!("using multiple versions of crate `%s`", name));
2013-08-01 02:16:42 -05:00
foreach match_ in matches.iter() {
diag.span_note(match_.span, "used here");
let attrs = ~[
2013-07-02 14:47:32 -05:00
attr::mk_attr(attr::mk_list_item(@"link",
(*match_.metas).clone()))
];
2012-07-18 18:18:02 -05:00
loader::note_linkage_attrs(e.intr, diag, attrs);
}
}
warn_if_multiple_versions(e, diag, non_matches);
}
2011-07-07 23:03:09 -05:00
}
struct Env {
diag: @span_handler,
filesearch: @FileSearch,
cstore: @mut cstore::CStore,
os: loader::os,
statik: bool,
crate_cache: @mut ~[cache_entry],
next_crate_num: ast::CrateNum,
intr: @ident_interner
}
2011-07-07 23:37:56 -05:00
fn visit_crate(e: &Env, c: &ast::Crate) {
let cstore = e.cstore;
2013-08-01 02:16:42 -05:00
foreach a in c.attrs.iter().filter(|m| "link_args" == m.name()) {
match a.value_str() {
Some(ref linkarg) => {
cstore::add_used_link_args(cstore, *linkarg);
}
None => {/* fallthrough */ }
}
}
}
2013-07-05 03:28:53 -05:00
fn visit_view_item(e: @mut Env, i: &ast::view_item) {
match i.node {
2013-05-29 18:59:33 -05:00
ast::view_item_extern_mod(ident, ref meta_items, id) => {
debug!("resolving extern mod stmt. ident: %?, meta: %?",
2013-05-29 18:59:33 -05:00
ident, *meta_items);
2013-07-02 14:47:32 -05:00
let cnum = resolve_crate(e,
ident,
(*meta_items).clone(),
@"",
i.span);
cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
2011-07-27 07:19:39 -05:00
}
2012-08-03 21:59:04 -05:00
_ => ()
2011-07-07 23:37:56 -05:00
}
}
fn visit_item(e: &Env, i: @ast::item) {
match i.node {
ast::item_foreign_mod(ref fm) => {
if fm.abis.is_rust() || fm.abis.is_intrinsic() {
return;
2011-07-07 23:37:56 -05:00
}
let cstore = e.cstore;
let mut already_added = false;
let link_args = i.attrs.iter()
.filter_map(|at| if "link_args" == at.name() {Some(at)} else {None})
.collect::<~[&ast::Attribute]>();
match fm.sort {
2013-02-16 11:48:28 -06:00
ast::named => {
let link_name = i.attrs.iter()
.find_(|at| "link_name" == at.name())
.chain(|at| at.value_str());
let foreign_name = match link_name {
Some(nn) => {
if nn.is_empty() {
2013-02-16 11:48:28 -06:00
e.diag.span_fatal(
i.span,
"empty #[link_name] not allowed; use \
#[nolink].");
2013-02-16 11:48:28 -06:00
}
nn
2013-02-16 11:48:28 -06:00
}
None => token::ident_to_str(&i.ident)
2013-02-16 11:48:28 -06:00
};
if !attr::contains_name(i.attrs, "nolink") {
2013-02-16 11:48:28 -06:00
already_added =
!cstore::add_used_library(cstore, foreign_name);
2013-02-16 11:48:28 -06:00
}
if !link_args.is_empty() && already_added {
e.diag.span_fatal(i.span, ~"library '" + foreign_name +
"' already added: can't specify link_args.");
2013-02-16 11:48:28 -06:00
}
}
2013-02-16 11:48:28 -06:00
ast::anonymous => { /* do nothing */ }
}
2013-08-01 02:16:42 -05:00
foreach m in link_args.iter() {
match m.value_str() {
Some(linkarg) => {
cstore::add_used_link_args(cstore, linkarg);
2013-02-16 11:48:28 -06:00
}
None => { /* fallthrough */ }
2011-07-27 07:19:39 -05:00
}
2011-07-07 23:37:56 -05:00
}
2011-07-27 07:19:39 -05:00
}
2012-08-03 21:59:04 -05:00
_ => { }
2011-07-07 23:37:56 -05:00
}
}
fn metas_with(ident: @str, key: @str, mut metas: ~[@ast::MetaItem])
-> ~[@ast::MetaItem] {
// Check if key isn't there yet.
if !attr::contains_name(metas, key) {
metas.push(attr::mk_name_value_item_str(key, ident));
}
metas
}
fn metas_with_ident(ident: @str, metas: ~[@ast::MetaItem])
-> ~[@ast::MetaItem] {
metas_with(ident, @"name", metas)
}
fn existing_match(e: &Env, metas: &[@ast::MetaItem], hash: &str)
-> Option<int> {
2013-08-01 02:16:42 -05:00
foreach c in e.crate_cache.iter() {
2012-06-04 10:03:14 -05:00
if loader::metadata_matches(*c.metas, metas)
&& (hash.is_empty() || c.hash.as_slice() == hash) {
2012-08-20 14:23:37 -05:00
return Some(c.cnum);
2012-06-04 10:03:14 -05:00
}
}
2012-08-20 14:23:37 -05:00
return None;
}
fn resolve_crate(e: @mut Env,
ident: ast::ident,
metas: ~[@ast::MetaItem],
hash: @str,
span: span)
-> ast::CrateNum {
let metas = metas_with_ident(token::ident_to_str(&ident), metas);
2012-08-06 14:34:08 -05:00
match existing_match(e, metas, hash) {
2012-08-20 14:23:37 -05:00
None => {
let load_ctxt = loader::Context {
diag: e.diag,
filesearch: e.filesearch,
span: span,
ident: ident,
2013-01-17 21:14:26 -06:00
metas: metas,
hash: hash,
os: e.os,
is_static: e.statik,
2012-07-18 18:18:02 -05:00
intr: e.intr
};
let (lident, ldata) = loader::load_library_crate(&load_ctxt);
let cfilename = Path(lident);
let cdata = ldata;
let attrs = decoder::get_crate_attributes(cdata);
let linkage_metas = attr::find_linkage_metas(attrs);
let hash = decoder::get_crate_hash(cdata);
// Claim this crate number and cache it
2011-07-27 07:19:39 -05:00
let cnum = e.next_crate_num;
e.crate_cache.push(cache_entry {
cnum: cnum,
span: span,
hash: hash,
metas: @linkage_metas
});
e.next_crate_num += 1;
// Now resolve the crates referenced by this crate
2011-07-27 07:19:39 -05:00
let cnum_map = resolve_crate_deps(e, cdata);
let cname =
2013-01-17 21:14:26 -06:00
match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
"name") {
Some(v) => v,
None => token::ident_to_str(&ident),
};
let cmeta = @cstore::crate_metadata {
name: cname,
data: cdata,
cnum_map: cnum_map,
cnum: cnum
};
let cstore = e.cstore;
cstore::set_crate_data(cstore, cnum, cmeta);
cstore::add_used_crate_file(cstore, &cfilename);
2012-08-01 19:30:05 -05:00
return cnum;
}
2012-08-20 14:23:37 -05:00
Some(cnum) => {
2012-08-01 19:30:05 -05:00
return cnum;
}
}
}
// Go through the crate metadata and load any crates that it references
fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
2012-08-22 19:24:52 -05:00
debug!("resolving deps of external crate");
// The map from crate numbers in the crate we're resolving to local crate
// numbers
let mut cnum_map = HashMap::new();
let r = decoder::get_crate_deps(cdata);
2013-08-01 02:16:42 -05:00
foreach dep in r.iter() {
2011-07-27 07:19:39 -05:00
let extrn_cnum = dep.cnum;
let cname = dep.name;
let cname_str = token::ident_to_str(&dep.name);
let cmetas = metas_with(dep.vers, @"vers", ~[]);
2012-08-22 19:24:52 -05:00
debug!("resolving dep crate %s ver: %s hash: %s",
cname_str, dep.vers, dep.hash);
2013-07-02 14:47:32 -05:00
match existing_match(e,
metas_with_ident(cname_str, cmetas.clone()),
2012-07-18 18:18:02 -05:00
dep.hash) {
2012-08-20 14:23:37 -05:00
Some(local_cnum) => {
2012-08-22 19:24:52 -05:00
debug!("already have it");
// We've already seen this crate
cnum_map.insert(extrn_cnum, local_cnum);
}
2012-08-20 14:23:37 -05:00
None => {
2012-08-22 19:24:52 -05:00
debug!("need to load it");
// This is a new one so we've got to load it
// FIXME (#2404): Need better error reporting than just a bogus
// span.
2013-01-30 11:56:33 -06:00
let fake_span = dummy_sp();
let local_cnum = resolve_crate(e, cname, cmetas, dep.hash,
fake_span);
cnum_map.insert(extrn_cnum, local_cnum);
}
}
}
2013-03-22 21:26:41 -05:00
return @mut cnum_map;
}