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