]> git.lizzy.rs Git - rust.git/blob - src/librustc/metadata/creader.rs
rollup merge of #20518: nagisa/weighted-bool
[rust.git] / src / librustc / metadata / creader.rs
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::svh::Svh;
16 use session::{config, Session};
17 use session::search_paths::PathKind;
18 use metadata::cstore;
19 use metadata::cstore::{CStore, CrateSource};
20 use metadata::decoder;
21 use metadata::loader;
22 use metadata::loader::CratePaths;
23 use plugin::load::PluginMetadata;
24 use util::nodemap::FnvHashMap;
25
26 use std::rc::Rc;
27 use std::collections::hash_map::Entry::{Occupied, Vacant};
28 use syntax::ast;
29 use syntax::abi;
30 use syntax::attr;
31 use syntax::attr::AttrMetaMethods;
32 use syntax::codemap::{Span};
33 use syntax::diagnostic::SpanHandler;
34 use syntax::parse::token::InternedString;
35 use syntax::parse::token;
36 use syntax::visit;
37 use util::fs;
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     for &(ref name, kind) in sess.opts.libs.iter() {
58         register_native_lib(sess, None, name.clone(), kind);
59     }
60 }
61
62 impl<'a, 'v> visit::Visitor<'v> for Env<'a> {
63     fn visit_view_item(&mut self, a: &ast::ViewItem) {
64         visit_view_item(self, a);
65         visit::walk_view_item(self, a);
66     }
67     fn visit_item(&mut self, a: &ast::Item) {
68         visit_item(self, a);
69         visit::walk_item(self, a);
70     }
71 }
72
73 fn dump_crates(cstore: &CStore) {
74     debug!("resolved crates:");
75     cstore.iter_crate_data_origins(|_, data, opt_source| {
76         debug!("  name: {}", data.name());
77         debug!("  cnum: {}", data.cnum);
78         debug!("  hash: {}", data.hash());
79         opt_source.map(|cs| {
80             let CrateSource { dylib, rlib, cnum: _ } = cs;
81             dylib.map(|dl| debug!("  dylib: {}", dl.display()));
82             rlib.map(|rl|  debug!("   rlib: {}", rl.display()));
83         });
84     })
85 }
86
87 fn warn_if_multiple_versions(diag: &SpanHandler, cstore: &CStore) {
88     let mut map = FnvHashMap::new();
89     cstore.iter_crate_data(|cnum, data| {
90         match map.entry(&data.name()) {
91             Vacant(entry) => { entry.insert(vec![cnum]); },
92             Occupied(mut entry) => { entry.get_mut().push(cnum); },
93         }
94     });
95
96     for (name, dupes) in map.into_iter() {
97         if dupes.len() == 1 { continue }
98         diag.handler().warn(
99             format!("using multiple versions of crate `{}`", name)[]);
100         for dupe in dupes.into_iter() {
101             let data = cstore.get_crate_data(dupe);
102             diag.span_note(data.span, "used here");
103             loader::note_crate_name(diag, data.name()[]);
104         }
105     }
106 }
107
108 fn visit_crate(e: &Env, c: &ast::Crate) {
109     for a in c.attrs.iter().filter(|m| m.name() == "link_args") {
110         match a.value_str() {
111             Some(ref linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
112             None => { /* fallthrough */ }
113         }
114     }
115 }
116
117 fn should_link(i: &ast::ViewItem) -> bool {
118     i.attrs.iter().all(|attr| {
119         attr.name().get() != "phase" ||
120             attr.meta_item_list().map_or(false, |phases| {
121                 attr::contains_name(phases[], "link")
122             })
123     })
124 }
125
126 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
127     if !should_link(i) {
128         return;
129     }
130
131     match extract_crate_info(e, i) {
132         Some(info) => {
133             let (cnum, _, _) = resolve_crate(e,
134                                              &None,
135                                              info.ident[],
136                                              info.name[],
137                                              None,
138                                              i.span,
139                                              PathKind::Crate);
140             e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
141         }
142         None => ()
143     }
144 }
145
146 struct CrateInfo {
147     ident: String,
148     name: String,
149     id: ast::NodeId,
150     should_link: bool,
151 }
152
153 fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
154     match i.node {
155         ast::ViewItemExternCrate(ident, ref path_opt, id) => {
156             let ident = token::get_ident(ident);
157             debug!("resolving extern crate stmt. ident: {} path_opt: {}",
158                    ident, path_opt);
159             let name = match *path_opt {
160                 Some((ref path_str, _)) => {
161                     let name = path_str.get().to_string();
162                     validate_crate_name(Some(e.sess), name[],
163                                         Some(i.span));
164                     name
165                 }
166                 None => ident.get().to_string(),
167             };
168             Some(CrateInfo {
169                 ident: ident.get().to_string(),
170                 name: name,
171                 id: id,
172                 should_link: should_link(i),
173             })
174         }
175         _ => None
176     }
177 }
178
179 pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option<Span>) {
180     let err = |&: s: &str| {
181         match (sp, sess) {
182             (_, None) => panic!("{}", s),
183             (Some(sp), Some(sess)) => sess.span_err(sp, s),
184             (None, Some(sess)) => sess.err(s),
185         }
186     };
187     if s.len() == 0 {
188         err("crate name must not be empty");
189     }
190     for c in s.chars() {
191         if c.is_alphanumeric() { continue }
192         if c == '_' || c == '-' { continue }
193         err(format!("invalid character `{}` in crate name: `{}`", c, s)[]);
194     }
195     match sess {
196         Some(sess) => sess.abort_if_errors(),
197         None => {}
198     }
199 }
200
201 fn visit_item(e: &Env, i: &ast::Item) {
202     match i.node {
203         ast::ItemForeignMod(ref fm) => {
204             if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
205                 return;
206             }
207
208             // First, add all of the custom link_args attributes
209             let link_args = i.attrs.iter()
210                 .filter_map(|at| if at.name() == "link_args" {
211                     Some(at)
212                 } else {
213                     None
214                 })
215                 .collect::<Vec<&ast::Attribute>>();
216             for m in link_args.iter() {
217                 match m.value_str() {
218                     Some(linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
219                     None => { /* fallthrough */ }
220                 }
221             }
222
223             // Next, process all of the #[link(..)]-style arguments
224             let link_args = i.attrs.iter()
225                 .filter_map(|at| if at.name() == "link" {
226                     Some(at)
227                 } else {
228                     None
229                 })
230                 .collect::<Vec<&ast::Attribute>>();
231             for m in link_args.iter() {
232                 match m.meta_item_list() {
233                     Some(items) => {
234                         let kind = items.iter().find(|k| {
235                             k.name() == "kind"
236                         }).and_then(|a| a.value_str());
237                         let kind = match kind {
238                             Some(k) => {
239                                 if k == "static" {
240                                     cstore::NativeStatic
241                                 } else if e.sess.target.target.options.is_like_osx
242                                           && k == "framework" {
243                                     cstore::NativeFramework
244                                 } else if k == "framework" {
245                                     cstore::NativeFramework
246                                 } else if k == "dylib" {
247                                     cstore::NativeUnknown
248                                 } else {
249                                     e.sess.span_err(m.span,
250                                         format!("unknown kind: `{}`",
251                                                 k)[]);
252                                     cstore::NativeUnknown
253                                 }
254                             }
255                             None => cstore::NativeUnknown
256                         };
257                         let n = items.iter().find(|n| {
258                             n.name() == "name"
259                         }).and_then(|a| a.value_str());
260                         let n = match n {
261                             Some(n) => n,
262                             None => {
263                                 e.sess.span_err(m.span,
264                                     "#[link(...)] specified without \
265                                      `name = \"foo\"`");
266                                 InternedString::new("foo")
267                             }
268                         };
269                         register_native_lib(e.sess, Some(m.span),
270                                             n.get().to_string(), kind);
271                     }
272                     None => {}
273                 }
274             }
275         }
276         _ => { }
277     }
278 }
279
280 fn register_native_lib(sess: &Session,
281                        span: Option<Span>,
282                        name: String,
283                        kind: cstore::NativeLibraryKind) {
284     if name.is_empty() {
285         match span {
286             Some(span) => {
287                 sess.span_err(span, "#[link(name = \"\")] given with \
288                                      empty name");
289             }
290             None => {
291                 sess.err("empty library name given via `-l`");
292             }
293         }
294         return
295     }
296     let is_osx = sess.target.target.options.is_like_osx;
297     if kind == cstore::NativeFramework && !is_osx {
298         let msg = "native frameworks are only available on OSX targets";
299         match span {
300             Some(span) => sess.span_err(span, msg),
301             None => sess.err(msg),
302         }
303     }
304     sess.cstore.add_used_library(name, kind);
305 }
306
307 fn existing_match(e: &Env, name: &str,
308                   hash: Option<&Svh>) -> Option<ast::CrateNum> {
309     let mut ret = None;
310     e.sess.cstore.iter_crate_data(|cnum, data| {
311         if data.name != name { return }
312
313         match hash {
314             Some(hash) if *hash == data.hash() => { ret = Some(cnum); return }
315             Some(..) => return,
316             None => {}
317         }
318
319         // When the hash is None we're dealing with a top-level dependency in
320         // which case we may have a specification on the command line for this
321         // library. Even though an upstream library may have loaded something of
322         // the same name, we have to make sure it was loaded from the exact same
323         // location as well.
324         //
325         // We're also sure to compare *paths*, not actual byte slices. The
326         // `source` stores paths which are normalized which may be different
327         // from the strings on the command line.
328         let source = e.sess.cstore.get_used_crate_source(cnum).unwrap();
329         match e.sess.opts.externs.get(name) {
330             Some(locs) => {
331                 let found = locs.iter().any(|l| {
332                     let l = fs::realpath(&Path::new(l[])).ok();
333                     l == source.dylib || l == source.rlib
334                 });
335                 if found {
336                     ret = Some(cnum);
337                 }
338             }
339             None => ret = Some(cnum),
340         }
341     });
342     return ret;
343 }
344
345 fn register_crate<'a>(e: &mut Env,
346                   root: &Option<CratePaths>,
347                   ident: &str,
348                   name: &str,
349                   span: Span,
350                   lib: loader::Library)
351                         -> (ast::CrateNum, Rc<cstore::crate_metadata>,
352                             cstore::CrateSource) {
353     // Claim this crate number and cache it
354     let cnum = e.next_crate_num;
355     e.next_crate_num += 1;
356
357     // Stash paths for top-most crate locally if necessary.
358     let crate_paths = if root.is_none() {
359         Some(CratePaths {
360             ident: ident.to_string(),
361             dylib: lib.dylib.clone(),
362             rlib:  lib.rlib.clone(),
363         })
364     } else {
365         None
366     };
367     // Maintain a reference to the top most crate.
368     let root = if root.is_some() { root } else { &crate_paths };
369
370     let cnum_map = resolve_crate_deps(e, root, lib.metadata.as_slice(), span);
371
372     let loader::Library{ dylib, rlib, metadata } = lib;
373
374     let cmeta = Rc::new( cstore::crate_metadata {
375         name: name.to_string(),
376         data: metadata,
377         cnum_map: cnum_map,
378         cnum: cnum,
379         span: span,
380     });
381
382     let source = cstore::CrateSource {
383         dylib: dylib,
384         rlib: rlib,
385         cnum: cnum,
386     };
387
388     e.sess.cstore.set_crate_data(cnum, cmeta.clone());
389     e.sess.cstore.add_used_crate_source(source.clone());
390     (cnum, cmeta, source)
391 }
392
393 fn resolve_crate(e: &mut Env,
394                  root: &Option<CratePaths>,
395                  ident: &str,
396                  name: &str,
397                  hash: Option<&Svh>,
398                  span: Span,
399                  kind: PathKind)
400                      -> (ast::CrateNum, Rc<cstore::crate_metadata>,
401                          cstore::CrateSource) {
402     match existing_match(e, name, hash) {
403         None => {
404             let mut load_ctxt = loader::Context {
405                 sess: e.sess,
406                 span: span,
407                 ident: ident,
408                 crate_name: name,
409                 hash: hash.map(|a| &*a),
410                 filesearch: e.sess.target_filesearch(kind),
411                 triple: e.sess.opts.target_triple[],
412                 root: root,
413                 rejected_via_hash: vec!(),
414                 rejected_via_triple: vec!(),
415                 should_match_name: true,
416             };
417             let library = load_ctxt.load_library_crate();
418             register_crate(e, root, ident, name, span, library)
419         }
420         Some(cnum) => (cnum,
421                        e.sess.cstore.get_crate_data(cnum),
422                        e.sess.cstore.get_used_crate_source(cnum).unwrap())
423     }
424 }
425
426 // Go through the crate metadata and load any crates that it references
427 fn resolve_crate_deps(e: &mut Env,
428                       root: &Option<CratePaths>,
429                       cdata: &[u8], span : Span)
430                    -> cstore::cnum_map {
431     debug!("resolving deps of external crate");
432     // The map from crate numbers in the crate we're resolving to local crate
433     // numbers
434     decoder::get_crate_deps(cdata).iter().map(|dep| {
435         debug!("resolving dep crate {} hash: `{}`", dep.name, dep.hash);
436         let (local_cnum, _, _) = resolve_crate(e, root,
437                                                dep.name[],
438                                                dep.name[],
439                                                Some(&dep.hash),
440                                                span,
441                                                PathKind::Dependency);
442         (dep.cnum, local_cnum)
443     }).collect()
444 }
445
446 pub struct PluginMetadataReader<'a> {
447     env: Env<'a>,
448 }
449
450 impl<'a> PluginMetadataReader<'a> {
451     pub fn new(sess: &'a Session) -> PluginMetadataReader<'a> {
452         PluginMetadataReader {
453             env: Env {
454                 sess: sess,
455                 next_crate_num: sess.cstore.next_crate_num(),
456             }
457         }
458     }
459
460     pub fn read_plugin_metadata(&mut self,
461                                 krate: &ast::ViewItem) -> PluginMetadata {
462         let info = extract_crate_info(&self.env, krate).unwrap();
463         let target_triple = self.env.sess.opts.target_triple[];
464         let is_cross = target_triple != config::host_triple();
465         let mut should_link = info.should_link && !is_cross;
466         let mut load_ctxt = loader::Context {
467             sess: self.env.sess,
468             span: krate.span,
469             ident: info.ident[],
470             crate_name: info.name[],
471             hash: None,
472             filesearch: self.env.sess.host_filesearch(PathKind::Crate),
473             triple: config::host_triple(),
474             root: &None,
475             rejected_via_hash: vec!(),
476             rejected_via_triple: vec!(),
477             should_match_name: true,
478         };
479         let library = match load_ctxt.maybe_load_library_crate() {
480             Some(l) => l,
481             None if is_cross => {
482                 // try loading from target crates (only valid if there are
483                 // no syntax extensions)
484                 load_ctxt.triple = target_triple;
485                 load_ctxt.filesearch = self.env.sess.target_filesearch(PathKind::Crate);
486                 let lib = load_ctxt.load_library_crate();
487                 if decoder::get_plugin_registrar_fn(lib.metadata.as_slice()).is_some() {
488                     let message = format!("crate `{}` contains a plugin_registrar fn but \
489                                   only a version for triple `{}` could be found (need {})",
490                                   info.ident, target_triple, config::host_triple());
491                     self.env.sess.span_err(krate.span, message[]);
492                     // need to abort now because the syntax expansion
493                     // code will shortly attempt to load and execute
494                     // code from the found library.
495                     self.env.sess.abort_if_errors();
496                 }
497                 should_link = info.should_link;
498                 lib
499             }
500             None => { load_ctxt.report_load_errs(); unreachable!() },
501         };
502         let macros = decoder::get_exported_macros(library.metadata.as_slice());
503         let registrar = decoder::get_plugin_registrar_fn(library.metadata.as_slice()).map(|id| {
504             decoder::get_symbol(library.metadata.as_slice(), id)
505         });
506         if library.dylib.is_none() && registrar.is_some() {
507             let message = format!("plugin crate `{}` only found in rlib format, \
508                                    but must be available in dylib format",
509                                   info.ident);
510             self.env.sess.span_err(krate.span, message[]);
511             // No need to abort because the loading code will just ignore this
512             // empty dylib.
513         }
514         let pc = PluginMetadata {
515             lib: library.dylib.clone(),
516             macros: macros,
517             registrar_symbol: registrar,
518         };
519         if should_link && existing_match(&self.env, info.name[],
520                                          None).is_none() {
521             // register crate now to avoid double-reading metadata
522             register_crate(&mut self.env, &None, info.ident[],
523                            info.name[], krate.span, library);
524         }
525         pc
526     }
527 }