]> git.lizzy.rs Git - rust.git/commitdiff
syntax: Add a new unstable #[linked_from] attribute
authorAlex Crichton <alex@alexcrichton.com>
Thu, 30 Jul 2015 21:20:36 +0000 (14:20 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 11 Aug 2015 01:20:00 +0000 (18:20 -0700)
To correctly reexport statically included libraries from a DLL on Windows, the
compiler will soon need to have knowledge about what symbols are statically
included and which are not. To solve this problem a new unstable
`#[linked_from]` attribute is being added and recognized on `extern` blocks to
indicate which native library the symbols are coming from.

The compiler then keeps track of what the set of FFI symbols are that are
included statically. This information will be used in a future commit to
configure how we invoke the linker on Windows.

src/doc/reference.md
src/librustc/metadata/creader.rs
src/librustc/metadata/cstore.rs
src/libsyntax/feature_gate.rs

index e905ed917d7fd1c49f27b9455a370bfa2742595e..4d5564d9fafb082b574e0eaf8f0480d234e50b29 100644 (file)
@@ -1924,10 +1924,16 @@ On an `extern` block, the following attributes are interpreted:
   name and type. This is feature gated and the exact behavior is
   implementation-defined (due to variety of linker invocation syntax).
 - `link` - indicate that a native library should be linked to for the
-  declarations in this block to be linked correctly. `link` supports an optional `kind`
-  key with three possible values: `dylib`, `static`, and `framework`. See [external blocks](#external-blocks) for more about external blocks. Two
+  declarations in this block to be linked correctly. `link` supports an optional
+  `kind` key with three possible values: `dylib`, `static`, and `framework`. See
+  [external blocks](#external-blocks) for more about external blocks. Two
   examples: `#[link(name = "readline")]` and
   `#[link(name = "CoreFoundation", kind = "framework")]`.
+- `linked_from` - indicates what native library this block of FFI items is
+  coming from. This attribute is of the form `#[linked_from = "foo"]` where
+  `foo` is the name of a library in either `#[link]` or a `-l` flag. This
+  attribute is currently required to export symbols from a Rust dynamic library
+  on Windows, and it is feature gated behind the `linked_from` feature.
 
 On declarations inside an `extern` block, the following attributes are
 interpreted:
index bbb2d4fade1f070be5818d624294abf1fd6918b1..3226a99c6b3ae285cf7885fa1d67eac578ff2c29 100644 (file)
@@ -20,6 +20,7 @@
 use metadata::decoder;
 use metadata::loader;
 use metadata::loader::CratePaths;
+use util::nodemap::FnvHashMap;
 
 use std::cell::RefCell;
 use std::path::PathBuf;
@@ -47,6 +48,7 @@ pub struct LocalCrateReader<'a, 'b:'a> {
 pub struct CrateReader<'a> {
     sess: &'a Session,
     next_crate_num: ast::CrateNum,
+    foreign_item_map: FnvHashMap<String, Vec<ast::NodeId>>,
 }
 
 impl<'a, 'b, 'v> visit::Visitor<'v> for LocalCrateReader<'a, 'b> {
@@ -157,6 +159,7 @@ pub fn new(sess: &'a Session) -> CrateReader<'a> {
         CrateReader {
             sess: sess,
             next_crate_num: sess.cstore.next_crate_num(),
+            foreign_item_map: FnvHashMap(),
         }
     }
 
@@ -490,6 +493,20 @@ pub fn find_plugin_registrar(&mut self, span: Span, name: &str)
             _ => None,
         }
     }
+
+    fn register_statically_included_foreign_items(&mut self) {
+        let libs = self.sess.cstore.get_used_libraries();
+        for (lib, list) in self.foreign_item_map.iter() {
+            let is_static = libs.borrow().iter().any(|&(ref name, kind)| {
+                lib == name && kind == cstore::NativeStatic
+            });
+            if is_static {
+                for id in list {
+                    self.sess.cstore.add_statically_included_foreign_item(*id);
+                }
+            }
+        }
+    }
 }
 
 impl<'a, 'b> LocalCrateReader<'a, 'b> {
@@ -515,6 +532,7 @@ pub fn read_crates(&mut self, krate: &ast::Crate) {
         for &(ref name, kind) in &self.sess.opts.libs {
             register_native_lib(self.sess, None, name.clone(), kind);
         }
+        self.creader.register_statically_included_foreign_items();
     }
 
     fn process_crate(&self, c: &ast::Crate) {
@@ -541,87 +559,73 @@ fn process_item(&mut self, i: &ast::Item) {
                                                               None,
                                                               i.span,
                                                               PathKind::Crate);
-                        self.ast_map.with_path(i.id, |path|
-                            cmeta.update_local_path(path));
+                        self.ast_map.with_path(i.id, |path| {
+                            cmeta.update_local_path(path)
+                        });
                         self.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
                     }
                     None => ()
                 }
             }
-            ast::ItemForeignMod(ref fm) => {
-                if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
-                    return;
-                }
+            ast::ItemForeignMod(ref fm) => self.process_foreign_mod(i, fm),
+            _ => { }
+        }
+    }
 
-                // First, add all of the custom link_args attributes
-                let link_args = i.attrs.iter()
-                    .filter_map(|at| if at.name() == "link_args" {
-                        Some(at)
-                    } else {
-                        None
-                    })
-                    .collect::<Vec<&ast::Attribute>>();
-                for m in &link_args {
-                    match m.value_str() {
-                        Some(linkarg) => self.sess.cstore.add_used_link_args(&linkarg),
-                        None => { /* fallthrough */ }
-                    }
-                }
+    fn process_foreign_mod(&mut self, i: &ast::Item, fm: &ast::ForeignMod) {
+        if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
+            return;
+        }
 
-                // Next, process all of the #[link(..)]-style arguments
-                let link_args = i.attrs.iter()
-                    .filter_map(|at| if at.name() == "link" {
-                        Some(at)
-                    } else {
-                        None
-                    })
-                    .collect::<Vec<&ast::Attribute>>();
-                for m in &link_args {
-                    match m.meta_item_list() {
-                        Some(items) => {
-                            let kind = items.iter().find(|k| {
-                                k.name() == "kind"
-                            }).and_then(|a| a.value_str());
-                            let kind = match kind {
-                                Some(k) => {
-                                    if k == "static" {
-                                        cstore::NativeStatic
-                                    } else if self.sess.target.target.options.is_like_osx
-                                              && k == "framework" {
-                                        cstore::NativeFramework
-                                    } else if k == "framework" {
-                                        cstore::NativeFramework
-                                    } else if k == "dylib" {
-                                        cstore::NativeUnknown
-                                    } else {
-                                        self.sess.span_err(m.span,
-                                            &format!("unknown kind: `{}`",
-                                                    k));
-                                        cstore::NativeUnknown
-                                    }
-                                }
-                                None => cstore::NativeUnknown
-                            };
-                            let n = items.iter().find(|n| {
-                                n.name() == "name"
-                            }).and_then(|a| a.value_str());
-                            let n = match n {
-                                Some(n) => n,
-                                None => {
-                                    self.sess.span_err(m.span,
-                                        "#[link(...)] specified without \
-                                         `name = \"foo\"`");
-                                    InternedString::new("foo")
-                                }
-                            };
-                            register_native_lib(self.sess, Some(m.span),
-                                                n.to_string(), kind);
-                        }
-                        None => {}
-                    }
-                }
+        // First, add all of the custom #[link_args] attributes
+        for m in i.attrs.iter().filter(|a| a.check_name("link_args")) {
+            if let Some(linkarg) = m.value_str() {
+                self.sess.cstore.add_used_link_args(&linkarg);
             }
-            _ => { }
+        }
+
+        // Next, process all of the #[link(..)]-style arguments
+        for m in i.attrs.iter().filter(|a| a.check_name("link")) {
+            let items = match m.meta_item_list() {
+                Some(item) => item,
+                None => continue,
+            };
+            let kind = items.iter().find(|k| {
+                k.check_name("kind")
+            }).and_then(|a| a.value_str());
+            let kind = match kind.as_ref().map(|s| &s[..]) {
+                Some("static") => cstore::NativeStatic,
+                Some("dylib") => cstore::NativeUnknown,
+                Some("framework") => cstore::NativeFramework,
+                Some(k) => {
+                    self.sess.span_err(m.span, &format!("unknown kind: `{}`", k));
+                    cstore::NativeUnknown
+                }
+                None => cstore::NativeUnknown
+            };
+            let n = items.iter().find(|n| {
+                n.check_name("name")
+            }).and_then(|a| a.value_str());
+            let n = match n {
+                Some(n) => n,
+                None => {
+                    self.sess.span_err(m.span, "#[link(...)] specified without \
+                                                `name = \"foo\"`");
+                    InternedString::new("foo")
+                }
+            };
+            register_native_lib(self.sess, Some(m.span), n.to_string(), kind);
+        }
+
+        // Finally, process the #[linked_from = "..."] attribute
+        for m in i.attrs.iter().filter(|a| a.check_name("linked_from")) {
+            let lib_name = match m.value_str() {
+                Some(name) => name,
+                None => continue,
+            };
+            let list = self.creader.foreign_item_map.entry(lib_name.to_string())
+                                                    .or_insert(Vec::new());
+            list.extend(fm.items.iter().map(|it| it.id));
         }
     }
 }
index 19d494824cf3aa92efd0591f6fca30cb6b2e8557..ae5e797a0299b591aa408c8fa29280c3da5842b8 100644 (file)
@@ -20,7 +20,7 @@
 use back::svh::Svh;
 use metadata::{creader, decoder, loader};
 use session::search_paths::PathKind;
-use util::nodemap::{FnvHashMap, NodeMap};
+use util::nodemap::{FnvHashMap, NodeMap, NodeSet};
 
 use std::cell::{RefCell, Ref};
 use std::rc::Rc;
@@ -97,6 +97,7 @@ pub struct CStore {
     used_crate_sources: RefCell<Vec<CrateSource>>,
     used_libraries: RefCell<Vec<(String, NativeLibraryKind)>>,
     used_link_args: RefCell<Vec<String>>,
+    statically_included_foreign_items: RefCell<NodeSet>,
     pub intr: Rc<IdentInterner>,
 }
 
@@ -108,7 +109,8 @@ pub fn new(intr: Rc<IdentInterner>) -> CStore {
             used_crate_sources: RefCell::new(Vec::new()),
             used_libraries: RefCell::new(Vec::new()),
             used_link_args: RefCell::new(Vec::new()),
-            intr: intr
+            intr: intr,
+            statically_included_foreign_items: RefCell::new(NodeSet()),
         }
     }
 
@@ -167,6 +169,7 @@ pub fn reset(&self) {
         self.used_crate_sources.borrow_mut().clear();
         self.used_libraries.borrow_mut().clear();
         self.used_link_args.borrow_mut().clear();
+        self.statically_included_foreign_items.borrow_mut().clear();
     }
 
     // This method is used when generating the command line to pass through to
@@ -240,6 +243,14 @@ pub fn find_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId)
                                      -> Option<ast::CrateNum> {
         self.extern_mod_crate_map.borrow().get(&emod_id).cloned()
     }
+
+    pub fn add_statically_included_foreign_item(&self, id: ast::NodeId) {
+        self.statically_included_foreign_items.borrow_mut().insert(id);
+    }
+
+    pub fn is_statically_included_foreign_item(&self, id: ast::NodeId) -> bool {
+        self.statically_included_foreign_items.borrow().contains(&id)
+    }
 }
 
 impl crate_metadata {
index 60a5730a3da0cd1a3f462bd5ad7a82a83cc4286e..87149ff81da78ef6a3db1836849e2a7bb681d83e 100644 (file)
@@ -85,6 +85,7 @@
     ("on_unimplemented", "1.0.0", Active),
     ("simd_ffi", "1.0.0", Active),
     ("allocator", "1.0.0", Active),
+    ("linked_from", "1.3.0", Active),
 
     ("if_let", "1.0.0", Accepted),
     ("while_let", "1.0.0", Accepted),
@@ -269,6 +270,10 @@ enum Status {
                           "the `#[fundamental]` attribute \
                            is an experimental feature")),
 
+    ("linked_from", Gated("linked_from",
+                          "the `#[linked_from]` attribute \
+                           is an experimental feature")),
+
     // FIXME: #14408 whitelist docs since rustdoc looks at them
     ("doc", Whitelisted),