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
::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
::source_map
::Span
;
12 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 fm
= match it
.kind
{
37 hir
::ItemKind
::ForeignMod(ref fm
) => fm
,
41 if fm
.abi
== Abi
::Rust
|| fm
.abi
== Abi
::RustIntrinsic
|| fm
.abi
== Abi
::PlatformIntrinsic
{
45 // Process all of the #[link(..)]-style arguments
46 let sess
= &self.tcx
.sess
;
47 for m
in it
.attrs
.iter().filter(|a
| sess
.check_name(a
, sym
::link
)) {
48 let items
= match m
.meta_item_list() {
52 let mut lib
= NativeLib
{
54 kind
: NativeLibKind
::Unspecified
,
56 foreign_module
: Some(self.tcx
.hir().local_def_id(it
.hir_id
).to_def_id()),
57 wasm_import_module
: None
,
59 let mut kind_specified
= false;
61 for item
in items
.iter() {
62 if item
.has_name(sym
::kind
) {
63 kind_specified
= true;
64 let kind
= match item
.value_str() {
66 None
=> continue, // skip like historical compilers
68 lib
.kind
= match &*kind
.as_str() {
69 "static" => NativeLibKind
::StaticBundle
,
70 "static-nobundle" => NativeLibKind
::StaticNoBundle
,
71 "dylib" => NativeLibKind
::Dylib
,
72 "framework" => NativeLibKind
::Framework
,
73 "raw-dylib" => NativeLibKind
::RawDylib
,
75 struct_span_err
!(sess
, item
.span(), E0458
, "unknown kind: `{}`", k
)
76 .span_label(item
.span(), "unknown kind")
77 .span_label(m
.span
, "")
79 NativeLibKind
::Unspecified
82 } else if item
.has_name(sym
::name
) {
83 lib
.name
= item
.value_str();
84 } else if item
.has_name(sym
::cfg
) {
85 let cfg
= match item
.meta_item_list() {
87 None
=> continue, // skip like historical compilers
90 sess
.span_err(item
.span(), "`cfg()` must have an argument");
91 } else if let cfg @
Some(..) = cfg
[0].meta_item() {
92 lib
.cfg
= cfg
.cloned();
94 sess
.span_err(cfg
[0].span(), "invalid argument for `cfg(..)`");
96 } else if item
.has_name(sym
::wasm_import_module
) {
97 match item
.value_str() {
98 Some(s
) => lib
.wasm_import_module
= Some(s
),
100 let msg
= "must be of the form `#[link(wasm_import_module = \"...\")]`";
101 sess
.span_err(item
.span(), msg
);
105 // currently, like past compilers, ignore unknown
110 // In general we require #[link(name = "...")] but we allow
111 // #[link(wasm_import_module = "...")] without the `name`.
112 let requires_name
= kind_specified
|| lib
.wasm_import_module
.is_none();
113 if lib
.name
.is_none() && requires_name
{
118 "`#[link(...)]` specified without \
121 .span_label(m
.span
, "missing `name` argument")
124 self.register_native_lib(Some(m
.span
), lib
);
128 fn visit_trait_item(&mut self, _it
: &'tcx hir
::TraitItem
<'tcx
>) {}
129 fn visit_impl_item(&mut self, _it
: &'tcx hir
::ImplItem
<'tcx
>) {}
132 impl Collector
<'tcx
> {
133 fn register_native_lib(&mut self, span
: Option
<Span
>, lib
: NativeLib
) {
134 if lib
.name
.as_ref().map(|&s
| s
== kw
::Invalid
).unwrap_or(false) {
141 "`#[link(name = \"\")]` given with empty name"
143 .span_label(span
, "empty name given")
147 self.tcx
.sess
.err("empty library name given via `-l`");
152 let is_osx
= self.tcx
.sess
.target
.is_like_osx
;
153 if lib
.kind
== NativeLibKind
::Framework
&& !is_osx
{
154 let msg
= "native frameworks are only available on macOS targets";
156 Some(span
) => struct_span_err
!(self.tcx
.sess
, span
, E0455
, "{}", msg
).emit(),
157 None
=> self.tcx
.sess
.err(msg
),
160 if lib
.cfg
.is_some() && !self.tcx
.features().link_cfg
{
162 &self.tcx
.sess
.parse_sess
,
165 "kind=\"link_cfg\" is unstable",
169 if lib
.kind
== NativeLibKind
::StaticNoBundle
&& !self.tcx
.features().static_nobundle
{
171 &self.tcx
.sess
.parse_sess
,
172 sym
::static_nobundle
,
173 span
.unwrap_or(rustc_span
::DUMMY_SP
),
174 "kind=\"static-nobundle\" is unstable",
178 if lib
.kind
== NativeLibKind
::RawDylib
&& !self.tcx
.features().raw_dylib
{
180 &self.tcx
.sess
.parse_sess
,
182 span
.unwrap_or(rustc_span
::DUMMY_SP
),
183 "kind=\"raw-dylib\" is unstable",
190 // Process libs passed on the command line
191 fn process_command_line(&mut self) {
192 // First, check for errors
193 let mut renames
= FxHashSet
::default();
194 for &(ref name
, ref new_name
, _
) in &self.tcx
.sess
.opts
.libs
{
195 if let &Some(ref new_name
) = new_name
{
196 let any_duplicate
= self
199 .filter_map(|lib
| lib
.name
.as_ref())
200 .any(|n
| n
.as_str() == *name
);
201 if new_name
.is_empty() {
202 self.tcx
.sess
.err(&format
!(
203 "an empty renaming target was specified for library `{}`",
206 } else if !any_duplicate
{
207 self.tcx
.sess
.err(&format
!(
208 "renaming of the library `{}` was specified, \
209 however this crate contains no `#[link(...)]` \
210 attributes referencing this library.",
213 } else if !renames
.insert(name
) {
214 self.tcx
.sess
.err(&format
!(
215 "multiple renamings were \
216 specified for library `{}` .",
223 // Update kind and, optionally, the name of all native libraries
224 // (there may be more than one) with the specified name. If any
225 // library is mentioned more than once, keep the latest mention
226 // of it, so that any possible dependent libraries appear before
227 // it. (This ensures that the linker is able to see symbols from
228 // all possible dependent libraries before linking in the library
230 for &(ref name
, ref new_name
, kind
) in &self.tcx
.sess
.opts
.libs
{
231 // If we've already added any native libraries with the same
232 // name, they will be pulled out into `existing`, so that we
233 // can move them to the end of the list below.
234 let mut existing
= self
236 .drain_filter(|lib
| {
237 if let Some(lib_name
) = lib
.name
{
238 if lib_name
.as_str() == *name
{
239 if kind
!= NativeLibKind
::Unspecified
{
242 if let &Some(ref new_name
) = new_name
{
243 lib
.name
= Some(Symbol
::intern(new_name
));
250 .collect
::<Vec
<_
>>();
251 if existing
.is_empty() {
253 let new_name
= new_name
.as_ref().map(|s
| &**s
); // &Option<String> -> Option<&str>
254 let lib
= NativeLib
{
255 name
: Some(Symbol
::intern(new_name
.unwrap_or(name
))),
258 foreign_module
: None
,
259 wasm_import_module
: None
,
261 self.register_native_lib(None
, lib
);
263 // Move all existing libraries with the same name to the
264 // end of the command line.
265 self.libs
.append(&mut existing
);