]> git.lizzy.rs Git - rust.git/blob - src/librustc/metadata/creader.rs
Add generation of static libraries to rustc
[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::session::Session;
14 use metadata::cstore;
15 use metadata::decoder;
16 use metadata::loader;
17
18 use std::hashmap::HashMap;
19 use syntax::ast;
20 use std::vec;
21 use syntax::attr;
22 use syntax::attr::AttrMetaMethods;
23 use syntax::codemap::{Span, dummy_sp};
24 use syntax::diagnostic::span_handler;
25 use syntax::parse::token;
26 use syntax::parse::token::ident_interner;
27 use syntax::visit;
28
29 // Traverses an AST, reading all the information about use'd crates and extern
30 // libraries necessary for later resolving, typechecking, linking, etc.
31 pub fn read_crates(sess: Session,
32                    crate: &ast::Crate,
33                    os: loader::Os,
34                    intr: @ident_interner) {
35     let e = @mut Env {
36         sess: sess,
37         os: os,
38         crate_cache: @mut ~[],
39         next_crate_num: 1,
40         intr: intr
41     };
42     let mut v = ReadCrateVisitor{ e:e };
43     visit_crate(e, crate);
44     visit::walk_crate(&mut v, crate, ());
45     dump_crates(*e.crate_cache);
46     warn_if_multiple_versions(e, sess.diagnostic(), *e.crate_cache);
47 }
48
49 struct ReadCrateVisitor { e:@mut Env }
50 impl visit::Visitor<()> for ReadCrateVisitor {
51     fn visit_view_item(&mut self, a:&ast::view_item, _:()) {
52         visit_view_item(self.e, a);
53         visit::walk_view_item(self, a, ());
54     }
55     fn visit_item(&mut self, a:@ast::item, _:()) {
56         visit_item(self.e, a);
57         visit::walk_item(self, a, ());
58     }
59 }
60
61 #[deriving(Clone)]
62 struct cache_entry {
63     cnum: ast::CrateNum,
64     span: Span,
65     hash: @str,
66     metas: @~[@ast::MetaItem]
67 }
68
69 fn dump_crates(crate_cache: &[cache_entry]) {
70     debug!("resolved crates:");
71     for entry in crate_cache.iter() {
72         debug!("cnum: {:?}", entry.cnum);
73         debug!("span: {:?}", entry.span);
74         debug!("hash: {:?}", entry.hash);
75     }
76 }
77
78 fn warn_if_multiple_versions(e: @mut Env,
79                              diag: @mut span_handler,
80                              crate_cache: &[cache_entry]) {
81     if crate_cache.len() != 0u {
82         let name = loader::crate_name_from_metas(
83             *crate_cache[crate_cache.len() - 1].metas
84         );
85
86         let (matches, non_matches) = crate_cache.partitioned(|entry|
87             name == loader::crate_name_from_metas(*entry.metas));
88
89         assert!(!matches.is_empty());
90
91         if matches.len() != 1u {
92             diag.handler().warn(
93                 format!("using multiple versions of crate `{}`", name));
94             for match_ in matches.iter() {
95                 diag.span_note(match_.span, "used here");
96                 let attrs = ~[
97                     attr::mk_attr(attr::mk_list_item(@"link",
98                                                      (*match_.metas).clone()))
99                 ];
100                 loader::note_linkage_attrs(e.intr, diag, attrs);
101             }
102         }
103
104         warn_if_multiple_versions(e, diag, non_matches);
105     }
106 }
107
108 struct Env {
109     sess: Session,
110     os: loader::Os,
111     crate_cache: @mut ~[cache_entry],
112     next_crate_num: ast::CrateNum,
113     intr: @ident_interner
114 }
115
116 fn visit_crate(e: &Env, c: &ast::Crate) {
117     let cstore = e.sess.cstore;
118
119     for a in c.attrs.iter().filter(|m| "link_args" == m.name()) {
120         match a.value_str() {
121           Some(ref linkarg) => {
122             cstore::add_used_link_args(cstore, *linkarg);
123           }
124           None => {/* fallthrough */ }
125         }
126     }
127 }
128
129 fn visit_view_item(e: @mut Env, i: &ast::view_item) {
130     match i.node {
131       ast::view_item_extern_mod(ident, path_opt, ref meta_items, id) => {
132           let ident = token::ident_to_str(&ident);
133           let meta_items = match path_opt {
134               None => meta_items.clone(),
135               Some((p, _path_str_style)) => {
136                   let p_path = Path::init(p);
137                   match p_path.filestem_str() {
138                       None|Some("") =>
139                           e.sess.span_bug(i.span, "Bad package path in `extern mod` item"),
140                       Some(s) =>
141                           vec::append(
142                               ~[attr::mk_name_value_item_str(@"package_id", p),
143                                attr::mk_name_value_item_str(@"name", s.to_managed())],
144                               *meta_items)
145                   }
146             }
147           };
148           debug!("resolving extern mod stmt. ident: {:?}, meta: {:?}",
149                  ident, meta_items);
150           let cnum = resolve_crate(e,
151                                    ident,
152                                    meta_items,
153                                    @"",
154                                    i.span);
155           cstore::add_extern_mod_stmt_cnum(e.sess.cstore, id, cnum);
156       }
157       _ => ()
158   }
159 }
160
161 fn visit_item(e: &Env, i: @ast::item) {
162     match i.node {
163         ast::item_foreign_mod(ref fm) => {
164             if fm.abis.is_rust() || fm.abis.is_intrinsic() {
165                 return;
166             }
167
168             // First, add all of the custom link_args attributes
169             let cstore = e.sess.cstore;
170             let link_args = i.attrs.iter()
171                 .filter_map(|at| if "link_args" == at.name() {Some(at)} else {None})
172                 .to_owned_vec();
173             for m in link_args.iter() {
174                 match m.value_str() {
175                     Some(linkarg) => {
176                         cstore::add_used_link_args(cstore, linkarg);
177                     }
178                     None => { /* fallthrough */ }
179                 }
180             }
181
182             // Next, process all of the #[link(..)]-style arguments
183             let cstore = e.sess.cstore;
184             let link_args = i.attrs.iter()
185                 .filter_map(|at| if "link" == at.name() {Some(at)} else {None})
186                 .to_owned_vec();
187             for m in link_args.iter() {
188                 match m.meta_item_list() {
189                     Some(items) => {
190                         let kind = do items.iter().find |k| {
191                             "kind" == k.name()
192                         }.and_then(|a| a.value_str());
193                         let kind = match kind {
194                             Some(k) if "static" == k => cstore::NativeStatic,
195                             Some(k) => {
196                                 e.sess.span_fatal(i.span,
197                                     format!("unknown kind: `{}`", k));
198                             }
199                             None => cstore::NativeUnknown
200                         };
201                         let n = do items.iter().find |n| {
202                             "name" == n.name()
203                         }.and_then(|a| a.value_str());
204                         let n = match n {
205                             Some(n) => n,
206                             None => {
207                                 e.sess.span_fatal(i.span,
208                                     "#[link(...)] specified without \
209                                      `name = \"foo\"`");
210                             }
211                         };
212                         cstore::add_used_library(cstore, n.to_owned(), kind);
213                     }
214                     None => {}
215                 }
216             }
217         }
218         _ => { }
219     }
220 }
221
222 fn metas_with(ident: @str, key: @str, mut metas: ~[@ast::MetaItem])
223     -> ~[@ast::MetaItem] {
224     // Check if key isn't there yet.
225     if !attr::contains_name(metas, key) {
226         metas.push(attr::mk_name_value_item_str(key, ident));
227     }
228     metas
229 }
230
231 fn metas_with_ident(ident: @str, metas: ~[@ast::MetaItem])
232     -> ~[@ast::MetaItem] {
233     metas_with(ident, @"name", metas)
234 }
235
236 fn existing_match(e: &Env, metas: &[@ast::MetaItem], hash: &str)
237                -> Option<ast::CrateNum> {
238     for c in e.crate_cache.iter() {
239         if loader::metadata_matches(*c.metas, metas)
240             && (hash.is_empty() || c.hash.as_slice() == hash) {
241             return Some(c.cnum);
242         }
243     }
244     return None;
245 }
246
247 fn resolve_crate(e: @mut Env,
248                  ident: @str,
249                  metas: ~[@ast::MetaItem],
250                  hash: @str,
251                  span: Span)
252               -> ast::CrateNum {
253     let metas = metas_with_ident(ident, metas);
254
255     match existing_match(e, metas, hash) {
256       None => {
257         let load_ctxt = loader::Context {
258             sess: e.sess,
259             span: span,
260             ident: ident,
261             metas: metas,
262             hash: hash,
263             os: e.os,
264             intr: e.intr
265         };
266         let loader::Library {
267             dylib, rlib, metadata
268         } = load_ctxt.load_library_crate();
269
270         let attrs = decoder::get_crate_attributes(metadata);
271         let linkage_metas = attr::find_linkage_metas(attrs);
272         let hash = decoder::get_crate_hash(metadata);
273
274         // Claim this crate number and cache it
275         let cnum = e.next_crate_num;
276         e.crate_cache.push(cache_entry {
277             cnum: cnum,
278             span: span,
279             hash: hash,
280             metas: @linkage_metas
281         });
282         e.next_crate_num += 1;
283
284         // Now resolve the crates referenced by this crate
285         let cnum_map = resolve_crate_deps(e, metadata);
286
287         let cname =
288             match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
289                                                          "name") {
290                 Some(v) => v,
291                 None => ident
292             };
293         let cmeta = @cstore::crate_metadata {
294             name: cname,
295             data: metadata,
296             cnum_map: cnum_map,
297             cnum: cnum
298         };
299
300         let cstore = e.sess.cstore;
301         cstore::set_crate_data(cstore, cnum, cmeta);
302         cstore::add_used_crate_source(cstore, cstore::CrateSource {
303             dylib: dylib,
304             rlib: rlib,
305             cnum: cnum,
306         });
307         return cnum;
308       }
309       Some(cnum) => {
310         return cnum;
311       }
312     }
313 }
314
315 // Go through the crate metadata and load any crates that it references
316 fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
317     debug!("resolving deps of external crate");
318     // The map from crate numbers in the crate we're resolving to local crate
319     // numbers
320     let mut cnum_map = HashMap::new();
321     let r = decoder::get_crate_deps(cdata);
322     for dep in r.iter() {
323         let extrn_cnum = dep.cnum;
324         let cname_str = token::ident_to_str(&dep.name);
325         let cmetas = metas_with(dep.vers, @"vers", ~[]);
326         debug!("resolving dep crate {} ver: {} hash: {}",
327                cname_str, dep.vers, dep.hash);
328         match existing_match(e,
329                              metas_with_ident(cname_str, cmetas.clone()),
330                              dep.hash) {
331           Some(local_cnum) => {
332             debug!("already have it");
333             // We've already seen this crate
334             cnum_map.insert(extrn_cnum, local_cnum);
335           }
336           None => {
337             debug!("need to load it");
338             // This is a new one so we've got to load it
339             // FIXME (#2404): Need better error reporting than just a bogus
340             // span.
341             let fake_span = dummy_sp();
342             let local_cnum = resolve_crate(e, cname_str, cmetas, dep.hash,
343                                            fake_span);
344             cnum_map.insert(extrn_cnum, local_cnum);
345           }
346         }
347     }
348     return @mut cnum_map;
349 }