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