]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/native_libs.rs
Rename `Item.node` to `Item.kind`
[rust.git] / src / librustc_metadata / native_libs.rs
1 use rustc::hir::itemlikevisit::ItemLikeVisitor;
2 use rustc::hir;
3 use rustc::middle::cstore::{self, NativeLibrary};
4 use rustc::session::Session;
5 use rustc::ty::TyCtxt;
6 use rustc::util::nodemap::FxHashSet;
7 use rustc_target::spec::abi::Abi;
8 use syntax::attr;
9 use syntax::source_map::Span;
10 use syntax::feature_gate::{self, GateIssue};
11 use syntax::symbol::{kw, sym, Symbol};
12 use syntax::{span_err, struct_span_err};
13
14 pub fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLibrary> {
15     let mut collector = Collector {
16         tcx,
17         libs: Vec::new(),
18     };
19     tcx.hir().krate().visit_all_item_likes(&mut collector);
20     collector.process_command_line();
21     return collector.libs;
22 }
23
24 pub fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool {
25     match lib.cfg {
26         Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None),
27         None => true,
28     }
29 }
30
31 struct Collector<'tcx> {
32     tcx: TyCtxt<'tcx>,
33     libs: Vec<NativeLibrary>,
34 }
35
36 impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
37     fn visit_item(&mut self, it: &'tcx hir::Item) {
38         let fm = match it.kind {
39             hir::ItemKind::ForeignMod(ref fm) => fm,
40             _ => return,
41         };
42
43         if fm.abi == Abi::Rust ||
44             fm.abi == Abi::RustIntrinsic ||
45             fm.abi == Abi::PlatformIntrinsic {
46             return
47         }
48
49         // Process all of the #[link(..)]-style arguments
50         for m in it.attrs.iter().filter(|a| a.check_name(sym::link)) {
51             let items = match m.meta_item_list() {
52                 Some(item) => item,
53                 None => continue,
54             };
55             let mut lib = NativeLibrary {
56                 name: None,
57                 kind: cstore::NativeUnknown,
58                 cfg: None,
59                 foreign_module: Some(self.tcx.hir().local_def_id(it.hir_id)),
60                 wasm_import_module: None,
61             };
62             let mut kind_specified = false;
63
64             for item in items.iter() {
65                 if item.check_name(sym::kind) {
66                     kind_specified = true;
67                     let kind = match item.value_str() {
68                         Some(name) => name,
69                         None => continue, // skip like historical compilers
70                     };
71                     lib.kind = match &kind.as_str()[..] {
72                         "static" => cstore::NativeStatic,
73                         "static-nobundle" => cstore::NativeStaticNobundle,
74                         "dylib" => cstore::NativeUnknown,
75                         "framework" => cstore::NativeFramework,
76                         k => {
77                             struct_span_err!(self.tcx.sess, item.span(), E0458,
78                                       "unknown kind: `{}`", k)
79                                 .span_label(item.span(), "unknown kind")
80                                 .span_label(m.span, "").emit();
81                             cstore::NativeUnknown
82                         }
83                     };
84                 } else if item.check_name(sym::name) {
85                     lib.name = item.value_str();
86                 } else if item.check_name(sym::cfg) {
87                     let cfg = match item.meta_item_list() {
88                         Some(list) => list,
89                         None => continue, // skip like historical compilers
90                     };
91                     if cfg.is_empty() {
92                         self.tcx.sess.span_err(
93                             item.span(),
94                             "`cfg()` must have an argument",
95                         );
96                     } else if let cfg @ Some(..) = cfg[0].meta_item() {
97                         lib.cfg = cfg.cloned();
98                     } else {
99                         self.tcx.sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
100                     }
101                 } else if item.check_name(sym::wasm_import_module) {
102                     match item.value_str() {
103                         Some(s) => lib.wasm_import_module = Some(s),
104                         None => {
105                             let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`";
106                             self.tcx.sess.span_err(item.span(), msg);
107                         }
108                     }
109                 } else {
110                     // currently, like past compilers, ignore unknown
111                     // directives here.
112                 }
113             }
114
115             // In general we require #[link(name = "...")] but we allow
116             // #[link(wasm_import_module = "...")] without the `name`.
117             let requires_name = kind_specified || lib.wasm_import_module.is_none();
118             if lib.name.is_none() && requires_name {
119                 struct_span_err!(self.tcx.sess, m.span, E0459,
120                                  "`#[link(...)]` specified without \
121                                   `name = \"foo\"`")
122                     .span_label(m.span, "missing `name` argument")
123                     .emit();
124             }
125             self.register_native_lib(Some(m.span), lib);
126         }
127     }
128
129     fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem) {}
130     fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem) {}
131 }
132
133 impl Collector<'tcx> {
134     fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLibrary) {
135         if lib.name.as_ref().map(|&s| s == kw::Invalid).unwrap_or(false) {
136             match span {
137                 Some(span) => {
138                     struct_span_err!(self.tcx.sess, span, E0454,
139                                      "`#[link(name = \"\")]` given with empty name")
140                         .span_label(span, "empty name given")
141                         .emit();
142                 }
143                 None => {
144                     self.tcx.sess.err("empty library name given via `-l`");
145                 }
146             }
147             return
148         }
149         let is_osx = self.tcx.sess.target.target.options.is_like_osx;
150         if lib.kind == cstore::NativeFramework && !is_osx {
151             let msg = "native frameworks are only available on macOS targets";
152             match span {
153                 Some(span) => span_err!(self.tcx.sess, span, E0455, "{}", msg),
154                 None => self.tcx.sess.err(msg),
155             }
156         }
157         if lib.cfg.is_some() && !self.tcx.features().link_cfg {
158             feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
159                                            sym::link_cfg,
160                                            span.unwrap(),
161                                            GateIssue::Language,
162                                            "is unstable");
163         }
164         if lib.kind == cstore::NativeStaticNobundle &&
165            !self.tcx.features().static_nobundle {
166             feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
167                                            sym::static_nobundle,
168                                            span.unwrap_or_else(|| syntax_pos::DUMMY_SP),
169                                            GateIssue::Language,
170                                            "kind=\"static-nobundle\" is unstable");
171         }
172         self.libs.push(lib);
173     }
174
175     // Process libs passed on the command line
176     fn process_command_line(&mut self) {
177         // First, check for errors
178         let mut renames = FxHashSet::default();
179         for &(ref name, ref new_name, _) in &self.tcx.sess.opts.libs {
180             if let &Some(ref new_name) = new_name {
181                 let any_duplicate = self.libs
182                     .iter()
183                     .filter_map(|lib| lib.name.as_ref())
184                     .any(|n| n.as_str() == *name);
185                 if new_name.is_empty() {
186                     self.tcx.sess.err(
187                         &format!("an empty renaming target was specified for library `{}`",name));
188                 } else if !any_duplicate {
189                     self.tcx.sess.err(&format!("renaming of the library `{}` was specified, \
190                                                 however this crate contains no `#[link(...)]` \
191                                                 attributes referencing this library.", name));
192                 } else if renames.contains(name) {
193                     self.tcx.sess.err(&format!("multiple renamings were \
194                                                 specified for library `{}` .",
195                                                name));
196                 } else {
197                     renames.insert(name);
198                 }
199             }
200         }
201
202         // Update kind and, optionally, the name of all native libraries
203         // (there may be more than one) with the specified name.  If any
204         // library is mentioned more than once, keep the latest mention
205         // of it, so that any possible dependent libraries appear before
206         // it.  (This ensures that the linker is able to see symbols from
207         // all possible dependent libraries before linking in the library
208         // in question.)
209         for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs {
210             // If we've already added any native libraries with the same
211             // name, they will be pulled out into `existing`, so that we
212             // can move them to the end of the list below.
213             let mut existing = self.libs.drain_filter(|lib| {
214                 if let Some(lib_name) = lib.name {
215                     if lib_name.as_str() == *name {
216                         if let Some(k) = kind {
217                             lib.kind = k;
218                         }
219                         if let &Some(ref new_name) = new_name {
220                             lib.name = Some(Symbol::intern(new_name));
221                         }
222                         return true;
223                     }
224                 }
225                 false
226             }).collect::<Vec<_>>();
227             if existing.is_empty() {
228                 // Add if not found
229                 let new_name = new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
230                 let lib = NativeLibrary {
231                     name: Some(Symbol::intern(new_name.unwrap_or(name))),
232                     kind: if let Some(k) = kind { k } else { cstore::NativeUnknown },
233                     cfg: None,
234                     foreign_module: None,
235                     wasm_import_module: None,
236                 };
237                 self.register_native_lib(None, lib);
238             } else {
239                 // Move all existing libraries with the same name to the
240                 // end of the command line.
241                 self.libs.append(&mut existing);
242             }
243         }
244     }
245 }