rust/src/rustc/metadata/loader.rs

217 lines
6.9 KiB
Rust
Raw Normal View History

//! Finds crate binaries and loads their metadata
import syntax::diagnostic::span_handler;
import syntax::{ast, attr};
import syntax::print::pprust;
import syntax::codemap::span;
import lib::llvm::{False, llvm, mk_object_file, mk_section_iter};
import filesearch::filesearch;
2012-08-14 15:38:35 -05:00
import io::WriterUtil;
export os;
export os_macos, os_win32, os_linux, os_freebsd;
export ctxt;
export load_library_crate;
export list_file_metadata;
export note_linkage_attrs;
export crate_name_from_metas;
export metadata_matches;
export meta_section_name;
enum os {
os_macos,
os_win32,
os_linux,
os_freebsd
}
type ctxt = {
diag: span_handler,
filesearch: filesearch,
span: span,
ident: ast::ident,
metas: ~[@ast::meta_item],
hash: ~str,
os: os,
static: bool
};
fn load_library_crate(cx: ctxt) -> {ident: ~str, data: @~[u8]} {
2012-08-06 14:34:08 -05:00
match find_library_crate(cx) {
2012-08-03 21:59:04 -05:00
some(t) => return t,
none => {
cx.diag.span_fatal(
cx.span, fmt!{"can't find crate for `%s`", *cx.ident});
}
}
}
fn find_library_crate(cx: ctxt) -> option<{ident: ~str, data: @~[u8]}> {
attr::require_unique_names(cx.diag, cx.metas);
find_library_crate_aux(cx, libname(cx), cx.filesearch)
}
fn libname(cx: ctxt) -> {prefix: ~str, suffix: ~str} {
2012-08-01 19:30:05 -05:00
if cx.static { return {prefix: ~"lib", suffix: ~".rlib"}; }
2012-08-06 14:34:08 -05:00
match cx.os {
2012-08-03 21:59:04 -05:00
os_win32 => return {prefix: ~"", suffix: ~".dll"},
os_macos => return {prefix: ~"lib", suffix: ~".dylib"},
os_linux => return {prefix: ~"lib", suffix: ~".so"},
os_freebsd => return {prefix: ~"lib", suffix: ~".so"}
}
}
fn find_library_crate_aux(cx: ctxt,
nn: {prefix: ~str, suffix: ~str},
filesearch: filesearch::filesearch) ->
option<{ident: ~str, data: @~[u8]}> {
let crate_name = crate_name_from_metas(cx.metas);
let prefix: ~str = nn.prefix + *crate_name + ~"-";
let suffix: ~str = nn.suffix;
let mut matches = ~[];
2012-06-30 18:19:07 -05:00
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};
2012-08-06 14:34:08 -05:00
match get_metadata_section(cx.os, path) {
2012-08-03 21:59:04 -05:00
option::some(cvec) => {
if !crate_matches(cvec, cx.metas, cx.hash) {
debug!{"skipping %s, metadata doesn't match", path};
option::none::<()>
} else {
debug!{"found %s with matching metadata", path};
vec::push(matches, {ident: path, data: cvec});
option::none::<()>
}
}
2012-08-03 21:59:04 -05:00
_ => {
debug!{"could not load metadata for %s", path};
option::none::<()>
}
}
}
});
if matches.is_empty() {
none
} else if matches.len() == 1u {
some(matches[0])
} else {
cx.diag.span_err(
cx.span, fmt!{"multiple matching crates for `%s`", *crate_name});
cx.diag.handler().note(~"candidates:");
for matches.each |match_| {
cx.diag.handler().note(fmt!{"path: %s", match_.ident});
let attrs = decoder::get_crate_attributes(match_.data);
note_linkage_attrs(cx.diag, attrs);
}
cx.diag.handler().abort_if_errors();
none
}
}
fn crate_name_from_metas(metas: ~[@ast::meta_item]) -> @~str {
let name_items = attr::find_meta_items_by_name(metas, ~"name");
2012-08-06 14:34:08 -05:00
match vec::last_opt(name_items) {
2012-08-03 21:59:04 -05:00
some(i) => {
2012-08-06 14:34:08 -05:00
match attr::get_meta_item_value_str(i) {
2012-08-03 21:59:04 -05:00
some(n) => n,
// FIXME (#2406): Probably want a warning here since the user
// is using the wrong type of meta item.
2012-08-03 21:59:04 -05:00
_ => fail
}
}
2012-08-03 21:59:04 -05:00
none => fail ~"expected to find the crate name"
}
}
fn note_linkage_attrs(diag: span_handler, attrs: ~[ast::attribute]) {
2012-06-30 18:19:07 -05:00
for attr::find_linkage_attrs(attrs).each |attr| {
diag.handler().note(fmt!{"meta: %s", pprust::attr_to_str(attr)});
}
}
fn crate_matches(crate_data: @~[u8], metas: ~[@ast::meta_item],
hash: ~str) -> bool {
let attrs = decoder::get_crate_attributes(crate_data);
let linkage_metas = attr::find_linkage_metas(attrs);
if hash.is_not_empty() {
let chash = decoder::get_crate_hash(crate_data);
2012-08-01 19:30:05 -05:00
if *chash != hash { return false; }
}
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:"};
2012-06-30 18:19:07 -05:00
for extern_metas.each |have| {
debug!{" %s", pprust::meta_item_to_str(*have)};
}
2012-06-30 18:19:07 -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)};
2012-08-01 19:30:05 -05:00
return false;
}
}
2012-08-01 19:30:05 -05:00
return true;
}
fn get_metadata_section(os: os,
filename: ~str) -> option<@~[u8]> unsafe {
2012-06-30 18:19:07 -05:00
let mb = str::as_c_str(filename, |buf| {
llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
});
2012-08-01 19:30:05 -05:00
if mb as int == 0 { return option::none::<@~[u8]>; }
2012-08-06 14:34:08 -05:00
let of = match mk_object_file(mb) {
2012-08-03 21:59:04 -05:00
option::some(of) => of,
_ => return 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 name == meta_section_name(os) {
let cbuf = llvm::LLVMGetSectionContents(si.llsi);
let csz = llvm::LLVMGetSectionSize(si.llsi) as uint;
unsafe {
let cvbuf: *u8 = unsafe::reinterpret_cast(cbuf);
2012-08-01 19:30:05 -05:00
return some(@vec::unsafe::from_buf(cvbuf, csz));
}
}
llvm::LLVMMoveToNextSection(si.llsi);
}
2012-08-01 19:30:05 -05:00
return option::none::<@~[u8]>;
}
fn meta_section_name(os: os) -> ~str {
2012-08-06 14:34:08 -05:00
match os {
2012-08-03 21:59:04 -05:00
os_macos => ~"__DATA,__note.rustc",
os_win32 => ~".note.rustc",
os_linux => ~".note.rustc",
os_freebsd => ~".note.rustc"
}
}
// A diagnostic function for dumping crate metadata to an output stream
2012-08-14 15:38:35 -05:00
fn list_file_metadata(os: os, path: ~str, out: io::Writer) {
2012-08-06 14:34:08 -05:00
match get_metadata_section(os, path) {
2012-08-03 21:59:04 -05:00
option::some(bytes) => decoder::list_crate_metadata(bytes, out),
option::none => {
out.write_str(~"could not find metadata in " + path + ~".\n");
}
}
}