Rollup merge of #67074 - ehuss:extern-options, r=petrochenkov

Add options to --extern flag.

This changes the `--extern` flag so that it can take a series of options that changes its behavior. The general syntax is `[opts ':'] name ['=' path]` where `opts` is a comma separated list of options. Two options are supported, `priv` which replaces `--extern-private` and `noprelude` which avoids adding the crate to the extern prelude.

```text
--extern priv:mylib=/path/to/libmylib.rlib
--extern noprelude:alloc=/path/to/liballoc.rlib
```

`noprelude` is to be used by Cargo's build-std feature in order to use `--extern` to reference standard library crates.

This also includes a second commit which adds the `aux-crate` directive to compiletest. I can split this off into a separate PR if desired, but it helps with defining these kinds of tests. It is based on #54020, and can be used in the future to replace and simplify some of the Makefile tests.
This commit is contained in:
Mazdak Farrokhzad 2019-12-11 10:10:44 +01:00 committed by GitHub
commit 4c3e95e84b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 363 additions and 196 deletions

View File

@ -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<S, I>(locations: I) -> ExternEntry
where
S: Into<String>,
I: IntoIterator<Item = Option<S>>,
I: IntoIterator<Item = S>,
{
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"])
),
]));

View File

@ -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
}

View File

@ -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.

View File

@ -1136,8 +1136,10 @@ impl<'a> Resolver<'a> {
definitions.create_root_def(crate_name, session.local_crate_disambiguator());
let mut extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'_>> =
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());

View File

@ -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<String, ExternEntry>);
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct ExternEntry {
pub locations: BTreeSet<Option<String>>,
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<String>),
}
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<impl Iterator<Item = &String>> {
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<RustcOptGroup> {
"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<String>, bool)>>`,
// and later convert it into a `BTreeSet<(Option<String>, 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<String, ExternEntry> = 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 entry = externs
.entry(name.to_owned())
.or_default();
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());
entry.locations.insert(location.clone());
use std::collections::btree_map::Entry;
// Crates start out being not private,
// and go to being private if we see an '--extern-private'
// flag
entry.is_private_dep |= private;
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()
}
}
};
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 `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)
}

View File

@ -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<Externs, String> {
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))
}

View File

@ -248,7 +248,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
..
} = options;
let extern_names: Vec<String> = externs.iter().map(|(s,_)| s).cloned().collect();
let extern_names: Vec<String> = 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());

View File

@ -145,10 +145,6 @@ fn opts() -> Vec<RustcOptGroup> {
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")

View File

@ -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.

View File

@ -0,0 +1,3 @@
pub fn somefun() {}
pub struct S;

View File

@ -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
}

View File

@ -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`.

View File

@ -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();
}

View File

@ -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();
}

View File

@ -0,0 +1,7 @@
// aux-crate:noprelude:somedep=somedep.rs
// compile-flags: -Zunstable-options
// edition:2018
fn main() {
somedep::somefun(); //~ ERROR failed to resolve
}

View File

@ -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`.

View File

@ -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() {}

View File

@ -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

View File

@ -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};

View File

@ -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;

View File

@ -71,6 +71,7 @@ pub struct EarlyProps {
pub ignore: Ignore,
pub should_fail: bool,
pub aux: Vec<String>,
pub aux_crate: Vec<(String, String)>,
pub revisions: Vec<String>,
}
@ -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<String>,
// 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<String>,
// 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<String> {
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<String> {

View File

@ -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,