syntax: Add a new unstable #[linked_from] attribute

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.
This commit is contained in:
Alex Crichton 2015-07-30 14:20:36 -07:00
parent 5aca49c693
commit 18607149fb
4 changed files with 105 additions and 79 deletions

View 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:

View 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) => {
ast::ItemForeignMod(ref fm) => self.process_foreign_mod(i, fm),
_ => { }
}
}
fn process_foreign_mod(&mut self, i: &ast::Item, fm: &ast::ForeignMod) {
if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
return;
}
// 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 */ }
// 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
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) => {
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.name() == "kind"
k.check_name("kind")
}).and_then(|a| a.value_str());
let kind = match kind {
let kind = match kind.as_ref().map(|s| &s[..]) {
Some("static") => cstore::NativeStatic,
Some("dylib") => cstore::NativeUnknown,
Some("framework") => cstore::NativeFramework,
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" {
self.sess.span_err(m.span, &format!("unknown kind: `{}`", k));
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"
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 \
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);
register_native_lib(self.sess, Some(m.span), n.to_string(), kind);
}
None => {}
}
}
}
_ => { }
// 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));
}
}
}

View 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 {

View 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),