diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index 4c630b56cb4ce..d4b5e833dfb23 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -7,7 +7,7 @@ use rustc::middle::cstore; use rustc::session::config::{build_configuration, build_session_options, to_crate_config}; use rustc::session::config::{LtoCli, LinkerPluginLto, SwitchWithOptPath, ExternEntry}; use rustc::session::config::{Externs, OutputType, OutputTypes, SymbolManglingVersion}; -use rustc::session::config::{rustc_optgroups, Options, ErrorOutputType, Passes}; +use rustc::session::config::{rustc_optgroups, Options, ErrorOutputType, Passes, ExternLocation}; use rustc::session::{build_session, Session}; use rustc::session::search_paths::SearchPath; use std::collections::{BTreeMap, BTreeSet}; @@ -38,14 +38,15 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { fn new_public_extern_entry(locations: I) -> ExternEntry where S: Into, - I: IntoIterator>, + I: IntoIterator, { - let locations: BTreeSet<_> = locations.into_iter().map(|o| o.map(|s| s.into())) + let locations: BTreeSet<_> = locations.into_iter().map(|s| s.into()) .collect(); ExternEntry { - locations, - is_private_dep: false + location: ExternLocation::ExactPaths(locations), + is_private_dep: false, + add_prelude: true, } } @@ -160,33 +161,33 @@ fn test_externs_tracking_hash_different_construction_order() { v1.externs = Externs::new(mk_map(vec![ ( String::from("a"), - new_public_extern_entry(vec![Some("b"), Some("c")]) + new_public_extern_entry(vec!["b", "c"]) ), ( String::from("d"), - new_public_extern_entry(vec![Some("e"), Some("f")]) + new_public_extern_entry(vec!["e", "f"]) ), ])); v2.externs = Externs::new(mk_map(vec![ ( String::from("d"), - new_public_extern_entry(vec![Some("e"), Some("f")]) + new_public_extern_entry(vec!["e", "f"]) ), ( String::from("a"), - new_public_extern_entry(vec![Some("b"), Some("c")]) + new_public_extern_entry(vec!["b", "c"]) ), ])); v3.externs = Externs::new(mk_map(vec![ ( String::from("a"), - new_public_extern_entry(vec![Some("b"), Some("c")]) + new_public_extern_entry(vec!["b", "c"]) ), ( String::from("d"), - new_public_extern_entry(vec![Some("f"), Some("e")]) + new_public_extern_entry(vec!["f", "e"]) ), ])); diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index dbf2dcf1c0aea..71871373e35e9 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -218,13 +218,14 @@ impl<'a> CrateLoader<'a> { let source = self.cstore.get_crate_data(cnum).source(); if let Some(entry) = self.sess.opts.externs.get(&name.as_str()) { // Only use `--extern crate_name=path` here, not `--extern crate_name`. - let found = entry.locations.iter().filter_map(|l| l.as_ref()).any(|l| { - let l = fs::canonicalize(l).ok(); - source.dylib.as_ref().map(|p| &p.0) == l.as_ref() || - source.rlib.as_ref().map(|p| &p.0) == l.as_ref() - }); - if found { - ret = Some(cnum); + if let Some(mut files) = entry.files() { + if files.any(|l| { + let l = fs::canonicalize(l).ok(); + source.dylib.as_ref().map(|p| &p.0) == l.as_ref() || + source.rlib.as_ref().map(|p| &p.0) == l.as_ref() + }) { + ret = Some(cnum); + } } return } diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index c6fb80eca055a..8a1eeea02512e 100644 --- a/src/librustc_metadata/locator.rs +++ b/src/librustc_metadata/locator.rs @@ -328,8 +328,9 @@ impl<'a> CrateLocator<'a> { crate_name, exact_paths: if hash.is_none() { sess.opts.externs.get(&crate_name.as_str()).into_iter() - .flat_map(|entry| entry.locations.iter()) - .filter_map(|location| location.clone().map(PathBuf::from)).collect() + .filter_map(|entry| entry.files()) + .flatten() + .map(|location| PathBuf::from(location)).collect() } else { // SVH being specified means this is a transitive dependency, // so `--extern` options do not apply. diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index be36e02f5b5b1..2aa79d7b0da50 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1126,8 +1126,10 @@ impl<'a> Resolver<'a> { definitions.create_root_def(crate_name, session.local_crate_disambiguator()); let mut extern_prelude: FxHashMap> = - session.opts.externs.iter().map(|kv| (Ident::from_str(kv.0), Default::default())) - .collect(); + session.opts.externs.iter() + .filter(|(_, entry)| entry.add_prelude) + .map(|(name, _)| (Ident::from_str(name), Default::default())) + .collect(); if !attr::contains_name(&krate.attrs, sym::no_core) { extern_prelude.insert(Ident::with_dummy_span(sym::core), Default::default()); diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs index 58113bb8cd6cb..7f3bab8f23299 100644 --- a/src/librustc_session/config.rs +++ b/src/librustc_session/config.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + //! Contains infrastructure for configuring the compiler, including parsing //! command-line options. @@ -31,7 +33,7 @@ use std::fmt; use std::str::{self, FromStr}; use std::hash::Hasher; use std::collections::hash_map::DefaultHasher; -use std::iter::FromIterator; +use std::iter::{self, FromIterator}; use std::path::{Path, PathBuf}; pub struct Config { @@ -322,10 +324,35 @@ impl OutputTypes { #[derive(Clone)] pub struct Externs(BTreeMap); -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct ExternEntry { - pub locations: BTreeSet>, - pub is_private_dep: bool + pub location: ExternLocation, + /// Indicates this is a "private" dependency for the + /// `exported_private_dependencies` lint. + /// + /// This can be set with the `priv` option like + /// `--extern priv:name=foo.rlib`. + pub is_private_dep: bool, + /// Add the extern entry to the extern prelude. + /// + /// This can be disabled with the `noprelude` option like + /// `--extern noprelude:name`. + pub add_prelude: bool, +} + +#[derive(Clone, Debug)] +pub enum ExternLocation { + /// Indicates to look for the library in the search paths. + /// + /// Added via `--extern name`. + FoundInLibrarySearchDirectories, + /// The locations where this extern entry must be found. + /// + /// The `CrateLoader` is responsible for loading these and figuring out + /// which one to use. + /// + /// Added via `--extern prelude_name=some_file.rlib` + ExactPaths(BTreeSet), } impl Externs { @@ -342,6 +369,18 @@ impl Externs { } } +impl ExternEntry { + fn new(location: ExternLocation) -> ExternEntry { + ExternEntry { location, is_private_dep: false, add_prelude: false } + } + + pub fn files(&self) -> Option> { + match &self.location { + ExternLocation::ExactPaths(set) => Some(set.iter()), + _ => None, + } + } +} macro_rules! hash_option { ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [UNTRACKED]) => ({}); @@ -1869,12 +1908,6 @@ pub fn rustc_optgroups() -> Vec { "Specify where an external rust library is located", "NAME[=PATH]", ), - opt::multi_s( - "", - "extern-private", - "Specify where an extern rust library is located, marking it as a private dependency", - "NAME=PATH", - ), opt::opt_s("", "sysroot", "Override the system root", "PATH"), opt::multi("Z", "", "Set internal debugging options", "FLAG"), opt::opt_s( @@ -2435,43 +2468,105 @@ fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) } } -fn parse_externs( +pub fn parse_externs( matches: &getopts::Matches, debugging_opts: &DebuggingOptions, error_format: ErrorOutputType, ) -> Externs { - if matches.opt_present("extern-private") && !debugging_opts.unstable_options { - early_error( - ErrorOutputType::default(), - "'--extern-private' is unstable and only \ - available for nightly builds of rustc." - ) - } - - // We start out with a `Vec<(Option, bool)>>`, - // and later convert it into a `BTreeSet<(Option, bool)>` - // This allows to modify entries in-place to set their correct - // 'public' value. + let is_unstable_enabled = debugging_opts.unstable_options; let mut externs: BTreeMap = BTreeMap::new(); - for (arg, private) in matches.opt_strs("extern").into_iter().map(|v| (v, false)) - .chain(matches.opt_strs("extern-private").into_iter().map(|v| (v, true))) { - + for arg in matches.opt_strs("extern") { let mut parts = arg.splitn(2, '='); - let name = parts.next().unwrap_or_else(|| - early_error(error_format, "--extern value must not be empty")); - let location = parts.next().map(|s| s.to_string()); + let name = parts + .next() + .unwrap_or_else(|| early_error(error_format, "--extern value must not be empty")); + let path = parts.next().map(|s| s.to_string()); + + let mut name_parts = name.splitn(2, ':'); + let first_part = name_parts.next(); + let second_part = name_parts.next(); + let (options, name) = match (first_part, second_part) { + (Some(opts), Some(name)) => (Some(opts), name), + (Some(name), None) => (None, name), + (None, None) => early_error(error_format, "--extern name must not be empty"), + _ => unreachable!(), + }; + + let entry = externs.entry(name.to_owned()); - let entry = externs - .entry(name.to_owned()) - .or_default(); + use std::collections::btree_map::Entry; + let entry = if let Some(path) = path { + // --extern prelude_name=some_file.rlib + match entry { + Entry::Vacant(vacant) => { + let files = BTreeSet::from_iter(iter::once(path)); + vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files))) + } + Entry::Occupied(occupied) => { + let ext_ent = occupied.into_mut(); + match ext_ent { + ExternEntry { location: ExternLocation::ExactPaths(files), .. } => { + files.insert(path); + } + ExternEntry { + location: location @ ExternLocation::FoundInLibrarySearchDirectories, + .. + } => { + // Exact paths take precedence over search directories. + let files = BTreeSet::from_iter(iter::once(path)); + *location = ExternLocation::ExactPaths(files); + } + } + ext_ent + } + } + } else { + // --extern prelude_name + match entry { + Entry::Vacant(vacant) => { + vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories)) + } + Entry::Occupied(occupied) => { + // Ignore if already specified. + occupied.into_mut() + } + } + }; - entry.locations.insert(location.clone()); + let mut is_private_dep = false; + let mut add_prelude = true; + if let Some(opts) = options { + if !is_unstable_enabled { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable `--extern options", + ); + } + for opt in opts.split(',') { + match opt { + "priv" => is_private_dep = true, + "noprelude" => { + if let ExternLocation::ExactPaths(_) = &entry.location { + add_prelude = false; + } else { + early_error( + error_format, + "the `noprelude` --extern option requires a file path", + ); + } + } + _ => early_error(error_format, &format!("unknown --extern option `{}`", opt)), + } + } + } - // Crates start out being not private, - // and go to being private if we see an '--extern-private' - // flag - entry.is_private_dep |= private; + // Crates start out being not private, and go to being private `priv` + // is specified. + entry.is_private_dep |= is_private_dep; + // If any flag is missing `noprelude`, then add to the prelude. + entry.add_prelude |= add_prelude; } Externs(externs) } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index cdb1a1f6997c9..0db3d28bf0e37 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -7,10 +7,10 @@ use errors; use getopts; use rustc::lint::Level; use rustc::session; -use rustc::session::config::{CrateType, parse_crate_types_from_list}; +use rustc::session::config::{CrateType, parse_crate_types_from_list, parse_externs}; use rustc::session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs}; use rustc::session::config::{nightly_options, build_codegen_options, build_debugging_options, - get_cmd_lint_options, host_triple, ExternEntry}; + get_cmd_lint_options, host_triple}; use rustc::session::search_paths::SearchPath; use rustc_driver; use rustc_target::spec::TargetTriple; @@ -320,13 +320,7 @@ impl Options { let libs = matches.opt_strs("L").iter() .map(|s| SearchPath::from_cli_opt(s, error_format)) .collect(); - let externs = match parse_externs(&matches) { - Ok(ex) => ex, - Err(err) => { - diag.struct_err(&err).emit(); - return Err(1); - } - }; + let externs = parse_externs(&matches, &debugging_options, error_format); let extern_html_root_urls = match parse_extern_html_roots(&matches) { Ok(ex) => ex, Err(err) => { @@ -617,24 +611,3 @@ fn parse_extern_html_roots( Ok(externs) } - -/// Extracts `--extern CRATE=PATH` arguments from `matches` and -/// returns a map mapping crate names to their paths or else an -/// error message. -/// Also handles `--extern-private` which for the purposes of rustdoc -/// we can treat as `--extern` -// FIXME(eddyb) This shouldn't be duplicated with `rustc::session`. -fn parse_externs(matches: &getopts::Matches) -> Result { - let mut externs: BTreeMap<_, ExternEntry> = BTreeMap::new(); - for arg in matches.opt_strs("extern").iter().chain(matches.opt_strs("extern-private").iter()) { - let mut parts = arg.splitn(2, '='); - let name = parts.next().ok_or("--extern value must not be empty".to_string())?; - let location = parts.next().map(|s| s.to_string()); - let name = name.to_string(); - // For Rustdoc purposes, we can treat all externs as public - externs.entry(name) - .or_default() - .locations.insert(location.clone()); - } - Ok(Externs::new(externs)) -} diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b77b1c720cfdf..a524801bea6bf 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -248,7 +248,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt .. } = options; - let extern_names: Vec = externs.iter().map(|(s,_)| s).cloned().collect(); + let extern_names: Vec = externs.iter() + .filter(|(_, entry)| entry.add_prelude) + .map(|(name, _)| name).cloned().collect(); // Add the doc cfg into the doc build. cfgs.push("doc".to_string()); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index be3644ecf96a7..a4be3dee938ed 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -145,10 +145,6 @@ fn opts() -> Vec { stable("extern", |o| { o.optmulti("", "extern", "pass an --extern to rustc", "NAME[=PATH]") }), - unstable("extern-private", |o| { - o.optmulti("", "extern-private", - "pass an --extern to rustc (compatibility only)", "NAME=PATH") - }), unstable("extern-html-root-url", |o| { o.optmulti("", "extern-html-root-url", "base URL to use for dependencies", "NAME=URL") diff --git a/src/test/rustdoc/issue-66159.rs b/src/test/rustdoc/issue-66159.rs index a69ba61a283d0..003d079a470c0 100644 --- a/src/test/rustdoc/issue-66159.rs +++ b/src/test/rustdoc/issue-66159.rs @@ -1,6 +1,5 @@ -// aux-build:issue-66159-1.rs +// aux-crate:priv:issue_66159_1=issue-66159-1.rs // compile-flags:-Z unstable-options -// extern-private:issue_66159_1 // The issue was an ICE which meant that we never actually generated the docs // so if we have generated the docs, we're okay. diff --git a/src/test/ui/extern-flag/auxiliary/somedep.rs b/src/test/ui/extern-flag/auxiliary/somedep.rs new file mode 100644 index 0000000000000..dd2f373f849c6 --- /dev/null +++ b/src/test/ui/extern-flag/auxiliary/somedep.rs @@ -0,0 +1,3 @@ +pub fn somefun() {} + +pub struct S; diff --git a/src/test/ui/extern-flag/multiple-opts.rs b/src/test/ui/extern-flag/multiple-opts.rs new file mode 100644 index 0000000000000..3dc2f1d73f8e4 --- /dev/null +++ b/src/test/ui/extern-flag/multiple-opts.rs @@ -0,0 +1,20 @@ +// aux-crate:priv,noprelude:somedep=somedep.rs +// compile-flags: -Zunstable-options +// edition:2018 + +// Test for multiple options to --extern. Can't test for errors from both +// options at the same time, so this only checks that noprelude is honored. + +#![warn(exported_private_dependencies)] + +// Module to avoid adding to prelude. +pub mod m { + extern crate somedep; + pub struct PublicType { + pub field: somedep::S, + } +} + +fn main() { + somedep::somefun(); //~ ERROR failed to resolve +} diff --git a/src/test/ui/extern-flag/multiple-opts.stderr b/src/test/ui/extern-flag/multiple-opts.stderr new file mode 100644 index 0000000000000..3bf73d11cfd22 --- /dev/null +++ b/src/test/ui/extern-flag/multiple-opts.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: use of undeclared type or module `somedep` + --> $DIR/multiple-opts.rs:19:5 + | +LL | somedep::somefun(); + | ^^^^^^^ use of undeclared type or module `somedep` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/extern-flag/noprelude-and-prelude.rs b/src/test/ui/extern-flag/noprelude-and-prelude.rs new file mode 100644 index 0000000000000..e6a150b9e8b9e --- /dev/null +++ b/src/test/ui/extern-flag/noprelude-and-prelude.rs @@ -0,0 +1,10 @@ +// check-pass +// aux-crate:noprelude:somedep=somedep.rs +// compile-flags: -Zunstable-options --extern somedep +// edition:2018 + +// Having a flag with `noprelude` and one without, will add to the prelude. + +fn main() { + somedep::somefun(); +} diff --git a/src/test/ui/extern-flag/noprelude-resolves.rs b/src/test/ui/extern-flag/noprelude-resolves.rs new file mode 100644 index 0000000000000..f69f552b69d8a --- /dev/null +++ b/src/test/ui/extern-flag/noprelude-resolves.rs @@ -0,0 +1,11 @@ +// check-pass +// aux-crate:noprelude:somedep=somedep.rs +// compile-flags: -Zunstable-options +// edition:2018 + +// `extern crate` can be used to add to prelude. +extern crate somedep; + +fn main() { + somedep::somefun(); +} diff --git a/src/test/ui/extern-flag/noprelude.rs b/src/test/ui/extern-flag/noprelude.rs new file mode 100644 index 0000000000000..cdbf34091007e --- /dev/null +++ b/src/test/ui/extern-flag/noprelude.rs @@ -0,0 +1,7 @@ +// aux-crate:noprelude:somedep=somedep.rs +// compile-flags: -Zunstable-options +// edition:2018 + +fn main() { + somedep::somefun(); //~ ERROR failed to resolve +} diff --git a/src/test/ui/extern-flag/noprelude.stderr b/src/test/ui/extern-flag/noprelude.stderr new file mode 100644 index 0000000000000..beb9200dddabc --- /dev/null +++ b/src/test/ui/extern-flag/noprelude.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: use of undeclared type or module `somedep` + --> $DIR/noprelude.rs:6:5 + | +LL | somedep::somefun(); + | ^^^^^^^ use of undeclared type or module `somedep` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/extern-flag/public-and-private.rs b/src/test/ui/extern-flag/public-and-private.rs new file mode 100644 index 0000000000000..a3a81cbf37223 --- /dev/null +++ b/src/test/ui/extern-flag/public-and-private.rs @@ -0,0 +1,13 @@ +// aux-crate:priv:somedep=somedep.rs +// compile-flags: -Zunstable-options --extern somedep +// edition:2018 + +#![deny(exported_private_dependencies)] + +// Having a flag with `priv` and one without, will remain private (it is sticky). + +pub struct PublicType { + pub field: somedep::S, //~ ERROR from private dependency +} + +fn main() {} diff --git a/src/test/ui/extern-flag/public-and-private.stderr b/src/test/ui/extern-flag/public-and-private.stderr new file mode 100644 index 0000000000000..72f1bb2d26f1a --- /dev/null +++ b/src/test/ui/extern-flag/public-and-private.stderr @@ -0,0 +1,14 @@ +error: type `somedep::S` from private dependency 'somedep' in public interface + --> $DIR/public-and-private.rs:10:5 + | +LL | pub field: somedep::S, + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/public-and-private.rs:5:9 + | +LL | #![deny(exported_private_dependencies)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/privacy/pub-priv-dep/pub-priv1.rs b/src/test/ui/privacy/pub-priv-dep/pub-priv1.rs index 784615354a95c..feab72b3efa42 100644 --- a/src/test/ui/privacy/pub-priv-dep/pub-priv1.rs +++ b/src/test/ui/privacy/pub-priv-dep/pub-priv1.rs @@ -1,11 +1,10 @@ - // aux-build:priv_dep.rs + // aux-crate:priv:priv_dep=priv_dep.rs // aux-build:pub_dep.rs - // extern-private:priv_dep #![deny(exported_private_dependencies)] // This crate is a private dependency extern crate priv_dep; -// This crate is a public dependenct +// This crate is a public dependency extern crate pub_dep; use priv_dep::{OtherType, OtherTrait}; diff --git a/src/test/ui/privacy/pub-priv-dep/pub-priv1.stderr b/src/test/ui/privacy/pub-priv-dep/pub-priv1.stderr index b31efdbd781dc..f21b11f5b32f8 100644 --- a/src/test/ui/privacy/pub-priv-dep/pub-priv1.stderr +++ b/src/test/ui/privacy/pub-priv-dep/pub-priv1.stderr @@ -1,23 +1,23 @@ error: type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:21:5 + --> $DIR/pub-priv1.rs:20:5 | LL | pub field: OtherType, | ^^^^^^^^^^^^^^^^^^^^ | note: lint level defined here - --> $DIR/pub-priv1.rs:4:9 + --> $DIR/pub-priv1.rs:3:9 | LL | #![deny(exported_private_dependencies)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:28:5 + --> $DIR/pub-priv1.rs:27:5 | LL | pub fn pub_fn(param: OtherType) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: trait `priv_dep::OtherTrait` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:34:1 + --> $DIR/pub-priv1.rs:33:1 | LL | / pub trait MyPubTrait { LL | | type Foo: OtherTrait; diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index dc4811e5d24ce..46cce6394e617 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -71,6 +71,7 @@ pub struct EarlyProps { pub ignore: Ignore, pub should_fail: bool, pub aux: Vec, + pub aux_crate: Vec<(String, String)>, pub revisions: Vec, } @@ -80,6 +81,7 @@ impl EarlyProps { ignore: Ignore::Run, should_fail: false, aux: Vec::new(), + aux_crate: Vec::new(), revisions: vec![], }; @@ -157,6 +159,10 @@ impl EarlyProps { props.aux.push(s); } + if let Some(ac) = config.parse_aux_crate(ln) { + props.aux_crate.push(ac); + } + if let Some(r) = config.parse_revisions(ln) { props.revisions.extend(r); } @@ -311,10 +317,9 @@ pub struct TestProps { // directory as the test, but for backwards compatibility reasons // we also check the auxiliary directory) pub aux_builds: Vec, - // A list of crates to pass '--extern-private name:PATH' flags for - // This should be a subset of 'aux_build' - // FIXME: Replace this with a better solution: https://github.com/rust-lang/rust/pull/54020 - pub extern_private: Vec, + // Similar to `aux_builds`, but a list of NAME=somelib.rs of dependencies + // to build and pass with the `--extern` flag. + pub aux_crates: Vec<(String, String)>, // Environment settings to use for compiling pub rustc_env: Vec<(String, String)>, // Environment variables to unset prior to compiling. @@ -387,7 +392,7 @@ impl TestProps { run_flags: None, pp_exact: None, aux_builds: vec![], - extern_private: vec![], + aux_crates: vec![], revisions: vec![], rustc_env: vec![], unset_rustc_env: vec![], @@ -514,8 +519,8 @@ impl TestProps { self.aux_builds.push(ab); } - if let Some(ep) = config.parse_extern_private(ln) { - self.extern_private.push(ep); + if let Some(ac) = config.parse_aux_crate(ln) { + self.aux_crates.push(ac); } if let Some(ee) = config.parse_env(ln, "exec-env") { @@ -713,8 +718,14 @@ impl Config { .map(|r| r.trim().to_string()) } - fn parse_extern_private(&self, line: &str) -> Option { - self.parse_name_value_directive(line, "extern-private") + fn parse_aux_crate(&self, line: &str) -> Option<(String, String)> { + self.parse_name_value_directive(line, "aux-crate").map(|r| { + let mut parts = r.trim().splitn(2, '='); + ( + parts.next().expect("aux-crate name").to_string(), + parts.next().expect("aux-crate value").to_string(), + ) + }) } fn parse_compile_flags(&self, line: &str) -> Option { diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 15ae67fb12c51..480868440b8dc 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1776,93 +1776,16 @@ impl<'test> TestCx<'test> { create_dir_all(&aux_dir).unwrap(); } - // Use a Vec instead of a HashMap to preserve original order - let mut extern_priv = self.props.extern_private.clone(); - - let mut add_extern_priv = |priv_dep: &str, dylib: bool| { - let lib_name = get_lib_name(priv_dep, dylib); - rustc - .arg("--extern-private") - .arg(format!("{}={}", priv_dep, aux_dir.join(lib_name).to_str().unwrap())); - }; - for rel_ab in &self.props.aux_builds { - let aux_testpaths = self.compute_aux_test_paths(rel_ab); - let aux_props = - self.props - .from_aux_file(&aux_testpaths.file, self.revision, self.config); - let aux_output = TargetLocation::ThisDirectory(self.aux_output_dir_name()); - let aux_cx = TestCx { - config: self.config, - props: &aux_props, - testpaths: &aux_testpaths, - revision: self.revision, - }; - // Create the directory for the stdout/stderr files. - create_dir_all(aux_cx.output_base_dir()).unwrap(); - let mut aux_rustc = aux_cx.make_compile_args(&aux_testpaths.file, aux_output); - - let (dylib, crate_type) = if aux_props.no_prefer_dynamic { - (true, None) - } else if self.config.target.contains("cloudabi") - || self.config.target.contains("emscripten") - || (self.config.target.contains("musl") - && !aux_props.force_host - && !self.config.host.contains("musl")) - || self.config.target.contains("wasm32") - || self.config.target.contains("nvptx") - || self.is_vxworks_pure_static() - { - // We primarily compile all auxiliary libraries as dynamic libraries - // to avoid code size bloat and large binaries as much as possible - // for the test suite (otherwise including libstd statically in all - // executables takes up quite a bit of space). - // - // For targets like MUSL or Emscripten, however, there is no support for - // dynamic libraries so we just go back to building a normal library. Note, - // however, that for MUSL if the library is built with `force_host` then - // it's ok to be a dylib as the host should always support dylibs. - (false, Some("lib")) - } else { - (true, Some("dylib")) - }; - - let trimmed = rel_ab.trim_end_matches(".rs").to_string(); - - // Normally, every 'extern-private' has a correspodning 'aux-build' - // entry. If so, we remove it from our list of private crates, - // and add an '--extern-private' flag to rustc - if extern_priv.remove_item(&trimmed).is_some() { - add_extern_priv(&trimmed, dylib); - } - - if let Some(crate_type) = crate_type { - aux_rustc.args(&["--crate-type", crate_type]); - } - - aux_rustc.arg("-L").arg(&aux_dir); - - let auxres = aux_cx.compose_and_run( - aux_rustc, - aux_cx.config.compile_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), - None, - ); - if !auxres.status.success() { - self.fatal_proc_rec( - &format!( - "auxiliary build of {:?} failed to compile: ", - aux_testpaths.file.display() - ), - &auxres, - ); - } + self.build_auxiliary(rel_ab, &aux_dir); } - // Add any '--extern-private' entries without a matching - // 'aux-build' - for private_lib in extern_priv { - add_extern_priv(&private_lib, true); + for (aux_name, aux_path) in &self.props.aux_crates { + let is_dylib = self.build_auxiliary(&aux_path, &aux_dir); + let lib_name = get_lib_name(&aux_path.trim_end_matches(".rs").replace('-', "_"), + is_dylib); + rustc.arg("--extern") + .arg(format!("{}={}/{}", aux_name, aux_dir.display(), lib_name)); } self.props.unset_rustc_env.clone() @@ -1877,6 +1800,74 @@ impl<'test> TestCx<'test> { ) } + /// Builds an aux dependency. + /// + /// Returns whether or not it is a dylib. + fn build_auxiliary(&self, source_path: &str, aux_dir: &Path) -> bool { + let aux_testpaths = self.compute_aux_test_paths(source_path); + let aux_props = + self.props + .from_aux_file(&aux_testpaths.file, self.revision, self.config); + let aux_output = TargetLocation::ThisDirectory(self.aux_output_dir_name()); + let aux_cx = TestCx { + config: self.config, + props: &aux_props, + testpaths: &aux_testpaths, + revision: self.revision, + }; + // Create the directory for the stdout/stderr files. + create_dir_all(aux_cx.output_base_dir()).unwrap(); + let mut aux_rustc = aux_cx.make_compile_args(&aux_testpaths.file, aux_output); + + let (dylib, crate_type) = if aux_props.no_prefer_dynamic { + (true, None) + } else if self.config.target.contains("cloudabi") + || self.config.target.contains("emscripten") + || (self.config.target.contains("musl") + && !aux_props.force_host + && !self.config.host.contains("musl")) + || self.config.target.contains("wasm32") + || self.config.target.contains("nvptx") + || self.is_vxworks_pure_static() + { + // We primarily compile all auxiliary libraries as dynamic libraries + // to avoid code size bloat and large binaries as much as possible + // for the test suite (otherwise including libstd statically in all + // executables takes up quite a bit of space). + // + // For targets like MUSL or Emscripten, however, there is no support for + // dynamic libraries so we just go back to building a normal library. Note, + // however, that for MUSL if the library is built with `force_host` then + // it's ok to be a dylib as the host should always support dylibs. + (false, Some("lib")) + } else { + (true, Some("dylib")) + }; + + if let Some(crate_type) = crate_type { + aux_rustc.args(&["--crate-type", crate_type]); + } + + aux_rustc.arg("-L").arg(&aux_dir); + + let auxres = aux_cx.compose_and_run( + aux_rustc, + aux_cx.config.compile_lib_path.to_str().unwrap(), + Some(aux_dir.to_str().unwrap()), + None, + ); + if !auxres.status.success() { + self.fatal_proc_rec( + &format!( + "auxiliary build of {:?} failed to compile: ", + aux_testpaths.file.display() + ), + &auxres, + ); + } + dylib + } + fn compose_and_run( &self, mut command: Command,