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