]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_metadata/src/native_libs.rs
Rollup merge of #85326 - infinity0:fix-cross-compile-tools, r=Mark-Simulacrum
[rust.git] / compiler / rustc_metadata / src / native_libs.rs
1 use rustc_attr as attr;
2 use rustc_data_structures::fx::FxHashSet;
3 use rustc_errors::struct_span_err;
4 use rustc_hir as hir;
5 use rustc_hir::itemlikevisit::ItemLikeVisitor;
6 use rustc_middle::middle::cstore::NativeLib;
7 use rustc_middle::ty::TyCtxt;
8 use rustc_session::parse::feature_err;
9 use rustc_session::utils::NativeLibKind;
10 use rustc_session::Session;
11 use rustc_span::symbol::{kw, sym, Symbol};
12 use rustc_span::Span;
13 use rustc_target::spec::abi::Abi;
14
15 crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
16     let mut collector = Collector { tcx, libs: Vec::new() };
17     tcx.hir().krate().visit_all_item_likes(&mut collector);
18     collector.process_command_line();
19     collector.libs
20 }
21
22 crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
23     match lib.cfg {
24         Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None),
25         None => true,
26     }
27 }
28
29 struct Collector<'tcx> {
30     tcx: TyCtxt<'tcx>,
31     libs: Vec<NativeLib>,
32 }
33
34 impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
35     fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
36         let abi = match it.kind {
37             hir::ItemKind::ForeignMod { abi, .. } => abi,
38             _ => return,
39         };
40
41         if abi == Abi::Rust || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
42             return;
43         }
44
45         // Process all of the #[link(..)]-style arguments
46         let sess = &self.tcx.sess;
47         for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| sess.check_name(a, sym::link))
48         {
49             let items = match m.meta_item_list() {
50                 Some(item) => item,
51                 None => continue,
52             };
53             let mut lib = NativeLib {
54                 name: None,
55                 kind: NativeLibKind::Unspecified,
56                 cfg: None,
57                 foreign_module: Some(it.def_id.to_def_id()),
58                 wasm_import_module: None,
59                 verbatim: None,
60             };
61             let mut kind_specified = false;
62
63             for item in items.iter() {
64                 if item.has_name(sym::kind) {
65                     kind_specified = true;
66                     let kind = match item.value_str() {
67                         Some(name) => name,
68                         None => continue, // skip like historical compilers
69                     };
70                     lib.kind = match &*kind.as_str() {
71                         "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
72                         "static-nobundle" => {
73                             sess.struct_span_warn(
74                                 item.span(),
75                                 "library kind `static-nobundle` has been superseded by specifying \
76                                 modifier `-bundle` with library kind `static`",
77                             )
78                             .emit();
79                             NativeLibKind::Static { bundle: Some(false), whole_archive: None }
80                         }
81                         "dylib" => NativeLibKind::Dylib { as_needed: None },
82                         "framework" => NativeLibKind::Framework { as_needed: None },
83                         "raw-dylib" => NativeLibKind::RawDylib,
84                         k => {
85                             struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k)
86                                 .span_label(item.span(), "unknown kind")
87                                 .span_label(m.span, "")
88                                 .emit();
89                             NativeLibKind::Unspecified
90                         }
91                     };
92                 } else if item.has_name(sym::name) {
93                     lib.name = item.value_str();
94                 } else if item.has_name(sym::cfg) {
95                     let cfg = match item.meta_item_list() {
96                         Some(list) => list,
97                         None => continue, // skip like historical compilers
98                     };
99                     if cfg.is_empty() {
100                         sess.span_err(item.span(), "`cfg()` must have an argument");
101                     } else if let cfg @ Some(..) = cfg[0].meta_item() {
102                         lib.cfg = cfg.cloned();
103                     } else {
104                         sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
105                     }
106                 } else if item.has_name(sym::wasm_import_module) {
107                     match item.value_str() {
108                         Some(s) => lib.wasm_import_module = Some(s),
109                         None => {
110                             let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`";
111                             sess.span_err(item.span(), msg);
112                         }
113                     }
114                 } else {
115                     // currently, like past compilers, ignore unknown
116                     // directives here.
117                 }
118             }
119
120             // Do this outside the above loop so we don't depend on modifiers coming
121             // after kinds
122             if let Some(item) = items.iter().find(|item| item.has_name(sym::modifiers)) {
123                 if let Some(modifiers) = item.value_str() {
124                     let span = item.name_value_literal_span().unwrap();
125                     for modifier in modifiers.as_str().split(',') {
126                         let (modifier, value) = match modifier.strip_prefix(&['+', '-'][..]) {
127                             Some(m) => (m, modifier.starts_with('+')),
128                             None => {
129                                 sess.span_err(
130                                     span,
131                                     "invalid linking modifier syntax, expected '+' or '-' prefix \
132                                     before one of: bundle, verbatim, whole-archive, as-needed",
133                                 );
134                                 continue;
135                             }
136                         };
137
138                         match (modifier, &mut lib.kind) {
139                             ("bundle", NativeLibKind::Static { bundle, .. }) => {
140                                 *bundle = Some(value);
141                             }
142                             ("bundle", _) => sess.span_err(
143                                 span,
144                                 "bundle linking modifier is only compatible with \
145                                 `static` linking kind",
146                             ),
147
148                             ("verbatim", _) => lib.verbatim = Some(value),
149
150                             ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
151                                 *whole_archive = Some(value);
152                             }
153                             ("whole-archive", _) => sess.span_err(
154                                 span,
155                                 "whole-archive linking modifier is only compatible with \
156                                 `static` linking kind",
157                             ),
158
159                             ("as-needed", NativeLibKind::Dylib { as_needed })
160                             | ("as-needed", NativeLibKind::Framework { as_needed }) => {
161                                 *as_needed = Some(value);
162                             }
163                             ("as-needed", _) => sess.span_err(
164                                 span,
165                                 "as-needed linking modifier is only compatible with \
166                                 `dylib` and `framework` linking kinds",
167                             ),
168
169                             _ => sess.span_err(
170                                 span,
171                                 &format!(
172                                     "unrecognized linking modifier `{}`, expected one \
173                                     of: bundle, verbatim, whole-archive, as-needed",
174                                     modifier
175                                 ),
176                             ),
177                         }
178                     }
179                 } else {
180                     let msg = "must be of the form `#[link(modifiers = \"...\")]`";
181                     sess.span_err(item.span(), msg);
182                 }
183             }
184
185             // In general we require #[link(name = "...")] but we allow
186             // #[link(wasm_import_module = "...")] without the `name`.
187             let requires_name = kind_specified || lib.wasm_import_module.is_none();
188             if lib.name.is_none() && requires_name {
189                 struct_span_err!(
190                     sess,
191                     m.span,
192                     E0459,
193                     "`#[link(...)]` specified without \
194                                   `name = \"foo\"`"
195                 )
196                 .span_label(m.span, "missing `name` argument")
197                 .emit();
198             }
199             self.register_native_lib(Some(m.span), lib);
200         }
201     }
202
203     fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem<'tcx>) {}
204     fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem<'tcx>) {}
205     fn visit_foreign_item(&mut self, _it: &'tcx hir::ForeignItem<'tcx>) {}
206 }
207
208 impl Collector<'tcx> {
209     fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) {
210         if lib.name.as_ref().map_or(false, |&s| s == kw::Empty) {
211             match span {
212                 Some(span) => {
213                     struct_span_err!(
214                         self.tcx.sess,
215                         span,
216                         E0454,
217                         "`#[link(name = \"\")]` given with empty name"
218                     )
219                     .span_label(span, "empty name given")
220                     .emit();
221                 }
222                 None => {
223                     self.tcx.sess.err("empty library name given via `-l`");
224                 }
225             }
226             return;
227         }
228         let is_osx = self.tcx.sess.target.is_like_osx;
229         if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx {
230             let msg = "native frameworks are only available on macOS targets";
231             match span {
232                 Some(span) => struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(),
233                 None => self.tcx.sess.err(msg),
234             }
235         }
236         if lib.cfg.is_some() && !self.tcx.features().link_cfg {
237             feature_err(
238                 &self.tcx.sess.parse_sess,
239                 sym::link_cfg,
240                 span.unwrap(),
241                 "kind=\"link_cfg\" is unstable",
242             )
243             .emit();
244         }
245         if matches!(lib.kind, NativeLibKind::Static { bundle: Some(false), .. })
246             && !self.tcx.features().static_nobundle
247         {
248             feature_err(
249                 &self.tcx.sess.parse_sess,
250                 sym::static_nobundle,
251                 span.unwrap_or(rustc_span::DUMMY_SP),
252                 "kind=\"static-nobundle\" is unstable",
253             )
254             .emit();
255         }
256         if lib.kind == NativeLibKind::RawDylib && !self.tcx.features().raw_dylib {
257             feature_err(
258                 &self.tcx.sess.parse_sess,
259                 sym::raw_dylib,
260                 span.unwrap_or(rustc_span::DUMMY_SP),
261                 "kind=\"raw-dylib\" is unstable",
262             )
263             .emit();
264         }
265         self.libs.push(lib);
266     }
267
268     // Process libs passed on the command line
269     fn process_command_line(&mut self) {
270         // First, check for errors
271         let mut renames = FxHashSet::default();
272         for lib in &self.tcx.sess.opts.libs {
273             if let Some(ref new_name) = lib.new_name {
274                 let any_duplicate = self
275                     .libs
276                     .iter()
277                     .filter_map(|lib| lib.name.as_ref())
278                     .any(|n| &n.as_str() == &lib.name);
279                 if new_name.is_empty() {
280                     self.tcx.sess.err(&format!(
281                         "an empty renaming target was specified for library `{}`",
282                         lib.name
283                     ));
284                 } else if !any_duplicate {
285                     self.tcx.sess.err(&format!(
286                         "renaming of the library `{}` was specified, \
287                                                 however this crate contains no `#[link(...)]` \
288                                                 attributes referencing this library.",
289                         lib.name
290                     ));
291                 } else if !renames.insert(&lib.name) {
292                     self.tcx.sess.err(&format!(
293                         "multiple renamings were \
294                                                 specified for library `{}` .",
295                         lib.name
296                     ));
297                 }
298             }
299         }
300
301         // Update kind and, optionally, the name of all native libraries
302         // (there may be more than one) with the specified name.  If any
303         // library is mentioned more than once, keep the latest mention
304         // of it, so that any possible dependent libraries appear before
305         // it.  (This ensures that the linker is able to see symbols from
306         // all possible dependent libraries before linking in the library
307         // in question.)
308         for passed_lib in &self.tcx.sess.opts.libs {
309             // If we've already added any native libraries with the same
310             // name, they will be pulled out into `existing`, so that we
311             // can move them to the end of the list below.
312             let mut existing = self
313                 .libs
314                 .drain_filter(|lib| {
315                     if let Some(lib_name) = lib.name {
316                         if lib_name.as_str() == passed_lib.name {
317                             if passed_lib.kind != NativeLibKind::Unspecified {
318                                 lib.kind = passed_lib.kind;
319                             }
320                             if let Some(new_name) = &passed_lib.new_name {
321                                 lib.name = Some(Symbol::intern(new_name));
322                             }
323                             lib.verbatim = passed_lib.verbatim;
324                             return true;
325                         }
326                     }
327                     false
328                 })
329                 .collect::<Vec<_>>();
330             if existing.is_empty() {
331                 // Add if not found
332                 let new_name = passed_lib.new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
333                 let lib = NativeLib {
334                     name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))),
335                     kind: passed_lib.kind,
336                     cfg: None,
337                     foreign_module: None,
338                     wasm_import_module: None,
339                     verbatim: passed_lib.verbatim,
340                 };
341                 self.register_native_lib(None, lib);
342             } else {
343                 // Move all existing libraries with the same name to the
344                 // end of the command line.
345                 self.libs.append(&mut existing);
346             }
347         }
348     }
349 }