]> git.lizzy.rs Git - rust.git/blob - src/librustc/metadata/creader.rs
165c1abdeedfa96376de205fb94b9ca42bf8b827
[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::link;
16 use back::svh::Svh;
17 use driver::{driver, session};
18 use driver::session::Session;
19 use metadata::csearch;
20 use metadata::cstore;
21 use metadata::decoder;
22 use metadata::loader;
23 use metadata::loader::Os;
24
25 use std::cell::RefCell;
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::{IdentInterner, InternedString};
35 use syntax::parse::token;
36 use syntax::crateid::CrateId;
37 use syntax::visit;
38
39 // Traverses an AST, reading all the information about use'd crates and extern
40 // libraries necessary for later resolving, typechecking, linking, etc.
41 pub fn read_crates(sess: Session,
42                    krate: &ast::Crate,
43                    os: loader::Os,
44                    intr: @IdentInterner) {
45     let mut e = Env {
46         sess: sess,
47         os: os,
48         crate_cache: @RefCell::new(~[]),
49         next_crate_num: 1,
50         intr: intr
51     };
52     visit_crate(&e, krate);
53     {
54         let mut v = ReadCrateVisitor {
55             e: &mut e
56         };
57         visit::walk_crate(&mut v, krate, ());
58     }
59     let crate_cache = e.crate_cache.borrow();
60     dump_crates(*crate_cache.get());
61     warn_if_multiple_versions(&mut e, sess.diagnostic(), *crate_cache.get());
62 }
63
64 struct ReadCrateVisitor<'a> {
65     e: &'a mut Env,
66 }
67
68 impl<'a> visit::Visitor<()> for ReadCrateVisitor<'a> {
69     fn visit_view_item(&mut self, a: &ast::ViewItem, _: ()) {
70         visit_view_item(self.e, a);
71         visit::walk_view_item(self, a, ());
72     }
73     fn visit_item(&mut self, a: &ast::Item, _: ()) {
74         visit_item(self.e, a);
75         visit::walk_item(self, a, ());
76     }
77 }
78
79 #[deriving(Clone)]
80 struct cache_entry {
81     cnum: ast::CrateNum,
82     span: Span,
83     hash: Svh,
84     crate_id: CrateId,
85 }
86
87 fn dump_crates(crate_cache: &[cache_entry]) {
88     debug!("resolved crates:");
89     for entry in crate_cache.iter() {
90         debug!("cnum: {:?}", entry.cnum);
91         debug!("span: {:?}", entry.span);
92         debug!("hash: {:?}", entry.hash);
93     }
94 }
95
96 fn warn_if_multiple_versions(e: &mut Env,
97                              diag: @SpanHandler,
98                              crate_cache: &[cache_entry]) {
99     if crate_cache.len() != 0u {
100         let name = crate_cache[crate_cache.len() - 1].crate_id.name.clone();
101
102         let (matches, non_matches) = crate_cache.partitioned(|entry|
103             name == entry.crate_id.name);
104
105         assert!(!matches.is_empty());
106
107         if matches.len() != 1u {
108             diag.handler().warn(
109                 format!("using multiple versions of crate `{}`", name));
110             for match_ in matches.iter() {
111                 diag.span_note(match_.span, "used here");
112                 loader::note_crateid_attr(diag, &match_.crate_id);
113             }
114         }
115
116         warn_if_multiple_versions(e, diag, non_matches);
117     }
118 }
119
120 struct Env {
121     sess: Session,
122     os: loader::Os,
123     crate_cache: @RefCell<~[cache_entry]>,
124     next_crate_num: ast::CrateNum,
125     intr: @IdentInterner
126 }
127
128 fn visit_crate(e: &Env, c: &ast::Crate) {
129     let cstore = e.sess.cstore;
130
131     for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) {
132         match a.value_str() {
133           Some(ref linkarg) => cstore.add_used_link_args(linkarg.get()),
134           None => { /* fallthrough */ }
135         }
136     }
137 }
138
139 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
140     let should_load = i.attrs.iter().all(|attr| {
141         attr.name().get() != "phase" ||
142             attr.meta_item_list().map_or(false, |phases| {
143                 attr::contains_name(phases, "link")
144             })
145     });
146
147     if !should_load {
148         return;
149     }
150
151     match extract_crate_info(e, i) {
152         Some(info) => {
153             let cnum = resolve_crate(e, None, info.ident, &info.crate_id, None,
154                                      i.span);
155             e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
156         }
157         None => ()
158     }
159 }
160
161 struct CrateInfo {
162     ident: ~str,
163     crate_id: CrateId,
164     id: ast::NodeId,
165 }
166
167 fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
168     match i.node {
169         ast::ViewItemExternMod(ident, ref path_opt, id) => {
170             let ident = token::get_ident(ident);
171             debug!("resolving extern crate stmt. ident: {:?} path_opt: {:?}",
172                    ident, path_opt);
173             let crate_id = match *path_opt {
174                 Some((ref path_str, _)) => {
175                     let crateid: Option<CrateId> = from_str(path_str.get());
176                     match crateid {
177                         None => {
178                             e.sess.span_err(i.span, "malformed crate id");
179                             return None
180                         }
181                         Some(id) => id
182                     }
183                 }
184                 None => from_str(ident.get().to_str()).unwrap()
185             };
186             Some(CrateInfo {
187                 ident: ident.get().to_str(),
188                 crate_id: crate_id,
189                 id: id,
190             })
191         }
192         _ => None
193     }
194 }
195
196 fn visit_item(e: &Env, i: &ast::Item) {
197     match i.node {
198         ast::ItemForeignMod(ref fm) => {
199             if fm.abis.is_rust() || fm.abis.is_intrinsic() {
200                 return;
201             }
202
203             // First, add all of the custom link_args attributes
204             let cstore = e.sess.cstore;
205             let link_args = i.attrs.iter()
206                 .filter_map(|at| if at.name().equiv(&("link_args")) {
207                     Some(at)
208                 } else {
209                     None
210                 })
211                 .to_owned_vec();
212             for m in link_args.iter() {
213                 match m.value_str() {
214                     Some(linkarg) => cstore.add_used_link_args(linkarg.get()),
215                     None => { /* fallthrough */ }
216                 }
217             }
218
219             // Next, process all of the #[link(..)]-style arguments
220             let cstore = e.sess.cstore;
221             let link_args = i.attrs.iter()
222                 .filter_map(|at| if at.name().equiv(&("link")) {
223                     Some(at)
224                 } else {
225                     None
226                 })
227                 .to_owned_vec();
228             for m in link_args.iter() {
229                 match m.meta_item_list() {
230                     Some(items) => {
231                         let kind = items.iter().find(|k| {
232                             k.name().equiv(&("kind"))
233                         }).and_then(|a| a.value_str());
234                         let kind = match kind {
235                             Some(k) => {
236                                 if k.equiv(&("static")) {
237                                     cstore::NativeStatic
238                                 } else if e.sess.targ_cfg.os == abi::OsMacos &&
239                                           k.equiv(&("framework")) {
240                                     cstore::NativeFramework
241                                 } else if k.equiv(&("framework")) {
242                                     e.sess.span_err(m.span,
243                                         "native frameworks are only available \
244                                          on OSX targets");
245                                     cstore::NativeUnknown
246                                 } else {
247                                     e.sess.span_err(m.span,
248                                         format!("unknown kind: `{}`", k));
249                                     cstore::NativeUnknown
250                                 }
251                             }
252                             None => cstore::NativeUnknown
253                         };
254                         let n = items.iter().find(|n| {
255                             n.name().equiv(&("name"))
256                         }).and_then(|a| a.value_str());
257                         let n = match n {
258                             Some(n) => n,
259                             None => {
260                                 e.sess.span_err(m.span,
261                                     "#[link(...)] specified without \
262                                      `name = \"foo\"`");
263                                 InternedString::new("foo")
264                             }
265                         };
266                         if n.get().is_empty() {
267                             e.sess.span_err(m.span, "#[link(name = \"\")] given with empty name");
268                         } else {
269                             cstore.add_used_library(n.get().to_owned(), kind);
270                         }
271                     }
272                     None => {}
273                 }
274             }
275         }
276         _ => { }
277     }
278 }
279
280 fn existing_match(e: &Env, crate_id: &CrateId,
281                   hash: Option<&Svh>) -> Option<ast::CrateNum> {
282     let crate_cache = e.crate_cache.borrow();
283     for c in crate_cache.get().iter() {
284         if !crate_id.matches(&c.crate_id) { continue }
285         match hash {
286             Some(hash) if *hash != c.hash => {}
287             Some(..) | None => return Some(c.cnum)
288         }
289     }
290     None
291 }
292
293 fn resolve_crate(e: &mut Env,
294                  root_ident: Option<&str>,
295                  ident: &str,
296                  crate_id: &CrateId,
297                  hash: Option<&Svh>,
298                  span: Span)
299               -> ast::CrateNum {
300     match existing_match(e, crate_id, hash) {
301         None => {
302             let id_hash = link::crate_id_hash(crate_id);
303             let mut load_ctxt = loader::Context {
304                 sess: e.sess,
305                 span: span,
306                 ident: ident,
307                 crate_id: crate_id,
308                 id_hash: id_hash,
309                 hash: hash.map(|a| &*a),
310                 os: e.os,
311                 intr: e.intr,
312                 rejected_via_hash: false,
313             };
314             let loader::Library {
315                 dylib, rlib, metadata
316             } = load_ctxt.load_library_crate(root_ident);
317
318             let crate_id = decoder::get_crate_id(metadata.as_slice());
319             let hash = decoder::get_crate_hash(metadata.as_slice());
320
321             // Claim this crate number and cache it
322             let cnum = e.next_crate_num;
323             {
324                 let mut crate_cache = e.crate_cache.borrow_mut();
325                 crate_cache.get().push(cache_entry {
326                     cnum: cnum,
327                     span: span,
328                     hash: hash,
329                     crate_id: crate_id,
330                 });
331             }
332             e.next_crate_num += 1;
333
334             // Maintain a reference to the top most crate.
335             let root_crate = match root_ident {
336                 Some(c) => c,
337                 None => load_ctxt.ident.clone()
338             };
339
340             // Now resolve the crates referenced by this crate
341             let cnum_map = resolve_crate_deps(e,
342             Some(root_crate),
343             metadata.as_slice(),
344             span);
345
346             let cmeta = @cstore::crate_metadata {
347                 name: load_ctxt.crate_id.name.to_owned(),
348                 data: metadata,
349                 cnum_map: cnum_map,
350                 cnum: cnum
351             };
352
353             let cstore = e.sess.cstore;
354             cstore.set_crate_data(cnum, cmeta);
355             cstore.add_used_crate_source(cstore::CrateSource {
356                 dylib: dylib,
357                 rlib: rlib,
358                 cnum: cnum,
359             });
360             return cnum;
361         }
362         Some(cnum) => {
363             return cnum;
364         }
365     }
366 }
367
368 // Go through the crate metadata and load any crates that it references
369 fn resolve_crate_deps(e: &mut Env,
370                       root_ident: Option<&str>,
371                       cdata: &[u8], span : Span)
372                    -> cstore::cnum_map {
373     debug!("resolving deps of external crate");
374     // The map from crate numbers in the crate we're resolving to local crate
375     // numbers
376     let mut cnum_map = HashMap::new();
377     let r = decoder::get_crate_deps(cdata);
378     for dep in r.iter() {
379         let extrn_cnum = dep.cnum;
380         debug!("resolving dep crate {} hash: `{}`", dep.crate_id, dep.hash);
381         let local_cnum = resolve_crate(e, root_ident,
382                                        dep.crate_id.name.as_slice(),
383                                        &dep.crate_id,
384                                        Some(&dep.hash),
385                                        span);
386         cnum_map.insert(extrn_cnum, local_cnum);
387     }
388     return @RefCell::new(cnum_map);
389 }
390
391 pub struct Loader {
392     priv env: Env,
393 }
394
395 impl Loader {
396     pub fn new(sess: Session) -> Loader {
397         let os = driver::get_os(driver::host_triple()).unwrap();
398         let os = session::sess_os_to_meta_os(os);
399         Loader {
400             env: Env {
401                 sess: sess,
402                 os: os,
403                 crate_cache: @RefCell::new(~[]),
404                 next_crate_num: 1,
405                 intr: token::get_ident_interner(),
406             }
407         }
408     }
409 }
410
411 impl CrateLoader for Loader {
412     fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate {
413         let info = extract_crate_info(&self.env, krate).unwrap();
414         let cnum = resolve_crate(&mut self.env, None, info.ident,
415                                  &info.crate_id, None, krate.span);
416         let library = self.env.sess.cstore.get_used_crate_source(cnum).unwrap();
417         MacroCrate {
418             lib: library.dylib,
419             cnum: cnum
420         }
421     }
422
423     fn get_exported_macros(&mut self, cnum: ast::CrateNum) -> ~[~str] {
424         csearch::get_exported_macros(self.env.sess.cstore, cnum)
425     }
426
427     fn get_registrar_symbol(&mut self, cnum: ast::CrateNum) -> Option<~str> {
428         let cstore = self.env.sess.cstore;
429         csearch::get_macro_registrar_fn(cstore, cnum)
430             .map(|did| csearch::get_symbol(cstore, did))
431     }
432 }