| //! Finds crate binaries and loads their metadata |
| |
| use syntax::diagnostic::span_handler; |
| use syntax::{ast, attr}; |
| use syntax::print::pprust; |
| use syntax::codemap::span; |
| use lib::llvm::{False, llvm, mk_object_file, mk_section_iter}; |
| use filesearch::filesearch; |
| use io::WriterUtil; |
| use syntax::parse::token::ident_interner; |
| |
| 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, |
| intr: @ident_interner |
| }; |
| |
| fn load_library_crate(cx: ctxt) -> {ident: ~str, data: @~[u8]} { |
| match find_library_crate(cx) { |
| Some(t) => return t, |
| None => { |
| cx.diag.span_fatal( |
| cx.span, fmt!("can't find crate for `%s`", |
| *cx.intr.get(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} { |
| if cx.static { return {prefix: ~"lib", suffix: ~".rlib"}; } |
| match cx.os { |
| 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 = ~[]; |
| filesearch::search(filesearch, |path| { |
| debug!("inspecting file %s", path.to_str()); |
| let f: ~str = path.filename().get(); |
| if !(str::starts_with(f, prefix) && str::ends_with(f, suffix)) { |
| debug!("skipping %s, doesn't look like %s*%s", path.to_str(), |
| prefix, suffix); |
| option::None::<()> |
| } else { |
| debug!("%s is a candidate", path.to_str()); |
| match get_metadata_section(cx.os, path) { |
| option::Some(cvec) => { |
| if !crate_matches(cvec, cx.metas, cx.hash) { |
| debug!("skipping %s, metadata doesn't match", |
| path.to_str()); |
| option::None::<()> |
| } else { |
| debug!("found %s with matching metadata", path.to_str()); |
| matches.push({ident: path.to_str(), data: cvec}); |
| option::None::<()> |
| } |
| } |
| _ => { |
| debug!("could not load metadata for %s", path.to_str()); |
| 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.intr, 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"); |
| match vec::last_opt(name_items) { |
| Some(i) => { |
| match attr::get_meta_item_value_str(i) { |
| Some(n) => n, |
| // FIXME (#2406): 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 note_linkage_attrs(intr: @ident_interner, diag: span_handler, |
| attrs: ~[ast::attribute]) { |
| for attr::find_linkage_metas(attrs).each |mi| { |
| diag.handler().note(fmt!("meta: %s", |
| pprust::meta_item_to_str(*mi,intr))); |
| } |
| } |
| |
| 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); |
| 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)); |
| |
| for local_metas.each |needed| { |
| if !attr::contains(extern_metas, *needed) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| fn get_metadata_section(os: os, |
| filename: &Path) -> Option<@~[u8]> unsafe { |
| let mb = str::as_c_str(filename.to_str(), |buf| { |
| llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf) |
| }); |
| if mb as int == 0 { return option::None::<@~[u8]>; } |
| let of = match mk_object_file(mb) { |
| 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::raw::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; |
| let mut found = None; |
| unsafe { |
| let cvbuf: *u8 = cast::reinterpret_cast(&cbuf); |
| let vlen = vec::len(encoder::metadata_encoding_version); |
| debug!("checking %u bytes of metadata-version stamp", |
| vlen); |
| let minsz = uint::min(vlen, csz); |
| let mut version_ok = false; |
| do vec::raw::buf_as_slice(cvbuf, minsz) |buf0| { |
| version_ok = (buf0 == |
| encoder::metadata_encoding_version); |
| } |
| if !version_ok { return None; } |
| |
| let cvbuf1 = ptr::offset(cvbuf, vlen); |
| debug!("inflating %u bytes of compressed metadata", |
| csz - vlen); |
| do vec::raw::buf_as_slice(cvbuf1, csz-vlen) |bytes| { |
| let inflated = flate::inflate_bytes(bytes); |
| found = move Some(@(move inflated)); |
| } |
| if found != None { |
| return found; |
| } |
| } |
| } |
| llvm::LLVMMoveToNextSection(si.llsi); |
| } |
| return option::None::<@~[u8]>; |
| } |
| |
| fn meta_section_name(os: os) -> ~str { |
| match os { |
| 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 |
| fn list_file_metadata(intr: @ident_interner, |
| os: os, path: &Path, out: io::Writer) { |
| match get_metadata_section(os, path) { |
| option::Some(bytes) => decoder::list_crate_metadata(intr, bytes, out), |
| option::None => { |
| out.write_str(~"could not find metadata in " |
| + path.to_str() + ~".\n"); |
| } |
| } |
| } |