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::cstore::{CStore, CrateSource, MetadataBlob};
|
|||
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 @@ impl<'a> CrateReader<'a> {
|
|||
CrateReader {
|
||||
sess: sess,
|
||||
next_crate_num: sess.cstore.next_crate_num(),
|
||||
foreign_item_map: FnvHashMap(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -490,6 +493,20 @@ impl<'a> CrateReader<'a> {
|
|||
_ => 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 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> {
|
|||
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,89 +559,75 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> {
|
|||
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;
|
||||
}
|
||||
|
||||
// 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 */ }
|
||||
}
|
||||
}
|
||||
|
||||
// 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 => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Imports the codemap from an external crate into the codemap of the crate
|
||||
|
|
|
@ -20,7 +20,7 @@ pub use self::NativeLibraryKind::*;
|
|||
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 @@ impl 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 @@ impl CStore {
|
|||
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 @@ impl CStore {
|
|||
-> 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 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
|
|||
("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 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
|
|||
"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