]> git.lizzy.rs Git - rust.git/blob - src/librustc/metadata/creader.rs
0d0f0d7ab69f3e3ffac57849a301112b266694f6
[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
12 //! Validates all used crates and extern libraries and loads their metadata
13
14 use metadata::cstore;
15 use metadata::decoder;
16 use metadata::filesearch::FileSearch;
17 use metadata::loader;
18
19 use core::hashmap::HashMap;
20 use syntax::attr;
21 use syntax::codemap::{span, dummy_sp};
22 use syntax::diagnostic::span_handler;
23 use syntax::parse::token::ident_interner;
24 use syntax::visit;
25 use syntax::ast;
26
27 // Traverses an AST, reading all the information about use'd crates and extern
28 // libraries necessary for later resolving, typechecking, linking, etc.
29 pub fn read_crates(diag: @span_handler,
30                    crate: @ast::crate,
31                    cstore: @mut cstore::CStore,
32                    filesearch: @FileSearch,
33                    os: loader::os,
34                    statik: bool,
35                    intr: @ident_interner) {
36     let e = @mut Env {
37         diag: diag,
38         filesearch: filesearch,
39         cstore: cstore,
40         os: os,
41         statik: statik,
42         crate_cache: @mut ~[],
43         next_crate_num: 1,
44         intr: intr
45     };
46     let v =
47         visit::mk_simple_visitor(@visit::SimpleVisitor {
48             visit_view_item: |a| visit_view_item(e, a),
49             visit_item: |a| visit_item(e, a),
50             .. *visit::default_simple_visitor()});
51     visit_crate(e, crate);
52     visit::visit_crate(crate, (), v);
53     dump_crates(e.crate_cache);
54     warn_if_multiple_versions(e, diag, e.crate_cache);
55 }
56
57 struct cache_entry {
58     cnum: int,
59     span: span,
60     hash: @~str,
61     metas: @~[@ast::meta_item]
62 }
63
64 fn dump_crates(crate_cache: @mut ~[cache_entry]) {
65     debug!("resolved crates:");
66     for crate_cache.each |entry| {
67         debug!("cnum: %?", entry.cnum);
68         debug!("span: %?", entry.span);
69         debug!("hash: %?", entry.hash);
70     }
71 }
72
73 fn warn_if_multiple_versions(e: @mut Env,
74                              diag: @span_handler,
75                              crate_cache: @mut ~[cache_entry]) {
76     use core::either::*;
77
78     let crate_cache = &mut *crate_cache;
79
80     if crate_cache.len() != 0u {
81         let name = loader::crate_name_from_metas(
82             *crate_cache[crate_cache.len() - 1].metas
83         );
84
85         let (matches, non_matches) =
86             partition(crate_cache.map_to_vec(|&entry| {
87                 let othername = loader::crate_name_from_metas(
88                     copy *entry.metas);
89                 if name == othername {
90                     Left(entry)
91                 } else {
92                     Right(entry)
93                 }
94             }));
95
96         assert!(!matches.is_empty());
97
98         if matches.len() != 1u {
99             diag.handler().warn(
100                 fmt!("using multiple versions of crate `%s`", *name));
101             for matches.each |match_| {
102                 diag.span_note(match_.span, ~"used here");
103                 let attrs = ~[
104                     attr::mk_attr(attr::mk_list_item(
105                         @~"link", /*bad*/copy *match_.metas))
106                 ];
107                 loader::note_linkage_attrs(e.intr, diag, attrs);
108             }
109         }
110
111         warn_if_multiple_versions(e, diag, @mut non_matches);
112     }
113 }
114
115 struct Env {
116     diag: @span_handler,
117     filesearch: @FileSearch,
118     cstore: @mut cstore::CStore,
119     os: loader::os,
120     statik: bool,
121     crate_cache: @mut ~[cache_entry],
122     next_crate_num: ast::crate_num,
123     intr: @ident_interner
124 }
125
126 fn visit_crate(e: @mut Env, c: &ast::crate) {
127     let cstore = e.cstore;
128     let link_args = attr::find_attrs_by_name(c.node.attrs, "link_args");
129
130     for link_args.each |a| {
131         match attr::get_meta_item_value_str(attr::attr_meta(*a)) {
132           Some(ref linkarg) => {
133             cstore::add_used_link_args(cstore, **linkarg);
134           }
135           None => {/* fallthrough */ }
136         }
137     }
138 }
139
140 fn visit_view_item(e: @mut Env, i: @ast::view_item) {
141     match i.node {
142       ast::view_item_extern_mod(ident, /*bad*/copy meta_items, id) => {
143         debug!("resolving extern mod stmt. ident: %?, meta: %?",
144                ident, meta_items);
145         let cnum = resolve_crate(e, ident, meta_items, @~"", i.span);
146         cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
147       }
148       _ => ()
149     }
150 }
151
152 fn visit_item(e: @mut Env, i: @ast::item) {
153     match i.node {
154       ast::item_foreign_mod(ref fm) => {
155         if fm.abis.is_rust() || fm.abis.is_intrinsic() {
156             return;
157         }
158
159         let cstore = e.cstore;
160         let mut already_added = false;
161         let link_args = attr::find_attrs_by_name(i.attrs, "link_args");
162
163         match fm.sort {
164             ast::named => {
165                 let foreign_name =
166                     match attr::first_attr_value_str_by_name(i.attrs,
167                                                             ~"link_name") {
168                         Some(nn) => {
169                             if *nn == ~"" {
170                                 e.diag.span_fatal(
171                                     i.span,
172                                     ~"empty #[link_name] not allowed; use " +
173                                     ~"#[nolink].");
174                             }
175                             nn
176                         }
177                         None => e.intr.get(i.ident)
178                     };
179                 if attr::find_attrs_by_name(i.attrs, ~"nolink").is_empty() {
180                     already_added =
181                         !cstore::add_used_library(cstore, foreign_name);
182                 }
183                 if !link_args.is_empty() && already_added {
184                     e.diag.span_fatal(i.span, ~"library '" + *foreign_name +
185                                ~"' already added: can't specify link_args.");
186                 }
187             }
188             ast::anonymous => { /* do nothing */ }
189         }
190
191         for link_args.each |a| {
192             match attr::get_meta_item_value_str(attr::attr_meta(*a)) {
193                 Some(linkarg) => {
194                     cstore::add_used_link_args(cstore, *linkarg);
195                 }
196                 None => { /* fallthrough */ }
197             }
198         }
199       }
200       _ => { }
201     }
202 }
203
204 fn metas_with(ident: @~str, key: @~str, metas: ~[@ast::meta_item])
205     -> ~[@ast::meta_item] {
206     let name_items = attr::find_meta_items_by_name(metas, *key);
207     if name_items.is_empty() {
208         vec::append_one(metas, attr::mk_name_value_item_str(key, ident))
209     } else {
210         metas
211     }
212 }
213
214 fn metas_with_ident(ident: @~str, metas: ~[@ast::meta_item])
215     -> ~[@ast::meta_item] {
216     metas_with(ident, @~"name", metas)
217 }
218
219 fn existing_match(e: @mut Env, metas: &[@ast::meta_item], hash: @~str)
220                -> Option<int> {
221     for e.crate_cache.each |c| {
222         if loader::metadata_matches(*c.metas, metas)
223             && (hash.is_empty() || c.hash == hash) {
224             return Some(c.cnum);
225         }
226     }
227     return None;
228 }
229
230 fn resolve_crate(e: @mut Env,
231                  ident: ast::ident,
232                  metas: ~[@ast::meta_item],
233                  hash: @~str,
234                  span: span)
235               -> ast::crate_num {
236     let metas = metas_with_ident(@/*bad*/copy *e.intr.get(ident), metas);
237
238     match existing_match(e, metas, hash) {
239       None => {
240         let load_ctxt = loader::Context {
241             diag: e.diag,
242             filesearch: e.filesearch,
243             span: span,
244             ident: ident,
245             metas: metas,
246             hash: hash,
247             os: e.os,
248             is_static: e.statik,
249             intr: e.intr
250         };
251         let (lident, ldata) = loader::load_library_crate(&load_ctxt);
252
253         let cfilename = Path(lident);
254         let cdata = ldata;
255
256         let attrs = decoder::get_crate_attributes(cdata);
257         let linkage_metas = attr::find_linkage_metas(attrs);
258         let hash = decoder::get_crate_hash(cdata);
259
260         // Claim this crate number and cache it
261         let cnum = e.next_crate_num;
262         e.crate_cache.push(cache_entry {
263             cnum: cnum,
264             span: span,
265             hash: hash,
266             metas: @linkage_metas
267         });
268         e.next_crate_num += 1;
269
270         // Now resolve the crates referenced by this crate
271         let cnum_map = resolve_crate_deps(e, cdata);
272
273         let cname =
274             match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
275                                                          ~"name") {
276                 Some(v) => v,
277                 None => e.intr.get(ident),
278             };
279         let cmeta = @cstore::crate_metadata {
280             name: cname,
281             data: cdata,
282             cnum_map: cnum_map,
283             cnum: cnum
284         };
285
286         let cstore = e.cstore;
287         cstore::set_crate_data(cstore, cnum, cmeta);
288         cstore::add_used_crate_file(cstore, &cfilename);
289         return cnum;
290       }
291       Some(cnum) => {
292         return cnum;
293       }
294     }
295 }
296
297 // Go through the crate metadata and load any crates that it references
298 fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
299     debug!("resolving deps of external crate");
300     // The map from crate numbers in the crate we're resolving to local crate
301     // numbers
302     let mut cnum_map = HashMap::new();
303     for decoder::get_crate_deps(e.intr, cdata).each |dep| {
304         let extrn_cnum = dep.cnum;
305         let cname = dep.name;
306         let cmetas = metas_with(dep.vers, @~"vers", ~[]);
307         debug!("resolving dep crate %s ver: %s hash: %s",
308                *e.intr.get(dep.name), *dep.vers, *dep.hash);
309         match existing_match(e, metas_with_ident(e.intr.get(cname),
310                                                  copy cmetas),
311                              dep.hash) {
312           Some(local_cnum) => {
313             debug!("already have it");
314             // We've already seen this crate
315             cnum_map.insert(extrn_cnum, local_cnum);
316           }
317           None => {
318             debug!("need to load it");
319             // This is a new one so we've got to load it
320             // FIXME (#2404): Need better error reporting than just a bogus
321             // span.
322             let fake_span = dummy_sp();
323             let local_cnum = resolve_crate(e, cname, cmetas, dep.hash,
324                                            fake_span);
325             cnum_map.insert(extrn_cnum, local_cnum);
326           }
327         }
328     }
329     return @mut cnum_map;
330 }
331
332 // Local Variables:
333 // mode: rust
334 // fill-column: 78;
335 // indent-tabs-mode: nil
336 // c-basic-offset: 4
337 // buffer-file-coding-system: utf-8-unix
338 // End: