1 use rustc
::hir
::itemlikevisit
::ItemLikeVisitor
;
3 use rustc
::middle
::cstore
::{self, NativeLibrary}
;
4 use rustc
::session
::Session
;
6 use rustc
::util
::nodemap
::FxHashSet
;
7 use rustc_target
::spec
::abi
::Abi
;
9 use syntax
::source_map
::Span
;
10 use syntax
::feature_gate
::feature_err
;
11 use syntax
::symbol
::{kw, sym, Symbol}
;
12 use syntax
::{span_err, struct_span_err}
;
14 use rustc_error_codes
::*;
16 crate fn collect(tcx
: TyCtxt
<'_
>) -> Vec
<NativeLibrary
> {
17 let mut collector
= Collector
{
21 tcx
.hir().krate().visit_all_item_likes(&mut collector
);
22 collector
.process_command_line();
23 return collector
.libs
;
26 crate fn relevant_lib(sess
: &Session
, lib
: &NativeLibrary
) -> bool
{
28 Some(ref cfg
) => attr
::cfg_matches(cfg
, &sess
.parse_sess
, None
),
33 struct Collector
<'tcx
> {
35 libs
: Vec
<NativeLibrary
>,
38 impl ItemLikeVisitor
<'tcx
> for Collector
<'tcx
> {
39 fn visit_item(&mut self, it
: &'tcx hir
::Item
) {
40 let fm
= match it
.kind
{
41 hir
::ItemKind
::ForeignMod(ref fm
) => fm
,
45 if fm
.abi
== Abi
::Rust
||
46 fm
.abi
== Abi
::RustIntrinsic
||
47 fm
.abi
== Abi
::PlatformIntrinsic
{
51 // Process all of the #[link(..)]-style arguments
52 for m
in it
.attrs
.iter().filter(|a
| a
.check_name(sym
::link
)) {
53 let items
= match m
.meta_item_list() {
57 let mut lib
= NativeLibrary
{
59 kind
: cstore
::NativeUnknown
,
61 foreign_module
: Some(self.tcx
.hir().local_def_id(it
.hir_id
)),
62 wasm_import_module
: None
,
64 let mut kind_specified
= false;
66 for item
in items
.iter() {
67 if item
.check_name(sym
::kind
) {
68 kind_specified
= true;
69 let kind
= match item
.value_str() {
71 None
=> continue, // skip like historical compilers
73 lib
.kind
= match &*kind
.as_str() {
74 "static" => cstore
::NativeStatic
,
75 "static-nobundle" => cstore
::NativeStaticNobundle
,
76 "dylib" => cstore
::NativeUnknown
,
77 "framework" => cstore
::NativeFramework
,
78 "raw-dylib" => cstore
::NativeRawDylib
,
80 struct_span_err
!(self.tcx
.sess
, item
.span(), E0458
,
81 "unknown kind: `{}`", k
)
82 .span_label(item
.span(), "unknown kind")
83 .span_label(m
.span
, "").emit();
87 } else if item
.check_name(sym
::name
) {
88 lib
.name
= item
.value_str();
89 } else if item
.check_name(sym
::cfg
) {
90 let cfg
= match item
.meta_item_list() {
92 None
=> continue, // skip like historical compilers
95 self.tcx
.sess
.span_err(
97 "`cfg()` must have an argument",
99 } else if let cfg @
Some(..) = cfg
[0].meta_item() {
100 lib
.cfg
= cfg
.cloned();
102 self.tcx
.sess
.span_err(cfg
[0].span(), "invalid argument for `cfg(..)`");
104 } else if item
.check_name(sym
::wasm_import_module
) {
105 match item
.value_str() {
106 Some(s
) => lib
.wasm_import_module
= Some(s
),
108 let msg
= "must be of the form `#[link(wasm_import_module = \"...\")]`";
109 self.tcx
.sess
.span_err(item
.span(), msg
);
113 // currently, like past compilers, ignore unknown
118 // In general we require #[link(name = "...")] but we allow
119 // #[link(wasm_import_module = "...")] without the `name`.
120 let requires_name
= kind_specified
|| lib
.wasm_import_module
.is_none();
121 if lib
.name
.is_none() && requires_name
{
122 struct_span_err
!(self.tcx
.sess
, m
.span
, E0459
,
123 "`#[link(...)]` specified without \
125 .span_label(m
.span
, "missing `name` argument")
128 self.register_native_lib(Some(m
.span
), lib
);
132 fn visit_trait_item(&mut self, _it
: &'tcx hir
::TraitItem
) {}
133 fn visit_impl_item(&mut self, _it
: &'tcx hir
::ImplItem
) {}
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) {
141 struct_span_err
!(self.tcx
.sess
, span
, E0454
,
142 "`#[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
.target
.options
.is_like_osx
;
153 if lib
.kind
== cstore
::NativeFramework
&& !is_osx
{
154 let msg
= "native frameworks are only available on macOS targets";
156 Some(span
) => span_err
!(self.tcx
.sess
, span
, E0455
, "{}", msg
),
157 None
=> self.tcx
.sess
.err(msg
),
160 if lib
.cfg
.is_some() && !self.tcx
.features().link_cfg
{
161 feature_err(&self.tcx
.sess
.parse_sess
, sym
::link_cfg
, span
.unwrap(), "is unstable")
164 if lib
.kind
== cstore
::NativeStaticNobundle
&&
165 !self.tcx
.features().static_nobundle
168 &self.tcx
.sess
.parse_sess
,
169 sym
::static_nobundle
,
170 span
.unwrap_or_else(|| syntax_pos
::DUMMY_SP
),
171 "kind=\"static-nobundle\" is unstable"
175 if lib
.kind
== cstore
::NativeRawDylib
&&
176 !self.tcx
.features().raw_dylib
{
178 &self.tcx
.sess
.parse_sess
,
180 span
.unwrap_or_else(|| syntax_pos
::DUMMY_SP
),
181 "kind=\"raw-dylib\" is unstable"
188 // Process libs passed on the command line
189 fn process_command_line(&mut self) {
190 // First, check for errors
191 let mut renames
= FxHashSet
::default();
192 for &(ref name
, ref new_name
, _
) in &self.tcx
.sess
.opts
.libs
{
193 if let &Some(ref new_name
) = new_name
{
194 let any_duplicate
= self.libs
196 .filter_map(|lib
| lib
.name
.as_ref())
197 .any(|n
| n
.as_str() == *name
);
198 if new_name
.is_empty() {
200 &format
!("an empty renaming target was specified for library `{}`",name
));
201 } else if !any_duplicate
{
202 self.tcx
.sess
.err(&format
!("renaming of the library `{}` was specified, \
203 however this crate contains no `#[link(...)]` \
204 attributes referencing this library.", name
));
205 } else if !renames
.insert(name
) {
206 self.tcx
.sess
.err(&format
!("multiple renamings were \
207 specified for library `{}` .",
213 // Update kind and, optionally, the name of all native libraries
214 // (there may be more than one) with the specified name. If any
215 // library is mentioned more than once, keep the latest mention
216 // of it, so that any possible dependent libraries appear before
217 // it. (This ensures that the linker is able to see symbols from
218 // all possible dependent libraries before linking in the library
220 for &(ref name
, ref new_name
, kind
) in &self.tcx
.sess
.opts
.libs
{
221 // If we've already added any native libraries with the same
222 // name, they will be pulled out into `existing`, so that we
223 // can move them to the end of the list below.
224 let mut existing
= self.libs
.drain_filter(|lib
| {
225 if let Some(lib_name
) = lib
.name
{
226 if lib_name
.as_str() == *name
{
227 if let Some(k
) = kind
{
230 if let &Some(ref new_name
) = new_name
{
231 lib
.name
= Some(Symbol
::intern(new_name
));
237 }).collect
::<Vec
<_
>>();
238 if existing
.is_empty() {
240 let new_name
= new_name
.as_ref().map(|s
| &**s
); // &Option<String> -> Option<&str>
241 let lib
= NativeLibrary
{
242 name
: Some(Symbol
::intern(new_name
.unwrap_or(name
))),
243 kind
: if let Some(k
) = kind { k }
else { cstore::NativeUnknown }
,
245 foreign_module
: None
,
246 wasm_import_module
: None
,
248 self.register_native_lib(None
, lib
);
250 // Move all existing libraries with the same name to the
251 // end of the command line.
252 self.libs
.append(&mut existing
);