]> git.proxmox.com Git - rustc.git/blame - src/librustc/metadata/loader.rs
Imported Upstream version 0.6
[rustc.git] / src / librustc / metadata / loader.rs
CommitLineData
223e47cc
LB
1// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11
12//! Finds crate binaries and loads their metadata
13
14use core::prelude::*;
15
16use lib::llvm::{False, llvm, mk_object_file, mk_section_iter};
17use metadata::decoder;
18use metadata::encoder;
19use metadata::filesearch::FileSearch;
20use metadata::filesearch;
21use syntax::codemap::span;
22use syntax::diagnostic::span_handler;
23use syntax::parse::token::ident_interner;
24use syntax::print::pprust;
25use syntax::{ast, attr};
26
27use core::cast;
28use core::flate;
29use core::io::WriterUtil;
30use core::io;
31use core::os::consts::{macos, freebsd, linux, android, win32};
32use core::option;
33use core::ptr;
34use core::str;
35use core::uint;
36use core::vec;
37
38pub enum os {
39 os_macos,
40 os_win32,
41 os_linux,
42 os_android,
43 os_freebsd
44}
45
46pub struct Context {
47 diag: @span_handler,
48 filesearch: @FileSearch,
49 span: span,
50 ident: ast::ident,
51 metas: ~[@ast::meta_item],
52 hash: @~str,
53 os: os,
54 is_static: bool,
55 intr: @ident_interner
56}
57
58pub fn load_library_crate(cx: Context) -> (~str, @~[u8]) {
59 match find_library_crate(cx) {
60 Some(ref t) => return (/*bad*/copy *t),
61 None => {
62 cx.diag.span_fatal(
63 cx.span, fmt!("can't find crate for `%s`",
64 *cx.intr.get(cx.ident)));
65 }
66 }
67}
68
69fn find_library_crate(cx: Context) -> Option<(~str, @~[u8])> {
70 attr::require_unique_names(cx.diag, cx.metas);
71 find_library_crate_aux(cx, libname(cx), cx.filesearch)
72}
73
74fn libname(cx: Context) -> (~str, ~str) {
75 if cx.is_static { return (~"lib", ~".rlib"); }
76 let (dll_prefix, dll_suffix) = match cx.os {
77 os_win32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX),
78 os_macos => (macos::DLL_PREFIX, macos::DLL_SUFFIX),
79 os_linux => (linux::DLL_PREFIX, linux::DLL_SUFFIX),
80 os_android => (android::DLL_PREFIX, android::DLL_SUFFIX),
81 os_freebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
82 };
83
84 (str::from_slice(dll_prefix), str::from_slice(dll_suffix))
85}
86
87fn find_library_crate_aux(
88 cx: Context,
89 (prefix, suffix): (~str, ~str),
90 filesearch: @filesearch::FileSearch
91) -> Option<(~str, @~[u8])> {
92 let crate_name = crate_name_from_metas(cx.metas);
93 let prefix: ~str = prefix + *crate_name + ~"-";
94 let suffix: ~str = /*bad*/copy suffix;
95
96 let mut matches = ~[];
97 filesearch::search(filesearch, |path| {
98 debug!("inspecting file %s", path.to_str());
99 let f: ~str = path.filename().get();
100 if !(f.starts_with(prefix) && f.ends_with(suffix)) {
101 debug!("skipping %s, doesn't look like %s*%s", path.to_str(),
102 prefix, suffix);
103 option::None::<()>
104 } else {
105 debug!("%s is a candidate", path.to_str());
106 match get_metadata_section(cx.os, path) {
107 option::Some(cvec) => {
108 if !crate_matches(cvec, cx.metas, cx.hash) {
109 debug!("skipping %s, metadata doesn't match",
110 path.to_str());
111 option::None::<()>
112 } else {
113 debug!("found %s with matching metadata", path.to_str());
114 matches.push((path.to_str(), cvec));
115 option::None::<()>
116 }
117 }
118 _ => {
119 debug!("could not load metadata for %s", path.to_str());
120 option::None::<()>
121 }
122 }
123 }
124 });
125
126 if matches.is_empty() {
127 None
128 } else if matches.len() == 1u {
129 Some(/*bad*/copy matches[0])
130 } else {
131 cx.diag.span_err(
132 cx.span, fmt!("multiple matching crates for `%s`", *crate_name));
133 cx.diag.handler().note(~"candidates:");
134 for matches.each |&(ident, data)| {
135 cx.diag.handler().note(fmt!("path: %s", ident));
136 let attrs = decoder::get_crate_attributes(data);
137 note_linkage_attrs(cx.intr, cx.diag, attrs);
138 }
139 cx.diag.handler().abort_if_errors();
140 None
141 }
142}
143
144pub fn crate_name_from_metas(metas: &[@ast::meta_item]) -> @~str {
145 let name_items = attr::find_meta_items_by_name(metas, ~"name");
146 match name_items.last_opt() {
147 Some(i) => {
148 match attr::get_meta_item_value_str(*i) {
149 Some(n) => n,
150 // FIXME (#2406): Probably want a warning here since the user
151 // is using the wrong type of meta item.
152 _ => fail!()
153 }
154 }
155 None => fail!(~"expected to find the crate name")
156 }
157}
158
159pub fn note_linkage_attrs(intr: @ident_interner,
160 diag: @span_handler,
161 attrs: ~[ast::attribute]) {
162 for attr::find_linkage_metas(attrs).each |mi| {
163 diag.handler().note(fmt!("meta: %s",
164 pprust::meta_item_to_str(*mi,intr)));
165 }
166}
167
168fn crate_matches(crate_data: @~[u8],
169 metas: &[@ast::meta_item],
170 hash: @~str) -> bool {
171 let attrs = decoder::get_crate_attributes(crate_data);
172 let linkage_metas = attr::find_linkage_metas(attrs);
173 if !hash.is_empty() {
174 let chash = decoder::get_crate_hash(crate_data);
175 if chash != hash { return false; }
176 }
177 metadata_matches(linkage_metas, metas)
178}
179
180pub fn metadata_matches(extern_metas: &[@ast::meta_item],
181 local_metas: &[@ast::meta_item]) -> bool {
182
183 debug!("matching %u metadata requirements against %u items",
184 vec::len(local_metas), vec::len(extern_metas));
185
186 for local_metas.each |needed| {
187 if !attr::contains(extern_metas, *needed) {
188 return false;
189 }
190 }
191 return true;
192}
193
194fn get_metadata_section(os: os,
195 filename: &Path) -> Option<@~[u8]> {
196 unsafe {
197 let mb = str::as_c_str(filename.to_str(), |buf| {
198 llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
199 });
200 if mb as int == 0 { return option::None::<@~[u8]>; }
201 let of = match mk_object_file(mb) {
202 option::Some(of) => of,
203 _ => return option::None::<@~[u8]>
204 };
205 let si = mk_section_iter(of.llof);
206 while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
207 let name_buf = llvm::LLVMGetSectionName(si.llsi);
208 let name = unsafe { str::raw::from_c_str(name_buf) };
209 if name == meta_section_name(os) {
210 let cbuf = llvm::LLVMGetSectionContents(si.llsi);
211 let csz = llvm::LLVMGetSectionSize(si.llsi) as uint;
212 let mut found = None;
213 unsafe {
214 let cvbuf: *u8 = cast::reinterpret_cast(&cbuf);
215 let vlen = vec::len(encoder::metadata_encoding_version);
216 debug!("checking %u bytes of metadata-version stamp",
217 vlen);
218 let minsz = uint::min(vlen, csz);
219 let mut version_ok = false;
220 do vec::raw::buf_as_slice(cvbuf, minsz) |buf0| {
221 version_ok = (buf0 ==
222 encoder::metadata_encoding_version);
223 }
224 if !version_ok { return None; }
225
226 let cvbuf1 = ptr::offset(cvbuf, vlen);
227 debug!("inflating %u bytes of compressed metadata",
228 csz - vlen);
229 do vec::raw::buf_as_slice(cvbuf1, csz-vlen) |bytes| {
230 let inflated = flate::inflate_bytes(bytes);
231 found = Some(@(inflated));
232 }
233 if found != None {
234 return found;
235 }
236 }
237 }
238 llvm::LLVMMoveToNextSection(si.llsi);
239 }
240 return option::None::<@~[u8]>;
241 }
242}
243
244pub fn meta_section_name(os: os) -> ~str {
245 match os {
246 os_macos => ~"__DATA,__note.rustc",
247 os_win32 => ~".note.rustc",
248 os_linux => ~".note.rustc",
249 os_android => ~".note.rustc",
250 os_freebsd => ~".note.rustc"
251 }
252}
253
254// A diagnostic function for dumping crate metadata to an output stream
255pub fn list_file_metadata(intr: @ident_interner,
256 os: os,
257 path: &Path,
258 out: @io::Writer) {
259 match get_metadata_section(os, path) {
260 option::Some(bytes) => decoder::list_crate_metadata(intr, bytes, out),
261 option::None => {
262 out.write_str(~"could not find metadata in "
263 + path.to_str() + ~".\n");
264 }
265 }
266}