]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2012-2013 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 | //! Used by `rustc` when loading a plugin, or a crate with exported macros. | |
12 | ||
13 | use session::Session; | |
14 | use metadata::creader::{CrateOrString, CrateReader}; | |
15 | use plugin::registry::Registry; | |
16 | ||
17 | use std::mem; | |
18 | use std::os; | |
19 | use std::dynamic_lib::DynamicLibrary; | |
20 | use std::collections::HashSet; | |
21 | use syntax::ast; | |
22 | use syntax::attr; | |
23 | use syntax::codemap::Span; | |
24 | use syntax::parse::token; | |
25 | use syntax::ptr::P; | |
26 | use syntax::visit; | |
27 | use syntax::visit::Visitor; | |
28 | use syntax::attr::AttrMetaMethods; | |
29 | ||
30 | /// Pointer to a registrar function. | |
31 | pub type PluginRegistrarFun = | |
32 | fn(&mut Registry); | |
33 | ||
34 | pub struct PluginRegistrar { | |
35 | pub fun: PluginRegistrarFun, | |
36 | pub args: P<ast::MetaItem>, | |
37 | } | |
38 | ||
39 | /// Information about loaded plugins. | |
40 | pub struct Plugins { | |
41 | /// Imported macros. | |
42 | pub macros: Vec<ast::MacroDef>, | |
43 | /// Registrars, as function pointers. | |
44 | pub registrars: Vec<PluginRegistrar>, | |
45 | } | |
46 | ||
47 | pub struct PluginLoader<'a> { | |
48 | sess: &'a Session, | |
49 | span_whitelist: HashSet<Span>, | |
50 | reader: CrateReader<'a>, | |
51 | pub plugins: Plugins, | |
52 | } | |
53 | ||
54 | impl<'a> PluginLoader<'a> { | |
55 | fn new(sess: &'a Session) -> PluginLoader<'a> { | |
56 | PluginLoader { | |
57 | sess: sess, | |
58 | reader: CrateReader::new(sess), | |
59 | span_whitelist: HashSet::new(), | |
60 | plugins: Plugins { | |
61 | macros: vec!(), | |
62 | registrars: vec!(), | |
63 | }, | |
64 | } | |
65 | } | |
66 | } | |
67 | ||
68 | /// Read plugin metadata and dynamically load registrar functions. | |
69 | pub fn load_plugins(sess: &Session, krate: &ast::Crate, | |
70 | addl_plugins: Option<Vec<String>>) -> Plugins { | |
71 | let mut loader = PluginLoader::new(sess); | |
72 | ||
73 | // We need to error on `#[macro_use] extern crate` when it isn't at the | |
74 | // crate root, because `$crate` won't work properly. Identify these by | |
75 | // spans, because the crate map isn't set up yet. | |
76 | for vi in krate.module.view_items.iter() { | |
77 | loader.span_whitelist.insert(vi.span); | |
78 | } | |
79 | ||
80 | visit::walk_crate(&mut loader, krate); | |
81 | ||
82 | if let Some(plugins) = addl_plugins { | |
83 | for plugin in plugins.iter() { | |
84 | loader.load_plugin(CrateOrString::Str(plugin.as_slice()), | |
85 | None, None, None) | |
86 | } | |
87 | } | |
88 | ||
89 | return loader.plugins; | |
90 | } | |
91 | ||
92 | // note that macros aren't expanded yet, and therefore macros can't add plugins. | |
93 | impl<'a, 'v> Visitor<'v> for PluginLoader<'a> { | |
94 | fn visit_view_item(&mut self, vi: &ast::ViewItem) { | |
95 | // We're only interested in `extern crate`. | |
96 | match vi.node { | |
97 | ast::ViewItemExternCrate(..) => (), | |
98 | _ => return, | |
99 | } | |
100 | ||
101 | // Parse the attributes relating to macro / plugin loading. | |
102 | let mut plugin_attr = None; | |
103 | let mut macro_selection = Some(HashSet::new()); // None => load all | |
104 | let mut reexport = HashSet::new(); | |
105 | for attr in vi.attrs.iter() { | |
106 | let mut used = true; | |
107 | match attr.name().get() { | |
108 | "phase" => { | |
109 | self.sess.span_err(attr.span, "#[phase] is deprecated; use \ | |
110 | #[macro_use], #[plugin], and/or #[no_link]"); | |
111 | } | |
112 | "plugin" => { | |
113 | if plugin_attr.is_some() { | |
114 | self.sess.span_err(attr.span, "#[plugin] specified multiple times"); | |
115 | } | |
116 | plugin_attr = Some(attr.node.value.clone()); | |
117 | } | |
118 | "macro_use" => { | |
119 | let names = attr.meta_item_list(); | |
120 | if names.is_none() { | |
121 | // no names => load all | |
122 | macro_selection = None; | |
123 | } | |
124 | if let (Some(sel), Some(names)) = (macro_selection.as_mut(), names) { | |
125 | for name in names.iter() { | |
126 | if let ast::MetaWord(ref name) = name.node { | |
127 | sel.insert(name.clone()); | |
128 | } else { | |
129 | self.sess.span_err(name.span, "bad macro import"); | |
130 | } | |
131 | } | |
132 | } | |
133 | } | |
134 | "macro_reexport" => { | |
135 | let names = match attr.meta_item_list() { | |
136 | Some(names) => names, | |
137 | None => { | |
138 | self.sess.span_err(attr.span, "bad macro reexport"); | |
139 | continue; | |
140 | } | |
141 | }; | |
142 | ||
143 | for name in names.iter() { | |
144 | if let ast::MetaWord(ref name) = name.node { | |
145 | reexport.insert(name.clone()); | |
146 | } else { | |
147 | self.sess.span_err(name.span, "bad macro reexport"); | |
148 | } | |
149 | } | |
150 | } | |
151 | _ => used = false, | |
152 | } | |
153 | if used { | |
154 | attr::mark_used(attr); | |
155 | } | |
156 | } | |
157 | ||
158 | self.load_plugin(CrateOrString::Krate(vi), plugin_attr, macro_selection, Some(reexport)) | |
159 | } | |
160 | ||
161 | fn visit_mac(&mut self, _: &ast::Mac) { | |
162 | // bummer... can't see plugins inside macros. | |
163 | // do nothing. | |
164 | } | |
165 | } | |
166 | ||
167 | impl<'a> PluginLoader<'a> { | |
168 | pub fn load_plugin<'b>(&mut self, | |
169 | c: CrateOrString<'b>, | |
170 | plugin_attr: Option<P<ast::MetaItem>>, | |
171 | macro_selection: Option<HashSet<token::InternedString>>, | |
172 | reexport: Option<HashSet<token::InternedString>>) { | |
173 | let mut macros = vec![]; | |
174 | let mut registrar = None; | |
175 | ||
176 | let load_macros = match (macro_selection.as_ref(), reexport.as_ref()) { | |
177 | (Some(sel), Some(re)) => sel.len() != 0 || re.len() != 0, | |
178 | _ => true, | |
179 | }; | |
180 | let load_registrar = plugin_attr.is_some(); | |
181 | ||
182 | if let CrateOrString::Krate(vi) = c { | |
183 | if load_macros && !self.span_whitelist.contains(&vi.span) { | |
184 | self.sess.span_err(vi.span, "an `extern crate` loading macros must be at \ | |
185 | the crate root"); | |
186 | } | |
187 | } | |
188 | ||
189 | if load_macros || load_registrar { | |
190 | let pmd = self.reader.read_plugin_metadata(c); | |
191 | if load_macros { | |
192 | macros = pmd.exported_macros(); | |
193 | } | |
194 | if load_registrar { | |
195 | registrar = pmd.plugin_registrar(); | |
196 | } | |
197 | } | |
198 | ||
199 | for mut def in macros.into_iter() { | |
200 | let name = token::get_ident(def.ident); | |
201 | def.use_locally = match macro_selection.as_ref() { | |
202 | None => true, | |
203 | Some(sel) => sel.contains(&name), | |
204 | }; | |
205 | def.export = if let Some(ref re) = reexport { | |
206 | re.contains(&name) | |
207 | } else { | |
208 | false // Don't reexport macros from crates loaded from the command line | |
209 | }; | |
210 | self.plugins.macros.push(def); | |
211 | } | |
212 | ||
213 | if let Some((lib, symbol)) = registrar { | |
214 | let fun = self.dylink_registrar(c, lib, symbol); | |
215 | self.plugins.registrars.push(PluginRegistrar { | |
216 | fun: fun, | |
217 | args: plugin_attr.unwrap(), | |
218 | }); | |
219 | } | |
220 | } | |
221 | ||
222 | // Dynamically link a registrar function into the compiler process. | |
223 | fn dylink_registrar<'b>(&mut self, | |
224 | c: CrateOrString<'b>, | |
225 | path: Path, | |
226 | symbol: String) -> PluginRegistrarFun { | |
227 | // Make sure the path contains a / or the linker will search for it. | |
228 | let path = os::make_absolute(&path).unwrap(); | |
229 | ||
230 | let lib = match DynamicLibrary::open(Some(&path)) { | |
231 | Ok(lib) => lib, | |
232 | // this is fatal: there are almost certainly macros we need | |
233 | // inside this crate, so continue would spew "macro undefined" | |
234 | // errors | |
235 | Err(err) => { | |
236 | if let CrateOrString::Krate(cr) = c { | |
237 | self.sess.span_fatal(cr.span, &err[]) | |
238 | } else { | |
239 | self.sess.fatal(&err[]) | |
240 | } | |
241 | } | |
242 | }; | |
243 | ||
244 | unsafe { | |
245 | let registrar = | |
246 | match lib.symbol(&symbol[]) { | |
247 | Ok(registrar) => { | |
248 | mem::transmute::<*mut u8,PluginRegistrarFun>(registrar) | |
249 | } | |
250 | // again fatal if we can't register macros | |
251 | Err(err) => { | |
252 | if let CrateOrString::Krate(cr) = c { | |
253 | self.sess.span_fatal(cr.span, &err[]) | |
254 | } else { | |
255 | self.sess.fatal(&err[]) | |
256 | } | |
257 | } | |
258 | }; | |
259 | ||
260 | // Intentionally leak the dynamic library. We can't ever unload it | |
261 | // since the library can make things that will live arbitrarily long | |
262 | // (e.g. an @-box cycle or a task). | |
263 | mem::forget(lib); | |
264 | ||
265 | registrar | |
266 | } | |
267 | } | |
268 | } |