]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/native_libs.rs
Auto merge of #65570 - tmandry:rollup-hck39pf, r=tmandry
[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 crate 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 crate 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                         "raw-dylib" => cstore::NativeRawDylib,
77                         k => {
78                             struct_span_err!(self.tcx.sess, item.span(), E0458,
79                                       "unknown kind: `{}`", k)
80                                 .span_label(item.span(), "unknown kind")
81                                 .span_label(m.span, "").emit();
82                             cstore::NativeUnknown
83                         }
84                     };
85                 } else if item.check_name(sym::name) {
86                     lib.name = item.value_str();
87                 } else if item.check_name(sym::cfg) {
88                     let cfg = match item.meta_item_list() {
89                         Some(list) => list,
90                         None => continue, // skip like historical compilers
91                     };
92                     if cfg.is_empty() {
93                         self.tcx.sess.span_err(
94                             item.span(),
95                             "`cfg()` must have an argument",
96                         );
97                     } else if let cfg @ Some(..) = cfg[0].meta_item() {
98                         lib.cfg = cfg.cloned();
99                     } else {
100                         self.tcx.sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
101                     }
102                 } else if item.check_name(sym::wasm_import_module) {
103                     match item.value_str() {
104                         Some(s) => lib.wasm_import_module = Some(s),
105                         None => {
106                             let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`";
107                             self.tcx.sess.span_err(item.span(), msg);
108                         }
109                     }
110                 } else {
111                     // currently, like past compilers, ignore unknown
112                     // directives here.
113                 }
114             }
115
116             // In general we require #[link(name = "...")] but we allow
117             // #[link(wasm_import_module = "...")] without the `name`.
118             let requires_name = kind_specified || lib.wasm_import_module.is_none();
119             if lib.name.is_none() && requires_name {
120                 struct_span_err!(self.tcx.sess, m.span, E0459,
121                                  "`#[link(...)]` specified without \
122                                   `name = \"foo\"`")
123                     .span_label(m.span, "missing `name` argument")
124                     .emit();
125             }
126             self.register_native_lib(Some(m.span), lib);
127         }
128     }
129
130     fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem) {}
131     fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem) {}
132 }
133
134 impl Collector<'tcx> {
135     fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLibrary) {
136         if lib.name.as_ref().map(|&s| s == kw::Invalid).unwrap_or(false) {
137             match span {
138                 Some(span) => {
139                     struct_span_err!(self.tcx.sess, span, E0454,
140                                      "`#[link(name = \"\")]` given with empty name")
141                         .span_label(span, "empty name given")
142                         .emit();
143                 }
144                 None => {
145                     self.tcx.sess.err("empty library name given via `-l`");
146                 }
147             }
148             return
149         }
150         let is_osx = self.tcx.sess.target.target.options.is_like_osx;
151         if lib.kind == cstore::NativeFramework && !is_osx {
152             let msg = "native frameworks are only available on macOS targets";
153             match span {
154                 Some(span) => span_err!(self.tcx.sess, span, E0455, "{}", msg),
155                 None => self.tcx.sess.err(msg),
156             }
157         }
158         if lib.cfg.is_some() && !self.tcx.features().link_cfg {
159             feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
160                                            sym::link_cfg,
161                                            span.unwrap(),
162                                            GateIssue::Language,
163                                            "is unstable");
164         }
165         if lib.kind == cstore::NativeStaticNobundle &&
166            !self.tcx.features().static_nobundle {
167             feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
168                                            sym::static_nobundle,
169                                            span.unwrap_or_else(|| syntax_pos::DUMMY_SP),
170                                            GateIssue::Language,
171                                            "kind=\"static-nobundle\" is unstable");
172         }
173         if lib.kind == cstore::NativeRawDylib &&
174            !self.tcx.features().raw_dylib {
175             feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
176                                            sym::raw_dylib,
177                                            span.unwrap_or_else(|| syntax_pos::DUMMY_SP),
178                                            GateIssue::Language,
179                                            "kind=\"raw-dylib\" is unstable");
180         }
181         self.libs.push(lib);
182     }
183
184     // Process libs passed on the command line
185     fn process_command_line(&mut self) {
186         // First, check for errors
187         let mut renames = FxHashSet::default();
188         for &(ref name, ref new_name, _) in &self.tcx.sess.opts.libs {
189             if let &Some(ref new_name) = new_name {
190                 let any_duplicate = self.libs
191                     .iter()
192                     .filter_map(|lib| lib.name.as_ref())
193                     .any(|n| n.as_str() == *name);
194                 if new_name.is_empty() {
195                     self.tcx.sess.err(
196                         &format!("an empty renaming target was specified for library `{}`",name));
197                 } else if !any_duplicate {
198                     self.tcx.sess.err(&format!("renaming of the library `{}` was specified, \
199                                                 however this crate contains no `#[link(...)]` \
200                                                 attributes referencing this library.", name));
201                 } else if renames.contains(name) {
202                     self.tcx.sess.err(&format!("multiple renamings were \
203                                                 specified for library `{}` .",
204                                                name));
205                 } else {
206                     renames.insert(name);
207                 }
208             }
209         }
210
211         // Update kind and, optionally, the name of all native libraries
212         // (there may be more than one) with the specified name.  If any
213         // library is mentioned more than once, keep the latest mention
214         // of it, so that any possible dependent libraries appear before
215         // it.  (This ensures that the linker is able to see symbols from
216         // all possible dependent libraries before linking in the library
217         // in question.)
218         for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs {
219             // If we've already added any native libraries with the same
220             // name, they will be pulled out into `existing`, so that we
221             // can move them to the end of the list below.
222             let mut existing = self.libs.drain_filter(|lib| {
223                 if let Some(lib_name) = lib.name {
224                     if lib_name.as_str() == *name {
225                         if let Some(k) = kind {
226                             lib.kind = k;
227                         }
228                         if let &Some(ref new_name) = new_name {
229                             lib.name = Some(Symbol::intern(new_name));
230                         }
231                         return true;
232                     }
233                 }
234                 false
235             }).collect::<Vec<_>>();
236             if existing.is_empty() {
237                 // Add if not found
238                 let new_name = new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
239                 let lib = NativeLibrary {
240                     name: Some(Symbol::intern(new_name.unwrap_or(name))),
241                     kind: if let Some(k) = kind { k } else { cstore::NativeUnknown },
242                     cfg: None,
243                     foreign_module: None,
244                     wasm_import_module: None,
245                 };
246                 self.register_native_lib(None, lib);
247             } else {
248                 // Move all existing libraries with the same name to the
249                 // end of the command line.
250                 self.libs.append(&mut existing);
251             }
252         }
253     }
254 }