1 use rustc_attr
as attr
;
2 use rustc_data_structures
::fx
::FxHashSet
;
3 use rustc_errors
::struct_span_err
;
5 use rustc_hir
::itemlikevisit
::ItemLikeVisitor
;
6 use rustc_middle
::middle
::cstore
::{DllImport, 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}
;
13 use rustc_target
::spec
::abi
::Abi
;
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();
22 crate fn relevant_lib(sess
: &Session
, lib
: &NativeLib
) -> bool
{
24 Some(ref cfg
) => attr
::cfg_matches(cfg
, &sess
.parse_sess
, None
),
29 struct Collector
<'tcx
> {
34 impl ItemLikeVisitor
<'tcx
> for Collector
<'tcx
> {
35 fn visit_item(&mut self, it
: &'tcx hir
::Item
<'tcx
>) {
36 let (abi
, foreign_mod_items
) = match it
.kind
{
37 hir
::ItemKind
::ForeignMod { abi, items }
=> (abi
, items
),
41 if abi
== Abi
::Rust
|| abi
== Abi
::RustIntrinsic
|| abi
== Abi
::PlatformIntrinsic
{
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
))
49 let items
= match m
.meta_item_list() {
53 let mut lib
= NativeLib
{
55 kind
: NativeLibKind
::Unspecified
,
57 foreign_module
: Some(it
.def_id
.to_def_id()),
58 wasm_import_module
: None
,
60 dll_imports
: Vec
::new(),
62 let mut kind_specified
= false;
64 for item
in items
.iter() {
65 if item
.has_name(sym
::kind
) {
66 kind_specified
= true;
67 let kind
= match item
.value_str() {
69 None
=> continue, // skip like historical compilers
71 lib
.kind
= match &*kind
.as_str() {
72 "static" => NativeLibKind
::Static { bundle: None, whole_archive: None }
,
73 "static-nobundle" => {
74 sess
.struct_span_warn(
76 "library kind `static-nobundle` has been superseded by specifying \
77 modifier `-bundle` with library kind `static`",
80 NativeLibKind
::Static { bundle: Some(false), whole_archive: None }
82 "dylib" => NativeLibKind
::Dylib { as_needed: None }
,
83 "framework" => NativeLibKind
::Framework { as_needed: None }
,
84 "raw-dylib" => NativeLibKind
::RawDylib
,
86 struct_span_err
!(sess
, item
.span(), E0458
, "unknown kind: `{}`", k
)
87 .span_label(item
.span(), "unknown kind")
88 .span_label(m
.span
, "")
90 NativeLibKind
::Unspecified
93 } else if item
.has_name(sym
::name
) {
94 lib
.name
= item
.value_str();
95 } else if item
.has_name(sym
::cfg
) {
96 let cfg
= match item
.meta_item_list() {
98 None
=> continue, // skip like historical compilers
101 sess
.span_err(item
.span(), "`cfg()` must have an argument");
102 } else if let cfg @
Some(..) = cfg
[0].meta_item() {
103 lib
.cfg
= cfg
.cloned();
105 sess
.span_err(cfg
[0].span(), "invalid argument for `cfg(..)`");
107 } else if item
.has_name(sym
::wasm_import_module
) {
108 match item
.value_str() {
109 Some(s
) => lib
.wasm_import_module
= Some(s
),
111 let msg
= "must be of the form `#[link(wasm_import_module = \"...\")]`";
112 sess
.span_err(item
.span(), msg
);
116 // currently, like past compilers, ignore unknown
121 // Do this outside the above loop so we don't depend on modifiers coming
123 if let Some(item
) = items
.iter().find(|item
| item
.has_name(sym
::modifiers
)) {
124 if let Some(modifiers
) = item
.value_str() {
125 let span
= item
.name_value_literal_span().unwrap();
126 for modifier
in modifiers
.as_str().split('
,'
) {
127 let (modifier
, value
) = match modifier
.strip_prefix(&['
+'
, '
-'
][..]) {
128 Some(m
) => (m
, modifier
.starts_with('
+'
)),
132 "invalid linking modifier syntax, expected '+' or '-' prefix \
133 before one of: bundle, verbatim, whole-archive, as-needed",
139 match (modifier
, &mut lib
.kind
) {
140 ("bundle", NativeLibKind
::Static { bundle, .. }
) => {
141 *bundle
= Some(value
);
143 ("bundle", _
) => sess
.span_err(
145 "bundle linking modifier is only compatible with \
146 `static` linking kind",
149 ("verbatim", _
) => lib
.verbatim
= Some(value
),
151 ("whole-archive", NativeLibKind
::Static { whole_archive, .. }
) => {
152 *whole_archive
= Some(value
);
154 ("whole-archive", _
) => sess
.span_err(
156 "whole-archive linking modifier is only compatible with \
157 `static` linking kind",
160 ("as-needed", NativeLibKind
::Dylib { as_needed }
)
161 | ("as-needed", NativeLibKind
::Framework { as_needed }
) => {
162 *as_needed
= Some(value
);
164 ("as-needed", _
) => sess
.span_err(
166 "as-needed linking modifier is only compatible with \
167 `dylib` and `framework` linking kinds",
173 "unrecognized linking modifier `{}`, expected one \
174 of: bundle, verbatim, whole-archive, as-needed",
181 let msg
= "must be of the form `#[link(modifiers = \"...\")]`";
182 sess
.span_err(item
.span(), msg
);
186 // In general we require #[link(name = "...")] but we allow
187 // #[link(wasm_import_module = "...")] without the `name`.
188 let requires_name
= kind_specified
|| lib
.wasm_import_module
.is_none();
189 if lib
.name
.is_none() && requires_name
{
194 "`#[link(...)]` specified without \
197 .span_label(m
.span
, "missing `name` argument")
201 if lib
.kind
== NativeLibKind
::RawDylib
{
206 if sess
.target
.arch
== "x86" {
209 r
#"`#[link(kind = "raw-dylib")]` only supports C and Cdecl ABIs"#,
214 lib
.dll_imports
.extend(
217 .map(|child_item
| DllImport { name: child_item.ident.name, ordinal: None }
),
221 self.register_native_lib(Some(m
.span
), lib
);
225 fn visit_trait_item(&mut self, _it
: &'tcx hir
::TraitItem
<'tcx
>) {}
226 fn visit_impl_item(&mut self, _it
: &'tcx hir
::ImplItem
<'tcx
>) {}
227 fn visit_foreign_item(&mut self, _it
: &'tcx hir
::ForeignItem
<'tcx
>) {}
230 impl Collector
<'tcx
> {
231 fn register_native_lib(&mut self, span
: Option
<Span
>, lib
: NativeLib
) {
232 if lib
.name
.as_ref().map_or(false, |&s
| s
== kw
::Empty
) {
239 "`#[link(name = \"\")]` given with empty name"
241 .span_label(span
, "empty name given")
245 self.tcx
.sess
.err("empty library name given via `-l`");
250 let is_osx
= self.tcx
.sess
.target
.is_like_osx
;
251 if matches
!(lib
.kind
, NativeLibKind
::Framework { .. }
) && !is_osx
{
252 let msg
= "native frameworks are only available on macOS targets";
254 Some(span
) => struct_span_err
!(self.tcx
.sess
, span
, E0455
, "{}", msg
).emit(),
255 None
=> self.tcx
.sess
.err(msg
),
258 if lib
.cfg
.is_some() && !self.tcx
.features().link_cfg
{
260 &self.tcx
.sess
.parse_sess
,
263 "kind=\"link_cfg\" is unstable",
267 if matches
!(lib
.kind
, NativeLibKind
::Static { bundle: Some(false), .. }
)
268 && !self.tcx
.features().static_nobundle
271 &self.tcx
.sess
.parse_sess
,
272 sym
::static_nobundle
,
273 span
.unwrap_or(rustc_span
::DUMMY_SP
),
274 "kind=\"static-nobundle\" is unstable",
278 // this just unwraps lib.name; we already established that it isn't empty above.
279 if let (NativeLibKind
::RawDylib
, Some(lib_name
)) = (lib
.kind
, lib
.name
) {
280 let span
= match span
{
283 bug
!("raw-dylib libraries are not supported on the command line");
287 if !self.tcx
.sess
.target
.options
.is_like_windows
{
288 self.tcx
.sess
.span_fatal(
290 "`#[link(...)]` with `kind = \"raw-dylib\"` only supported on Windows",
292 } else if !self.tcx
.sess
.target
.options
.is_like_msvc
{
293 self.tcx
.sess
.span_warn(
295 "`#[link(...)]` with `kind = \"raw-dylib\"` not supported on windows-gnu",
299 if lib_name
.as_str().contains('
\0'
) {
300 self.tcx
.sess
.span_err(span
, "library name may not contain NUL characters");
303 if !self.tcx
.features().raw_dylib
{
305 &self.tcx
.sess
.parse_sess
,
308 "kind=\"raw-dylib\" is unstable",
317 // Process libs passed on the command line
318 fn process_command_line(&mut self) {
319 // First, check for errors
320 let mut renames
= FxHashSet
::default();
321 for lib
in &self.tcx
.sess
.opts
.libs
{
322 if let Some(ref new_name
) = lib
.new_name
{
323 let any_duplicate
= self
326 .filter_map(|lib
| lib
.name
.as_ref())
327 .any(|n
| &n
.as_str() == &lib
.name
);
328 if new_name
.is_empty() {
329 self.tcx
.sess
.err(&format
!(
330 "an empty renaming target was specified for library `{}`",
333 } else if !any_duplicate
{
334 self.tcx
.sess
.err(&format
!(
335 "renaming of the library `{}` was specified, \
336 however this crate contains no `#[link(...)]` \
337 attributes referencing this library.",
340 } else if !renames
.insert(&lib
.name
) {
341 self.tcx
.sess
.err(&format
!(
342 "multiple renamings were \
343 specified for library `{}` .",
350 // Update kind and, optionally, the name of all native libraries
351 // (there may be more than one) with the specified name. If any
352 // library is mentioned more than once, keep the latest mention
353 // of it, so that any possible dependent libraries appear before
354 // it. (This ensures that the linker is able to see symbols from
355 // all possible dependent libraries before linking in the library
357 for passed_lib
in &self.tcx
.sess
.opts
.libs
{
358 // If we've already added any native libraries with the same
359 // name, they will be pulled out into `existing`, so that we
360 // can move them to the end of the list below.
361 let mut existing
= self
363 .drain_filter(|lib
| {
364 if let Some(lib_name
) = lib
.name
{
365 if lib_name
.as_str() == passed_lib
.name
{
366 if passed_lib
.kind
!= NativeLibKind
::Unspecified
{
367 lib
.kind
= passed_lib
.kind
;
369 if let Some(new_name
) = &passed_lib
.new_name
{
370 lib
.name
= Some(Symbol
::intern(new_name
));
372 lib
.verbatim
= passed_lib
.verbatim
;
378 .collect
::<Vec
<_
>>();
379 if existing
.is_empty() {
381 let new_name
= passed_lib
.new_name
.as_ref().map(|s
| &**s
); // &Option<String> -> Option<&str>
382 let lib
= NativeLib
{
383 name
: Some(Symbol
::intern(new_name
.unwrap_or(&passed_lib
.name
))),
384 kind
: passed_lib
.kind
,
386 foreign_module
: None
,
387 wasm_import_module
: None
,
388 verbatim
: passed_lib
.verbatim
,
389 dll_imports
: Vec
::new(),
391 self.register_native_lib(None
, lib
);
393 // Move all existing libraries with the same name to the
394 // end of the command line.
395 self.libs
.append(&mut existing
);