rustc: Implement #[link(cfg(..))] and crt-static

This commit is an implementation of [RFC 1721] which adds a new target feature
to the compiler, `crt-static`, which can be used to select how the C runtime for
a target is linked. Most targets dynamically linke the C runtime by default with
the notable exception of some of the musl targets.

[RFC 1721]: https://github.com/rust-lang/rfcs/blob/master/text/1721-crt-static.md

This commit first adds the new target-feature, `crt-static`. If enabled, then
the `cfg(target_feature = "crt-static")` will be available. Targets like musl
will have this enabled by default. This feature can be controlled through the
standard target-feature interface, `-C target-feature=+crt-static` or
`-C target-feature=-crt-static`.

Next this adds an gated and unstable `#[link(cfg(..))]` feature to enable the
`crt-static` semantics we want with libc. The exact behavior of this attribute
is a little squishy, but it's intended to be a forever-unstable
implementation detail of the liblibc crate.

Specifically the `#[link(cfg(..))]` annotation means that the `#[link]`
directive is only active in a compilation unit if that `cfg` value is satisfied.
For example when compiling an rlib, these directives are just encoded and
ignored for dylibs, and all staticlibs are continued to be put into the rlib as
usual. When placing that rlib into a staticlib, executable, or dylib, however,
the `cfg` is evaluated *as if it were defined in the final artifact* and the
library is decided to be linked or not.

Essentially, what'll happen is:

* On MSVC with `-C target-feature=-crt-static`, the `msvcrt.lib` library will be
  linked to.
* On MSVC with `-C target-feature=+crt-static`, the `libcmt.lib` library will be
  linked to.
* On musl with `-C target-feature=-crt-static`, the object files in liblibc.rlib
  are removed and `-lc` is passed instead.
* On musl with `-C target-feature=+crt-static`, the object files in liblibc.rlib
  are used and `-lc` is not passed.

This commit does **not** include an update to the liblibc module to implement
these changes. I plan to do that just after the 1.14.0 beta release is cut to
ensure we get ample time to test this feature.

cc #37406
This commit is contained in:
Alex Crichton 2016-10-31 16:40:13 -07:00
parent c8867f8b46
commit 06242ff15d
31 changed files with 547 additions and 92 deletions

@ -1 +1 @@
Subproject commit 7d9b71f0971f8fa196d864d7071f216a59036d6e
Subproject commit 6e8c1b490ccbe5e84d248bab883515bc85394b5f

View File

@ -89,6 +89,13 @@ pub enum NativeLibraryKind {
NativeUnknown, // default way to specify a dynamic library
}
#[derive(Clone, Hash, RustcEncodable, RustcDecodable)]
pub struct NativeLibrary {
pub kind: NativeLibraryKind,
pub name: String,
pub cfg: Option<P<ast::MetaItem>>,
}
/// The data we save and restore about an inlined item or method. This is not
/// part of the AST that we parse from a file, but it becomes part of the tree
/// that we trans.
@ -204,7 +211,7 @@ pub trait CrateStore<'tcx> {
fn crate_hash(&self, cnum: CrateNum) -> Svh;
fn crate_disambiguator(&self, cnum: CrateNum) -> InternedString;
fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>;
fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)>;
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>;
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId>;
fn is_no_builtins(&self, cnum: CrateNum) -> bool;
@ -231,7 +238,7 @@ pub trait CrateStore<'tcx> {
// This is basically a 1-based range of ints, which is a little
// silly - I may fix that.
fn crates(&self) -> Vec<CrateNum>;
fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)>;
fn used_libraries(&self) -> Vec<NativeLibrary>;
fn used_link_args(&self) -> Vec<String>;
// utility functions
@ -377,7 +384,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
-> InternedString { bug!("crate_disambiguator") }
fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>
{ bug!("plugin_registrar_fn") }
fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)>
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>
{ bug!("native_libraries") }
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId> { bug!("reachable_ids") }
fn is_no_builtins(&self, cnum: CrateNum) -> bool { bug!("is_no_builtins") }
@ -412,7 +419,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
// This is basically a 1-based range of ints, which is a little
// silly - I may fix that.
fn crates(&self) -> Vec<CrateNum> { vec![] }
fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)> { vec![] }
fn used_libraries(&self) -> Vec<NativeLibrary> {
vec![]
}
fn used_link_args(&self) -> Vec<String> { vec![] }
// utility functions

View File

@ -16,7 +16,6 @@ pub fn opts() -> TargetOptions {
// Make sure that the linker/gcc really don't pull in anything, including
// default objects, libs, etc.
base.pre_link_args.push("-nostdlib".to_string());
base.pre_link_args.push("-static".to_string());
// At least when this was tested, the linker would not add the
// `GNU_EH_FRAME` program header to executables generated, which is required
@ -67,5 +66,8 @@ pub fn opts() -> TargetOptions {
base.has_rpath = false;
base.position_independent_executables = false;
// These targets statically link libc by default
base.crt_static_default = true;
base
}

View File

@ -373,6 +373,9 @@ pub struct TargetOptions {
/// A blacklist of ABIs unsupported by the current target. Note that generic
/// ABIs are considered to be supported on all platforms and cannot be blacklisted.
pub abi_blacklist: Vec<Abi>,
/// Whether or not the CRT is statically linked by default.
pub crt_static_default: bool,
}
impl Default for TargetOptions {
@ -425,6 +428,7 @@ impl Default for TargetOptions {
max_atomic_width: None,
panic_strategy: PanicStrategy::Unwind,
abi_blacklist: vec![],
crt_static_default: false,
}
}
}
@ -585,6 +589,7 @@ impl Target {
key!(no_integrated_as, bool);
key!(max_atomic_width, Option<u64>);
try!(key!(panic_strategy, PanicStrategy));
key!(crt_static_default, bool);
if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
for name in array.iter().filter_map(|abi| abi.as_string()) {
@ -745,6 +750,7 @@ impl ToJson for Target {
target_option_val!(no_integrated_as);
target_option_val!(max_atomic_width);
target_option_val!(panic_strategy);
target_option_val!(crt_static_default);
if default.abi_blacklist != self.options.abi_blacklist {
d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter()

View File

@ -12,6 +12,7 @@ use syntax::{ast, attr};
use llvm::LLVMRustHasFeature;
use rustc::session::Session;
use rustc_trans::back::write::create_target_machine;
use syntax::feature_gate::UnstableFeatures;
use syntax::parse::token::InternedString;
use syntax::parse::token::intern_and_get_ident as intern;
use libc::c_char;
@ -47,4 +48,32 @@ pub fn add_configuration(cfg: &mut ast::CrateConfig, sess: &Session) {
cfg.push(attr::mk_name_value_item_str(tf.clone(), intern(&feat[..feat.len() - 1])))
}
}
let requested_features = sess.opts.cg.target_feature.split(',');
let unstable_options = sess.opts.debugging_opts.unstable_options;
let is_nightly = UnstableFeatures::from_environment().is_nightly_build();
let found_negative = requested_features.clone().any(|r| r == "-crt-static");
let found_positive = requested_features.clone().any(|r| r == "+crt-static");
// If the target we're compiling for requests a static crt by default,
// then see if the `-crt-static` feature was passed to disable that.
// Otherwise if we don't have a static crt by default then see if the
// `+crt-static` feature was passed.
let crt_static = if sess.target.target.options.crt_static_default {
!found_negative
} else {
found_positive
};
// If we switched from the default then that's only allowed on nightly, so
// gate that here.
if (found_positive || found_negative) && (!is_nightly || !unstable_options) {
sess.fatal("specifying the `crt-static` target feature is only allowed \
on the nightly channel with `-Z unstable-options` passed \
as well");
}
if crt_static {
cfg.push(attr::mk_name_value_item_str(tf.clone(), intern("crt-static")));
}
}

View File

@ -23,6 +23,7 @@ use rustc::session::search_paths::PathKind;
use rustc::middle;
use rustc::middle::cstore::{CrateStore, validate_crate_name, ExternCrate};
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc::middle::cstore::NativeLibrary;
use rustc::hir::map::Definitions;
use std::cell::{RefCell, Cell};
@ -35,6 +36,7 @@ use syntax::ast;
use syntax::abi::Abi;
use syntax::attr;
use syntax::ext::base::SyntaxExtension;
use syntax::feature_gate::{self, GateIssue};
use syntax::parse::token::{InternedString, intern};
use syntax_pos::{Span, DUMMY_SP};
use log;
@ -77,9 +79,8 @@ struct ExternCrateInfo {
fn register_native_lib(sess: &Session,
cstore: &CStore,
span: Option<Span>,
name: String,
kind: cstore::NativeLibraryKind) {
if name.is_empty() {
lib: NativeLibrary) {
if lib.name.is_empty() {
match span {
Some(span) => {
struct_span_err!(sess, span, E0454,
@ -94,17 +95,21 @@ fn register_native_lib(sess: &Session,
return
}
let is_osx = sess.target.target.options.is_like_osx;
if kind == cstore::NativeFramework && !is_osx {
if lib.kind == cstore::NativeFramework && !is_osx {
let msg = "native frameworks are only available on OSX targets";
match span {
Some(span) => {
span_err!(sess, span, E0455,
"{}", msg)
}
Some(span) => span_err!(sess, span, E0455, "{}", msg),
None => sess.err(msg),
}
}
cstore.add_used_library(name, kind);
if lib.cfg.is_some() && !sess.features.borrow().link_cfg {
feature_gate::emit_feature_err(&sess.parse_sess,
"link_cfg",
span.unwrap(),
GateIssue::Language,
"is feature gated");
}
cstore.add_used_library(lib);
}
// Extra info about a crate loaded for plugins or exported macros.
@ -635,9 +640,9 @@ impl<'a> CrateLoader<'a> {
fn register_statically_included_foreign_items(&mut self) {
let libs = self.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
for (foreign_lib, list) in self.foreign_item_map.iter() {
let is_static = libs.borrow().iter().any(|lib| {
*foreign_lib == lib.name && lib.kind == cstore::NativeStatic
});
if is_static {
for id in list {
@ -898,7 +903,18 @@ impl<'a> CrateLoader<'a> {
InternedString::new("foo")
}
};
register_native_lib(self.sess, self.cstore, Some(m.span), n.to_string(), kind);
let cfg = items.iter().find(|k| {
k.check_name("cfg")
}).and_then(|a| a.meta_item_list());
let cfg = cfg.map(|list| {
list[0].meta_item().unwrap().clone()
});
let lib = NativeLibrary {
name: n.to_string(),
kind: kind,
cfg: cfg,
};
register_native_lib(self.sess, self.cstore, Some(m.span), lib);
}
// Finally, process the #[linked_from = "..."] attribute
@ -924,7 +940,12 @@ impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> {
}
for &(ref name, kind) in &self.sess.opts.libs {
register_native_lib(self.sess, self.cstore, None, name.clone(), kind);
let lib = NativeLibrary {
name: name.clone(),
kind: kind,
cfg: None,
};
register_native_lib(self.sess, self.cstore, None, lib);
}
self.register_statically_included_foreign_items();
}

View File

@ -31,7 +31,7 @@ use syntax::{ast, attr};
use syntax::ext::base::SyntaxExtension;
use syntax_pos;
pub use rustc::middle::cstore::{NativeLibraryKind, LinkagePreference};
pub use rustc::middle::cstore::{NativeLibrary, LinkagePreference};
pub use rustc::middle::cstore::{NativeStatic, NativeFramework, NativeUnknown};
pub use rustc::middle::cstore::{CrateSource, LinkMeta};
@ -97,7 +97,7 @@ pub struct CStore {
metas: RefCell<FxHashMap<CrateNum, Rc<CrateMetadata>>>,
/// Map from NodeId's of local extern crate statements to crate numbers
extern_mod_crate_map: RefCell<NodeMap<CrateNum>>,
used_libraries: RefCell<Vec<(String, NativeLibraryKind)>>,
used_libraries: RefCell<Vec<NativeLibrary>>,
used_link_args: RefCell<Vec<String>>,
statically_included_foreign_items: RefCell<NodeSet>,
pub inlined_item_cache: RefCell<DefIdMap<Option<CachedInlinedItem>>>,
@ -212,12 +212,12 @@ impl CStore {
libs
}
pub fn add_used_library(&self, lib: String, kind: NativeLibraryKind) {
assert!(!lib.is_empty());
self.used_libraries.borrow_mut().push((lib, kind));
pub fn add_used_library(&self, lib: NativeLibrary) {
assert!(!lib.name.is_empty());
self.used_libraries.borrow_mut().push(lib);
}
pub fn get_used_libraries<'a>(&'a self) -> &'a RefCell<Vec<(String, NativeLibraryKind)>> {
pub fn get_used_libraries(&self) -> &RefCell<Vec<NativeLibrary>> {
&self.used_libraries
}

View File

@ -14,7 +14,7 @@ use locator;
use schema;
use rustc::middle::cstore::{InlinedItem, CrateStore, CrateSource, DepKind, ExternCrate};
use rustc::middle::cstore::{NativeLibraryKind, LinkMeta, LinkagePreference, LoadedMacro};
use rustc::middle::cstore::{NativeLibrary, LinkMeta, LinkagePreference, LoadedMacro};
use rustc::hir::def::{self, Def};
use rustc::middle::lang_items;
use rustc::session::Session;
@ -295,7 +295,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
})
}
fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)>
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>
{
self.get_crate_data(cnum).get_native_libraries()
}
@ -524,7 +524,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
result
}
fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)>
fn used_libraries(&self) -> Vec<NativeLibrary>
{
self.get_used_libraries().borrow().clone()
}

View File

@ -11,7 +11,7 @@
// Decoding metadata from a single crate's metadata
use astencode::decode_inlined_item;
use cstore::{self, CrateMetadata, MetadataBlob, NativeLibraryKind};
use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary};
use index::Index;
use schema::*;
@ -980,7 +980,7 @@ impl<'a, 'tcx> CrateMetadata {
}
pub fn get_native_libraries(&self) -> Vec<(NativeLibraryKind, String)> {
pub fn get_native_libraries(&self) -> Vec<NativeLibrary> {
self.root.native_libraries.decode(self).collect()
}

View File

@ -13,7 +13,7 @@ use index::Index;
use schema::*;
use rustc::middle::cstore::{InlinedItemRef, LinkMeta};
use rustc::middle::cstore::{LinkagePreference, NativeLibraryKind};
use rustc::middle::cstore::{LinkagePreference, NativeLibrary};
use rustc::hir::def;
use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId};
use rustc::middle::dependency_format::Linkage;
@ -1134,14 +1134,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
self.lazy_seq_ref(&tcx.lang_items.missing))
}
fn encode_native_libraries(&mut self) -> LazySeq<(NativeLibraryKind, String)> {
fn encode_native_libraries(&mut self) -> LazySeq<NativeLibrary> {
let used_libraries = self.tcx.sess.cstore.used_libraries();
self.lazy_seq(used_libraries.into_iter().filter_map(|(lib, kind)| {
match kind {
cstore::NativeStatic => None, // these libraries are not propagated
cstore::NativeFramework | cstore::NativeUnknown => Some((kind, lib)),
}
}))
self.lazy_seq(used_libraries)
}
fn encode_codemap(&mut self) -> LazySeq<syntax_pos::FileMap> {

View File

@ -14,7 +14,7 @@ use index;
use rustc::hir;
use rustc::hir::def::{self, CtorKind};
use rustc::hir::def_id::{DefIndex, DefId};
use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibraryKind};
use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary};
use rustc::middle::lang_items;
use rustc::mir;
use rustc::ty::{self, Ty};
@ -175,7 +175,7 @@ pub struct CrateRoot {
pub dylib_dependency_formats: LazySeq<Option<LinkagePreference>>,
pub lang_items: LazySeq<(DefIndex, usize)>,
pub lang_items_missing: LazySeq<lang_items::LangItem>,
pub native_libraries: LazySeq<(NativeLibraryKind, String)>,
pub native_libraries: LazySeq<NativeLibrary>,
pub codemap: LazySeq<syntax_pos::FileMap>,
pub impls: LazySeq<TraitImpls>,
pub reachable_ids: LazySeq<DefIndex>,

View File

@ -145,8 +145,11 @@ impl<'a> ArchiveBuilder<'a> {
///
/// This ignores adding the bytecode from the rlib, and if LTO is enabled
/// then the object file also isn't added.
pub fn add_rlib(&mut self, rlib: &Path, name: &str, lto: bool)
-> io::Result<()> {
pub fn add_rlib(&mut self,
rlib: &Path,
name: &str,
lto: bool,
skip_objects: bool) -> io::Result<()> {
// Ignoring obj file starting with the crate name
// as simple comparison is not enough - there
// might be also an extra name suffix
@ -159,9 +162,23 @@ impl<'a> ArchiveBuilder<'a> {
self.config.sess.cstore.metadata_filename().to_owned();
self.add_archive(rlib, move |fname: &str| {
let skip_obj = lto && fname.starts_with(&obj_start)
&& fname.ends_with(".o");
skip_obj || fname.ends_with(bc_ext) || fname == metadata_filename
if fname.ends_with(bc_ext) || fname == metadata_filename {
return true
}
// Don't include Rust objects if LTO is enabled
if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
return true
}
// Otherwise if this is *not* a rust object and we're skipping
// objects then skip this file
if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
return true
}
// ok, don't skip this
return false
})
}

View File

@ -19,7 +19,7 @@ use session::config::{OutputFilenames, Input, OutputType};
use session::filesearch;
use session::search_paths::PathKind;
use session::Session;
use middle::cstore::{self, LinkMeta};
use middle::cstore::{self, LinkMeta, NativeLibrary};
use middle::cstore::{LinkagePreference, NativeLibraryKind};
use middle::dependency_format::Linkage;
use CrateTranslation;
@ -43,6 +43,7 @@ use std::process::Command;
use std::str;
use flate;
use syntax::ast;
use syntax::attr;
use syntax_pos::Span;
// RLIB LLVM-BYTECODE OBJECT LAYOUT
@ -406,12 +407,29 @@ fn link_rlib<'a>(sess: &'a Session,
ab.add_file(obj);
}
for (l, kind) in sess.cstore.used_libraries() {
match kind {
NativeLibraryKind::NativeStatic => ab.add_native_library(&l),
// Note that in this loop we are ignoring the value of `lib.cfg`. That is,
// we may not be configured to actually include a static library if we're
// adding it here. That's because later when we consume this rlib we'll
// decide whether we actually needed the static library or not.
//
// To do this "correctly" we'd need to keep track of which libraries added
// which object files to the archive. We don't do that here, however. The
// #[link(cfg(..))] feature is unstable, though, and only intended to get
// liblibc working. In that sense the check below just indicates that if
// there are any libraries we want to omit object files for at link time we
// just exclude all custom object files.
//
// Eventually if we want to stabilize or flesh out the #[link(cfg(..))]
// feature then we'll need to figure out how to record what objects were
// loaded from the libraries found here and then encode that into the
// metadata of the rlib we're generating somehow.
for lib in sess.cstore.used_libraries() {
match lib.kind {
NativeLibraryKind::NativeStatic => {}
NativeLibraryKind::NativeFramework |
NativeLibraryKind::NativeUnknown => {}
NativeLibraryKind::NativeUnknown => continue,
}
ab.add_native_library(&lib.name);
}
// After adding all files to the archive, we need to update the
@ -578,10 +596,28 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
each_linked_rlib(sess, &mut |cnum, path| {
let name = sess.cstore.crate_name(cnum);
ab.add_rlib(path, &name, sess.lto()).unwrap();
let native_libs = sess.cstore.native_libraries(cnum);
all_native_libs.extend(native_libs);
// Here when we include the rlib into our staticlib we need to make a
// decision whether to include the extra object files along the way.
// These extra object files come from statically included native
// libraries, but they may be cfg'd away with #[link(cfg(..))].
//
// This unstable feature, though, only needs liblibc to work. The only
// use case there is where musl is statically included in liblibc.rlib,
// so if we don't want the included version we just need to skip it. As
// a result the logic here is that if *any* linked library is cfg'd away
// we just skip all object files.
//
// Clearly this is not sufficient for a general purpose feature, and
// we'd want to read from the library's metadata to determine which
// object files come from where and selectively skip them.
let skip_object_files = native_libs.iter().any(|lib| {
lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib)
});
ab.add_rlib(path, &name, sess.lto(), skip_object_files).unwrap();
all_native_libs.extend(sess.cstore.native_libraries(cnum));
});
ab.update_symbols();
@ -594,13 +630,14 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
platforms, and so may need to be preserved");
}
for &(kind, ref lib) in &all_native_libs {
let name = match kind {
NativeLibraryKind::NativeStatic => "static library",
for lib in all_native_libs.iter().filter(|l| relevant_lib(sess, l)) {
let name = match lib.kind {
NativeLibraryKind::NativeUnknown => "library",
NativeLibraryKind::NativeFramework => "framework",
// These are included, no need to print them
NativeLibraryKind::NativeStatic => continue,
};
sess.note_without_error(&format!("{}: {}", name, *lib));
sess.note_without_error(&format!("{}: {}", name, lib.name));
}
}
@ -876,14 +913,12 @@ fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) {
}
});
let libs = sess.cstore.used_libraries();
let staticlibs = libs.iter().filter_map(|&(ref l, kind)| {
if kind == NativeLibraryKind::NativeStatic {Some(l)} else {None}
});
let others = libs.iter().filter(|&&(_, kind)| {
kind != NativeLibraryKind::NativeStatic
let pair = sess.cstore.used_libraries().into_iter().filter(|l| {
relevant_lib(sess, l)
}).partition(|lib| {
lib.kind == NativeLibraryKind::NativeStatic
});
let (staticlibs, others): (Vec<_>, Vec<_>) = pair;
// Some platforms take hints about whether a library is static or dynamic.
// For those that support this, we ensure we pass the option if the library
@ -899,15 +934,15 @@ fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) {
// don't otherwise explicitly reference them. This can occur for
// libraries which are just providing bindings, libraries with generic
// functions, etc.
cmd.link_whole_staticlib(l, &search_path);
cmd.link_whole_staticlib(&l.name, &search_path);
}
cmd.hint_dynamic();
for &(ref l, kind) in others {
match kind {
NativeLibraryKind::NativeUnknown => cmd.link_dylib(l),
NativeLibraryKind::NativeFramework => cmd.link_framework(l),
for lib in others {
match lib.kind {
NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name),
NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name),
NativeLibraryKind::NativeStatic => bug!(),
}
}
@ -1017,7 +1052,16 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
cnum: CrateNum) {
let src = sess.cstore.used_crate_source(cnum);
let cratepath = &src.rlib.unwrap().0;
if !sess.lto() && crate_type != config::CrateTypeDylib {
// See the comment above in `link_staticlib` and `link_rlib` for why if
// there's a static library that's not relevant we skip all object
// files.
let native_libs = sess.cstore.native_libraries(cnum);
let skip_native = native_libs.iter().any(|lib| {
lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib)
});
if !sess.lto() && crate_type != config::CrateTypeDylib && !skip_native {
cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath));
return
}
@ -1029,33 +1073,42 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
time(sess.time_passes(), &format!("altering {}.rlib", name), || {
let cfg = archive_config(sess, &dst, Some(cratepath));
let mut archive = ArchiveBuilder::new(cfg);
archive.remove_file(sess.cstore.metadata_filename());
archive.update_symbols();
let mut any_objects = false;
for f in archive.src_files() {
if f.ends_with("bytecode.deflate") {
if f.ends_with("bytecode.deflate") ||
f == sess.cstore.metadata_filename() {
archive.remove_file(&f);
continue
}
let canonical = f.replace("-", "_");
let canonical_name = name.replace("-", "_");
let is_rust_object =
canonical.starts_with(&canonical_name) && {
let num = &f[name.len()..f.len() - 2];
num.len() > 0 && num[1..].parse::<u32>().is_ok()
};
// If we've been requested to skip all native object files
// (those not generated by the rust compiler) then we can skip
// this file. See above for why we may want to do this.
let skip_because_cfg_say_so = skip_native && !is_rust_object;
// If we're performing LTO and this is a rust-generated object
// file, then we don't need the object file as it's part of the
// LTO module. Note that `#![no_builtins]` is excluded from LTO,
// though, so we let that object file slide.
if sess.lto() &&
!sess.cstore.is_no_builtins(cnum) &&
canonical.starts_with(&canonical_name) &&
canonical.ends_with(".o") {
let num = &f[name.len()..f.len() - 2];
if num.len() > 0 && num[1..].parse::<u32>().is_ok() {
archive.remove_file(&f);
continue
}
let skip_because_lto = sess.lto() && is_rust_object &&
!sess.cstore.is_no_builtins(cnum);
if skip_because_cfg_say_so || skip_because_lto {
archive.remove_file(&f);
} else {
any_objects = true;
}
any_objects = true;
}
if !any_objects {
@ -1127,15 +1180,26 @@ fn add_upstream_native_libraries(cmd: &mut Linker, sess: &Session) {
// the paths.
let crates = sess.cstore.used_crates(LinkagePreference::RequireStatic);
for (cnum, _) in crates {
let libs = sess.cstore.native_libraries(cnum);
for &(kind, ref lib) in &libs {
match kind {
NativeLibraryKind::NativeUnknown => cmd.link_dylib(lib),
NativeLibraryKind::NativeFramework => cmd.link_framework(lib),
NativeLibraryKind::NativeStatic => {
bug!("statics shouldn't be propagated");
}
for lib in sess.cstore.native_libraries(cnum) {
if !relevant_lib(sess, &lib) {
continue
}
match lib.kind {
NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name),
NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name),
// ignore statically included native libraries here as we've
// already included them when we included the rust library
// previously
NativeLibraryKind::NativeStatic => {}
}
}
}
}
fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool {
match lib.cfg {
Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None),
None => true,
}
}

View File

@ -147,7 +147,16 @@ impl Emitter for SharedEmitter {
// arise as some of intrinsics are converted into function calls
// and nobody provides implementations those functions
fn target_feature(sess: &Session) -> String {
format!("{},{}", sess.target.target.options.features, sess.opts.cg.target_feature)
let rustc_features = [
"crt-static",
];
let requested_features = sess.opts.cg.target_feature.split(',');
let llvm_features = requested_features.filter(|f| {
!rustc_features.iter().any(|s| f.contains(s))
});
format!("{},{}",
sess.target.target.options.features,
llvm_features.collect::<Vec<_>>().join(","))
}
fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {

View File

@ -311,6 +311,9 @@ declare_features! (
// Allows using `Self` and associated types in struct expressions and patterns.
(active, more_struct_aliases, "1.14.0", Some(37544)),
// Allows #[link(..., cfg(..))]
(active, link_cfg, "1.14.0", Some(37406)),
);
declare_features! (

View File

@ -0,0 +1,14 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-C target-feature=+crt-static
// error-pattern: specifying the `crt-static` target feature is only allowed
fn main() {}

View File

@ -0,0 +1,15 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[link(name = "foo", cfg(foo))]
//~^ ERROR: is feature gated
extern {}
fn main() {}

View File

@ -0,0 +1,22 @@
-include ../tools.mk
all: $(call DYLIB,return1) $(call DYLIB,return2) $(call NATIVE_STATICLIB,return3)
ls $(TMPDIR)
$(RUSTC) --print cfg --target x86_64-unknown-linux-musl | grep crt-static
$(RUSTC) no-deps.rs --cfg foo
$(call RUN,no-deps)
$(RUSTC) no-deps.rs --cfg bar
$(call RUN,no-deps)
$(RUSTC) dep.rs
$(RUSTC) with-deps.rs --cfg foo
$(call RUN,with-deps)
$(RUSTC) with-deps.rs --cfg bar
$(call RUN,with-deps)
$(RUSTC) dep-with-staticlib.rs
$(RUSTC) with-staticlib-deps.rs --cfg foo
$(call RUN,with-staticlib-deps)
$(RUSTC) with-staticlib-deps.rs --cfg bar
$(call RUN,with-staticlib-deps)

View File

@ -0,0 +1,18 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(link_cfg)]
#![crate_type = "rlib"]
#[link(name = "return1", cfg(foo))]
#[link(name = "return3", kind = "static", cfg(bar))]
extern {
pub fn my_function() -> i32;
}

View File

@ -0,0 +1,18 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(link_cfg)]
#![crate_type = "rlib"]
#[link(name = "return1", cfg(foo))]
#[link(name = "return2", cfg(bar))]
extern {
pub fn my_function() -> i32;
}

View File

@ -0,0 +1,30 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(link_cfg)]
#[link(name = "return1", cfg(foo))]
#[link(name = "return2", cfg(bar))]
extern {
fn my_function() -> i32;
}
fn main() {
unsafe {
let v = my_function();
if cfg!(foo) {
assert_eq!(v, 1);
} else if cfg!(bar) {
assert_eq!(v, 2);
} else {
panic!("unknown");
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#ifdef _WIN32
__declspec(dllexport)
#endif
int my_function() {
return 1;
}

View File

@ -0,0 +1,16 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#ifdef _WIN32
__declspec(dllexport)
#endif
int my_function() {
return 2;
}

View File

@ -0,0 +1,16 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#ifdef _WIN32
__declspec(dllexport)
#endif
int my_function() {
return 3;
}

View File

@ -0,0 +1,24 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern crate dep;
fn main() {
unsafe {
let v = dep::my_function();
if cfg!(foo) {
assert_eq!(v, 1);
} else if cfg!(bar) {
assert_eq!(v, 2);
} else {
panic!("unknown");
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern crate dep_with_staticlib;
fn main() {
unsafe {
let v = dep_with_staticlib::my_function();
if cfg!(foo) {
assert_eq!(v, 1);
} else if cfg!(bar) {
assert_eq!(v, 3);
} else {
panic!("unknown");
}
}
}

View File

@ -0,0 +1,14 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(link_cfg)]
#[link(name = "foo", cfg(foo))]
extern {}

View File

@ -0,0 +1,17 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![feature(link_cfg)]
#![crate_type = "rlib"]
#[link(name = "foo", cfg(foo))]
extern {}

View File

@ -0,0 +1,17 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-C target-feature=-crt-static -Z unstable-options
// ignore-musl - requires changing the linker which is hard
#![feature(cfg_target_feature)]
#[cfg(not(target_feature = "crt-static"))]
fn main() {}

View File

@ -0,0 +1,16 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-C target-feature=+crt-static -Z unstable-options
#![feature(cfg_target_feature)]
#[cfg(target_feature = "crt-static")]
fn main() {}

View File

@ -0,0 +1,23 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:link-cfg-works-transitive-rlib.rs
// aux-build:link-cfg-works-transitive-dylib.rs
#![feature(link_cfg)]
extern crate link_cfg_works_transitive_rlib;
extern crate link_cfg_works_transitive_dylib;
#[link(name = "foo", cfg(foo))]
extern {}
fn main() {}