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:
parent
5aca49c693
commit
18607149fb
@ -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:
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user