about summary refs log tree commit diff
path: root/compiler/rustc_metadata/src/native_libs.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_metadata/src/native_libs.rs')
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs269
1 files changed, 269 insertions, 0 deletions
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
new file mode 100644
index 00000000000..3976475cb06
--- /dev/null
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -0,0 +1,269 @@
+use rustc_attr as attr;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::itemlikevisit::ItemLikeVisitor;
+use rustc_middle::middle::cstore::NativeLib;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::parse::feature_err;
+use rustc_session::utils::NativeLibKind;
+use rustc_session::Session;
+use rustc_span::source_map::Span;
+use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_target::spec::abi::Abi;
+
+crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
+    let mut collector = Collector { tcx, libs: Vec::new() };
+    tcx.hir().krate().visit_all_item_likes(&mut collector);
+    collector.process_command_line();
+    collector.libs
+}
+
+crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
+    match lib.cfg {
+        Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None),
+        None => true,
+    }
+}
+
+struct Collector<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    libs: Vec<NativeLib>,
+}
+
+impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
+    fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
+        let fm = match it.kind {
+            hir::ItemKind::ForeignMod(ref fm) => fm,
+            _ => return,
+        };
+
+        if fm.abi == Abi::Rust || fm.abi == Abi::RustIntrinsic || fm.abi == Abi::PlatformIntrinsic {
+            return;
+        }
+
+        // Process all of the #[link(..)]-style arguments
+        let sess = &self.tcx.sess;
+        for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link)) {
+            let items = match m.meta_item_list() {
+                Some(item) => item,
+                None => continue,
+            };
+            let mut lib = NativeLib {
+                name: None,
+                kind: NativeLibKind::Unspecified,
+                cfg: None,
+                foreign_module: Some(self.tcx.hir().local_def_id(it.hir_id).to_def_id()),
+                wasm_import_module: None,
+            };
+            let mut kind_specified = false;
+
+            for item in items.iter() {
+                if item.has_name(sym::kind) {
+                    kind_specified = true;
+                    let kind = match item.value_str() {
+                        Some(name) => name,
+                        None => continue, // skip like historical compilers
+                    };
+                    lib.kind = match &*kind.as_str() {
+                        "static" => NativeLibKind::StaticBundle,
+                        "static-nobundle" => NativeLibKind::StaticNoBundle,
+                        "dylib" => NativeLibKind::Dylib,
+                        "framework" => NativeLibKind::Framework,
+                        "raw-dylib" => NativeLibKind::RawDylib,
+                        k => {
+                            struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k)
+                                .span_label(item.span(), "unknown kind")
+                                .span_label(m.span, "")
+                                .emit();
+                            NativeLibKind::Unspecified
+                        }
+                    };
+                } else if item.has_name(sym::name) {
+                    lib.name = item.value_str();
+                } else if item.has_name(sym::cfg) {
+                    let cfg = match item.meta_item_list() {
+                        Some(list) => list,
+                        None => continue, // skip like historical compilers
+                    };
+                    if cfg.is_empty() {
+                        sess.span_err(item.span(), "`cfg()` must have an argument");
+                    } else if let cfg @ Some(..) = cfg[0].meta_item() {
+                        lib.cfg = cfg.cloned();
+                    } else {
+                        sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
+                    }
+                } else if item.has_name(sym::wasm_import_module) {
+                    match item.value_str() {
+                        Some(s) => lib.wasm_import_module = Some(s),
+                        None => {
+                            let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`";
+                            sess.span_err(item.span(), msg);
+                        }
+                    }
+                } else {
+                    // currently, like past compilers, ignore unknown
+                    // directives here.
+                }
+            }
+
+            // In general we require #[link(name = "...")] but we allow
+            // #[link(wasm_import_module = "...")] without the `name`.
+            let requires_name = kind_specified || lib.wasm_import_module.is_none();
+            if lib.name.is_none() && requires_name {
+                struct_span_err!(
+                    sess,
+                    m.span,
+                    E0459,
+                    "`#[link(...)]` specified without \
+                                  `name = \"foo\"`"
+                )
+                .span_label(m.span, "missing `name` argument")
+                .emit();
+            }
+            self.register_native_lib(Some(m.span), lib);
+        }
+    }
+
+    fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem<'tcx>) {}
+    fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem<'tcx>) {}
+}
+
+impl Collector<'tcx> {
+    fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) {
+        if lib.name.as_ref().map(|&s| s == kw::Invalid).unwrap_or(false) {
+            match span {
+                Some(span) => {
+                    struct_span_err!(
+                        self.tcx.sess,
+                        span,
+                        E0454,
+                        "`#[link(name = \"\")]` given with empty name"
+                    )
+                    .span_label(span, "empty name given")
+                    .emit();
+                }
+                None => {
+                    self.tcx.sess.err("empty library name given via `-l`");
+                }
+            }
+            return;
+        }
+        let is_osx = self.tcx.sess.target.target.options.is_like_osx;
+        if lib.kind == NativeLibKind::Framework && !is_osx {
+            let msg = "native frameworks are only available on macOS targets";
+            match span {
+                Some(span) => struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(),
+                None => self.tcx.sess.err(msg),
+            }
+        }
+        if lib.cfg.is_some() && !self.tcx.features().link_cfg {
+            feature_err(
+                &self.tcx.sess.parse_sess,
+                sym::link_cfg,
+                span.unwrap(),
+                "kind=\"link_cfg\" is unstable",
+            )
+            .emit();
+        }
+        if lib.kind == NativeLibKind::StaticNoBundle && !self.tcx.features().static_nobundle {
+            feature_err(
+                &self.tcx.sess.parse_sess,
+                sym::static_nobundle,
+                span.unwrap_or_else(|| rustc_span::DUMMY_SP),
+                "kind=\"static-nobundle\" is unstable",
+            )
+            .emit();
+        }
+        if lib.kind == NativeLibKind::RawDylib && !self.tcx.features().raw_dylib {
+            feature_err(
+                &self.tcx.sess.parse_sess,
+                sym::raw_dylib,
+                span.unwrap_or_else(|| rustc_span::DUMMY_SP),
+                "kind=\"raw-dylib\" is unstable",
+            )
+            .emit();
+        }
+        self.libs.push(lib);
+    }
+
+    // Process libs passed on the command line
+    fn process_command_line(&mut self) {
+        // First, check for errors
+        let mut renames = FxHashSet::default();
+        for &(ref name, ref new_name, _) in &self.tcx.sess.opts.libs {
+            if let &Some(ref new_name) = new_name {
+                let any_duplicate = self
+                    .libs
+                    .iter()
+                    .filter_map(|lib| lib.name.as_ref())
+                    .any(|n| n.as_str() == *name);
+                if new_name.is_empty() {
+                    self.tcx.sess.err(&format!(
+                        "an empty renaming target was specified for library `{}`",
+                        name
+                    ));
+                } else if !any_duplicate {
+                    self.tcx.sess.err(&format!(
+                        "renaming of the library `{}` was specified, \
+                                                however this crate contains no `#[link(...)]` \
+                                                attributes referencing this library.",
+                        name
+                    ));
+                } else if !renames.insert(name) {
+                    self.tcx.sess.err(&format!(
+                        "multiple renamings were \
+                                                specified for library `{}` .",
+                        name
+                    ));
+                }
+            }
+        }
+
+        // Update kind and, optionally, the name of all native libraries
+        // (there may be more than one) with the specified name.  If any
+        // library is mentioned more than once, keep the latest mention
+        // of it, so that any possible dependent libraries appear before
+        // it.  (This ensures that the linker is able to see symbols from
+        // all possible dependent libraries before linking in the library
+        // in question.)
+        for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs {
+            // If we've already added any native libraries with the same
+            // name, they will be pulled out into `existing`, so that we
+            // can move them to the end of the list below.
+            let mut existing = self
+                .libs
+                .drain_filter(|lib| {
+                    if let Some(lib_name) = lib.name {
+                        if lib_name.as_str() == *name {
+                            if kind != NativeLibKind::Unspecified {
+                                lib.kind = kind;
+                            }
+                            if let &Some(ref new_name) = new_name {
+                                lib.name = Some(Symbol::intern(new_name));
+                            }
+                            return true;
+                        }
+                    }
+                    false
+                })
+                .collect::<Vec<_>>();
+            if existing.is_empty() {
+                // Add if not found
+                let new_name = new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
+                let lib = NativeLib {
+                    name: Some(Symbol::intern(new_name.unwrap_or(name))),
+                    kind,
+                    cfg: None,
+                    foreign_module: None,
+                    wasm_import_module: None,
+                };
+                self.register_native_lib(None, lib);
+            } else {
+                // Move all existing libraries with the same name to the
+                // end of the command line.
+                self.libs.append(&mut existing);
+            }
+        }
+    }
+}