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