1 // Copyright 2012-2014 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 #![allow(non_camel_case_types)]
12
13 //! Validates all used crates and extern libraries and loads their metadata
14
15 use back::link;
16 use back::svh::Svh;
17 use driver::session::Session;
18 use driver::{driver, session};
19 use metadata::cstore;
20 use metadata::cstore::CStore;
21 use metadata::decoder;
22 use metadata::loader;
23 use metadata::loader::CratePaths;
24
25 use std::rc::Rc;
26 use collections::HashMap;
27 use syntax::ast;
28 use syntax::abi;
29 use syntax::attr;
30 use syntax::attr::AttrMetaMethods;
31 use syntax::codemap::{Span};
32 use syntax::diagnostic::SpanHandler;
33 use syntax::ext::base::{CrateLoader, MacroCrate};
34 use syntax::parse::token::InternedString;
35 use syntax::parse::token;
36 use syntax::crateid::CrateId;
37 use syntax::visit;
38
39 struct Env<'a> {
40 sess: &'a Session,
41 next_crate_num: ast::CrateNum,
42 }
43
44 // Traverses an AST, reading all the information about use'd crates and extern
45 // libraries necessary for later resolving, typechecking, linking, etc.
46 pub fn read_crates(sess: &Session,
47 krate: &ast::Crate) {
48 let mut e = Env {
49 sess: sess,
50 next_crate_num: sess.cstore.next_crate_num(),
51 };
52 visit_crate(&e, krate);
53 visit::walk_crate(&mut e, krate, ());
54 dump_crates(&sess.cstore);
55 warn_if_multiple_versions(sess.diagnostic(), &sess.cstore)
56 }
57
58 impl<'a> visit::Visitor<()> for Env<'a> {
59 fn visit_view_item(&mut self, a: &ast::ViewItem, _: ()) {
60 visit_view_item(self, a);
61 visit::walk_view_item(self, a, ());
62 }
63 fn visit_item(&mut self, a: &ast::Item, _: ()) {
64 visit_item(self, a);
65 visit::walk_item(self, a, ());
66 }
67 }
68
69 fn dump_crates(cstore: &CStore) {
70 debug!("resolved crates:");
71 cstore.iter_crate_data(|_, data| {
72 debug!("crate_id: {}", data.crate_id());
73 debug!(" cnum: {}", data.cnum);
74 debug!(" hash: {}", data.hash());
75 })
76 }
77
78 fn warn_if_multiple_versions(diag: &SpanHandler, cstore: &CStore) {
79 let mut map = HashMap::new();
80 cstore.iter_crate_data(|cnum, data| {
81 let crateid = data.crate_id();
82 let key = (crateid.name.clone(), crateid.path.clone());
83 map.find_or_insert_with(key, |_| Vec::new()).push(cnum);
84 });
85
86 for ((name, _), dupes) in map.move_iter() {
87 if dupes.len() == 1 { continue }
88 diag.handler().warn(
89 format!("using multiple versions of crate `{}`", name));
90 for dupe in dupes.move_iter() {
91 let data = cstore.get_crate_data(dupe);
92 diag.span_note(data.span, "used here");
93 loader::note_crateid_attr(diag, &data.crate_id());
94 }
95 }
96 }
97
98 fn visit_crate(e: &Env, c: &ast::Crate) {
99 for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) {
100 match a.value_str() {
101 Some(ref linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
102 None => { /* fallthrough */ }
103 }
104 }
105 }
106
107 fn should_link(i: &ast::ViewItem) -> bool {
108 i.attrs.iter().all(|attr| {
109 attr.name().get() != "phase" ||
110 attr.meta_item_list().map_or(false, |phases| {
111 attr::contains_name(phases.as_slice(), "link")
112 })
113 })
114 }
115
116 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
117 if !should_link(i) {
118 return;
119 }
120
121 match extract_crate_info(e, i) {
122 Some(info) => {
123 let (cnum, _, _) = resolve_crate(e, &None, info.ident,
124 &info.crate_id, None,
125 i.span);
126 e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
127 }
128 None => ()
129 }
130 }
131
132 struct CrateInfo {
133 ident: ~str,
134 crate_id: CrateId,
135 id: ast::NodeId,
136 should_link: bool,
137 }
138
139 fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
140 match i.node {
141 ast::ViewItemExternCrate(ident, ref path_opt, id) => {
142 let ident = token::get_ident(ident);
143 debug!("resolving extern crate stmt. ident: {:?} path_opt: {:?}",
144 ident, path_opt);
145 let crate_id = match *path_opt {
146 Some((ref path_str, _)) => {
147 let crateid: Option<CrateId> = from_str(path_str.get());
148 match crateid {
149 None => {
150 e.sess.span_err(i.span, "malformed crate id");
151 return None
152 }
153 Some(id) => id
154 }
155 }
156 None => from_str(ident.get().to_str()).unwrap()
157 };
158 Some(CrateInfo {
159 ident: ident.get().to_str(),
160 crate_id: crate_id,
161 id: id,
162 should_link: should_link(i),
163 })
164 }
165 _ => None
166 }
167 }
168
169 fn visit_item(e: &Env, i: &ast::Item) {
170 match i.node {
171 ast::ItemForeignMod(ref fm) => {
172 if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
173 return;
174 }
175
176 // First, add all of the custom link_args attributes
177 let link_args = i.attrs.iter()
178 .filter_map(|at| if at.name().equiv(&("link_args")) {
179 Some(at)
180 } else {
181 None
182 })
183 .collect::<Vec<&ast::Attribute>>();
184 for m in link_args.iter() {
185 match m.value_str() {
186 Some(linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
187 None => { /* fallthrough */ }
188 }
189 }
190
191 // Next, process all of the #[link(..)]-style arguments
192 let link_args = i.attrs.iter()
193 .filter_map(|at| if at.name().equiv(&("link")) {
194 Some(at)
195 } else {
196 None
197 })
198 .collect::<Vec<&ast::Attribute>>();
199 for m in link_args.iter() {
200 match m.meta_item_list() {
201 Some(items) => {
202 let kind = items.iter().find(|k| {
203 k.name().equiv(&("kind"))
204 }).and_then(|a| a.value_str());
205 let kind = match kind {
206 Some(k) => {
207 if k.equiv(&("static")) {
208 cstore::NativeStatic
209 } else if e.sess.targ_cfg.os == abi::OsMacos &&
210 k.equiv(&("framework")) {
211 cstore::NativeFramework
212 } else if k.equiv(&("framework")) {
213 e.sess.span_err(m.span,
214 "native frameworks are only available \
215 on OSX targets");
216 cstore::NativeUnknown
217 } else {
218 e.sess.span_err(m.span,
219 format!("unknown kind: `{}`", k));
220 cstore::NativeUnknown
221 }
222 }
223 None => cstore::NativeUnknown
224 };
225 let n = items.iter().find(|n| {
226 n.name().equiv(&("name"))
227 }).and_then(|a| a.value_str());
228 let n = match n {
229 Some(n) => n,
230 None => {
231 e.sess.span_err(m.span,
232 "#[link(...)] specified without \
233 `name = \"foo\"`");
234 InternedString::new("foo")
235 }
236 };
237 if n.get().is_empty() {
238 e.sess.span_err(m.span, "#[link(name = \"\")] given with empty name");
239 } else {
240 e.sess.cstore.add_used_library(n.get().to_owned(), kind);
241 }
242 }
243 None => {}
244 }
245 }
246 }
247 _ => { }
248 }
249 }
250
251 fn existing_match(e: &Env, crate_id: &CrateId,
252 hash: Option<&Svh>) -> Option<ast::CrateNum> {
253 let mut ret = None;
254 e.sess.cstore.iter_crate_data(|cnum, data| {
255 let other_id = data.crate_id();
256 if crate_id.matches(&other_id) {
257 let other_hash = data.hash();
258 match hash {
259 Some(hash) if *hash != other_hash => {}
260 Some(..) | None => { ret = Some(cnum); }
261 }
262 }
263 });
264 return ret;
265 }
266
267 fn register_crate<'a>(e: &mut Env,
268 root: &Option<CratePaths>,
269 ident: &str,
270 crate_id: &CrateId,
271 span: Span,
272 lib: loader::Library)
273 -> (ast::CrateNum, Rc<cstore::crate_metadata>,
274 cstore::CrateSource) {
275 // Claim this crate number and cache it
276 let cnum = e.next_crate_num;
277 e.next_crate_num += 1;
278
279 // Stash paths for top-most crate locally if necessary.
280 let crate_paths = if root.is_none() {
281 Some(CratePaths {
282 ident: ident.to_owned(),
283 dylib: lib.dylib.clone(),
284 rlib: lib.rlib.clone(),
285 })
286 } else {
287 None
288 };
289 // Maintain a reference to the top most crate.
290 let root = if root.is_some() { root } else { &crate_paths };
291
292 let cnum_map = resolve_crate_deps(e, root, lib.metadata.as_slice(), span);
293
294 let loader::Library{ dylib, rlib, metadata } = lib;
295
296 let cmeta = Rc::new( cstore::crate_metadata {
297 name: crate_id.name.to_owned(),
298 data: metadata,
299 cnum_map: cnum_map,
300 cnum: cnum,
301 span: span,
302 });
303
304 let source = cstore::CrateSource {
305 dylib: dylib,
306 rlib: rlib,
307 cnum: cnum,
308 };
309
310 e.sess.cstore.set_crate_data(cnum, cmeta.clone());
311 e.sess.cstore.add_used_crate_source(source.clone());
312 (cnum, cmeta, source)
313 }
314
315 fn resolve_crate<'a>(e: &mut Env,
316 root: &Option<CratePaths>,
317 ident: &str,
318 crate_id: &CrateId,
319 hash: Option<&Svh>,
320 span: Span)
321 -> (ast::CrateNum, Rc<cstore::crate_metadata>,
322 cstore::CrateSource) {
323 match existing_match(e, crate_id, hash) {
324 None => {
325 let id_hash = link::crate_id_hash(crate_id);
326 let mut load_ctxt = loader::Context {
327 sess: e.sess,
328 span: span,
329 ident: ident,
330 crate_id: crate_id,
331 id_hash: id_hash,
332 hash: hash.map(|a| &*a),
333 filesearch: e.sess.target_filesearch(),
334 os: session::sess_os_to_meta_os(e.sess.targ_cfg.os),
335 triple: e.sess.targ_cfg.target_strs.target_triple.as_slice(),
336 root: root,
337 rejected_via_hash: vec!(),
338 rejected_via_triple: vec!(),
339 };
340 let library = load_ctxt.load_library_crate();
341 register_crate(e, root, ident, crate_id, span, library)
342 }
343 Some(cnum) => (cnum,
344 e.sess.cstore.get_crate_data(cnum),
345 e.sess.cstore.get_used_crate_source(cnum).unwrap())
346 }
347 }
348
349 // Go through the crate metadata and load any crates that it references
350 fn resolve_crate_deps(e: &mut Env,
351 root: &Option<CratePaths>,
352 cdata: &[u8], span : Span)
353 -> cstore::cnum_map {
354 debug!("resolving deps of external crate");
355 // The map from crate numbers in the crate we're resolving to local crate
356 // numbers
357 decoder::get_crate_deps(cdata).iter().map(|dep| {
358 debug!("resolving dep crate {} hash: `{}`", dep.crate_id, dep.hash);
359 let (local_cnum, _, _) = resolve_crate(e, root,
360 dep.crate_id.name.as_slice(),
361 &dep.crate_id,
362 Some(&dep.hash),
363 span);
364 (dep.cnum, local_cnum)
365 }).collect()
366 }
367
368 pub struct Loader<'a> {
369 env: Env<'a>,
370 }
371
372 impl<'a> Loader<'a> {
373 pub fn new(sess: &'a Session) -> Loader<'a> {
374 Loader {
375 env: Env {
376 sess: sess,
377 next_crate_num: sess.cstore.next_crate_num(),
378 }
379 }
380 }
381 }
382
383 impl<'a> CrateLoader for Loader<'a> {
384 fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate {
385 let info = extract_crate_info(&self.env, krate).unwrap();
386 let target_triple = self.env.sess.targ_cfg.target_strs.target_triple.as_slice();
387 let is_cross = target_triple != driver::host_triple();
388 let mut should_link = info.should_link && !is_cross;
389 let id_hash = link::crate_id_hash(&info.crate_id);
390 let os = driver::get_os(driver::host_triple()).unwrap();
391 let mut load_ctxt = loader::Context {
392 sess: self.env.sess,
393 span: krate.span,
394 ident: info.ident,
395 crate_id: &info.crate_id,
396 id_hash: id_hash,
397 hash: None,
398 filesearch: self.env.sess.host_filesearch(),
399 triple: driver::host_triple(),
400 os: session::sess_os_to_meta_os(os),
401 root: &None,
402 rejected_via_hash: vec!(),
403 rejected_via_triple: vec!(),
404 };
405 let library = match load_ctxt.maybe_load_library_crate() {
406 Some (l) => l,
407 None if is_cross => {
408 // try loading from target crates (only valid if there are
409 // no syntax extensions)
410 load_ctxt.triple = target_triple;
411 load_ctxt.os = session::sess_os_to_meta_os(self.env.sess.targ_cfg.os);
412 load_ctxt.filesearch = self.env.sess.target_filesearch();
413 let lib = load_ctxt.load_library_crate();
414 if decoder::get_macro_registrar_fn(lib.metadata.as_slice()).is_some() {
415 let message = format!("crate `{}` contains a macro_registrar fn but \
416 only a version for triple `{}` could be found (need {})",
417 info.ident, target_triple, driver::host_triple());
418 self.env.sess.span_err(krate.span, message);
419 // need to abort now because the syntax expansion
420 // code will shortly attempt to load and execute
421 // code from the found library.
422 self.env.sess.abort_if_errors();
423 }
424 should_link = info.should_link;
425 lib
426 }
427 None => { load_ctxt.report_load_errs(); unreachable!() },
428 };
429 let macros = decoder::get_exported_macros(library.metadata.as_slice());
430 let registrar = decoder::get_macro_registrar_fn(library.metadata.as_slice()).map(|id| {
431 decoder::get_symbol(library.metadata.as_slice(), id).to_strbuf()
432 });
433 let mc = MacroCrate {
434 lib: library.dylib.clone(),
435 macros: macros.move_iter().map(|x| x.to_strbuf()).collect(),
436 registrar_symbol: registrar,
437 };
438 if should_link {
439 // register crate now to avoid double-reading metadata
440 register_crate(&mut self.env, &None, info.ident.as_slice(),
441 &info.crate_id, krate.span, library);
442 }
443 mc
444 }
445 }
librustc/metadata/creader.rs:367:1-367:1 -struct- definition:
pub struct Loader<'a> {
env: Env<'a>,
}
references:- 4373: pub fn new(sess: &'a Session) -> Loader<'a> {
374: Loader {
375: env: Env {
--
383: impl<'a> CrateLoader for Loader<'a> {
384: fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate {
librustc/metadata/creader.rs:138:1-138:1 -fn- definition:
fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
match i.node {
ast::ViewItemExternCrate(ident, ref path_opt, id) => {
references:- 2384: fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate {
385: let info = extract_crate_info(&self.env, krate).unwrap();
386: let target_triple = self.env.sess.targ_cfg.target_strs.target_triple.as_slice();
librustc/metadata/creader.rs:131:1-131:1 -struct- definition:
struct CrateInfo {
ident: ~str,
crate_id: CrateId,
references:- 2157: };
158: Some(CrateInfo {
159: ident: ident.get().to_str(),
librustc/metadata/creader.rs:106:1-106:1 -fn- definition:
fn should_link(i: &ast::ViewItem) -> bool {
i.attrs.iter().all(|attr| {
attr.name().get() != "phase" ||
references:- 2116: fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
117: if !should_link(i) {
118: return;
--
161: id: id,
162: should_link: should_link(i),
163: })
librustc/metadata/creader.rs:38:1-38:1 -struct- definition:
struct Env<'a> {
sess: &'a Session,
next_crate_num: ast::CrateNum,
references:- 1247: krate: &ast::Crate) {
48: let mut e = Env {
49: sess: sess,
--
374: Loader {
375: env: Env {
376: sess: sess,
librustc/metadata/creader.rs:314:1-314:1 -fn- definition:
fn resolve_crate<'a>(e: &mut Env,
root: &Option<CratePaths>,
ident: &str,
references:- 2122: Some(info) => {
123: let (cnum, _, _) = resolve_crate(e, &None, info.ident,
124: &info.crate_id, None,
--
358: debug!("resolving dep crate {} hash: `{}`", dep.crate_id, dep.hash);
359: let (local_cnum, _, _) = resolve_crate(e, root,
360: dep.crate_id.name.as_slice(),
librustc/metadata/creader.rs:266:1-266:1 -fn- definition:
fn register_crate<'a>(e: &mut Env,
root: &Option<CratePaths>,
ident: &str,
references:- 2340: let library = load_ctxt.load_library_crate();
341: register_crate(e, root, ident, crate_id, span, library)
342: }
--
439: // register crate now to avoid double-reading metadata
440: register_crate(&mut self.env, &None, info.ident.as_slice(),
441: &info.crate_id, krate.span, library);