rust/src/librustc/metadata/loader.rs

278 lines
9.0 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.
//! Finds crate binaries and loads their metadata
2013-08-24 03:40:15 +02:00
use lib::llvm::{False, llvm, mk_object_file, mk_section_iter, ObjectFile};
use metadata::decoder;
use metadata::encoder;
use metadata::filesearch::FileSearch;
use metadata::filesearch;
use syntax::codemap::span;
use syntax::diagnostic::span_handler;
2012-09-04 20:54:36 +02:00
use syntax::parse::token::ident_interner;
use syntax::print::pprust;
use syntax::{ast, attr};
use syntax::attr::AttrMetaMethods;
use std::c_str::ToCStr;
use std::cast;
use std::io;
use std::num;
use std::option;
use std::os::consts::{macos, freebsd, linux, android, win32};
use std::ptr;
use std::str;
use std::vec;
pub enum os {
os_macos,
os_win32,
os_linux,
os_android,
os_freebsd
}
pub struct Context {
diag: @mut span_handler,
filesearch: @FileSearch,
span: span,
ident: @str,
metas: ~[@ast::MetaItem],
hash: @str,
os: os,
is_static: bool,
intr: @ident_interner
}
2013-08-24 03:40:15 +02:00
#[deriving(Clone)]
pub enum MetadataSection {
2013-08-25 05:57:35 +02:00
// A pointer to the object file metadata section, along with
// the ObjectFile handle that keeps it from being destructed
2013-08-24 03:40:15 +02:00
UnsafeSection(@ObjectFile, *u8, uint)
}
pub fn load_library_crate(cx: &Context) -> (~str, MetadataSection) {
2012-08-06 21:34:08 +02:00
match find_library_crate(cx) {
Some(t) => t,
2012-08-20 21:23:37 +02:00
None => {
2013-07-02 21:47:32 +02:00
cx.diag.span_fatal(cx.span,
fmt!("can't find crate for `%s`",
cx.ident));
}
}
}
2013-08-24 03:40:15 +02:00
fn find_library_crate(cx: &Context) -> Option<(~str, MetadataSection)> {
attr::require_unique_names(cx.diag, cx.metas);
find_library_crate_aux(cx, libname(cx), cx.filesearch)
}
fn libname(cx: &Context) -> (~str, ~str) {
if cx.is_static { return (~"lib", ~".rlib"); }
2012-12-20 10:26:27 +01:00
let (dll_prefix, dll_suffix) = match cx.os {
os_win32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX),
os_macos => (macos::DLL_PREFIX, macos::DLL_SUFFIX),
os_linux => (linux::DLL_PREFIX, linux::DLL_SUFFIX),
os_android => (android::DLL_PREFIX, android::DLL_SUFFIX),
2012-12-20 10:26:27 +01:00
os_freebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
};
(dll_prefix.to_owned(), dll_suffix.to_owned())
}
2013-03-03 17:50:20 +01:00
fn find_library_crate_aux(
cx: &Context,
2013-03-03 17:50:20 +01:00
(prefix, suffix): (~str, ~str),
filesearch: @filesearch::FileSearch
2013-08-24 03:40:15 +02:00
) -> Option<(~str, MetadataSection)> {
2013-03-03 17:50:20 +01:00
let crate_name = crate_name_from_metas(cx.metas);
// want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
let prefix = fmt!("%s%s-", prefix, crate_name);
let mut matches = ~[];
filesearch::search(filesearch, |path| -> Option<()> {
let path_str = path.filename();
match path_str {
None => None,
Some(path_str) =>
if path_str.starts_with(prefix) && path_str.ends_with(suffix) {
debug!("%s is a candidate", path.to_str());
match get_metadata_section(cx.os, path) {
Some(cvec) =>
if !crate_matches(cvec, cx.metas, cx.hash) {
debug!("skipping %s, metadata doesn't match",
path.to_str());
None
} else {
debug!("found %s with matching metadata", path.to_str());
matches.push((path.to_str(), cvec));
None
},
_ => {
debug!("could not load metadata for %s", path.to_str());
None
}
}
}
else {
None
}
}
});
match matches.len() {
0 => None,
1 => Some(matches[0]),
_ => {
cx.diag.span_err(
cx.span, fmt!("multiple matching crates for `%s`", crate_name));
cx.diag.handler().note("candidates:");
for pair in matches.iter() {
2013-06-20 21:26:54 +02:00
let ident = pair.first();
let data = pair.second();
cx.diag.handler().note(fmt!("path: %s", ident));
let attrs = decoder::get_crate_attributes(data);
note_linkage_attrs(cx.intr, cx.diag, attrs);
}
cx.diag.handler().abort_if_errors();
None
}
}
}
pub fn crate_name_from_metas(metas: &[@ast::MetaItem]) -> @str {
for m in metas.iter() {
match m.name_str_pair() {
Some((name, s)) if "name" == name => { return s; }
_ => {}
}
}
fail!("expected to find the crate name")
}
pub fn package_id_from_metas(metas: &[@ast::MetaItem]) -> Option<@str> {
for m in metas.iter() {
match m.name_str_pair() {
Some((name, s)) if "package_id" == name => { return Some(s); }
_ => {}
}
}
None
}
pub fn note_linkage_attrs(intr: @ident_interner,
diag: @mut span_handler,
attrs: ~[ast::Attribute]) {
let r = attr::find_linkage_metas(attrs);
for mi in r.iter() {
diag.handler().note(fmt!("meta: %s", pprust::meta_item_to_str(*mi,intr)));
}
}
2013-08-24 03:40:15 +02:00
fn crate_matches(crate_data: MetadataSection,
metas: &[@ast::MetaItem],
hash: @str) -> bool {
let attrs = decoder::get_crate_attributes(crate_data);
let linkage_metas = attr::find_linkage_metas(attrs);
if !hash.is_empty() {
let chash = decoder::get_crate_hash(crate_data);
2012-07-19 01:18:02 +02:00
if chash != hash { return false; }
}
metadata_matches(linkage_metas, metas)
}
pub fn metadata_matches(extern_metas: &[@ast::MetaItem],
local_metas: &[@ast::MetaItem]) -> bool {
// extern_metas: metas we read from the crate
// local_metas: metas we're looking for
2012-08-23 02:24:52 +02:00
debug!("matching %u metadata requirements against %u items",
2013-05-14 11:52:12 +02:00
local_metas.len(), extern_metas.len());
do local_metas.iter().all |needed| {
attr::contains(extern_metas, *needed)
}
}
fn get_metadata_section(os: os,
2013-08-24 03:40:15 +02:00
filename: &Path) -> Option<MetadataSection> {
unsafe {
let mb = do filename.with_c_str |buf| {
llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
};
2013-08-24 03:40:15 +02:00
if mb as int == 0 { return None; }
let of = @match mk_object_file(mb) {
option::Some(of) => of,
2013-08-24 03:40:15 +02:00
_ => return None
};
2013-08-24 03:40:15 +02:00
let si = mk_section_iter((*of).llof);
while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
let name_buf = llvm::LLVMGetSectionName(si.llsi);
2013-06-20 07:52:02 +02:00
let name = str::raw::from_c_str(name_buf);
2013-05-04 01:20:29 +02:00
debug!("get_metadata_section: name %s", name);
2013-03-13 09:22:01 +01:00
if name == read_meta_section_name(os) {
let cbuf = llvm::LLVMGetSectionContents(si.llsi);
let csz = llvm::LLVMGetSectionSize(si.llsi) as uint;
2013-06-20 07:52:02 +02:00
let cvbuf: *u8 = cast::transmute(cbuf);
let vlen = encoder::metadata_encoding_version.len();
debug!("checking %u bytes of metadata-version stamp",
vlen);
let minsz = num::min(vlen, csz);
2013-06-20 07:52:02 +02:00
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; }
2013-08-25 05:57:35 +02:00
let cvbuf1 = ptr::offset(cvbuf, vlen as int);
2013-08-22 22:46:47 +02:00
2013-08-25 05:57:35 +02:00
return Some(UnsafeSection(of, cvbuf1, csz-vlen))
}
llvm::LLVMMoveToNextSection(si.llsi);
}
2013-08-24 03:40:15 +02:00
return None;
}
}
pub fn meta_section_name(os: os) -> ~str {
2012-08-06 21:34:08 +02:00
match os {
2012-08-04 04:59:04 +02:00
os_macos => ~"__DATA,__note.rustc",
os_win32 => ~".note.rustc",
os_linux => ~".note.rustc",
os_android => ~".note.rustc",
2012-08-04 04:59:04 +02:00
os_freebsd => ~".note.rustc"
}
}
2013-03-13 09:22:01 +01:00
pub fn read_meta_section_name(os: os) -> ~str {
match os {
os_macos => ~"__note.rustc",
os_win32 => ~".note.rustc",
os_linux => ~".note.rustc",
os_android => ~".note.rustc",
os_freebsd => ~".note.rustc"
}
}
// A diagnostic function for dumping crate metadata to an output stream
pub fn list_file_metadata(intr: @ident_interner,
os: os,
path: &Path,
out: @io::Writer) {
2012-08-06 21:34:08 +02:00
match get_metadata_section(os, path) {
2012-08-20 21:23:37 +02:00
option::Some(bytes) => decoder::list_crate_metadata(intr, bytes, out),
option::None => {
out.write_str(fmt!("could not find metadata in %s.\n", path.to_str()))
}
}
}