Skip to content

Commit ce0c20a

Browse files
committed
Auto 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.
2 parents 7de9402 + 60d4e20 commit ce0c20a

File tree

22 files changed

+363
-196
lines changed

22 files changed

+363
-196
lines changed

src/librustc_interface/tests.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc::middle::cstore;
77
use rustc::session::config::{build_configuration, build_session_options, to_crate_config};
88
use rustc::session::config::{LtoCli, LinkerPluginLto, SwitchWithOptPath, ExternEntry};
99
use rustc::session::config::{Externs, OutputType, OutputTypes, SymbolManglingVersion};
10-
use rustc::session::config::{rustc_optgroups, Options, ErrorOutputType, Passes};
10+
use rustc::session::config::{rustc_optgroups, Options, ErrorOutputType, Passes, ExternLocation};
1111
use rustc::session::{build_session, Session};
1212
use rustc::session::search_paths::SearchPath;
1313
use std::collections::{BTreeMap, BTreeSet};
@@ -38,14 +38,15 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
3838
fn new_public_extern_entry<S, I>(locations: I) -> ExternEntry
3939
where
4040
S: Into<String>,
41-
I: IntoIterator<Item = Option<S>>,
41+
I: IntoIterator<Item = S>,
4242
{
43-
let locations: BTreeSet<_> = locations.into_iter().map(|o| o.map(|s| s.into()))
43+
let locations: BTreeSet<_> = locations.into_iter().map(|s| s.into())
4444
.collect();
4545

4646
ExternEntry {
47-
locations,
48-
is_private_dep: false
47+
location: ExternLocation::ExactPaths(locations),
48+
is_private_dep: false,
49+
add_prelude: true,
4950
}
5051
}
5152

@@ -160,33 +161,33 @@ fn test_externs_tracking_hash_different_construction_order() {
160161
v1.externs = Externs::new(mk_map(vec![
161162
(
162163
String::from("a"),
163-
new_public_extern_entry(vec![Some("b"), Some("c")])
164+
new_public_extern_entry(vec!["b", "c"])
164165
),
165166
(
166167
String::from("d"),
167-
new_public_extern_entry(vec![Some("e"), Some("f")])
168+
new_public_extern_entry(vec!["e", "f"])
168169
),
169170
]));
170171

171172
v2.externs = Externs::new(mk_map(vec![
172173
(
173174
String::from("d"),
174-
new_public_extern_entry(vec![Some("e"), Some("f")])
175+
new_public_extern_entry(vec!["e", "f"])
175176
),
176177
(
177178
String::from("a"),
178-
new_public_extern_entry(vec![Some("b"), Some("c")])
179+
new_public_extern_entry(vec!["b", "c"])
179180
),
180181
]));
181182

182183
v3.externs = Externs::new(mk_map(vec![
183184
(
184185
String::from("a"),
185-
new_public_extern_entry(vec![Some("b"), Some("c")])
186+
new_public_extern_entry(vec!["b", "c"])
186187
),
187188
(
188189
String::from("d"),
189-
new_public_extern_entry(vec![Some("f"), Some("e")])
190+
new_public_extern_entry(vec!["f", "e"])
190191
),
191192
]));
192193

src/librustc_metadata/creader.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,14 @@ impl<'a> CrateLoader<'a> {
218218
let source = self.cstore.get_crate_data(cnum).source();
219219
if let Some(entry) = self.sess.opts.externs.get(&name.as_str()) {
220220
// Only use `--extern crate_name=path` here, not `--extern crate_name`.
221-
let found = entry.locations.iter().filter_map(|l| l.as_ref()).any(|l| {
222-
let l = fs::canonicalize(l).ok();
223-
source.dylib.as_ref().map(|p| &p.0) == l.as_ref() ||
224-
source.rlib.as_ref().map(|p| &p.0) == l.as_ref()
225-
});
226-
if found {
227-
ret = Some(cnum);
221+
if let Some(mut files) = entry.files() {
222+
if files.any(|l| {
223+
let l = fs::canonicalize(l).ok();
224+
source.dylib.as_ref().map(|p| &p.0) == l.as_ref() ||
225+
source.rlib.as_ref().map(|p| &p.0) == l.as_ref()
226+
}) {
227+
ret = Some(cnum);
228+
}
228229
}
229230
return
230231
}

src/librustc_metadata/locator.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,9 @@ impl<'a> CrateLocator<'a> {
328328
crate_name,
329329
exact_paths: if hash.is_none() {
330330
sess.opts.externs.get(&crate_name.as_str()).into_iter()
331-
.flat_map(|entry| entry.locations.iter())
332-
.filter_map(|location| location.clone().map(PathBuf::from)).collect()
331+
.filter_map(|entry| entry.files())
332+
.flatten()
333+
.map(|location| PathBuf::from(location)).collect()
333334
} else {
334335
// SVH being specified means this is a transitive dependency,
335336
// so `--extern` options do not apply.

src/librustc_resolve/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1127,8 +1127,10 @@ impl<'a> Resolver<'a> {
11271127
definitions.create_root_def(crate_name, session.local_crate_disambiguator());
11281128

11291129
let mut extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'_>> =
1130-
session.opts.externs.iter().map(|kv| (Ident::from_str(kv.0), Default::default()))
1131-
.collect();
1130+
session.opts.externs.iter()
1131+
.filter(|(_, entry)| entry.add_prelude)
1132+
.map(|(name, _)| (Ident::from_str(name), Default::default()))
1133+
.collect();
11321134

11331135
if !attr::contains_name(&krate.attrs, sym::no_core) {
11341136
extern_prelude.insert(Ident::with_dummy_span(sym::core), Default::default());

src/librustc_session/config.rs

+132-37
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// ignore-tidy-filelength
2+
13
//! Contains infrastructure for configuring the compiler, including parsing
24
//! command-line options.
35
@@ -31,7 +33,7 @@ use std::fmt;
3133
use std::str::{self, FromStr};
3234
use std::hash::Hasher;
3335
use std::collections::hash_map::DefaultHasher;
34-
use std::iter::FromIterator;
36+
use std::iter::{self, FromIterator};
3537
use std::path::{Path, PathBuf};
3638

3739
pub struct Config {
@@ -322,10 +324,35 @@ impl OutputTypes {
322324
#[derive(Clone)]
323325
pub struct Externs(BTreeMap<String, ExternEntry>);
324326

325-
#[derive(Clone, Debug, Default)]
327+
#[derive(Clone, Debug)]
326328
pub struct ExternEntry {
327-
pub locations: BTreeSet<Option<String>>,
328-
pub is_private_dep: bool
329+
pub location: ExternLocation,
330+
/// Indicates this is a "private" dependency for the
331+
/// `exported_private_dependencies` lint.
332+
///
333+
/// This can be set with the `priv` option like
334+
/// `--extern priv:name=foo.rlib`.
335+
pub is_private_dep: bool,
336+
/// Add the extern entry to the extern prelude.
337+
///
338+
/// This can be disabled with the `noprelude` option like
339+
/// `--extern noprelude:name`.
340+
pub add_prelude: bool,
341+
}
342+
343+
#[derive(Clone, Debug)]
344+
pub enum ExternLocation {
345+
/// Indicates to look for the library in the search paths.
346+
///
347+
/// Added via `--extern name`.
348+
FoundInLibrarySearchDirectories,
349+
/// The locations where this extern entry must be found.
350+
///
351+
/// The `CrateLoader` is responsible for loading these and figuring out
352+
/// which one to use.
353+
///
354+
/// Added via `--extern prelude_name=some_file.rlib`
355+
ExactPaths(BTreeSet<String>),
329356
}
330357

331358
impl Externs {
@@ -342,6 +369,18 @@ impl Externs {
342369
}
343370
}
344371

372+
impl ExternEntry {
373+
fn new(location: ExternLocation) -> ExternEntry {
374+
ExternEntry { location, is_private_dep: false, add_prelude: false }
375+
}
376+
377+
pub fn files(&self) -> Option<impl Iterator<Item = &String>> {
378+
match &self.location {
379+
ExternLocation::ExactPaths(set) => Some(set.iter()),
380+
_ => None,
381+
}
382+
}
383+
}
345384

346385
macro_rules! hash_option {
347386
($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [UNTRACKED]) => ({});
@@ -1869,12 +1908,6 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
18691908
"Specify where an external rust library is located",
18701909
"NAME[=PATH]",
18711910
),
1872-
opt::multi_s(
1873-
"",
1874-
"extern-private",
1875-
"Specify where an extern rust library is located, marking it as a private dependency",
1876-
"NAME=PATH",
1877-
),
18781911
opt::opt_s("", "sysroot", "Override the system root", "PATH"),
18791912
opt::multi("Z", "", "Set internal debugging options", "FLAG"),
18801913
opt::opt_s(
@@ -2435,43 +2468,105 @@ fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType)
24352468
}
24362469
}
24372470

2438-
fn parse_externs(
2471+
pub fn parse_externs(
24392472
matches: &getopts::Matches,
24402473
debugging_opts: &DebuggingOptions,
24412474
error_format: ErrorOutputType,
24422475
) -> Externs {
2443-
if matches.opt_present("extern-private") && !debugging_opts.unstable_options {
2444-
early_error(
2445-
ErrorOutputType::default(),
2446-
"'--extern-private' is unstable and only \
2447-
available for nightly builds of rustc."
2448-
)
2449-
}
2450-
2451-
// We start out with a `Vec<(Option<String>, bool)>>`,
2452-
// and later convert it into a `BTreeSet<(Option<String>, bool)>`
2453-
// This allows to modify entries in-place to set their correct
2454-
// 'public' value.
2476+
let is_unstable_enabled = debugging_opts.unstable_options;
24552477
let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
2456-
for (arg, private) in matches.opt_strs("extern").into_iter().map(|v| (v, false))
2457-
.chain(matches.opt_strs("extern-private").into_iter().map(|v| (v, true))) {
2458-
2478+
for arg in matches.opt_strs("extern") {
24592479
let mut parts = arg.splitn(2, '=');
2460-
let name = parts.next().unwrap_or_else(||
2461-
early_error(error_format, "--extern value must not be empty"));
2462-
let location = parts.next().map(|s| s.to_string());
2480+
let name = parts
2481+
.next()
2482+
.unwrap_or_else(|| early_error(error_format, "--extern value must not be empty"));
2483+
let path = parts.next().map(|s| s.to_string());
2484+
2485+
let mut name_parts = name.splitn(2, ':');
2486+
let first_part = name_parts.next();
2487+
let second_part = name_parts.next();
2488+
let (options, name) = match (first_part, second_part) {
2489+
(Some(opts), Some(name)) => (Some(opts), name),
2490+
(Some(name), None) => (None, name),
2491+
(None, None) => early_error(error_format, "--extern name must not be empty"),
2492+
_ => unreachable!(),
2493+
};
2494+
2495+
let entry = externs.entry(name.to_owned());
24632496

2464-
let entry = externs
2465-
.entry(name.to_owned())
2466-
.or_default();
2497+
use std::collections::btree_map::Entry;
24672498

2499+
let entry = if let Some(path) = path {
2500+
// --extern prelude_name=some_file.rlib
2501+
match entry {
2502+
Entry::Vacant(vacant) => {
2503+
let files = BTreeSet::from_iter(iter::once(path));
2504+
vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files)))
2505+
}
2506+
Entry::Occupied(occupied) => {
2507+
let ext_ent = occupied.into_mut();
2508+
match ext_ent {
2509+
ExternEntry { location: ExternLocation::ExactPaths(files), .. } => {
2510+
files.insert(path);
2511+
}
2512+
ExternEntry {
2513+
location: location @ ExternLocation::FoundInLibrarySearchDirectories,
2514+
..
2515+
} => {
2516+
// Exact paths take precedence over search directories.
2517+
let files = BTreeSet::from_iter(iter::once(path));
2518+
*location = ExternLocation::ExactPaths(files);
2519+
}
2520+
}
2521+
ext_ent
2522+
}
2523+
}
2524+
} else {
2525+
// --extern prelude_name
2526+
match entry {
2527+
Entry::Vacant(vacant) => {
2528+
vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories))
2529+
}
2530+
Entry::Occupied(occupied) => {
2531+
// Ignore if already specified.
2532+
occupied.into_mut()
2533+
}
2534+
}
2535+
};
24682536

2469-
entry.locations.insert(location.clone());
2537+
let mut is_private_dep = false;
2538+
let mut add_prelude = true;
2539+
if let Some(opts) = options {
2540+
if !is_unstable_enabled {
2541+
early_error(
2542+
error_format,
2543+
"the `-Z unstable-options` flag must also be passed to \
2544+
enable `--extern options",
2545+
);
2546+
}
2547+
for opt in opts.split(',') {
2548+
match opt {
2549+
"priv" => is_private_dep = true,
2550+
"noprelude" => {
2551+
if let ExternLocation::ExactPaths(_) = &entry.location {
2552+
add_prelude = false;
2553+
} else {
2554+
early_error(
2555+
error_format,
2556+
"the `noprelude` --extern option requires a file path",
2557+
);
2558+
}
2559+
}
2560+
_ => early_error(error_format, &format!("unknown --extern option `{}`", opt)),
2561+
}
2562+
}
2563+
}
24702564

2471-
// Crates start out being not private,
2472-
// and go to being private if we see an '--extern-private'
2473-
// flag
2474-
entry.is_private_dep |= private;
2565+
// Crates start out being not private, and go to being private `priv`
2566+
// is specified.
2567+
entry.is_private_dep |= is_private_dep;
2568+
// If any flag is missing `noprelude`, then add to the prelude.
2569+
entry.add_prelude |= add_prelude;
24752570
}
24762571
Externs(externs)
24772572
}

src/librustdoc/config.rs

+3-30
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use errors;
77
use getopts;
88
use rustc::lint::Level;
99
use rustc::session;
10-
use rustc::session::config::{CrateType, parse_crate_types_from_list};
10+
use rustc::session::config::{CrateType, parse_crate_types_from_list, parse_externs};
1111
use rustc::session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs};
1212
use rustc::session::config::{nightly_options, build_codegen_options, build_debugging_options,
13-
get_cmd_lint_options, host_triple, ExternEntry};
13+
get_cmd_lint_options, host_triple};
1414
use rustc::session::search_paths::SearchPath;
1515
use rustc_driver;
1616
use rustc_target::spec::TargetTriple;
@@ -320,13 +320,7 @@ impl Options {
320320
let libs = matches.opt_strs("L").iter()
321321
.map(|s| SearchPath::from_cli_opt(s, error_format))
322322
.collect();
323-
let externs = match parse_externs(&matches) {
324-
Ok(ex) => ex,
325-
Err(err) => {
326-
diag.struct_err(&err).emit();
327-
return Err(1);
328-
}
329-
};
323+
let externs = parse_externs(&matches, &debugging_options, error_format);
330324
let extern_html_root_urls = match parse_extern_html_roots(&matches) {
331325
Ok(ex) => ex,
332326
Err(err) => {
@@ -617,24 +611,3 @@ fn parse_extern_html_roots(
617611

618612
Ok(externs)
619613
}
620-
621-
/// Extracts `--extern CRATE=PATH` arguments from `matches` and
622-
/// returns a map mapping crate names to their paths or else an
623-
/// error message.
624-
/// Also handles `--extern-private` which for the purposes of rustdoc
625-
/// we can treat as `--extern`
626-
// FIXME(eddyb) This shouldn't be duplicated with `rustc::session`.
627-
fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> {
628-
let mut externs: BTreeMap<_, ExternEntry> = BTreeMap::new();
629-
for arg in matches.opt_strs("extern").iter().chain(matches.opt_strs("extern-private").iter()) {
630-
let mut parts = arg.splitn(2, '=');
631-
let name = parts.next().ok_or("--extern value must not be empty".to_string())?;
632-
let location = parts.next().map(|s| s.to_string());
633-
let name = name.to_string();
634-
// For Rustdoc purposes, we can treat all externs as public
635-
externs.entry(name)
636-
.or_default()
637-
.locations.insert(location.clone());
638-
}
639-
Ok(Externs::new(externs))
640-
}

src/librustdoc/core.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
248248
..
249249
} = options;
250250

251-
let extern_names: Vec<String> = externs.iter().map(|(s,_)| s).cloned().collect();
251+
let extern_names: Vec<String> = externs.iter()
252+
.filter(|(_, entry)| entry.add_prelude)
253+
.map(|(name, _)| name).cloned().collect();
252254

253255
// Add the doc cfg into the doc build.
254256
cfgs.push("doc".to_string());

0 commit comments

Comments
 (0)