rust/src/rustc/metadata/creader.rs

411 lines
14 KiB
Rust
Raw Normal View History

2011-06-27 18:03:01 -05:00
// Extracting metadata from crate files
import driver::session;
import session::session;
2011-09-12 18:13:28 -05:00
import syntax::{ast, ast_util};
import lib::llvm::{False, llvm, mk_object_file, mk_section_iter};
import syntax::attr;
import syntax::visit;
import syntax::codemap::span;
2011-11-10 10:41:42 -06:00
import util::{filesearch};
import io::writer_util;
import std::map::{hashmap, int_hash};
import syntax::print::pprust;
import common::*;
2011-06-22 21:04:04 -05:00
export read_crates;
export list_file_metadata;
2011-07-07 23:03:09 -05:00
// Traverses an AST, reading all the information about use'd crates and native
// libraries necessary for later resolving, typechecking, linking, etc.
fn read_crates(sess: session::session, crate: ast::crate) {
let e = @{sess: sess,
mut crate_cache: [],
2012-03-26 20:35:18 -05:00
mut next_crate_num: 1};
2011-07-27 07:19:39 -05:00
let v =
visit::mk_simple_visitor(@{visit_view_item:
bind visit_view_item(e, _),
visit_item: bind visit_item(e, _)
with *visit::default_simple_visitor()});
visit::visit_crate(crate, (), v);
warn_if_multiple_versions(sess, copy e.crate_cache);
}
type cache_entry = {
cnum: int,
span: span,
metas: @[@ast::meta_item]
};
fn warn_if_multiple_versions(sess: session::session,
crate_cache: [cache_entry]) {
import either::*;
if crate_cache.is_not_empty() {
let name = crate_name_from_metas(*crate_cache.last().metas);
let {lefts: matches, rights: non_matches} =
partition(crate_cache.map {|entry|
let othername = crate_name_from_metas(*entry.metas);
if name == othername {
left(entry)
} else {
right(entry)
}
});
assert matches.is_not_empty();
if matches.len() != 1u {
sess.warn(#fmt("using multiple versions of crate `%s`", name));
for matches.each {|match|
sess.span_note(match.span, "used here");
let attrs = [
attr::mk_attr(attr::mk_list_item("link", *match.metas))
];
note_linkage_attrs(sess, attrs);
}
}
warn_if_multiple_versions(sess, non_matches);
}
2011-07-07 23:03:09 -05:00
}
type env = @{sess: session::session,
mut crate_cache: [cache_entry],
2012-03-26 20:35:18 -05:00
mut next_crate_num: ast::crate_num};
2011-07-07 23:37:56 -05:00
fn visit_view_item(e: env, i: @ast::view_item) {
2011-07-27 07:19:39 -05:00
alt i.node {
ast::view_item_use(ident, meta_items, id) {
let cnum = resolve_crate(e, ident, meta_items, i.span);
cstore::add_use_stmt_cnum(e.sess.cstore, id, cnum);
2011-07-27 07:19:39 -05:00
}
_ { }
2011-07-07 23:37:56 -05:00
}
}
fn visit_item(e: env, i: @ast::item) {
2011-07-27 07:19:39 -05:00
alt i.node {
ast::item_native_mod(m) {
2011-11-20 12:15:40 -06:00
alt attr::native_abi(i.attrs) {
either::right(abi) {
if abi != ast::native_abi_cdecl &&
abi != ast::native_abi_stdcall { ret; }
}
either::left(msg) { e.sess.span_fatal(i.span, msg); }
2011-07-07 23:37:56 -05:00
}
let cstore = e.sess.cstore;
let native_name =
alt attr::get_meta_item_value_str_by_name(i.attrs, "link_name") {
some(nn) {
if nn == "" {
e.sess.span_fatal(
i.span,
"empty #[link_name] not allowed; use #[nolink].");
}
nn
}
none { i.ident }
};
let mut already_added = false;
if vec::len(attr::find_attrs_by_name(i.attrs, "nolink")) == 0u {
already_added = !cstore::add_used_library(cstore, native_name);
}
let link_args = attr::find_attrs_by_name(i.attrs, "link_args");
if vec::len(link_args) > 0u && already_added {
e.sess.span_fatal(i.span, "library '" + native_name +
"' already added: can't specify link_args.");
}
for link_args.each {|a|
2011-07-27 07:19:39 -05:00
alt attr::get_meta_item_value_str(attr::attr_meta(a)) {
some(linkarg) {
cstore::add_used_link_args(cstore, linkarg);
}
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
}
_ { }
2011-07-07 23:37:56 -05:00
}
}
2011-07-07 23:03:09 -05:00
// A diagnostic function for dumping crate metadata to an output stream
fn list_file_metadata(sess: session::session, path: str, out: io::writer) {
alt get_metadata_section(sess, path) {
2011-07-27 07:19:39 -05:00
option::some(bytes) { decoder::list_crate_metadata(bytes, out); }
option::none {
2012-03-05 18:27:27 -06:00
out.write_str("could not find metadata in " + path + ".\n");
2011-07-27 07:19:39 -05:00
}
2011-07-07 23:03:09 -05:00
}
}
fn crate_matches(crate_data: @[u8], metas: [@ast::meta_item]) -> bool {
2011-07-27 07:19:39 -05:00
let attrs = decoder::get_crate_attributes(crate_data);
let linkage_metas = attr::find_linkage_metas(attrs);
metadata_matches(linkage_metas, metas)
}
fn metadata_matches(extern_metas: [@ast::meta_item],
local_metas: [@ast::meta_item]) -> bool {
#debug("matching %u metadata requirements against %u items",
vec::len(local_metas), vec::len(extern_metas));
#debug("crate metadata:");
for extern_metas.each {|have|
#debug(" %s", pprust::meta_item_to_str(*have));
2011-10-29 17:04:44 -05:00
}
for local_metas.each {|needed|
#debug("looking for %s", pprust::meta_item_to_str(*needed));
if !attr::contains(extern_metas, needed) {
#debug("missing %s", pprust::meta_item_to_str(*needed));
ret false;
}
}
ret true;
}
2011-07-27 07:19:39 -05:00
fn default_native_lib_naming(sess: session::session, static: bool) ->
2011-09-02 17:34:58 -05:00
{prefix: str, suffix: str} {
if static { ret {prefix: "lib", suffix: ".rlib"}; }
alt sess.targ_cfg.os {
session::os_win32 { ret {prefix: "", suffix: ".dll"}; }
session::os_macos { ret {prefix: "lib", suffix: ".dylib"}; }
session::os_linux { ret {prefix: "lib", suffix: ".so"}; }
session::os_freebsd { ret {prefix: "lib", suffix: ".so"}; }
}
}
fn crate_name_from_metas(metas: [@ast::meta_item]) -> str {
let name_items = attr::find_meta_items_by_name(metas, "name");
alt vec::last_opt(name_items) {
some(i) {
alt attr::get_meta_item_value_str(i) {
some(n) { n }
// FIXME: Probably want a warning here since the user
// is using the wrong type of meta item
_ { fail }
}
}
none { fail "expected to find the crate name" }
}
}
fn find_library_crate(sess: session::session, span: span,
metas: [@ast::meta_item])
-> option<{ident: str, data: @[u8]}> {
2011-06-28 18:52:11 -05:00
attr::require_unique_names(sess.diagnostic(), metas);
let metas = metas;
let crate_name = crate_name_from_metas(metas);
let nn = default_native_lib_naming(sess, sess.opts.static);
2011-07-27 07:19:39 -05:00
let x =
find_library_crate_aux(sess, span, nn, crate_name,
metas, sess.filesearch);
if x != none || sess.opts.static { ret x; }
2011-07-27 07:19:39 -05:00
let nn2 = default_native_lib_naming(sess, true);
ret find_library_crate_aux(sess, span, nn2, crate_name, metas,
sess.filesearch);
}
fn find_library_crate_aux(sess: session::session,
span: span,
nn: {prefix: str, suffix: str},
crate_name: str,
metas: [@ast::meta_item],
filesearch: filesearch::filesearch) ->
option<{ident: str, data: @[u8]}> {
let prefix: str = nn.prefix + crate_name + "-";
2011-09-02 17:34:58 -05:00
let suffix: str = nn.suffix;
let mut matches = [];
filesearch::search(filesearch, { |path|
#debug("inspecting file %s", path);
let f: str = path::basename(path);
if !(str::starts_with(f, prefix) && str::ends_with(f, suffix)) {
#debug("skipping %s, doesn't look like %s*%s", path, prefix,
suffix);
option::none
} else {
#debug("%s is a candidate", path);
alt get_metadata_section(sess, path) {
2011-07-27 07:19:39 -05:00
option::some(cvec) {
if !crate_matches(cvec, metas) {
#debug("skipping %s, metadata doesn't match", path);
option::none
} else {
#debug("found %s with matching metadata", path);
matches += [{ident: path, data: cvec}];
option::none
}
2011-07-27 07:19:39 -05:00
}
2011-10-29 17:04:44 -05:00
_ {
#debug("could not load metadata for %s", path);
2011-10-29 17:04:44 -05:00
option::none
}
}
}
});
if matches.is_empty() {
none
} else if matches.len() == 1u {
some(matches[0])
} else {
sess.span_err(
span, #fmt("multiple matching crates for `%s`", crate_name));
sess.note("candidates:");
for matches.each {|match|
sess.note(#fmt("path: %s", match.ident));
let attrs = decoder::get_crate_attributes(match.data);
note_linkage_attrs(sess, attrs);
}
sess.abort_if_errors();
none
}
}
fn note_linkage_attrs(sess: session::session, attrs: [ast::attribute]) {
for attr::find_linkage_attrs(attrs).each {|attr|
sess.note(#fmt("meta: %s", pprust::attr_to_str(attr)));
}
}
fn get_metadata_section(sess: session::session,
filename: str) -> option<@[u8]> unsafe {
let mb = str::as_c_str(filename, {|buf|
llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
});
if mb as int == 0 { ret option::none::<@[u8]>; }
let of = alt mk_object_file(mb) {
option::some(of) { of }
_ { ret option::none::<@[u8]>; }
};
let si = mk_section_iter(of.llof);
while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
let name_buf = llvm::LLVMGetSectionName(si.llsi);
let name = unsafe { str::unsafe::from_c_str(name_buf) };
if str::eq(name, sess.targ_cfg.target_strs.meta_sect_name) {
let cbuf = llvm::LLVMGetSectionContents(si.llsi);
let csz = llvm::LLVMGetSectionSize(si.llsi) as uint;
unsafe {
let cvbuf: *u8 = unsafe::reinterpret_cast(cbuf);
ret some(@vec::unsafe::from_buf(cvbuf, csz));
}
}
llvm::LLVMMoveToNextSection(si.llsi);
}
ret option::none::<@[u8]>;
}
fn load_library_crate(sess: session::session, ident: ast::ident, span: span,
metas: [@ast::meta_item])
2011-09-02 17:34:58 -05:00
-> {ident: str, data: @[u8]} {
2011-07-27 07:19:39 -05:00
alt find_library_crate(sess, span, metas) {
2011-07-27 07:19:39 -05:00
some(t) { ret t; }
none {
2011-09-02 17:34:58 -05:00
sess.span_fatal(span, #fmt["can't find crate for '%s'", ident]);
2011-07-27 07:19:39 -05:00
}
}
}
fn metas_with_ident(ident: ast::ident,
metas: [@ast::meta_item]) -> [@ast::meta_item] {
let name_items = attr::find_meta_items_by_name(metas, "name");
if name_items.is_empty() {
metas + [attr::mk_name_value_item_str("name", ident)]
} else {
metas
}
}
fn existing_match(e: env, metas: [@ast::meta_item]) -> option<int> {
let maybe_entry = e.crate_cache.find {|c|
metadata_matches(*c.metas, metas)
};
maybe_entry.map {|c| c.cnum }
}
fn resolve_crate(e: env, ident: ast::ident, metas: [@ast::meta_item],
2011-07-27 07:19:39 -05:00
span: span) -> ast::crate_num {
let metas = metas_with_ident(ident, metas);
alt existing_match(e, metas) {
none {
2011-07-27 07:19:39 -05:00
let cinfo =
load_library_crate(e.sess, ident, span, metas);
2011-07-27 07:19:39 -05:00
let cfilename = cinfo.ident;
let cdata = cinfo.data;
let attrs = decoder::get_crate_attributes(cdata);
let linkage_metas = attr::find_linkage_metas(attrs);
// Claim this crate number and cache it
2011-07-27 07:19:39 -05:00
let cnum = e.next_crate_num;
e.crate_cache += [{cnum: cnum, span: span, 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 cmeta = @{name: ident, data: cdata,
cnum_map: cnum_map, cnum: cnum};
let cstore = e.sess.cstore;
cstore::set_crate_data(cstore, cnum, cmeta);
2011-09-02 17:34:58 -05:00
cstore::add_used_crate_file(cstore, cfilename);
ret cnum;
}
some(cnum) {
ret cnum;
}
}
}
// Go through the crate metadata and load any crates that it references
fn resolve_crate_deps(e: env, cdata: @[u8]) -> cstore::cnum_map {
#debug("resolving deps of external crate");
// The map from crate numbers in the crate we're resolving to local crate
// numbers
let cnum_map = int_hash::<ast::crate_num>();
for decoder::get_crate_deps(cdata).each {|dep|
2011-07-27 07:19:39 -05:00
let extrn_cnum = dep.cnum;
let cname = dep.ident;
// FIXME: We really need to know the linkage metas of our transitive
// dependencies in order to resolve them correctly.
let cmetas = [];
#debug("resolving dep %s", cname);
alt existing_match(e, metas_with_ident(cname, cmetas)) {
some(local_cnum) {
#debug("already have it");
// We've already seen this crate
cnum_map.insert(extrn_cnum, local_cnum);
}
none {
#debug("need to load it");
// This is a new one so we've got to load it
// FIXME: Need better error reporting than just a bogus span
let fake_span = ast_util::dummy_sp();
let local_cnum = resolve_crate(e, cname, cmetas, fake_span);
cnum_map.insert(extrn_cnum, local_cnum);
}
}
}
ret cnum_map;
}
2011-03-24 19:35:27 -05:00
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End: