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