diff --git a/src/liblibc b/src/liblibc index 7d9b71f0971f8..6e8c1b490ccbe 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit 7d9b71f0971f8fa196d864d7071f216a59036d6e +Subproject commit 6e8c1b490ccbe5e84d248bab883515bc85394b5f diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 3583ccdb97bad..f61978271e7f6 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -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>, +} + /// 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; - fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)>; + fn native_libraries(&self, cnum: CrateNum) -> Vec; fn reachable_ids(&self, cnum: CrateNum) -> Vec; 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; - fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)>; + fn used_libraries(&self) -> Vec; fn used_link_args(&self) -> Vec; // utility functions @@ -377,7 +384,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { -> InternedString { bug!("crate_disambiguator") } fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option { bug!("plugin_registrar_fn") } - fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)> + fn native_libraries(&self, cnum: CrateNum) -> Vec { bug!("native_libraries") } fn reachable_ids(&self, cnum: CrateNum) -> Vec { 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 { vec![] } - fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)> { vec![] } + fn used_libraries(&self) -> Vec { + vec![] + } fn used_link_args(&self) -> Vec { vec![] } // utility functions diff --git a/src/librustc_back/target/linux_musl_base.rs b/src/librustc_back/target/linux_musl_base.rs index d55907aeedfbc..18cca425a32c8 100644 --- a/src/librustc_back/target/linux_musl_base.rs +++ b/src/librustc_back/target/linux_musl_base.rs @@ -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 } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index cc04582b19e28..f195ccb3f4292 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -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, + + /// 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); 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() diff --git a/src/librustc_driver/target_features.rs b/src/librustc_driver/target_features.rs index ba51947a33301..d0ed81bcffcbc 100644 --- a/src/librustc_driver/target_features.rs +++ b/src/librustc_driver/target_features.rs @@ -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 mut 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.any(|r| r == "-crt-static"); + let found_positive = requested_features.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"))); + } } diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 75944122f5c10..4298bb47fea62 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -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, - 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(); } diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 8c95e4aec0a04..37853b7473a65 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -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>>, /// Map from NodeId's of local extern crate statements to crate numbers extern_mod_crate_map: RefCell>, - used_libraries: RefCell>, + used_libraries: RefCell>, used_link_args: RefCell>, statically_included_foreign_items: RefCell, pub inlined_item_cache: RefCell>>, @@ -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> { + pub fn get_used_libraries(&self) -> &RefCell> { &self.used_libraries } diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 83de8acdb6056..5419f9955e44e 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -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 { 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 { self.get_used_libraries().borrow().clone() } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index ba85544326f8f..6ffe53345332f 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -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 { self.root.native_libraries.decode(self).collect() } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 778a9d185e145..823c50e1e5fd6 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -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 { 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 { diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index d7a5f7ad71544..5292a4a7c3768 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -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>, pub lang_items: LazySeq<(DefIndex, usize)>, pub lang_items_missing: LazySeq, - pub native_libraries: LazySeq<(NativeLibraryKind, String)>, + pub native_libraries: LazySeq, pub codemap: LazySeq, pub impls: LazySeq, pub reachable_ids: LazySeq, diff --git a/src/librustc_trans/back/archive.rs b/src/librustc_trans/back/archive.rs index e063209799f16..df8dd7750ae0c 100644 --- a/src/librustc_trans/back/archive.rs +++ b/src/librustc_trans/back/archive.rs @@ -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 }) } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index ad8e0c1ee59f6..95d63311ee6e4 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -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::().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::().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, + } +} diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 9012914deeb09..01eea08c50bc5 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -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::>().join(",")) } fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 9116b392f1784..27f720b760998 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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! ( diff --git a/src/test/compile-fail/crt-static-gated.rs b/src/test/compile-fail/crt-static-gated.rs new file mode 100644 index 0000000000000..6c7c60b653a25 --- /dev/null +++ b/src/test/compile-fail/crt-static-gated.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/link-cfg-gated.rs b/src/test/compile-fail/link-cfg-gated.rs new file mode 100644 index 0000000000000..27918a27caf50 --- /dev/null +++ b/src/test/compile-fail/link-cfg-gated.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/run-make/link-cfg/Makefile b/src/test/run-make/link-cfg/Makefile new file mode 100644 index 0000000000000..4abc0caa69864 --- /dev/null +++ b/src/test/run-make/link-cfg/Makefile @@ -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) diff --git a/src/test/run-make/link-cfg/dep-with-staticlib.rs b/src/test/run-make/link-cfg/dep-with-staticlib.rs new file mode 100644 index 0000000000000..ecc2365ddb06d --- /dev/null +++ b/src/test/run-make/link-cfg/dep-with-staticlib.rs @@ -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 or the MIT license +// , 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; +} diff --git a/src/test/run-make/link-cfg/dep.rs b/src/test/run-make/link-cfg/dep.rs new file mode 100644 index 0000000000000..7da879c2bfa2b --- /dev/null +++ b/src/test/run-make/link-cfg/dep.rs @@ -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 or the MIT license +// , 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; +} diff --git a/src/test/run-make/link-cfg/no-deps.rs b/src/test/run-make/link-cfg/no-deps.rs new file mode 100644 index 0000000000000..6b1141067440b --- /dev/null +++ b/src/test/run-make/link-cfg/no-deps.rs @@ -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 or the MIT license +// , 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"); + } + } +} diff --git a/src/test/run-make/link-cfg/return1.c b/src/test/run-make/link-cfg/return1.c new file mode 100644 index 0000000000000..a2a3d051dd14f --- /dev/null +++ b/src/test/run-make/link-cfg/return1.c @@ -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 or the MIT license +// , 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; +} diff --git a/src/test/run-make/link-cfg/return2.c b/src/test/run-make/link-cfg/return2.c new file mode 100644 index 0000000000000..d6ddcccf2fb7b --- /dev/null +++ b/src/test/run-make/link-cfg/return2.c @@ -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 or the MIT license +// , 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; +} diff --git a/src/test/run-make/link-cfg/return3.c b/src/test/run-make/link-cfg/return3.c new file mode 100644 index 0000000000000..6a3b695f20811 --- /dev/null +++ b/src/test/run-make/link-cfg/return3.c @@ -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 or the MIT license +// , 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; +} diff --git a/src/test/run-make/link-cfg/with-deps.rs b/src/test/run-make/link-cfg/with-deps.rs new file mode 100644 index 0000000000000..799555c500a16 --- /dev/null +++ b/src/test/run-make/link-cfg/with-deps.rs @@ -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 or the MIT license +// , 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"); + } + } +} diff --git a/src/test/run-make/link-cfg/with-staticlib-deps.rs b/src/test/run-make/link-cfg/with-staticlib-deps.rs new file mode 100644 index 0000000000000..33a9c7720e268 --- /dev/null +++ b/src/test/run-make/link-cfg/with-staticlib-deps.rs @@ -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 or the MIT license +// , 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"); + } + } +} diff --git a/src/test/run-pass/auxiliary/link-cfg-works-transitive-dylib.rs b/src/test/run-pass/auxiliary/link-cfg-works-transitive-dylib.rs new file mode 100644 index 0000000000000..d41fd490f58e2 --- /dev/null +++ b/src/test/run-pass/auxiliary/link-cfg-works-transitive-dylib.rs @@ -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 or the MIT license +// , 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 {} diff --git a/src/test/run-pass/auxiliary/link-cfg-works-transitive-rlib.rs b/src/test/run-pass/auxiliary/link-cfg-works-transitive-rlib.rs new file mode 100644 index 0000000000000..9f096c351fbe3 --- /dev/null +++ b/src/test/run-pass/auxiliary/link-cfg-works-transitive-rlib.rs @@ -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 or the MIT license +// , 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 {} diff --git a/src/test/run-pass/crt-static-off-works.rs b/src/test/run-pass/crt-static-off-works.rs new file mode 100644 index 0000000000000..c94c877c12c6d --- /dev/null +++ b/src/test/run-pass/crt-static-off-works.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/run-pass/crt-static-on-works.rs b/src/test/run-pass/crt-static-on-works.rs new file mode 100644 index 0000000000000..ae8e5f6297048 --- /dev/null +++ b/src/test/run-pass/crt-static-on-works.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/run-pass/link-cfg-works.rs b/src/test/run-pass/link-cfg-works.rs new file mode 100644 index 0000000000000..7db948c7daa9f --- /dev/null +++ b/src/test/run-pass/link-cfg-works.rs @@ -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 or the MIT license +// , 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() {} +