Skip to content

Commit 28e85d7

Browse files
committed
Auto merge of #64328 - Mark-Simulacrum:rustdoc-find-rustc, r=GuillaumeGomez
rustdoc: change doctests locating rustc binary We previously used the "naive" approach of replacing the `current_exe()`'s file name with rustc, but now load from the sysroot by default (`$sysroot/bin/rustc`). The functionality of locating the sysroot overlaps/is the same as the functionality used by codegen backend loading; this ensures that any failure cases we've introduced are not exceeding those, and that improvements to finding the sysroot for loading codegen backends likewise enhance rustdoc. The second commit adds an unstable `--test-builder` flag to rustdoc, and is largely separate (I can split into separate PR, but it's a simple and related change). This is largely intended for "advanced" users at this point (I'm not sure if we'll ever stabilize it); it permits use of a different rustc binary for rustdoc compilation of doctests than the rustdoc binary used when loading. Note, that this may not be what you want as the parsers and such differ (and rustdoc uses its own libsyntax, etc.). However, I've been told that running doctests in miri may be assisted by this change, so I've implemented it; I'll file a tracking issue for it if there's interest in it (and we land this PR).
2 parents f71826e + 093cbd6 commit 28e85d7

File tree

10 files changed

+143
-79
lines changed

10 files changed

+143
-79
lines changed

Cargo.lock

+7
Original file line numberDiff line numberDiff line change
@@ -2137,6 +2137,12 @@ dependencies = [
21372137
"libc",
21382138
]
21392139

2140+
[[package]]
2141+
name = "once_cell"
2142+
version = "1.1.0"
2143+
source = "registry+https://github.com/rust-lang/crates.io-index"
2144+
checksum = "d6a04cb71e910d0034815600180f62a95bf6e67942d7ab52a166a68c7d7e9cd0"
2145+
21402146
[[package]]
21412147
name = "open"
21422148
version = "1.2.1"
@@ -3425,6 +3431,7 @@ name = "rustc_interface"
34253431
version = "0.0.0"
34263432
dependencies = [
34273433
"log",
3434+
"once_cell",
34283435
"rustc",
34293436
"rustc-rayon",
34303437
"rustc_ast_borrowck",

src/bootstrap/builder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,8 @@ impl<'a> Builder<'a> {
11671167
cargo.arg("--frozen");
11681168
}
11691169

1170+
cargo.env("RUSTC_INSTALL_BINDIR", &self.config.bindir);
1171+
11701172
self.ci_env.force_coloring_in_ci(&mut cargo);
11711173

11721174
cargo

src/bootstrap/config.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ pub struct Config {
137137
pub sysconfdir: Option<PathBuf>,
138138
pub datadir: Option<PathBuf>,
139139
pub docdir: Option<PathBuf>,
140-
pub bindir: Option<PathBuf>,
140+
pub bindir: PathBuf,
141141
pub libdir: Option<PathBuf>,
142142
pub mandir: Option<PathBuf>,
143143
pub codegen_tests: bool,
@@ -400,6 +400,7 @@ impl Config {
400400
config.incremental = flags.incremental;
401401
config.dry_run = flags.dry_run;
402402
config.keep_stage = flags.keep_stage;
403+
config.bindir = "bin".into(); // default
403404
if let Some(value) = flags.deny_warnings {
404405
config.deny_warnings = value;
405406
}
@@ -482,7 +483,7 @@ impl Config {
482483
config.sysconfdir = install.sysconfdir.clone().map(PathBuf::from);
483484
config.datadir = install.datadir.clone().map(PathBuf::from);
484485
config.docdir = install.docdir.clone().map(PathBuf::from);
485-
config.bindir = install.bindir.clone().map(PathBuf::from);
486+
set(&mut config.bindir, install.bindir.clone().map(PathBuf::from));
486487
config.libdir = install.libdir.clone().map(PathBuf::from);
487488
config.mandir = install.mandir.clone().map(PathBuf::from);
488489
}

src/bootstrap/install.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ fn install_sh(
6767
let sysconfdir_default = PathBuf::from("/etc");
6868
let datadir_default = PathBuf::from("share");
6969
let docdir_default = datadir_default.join("doc/rust");
70-
let bindir_default = PathBuf::from("bin");
7170
let libdir_default = PathBuf::from("lib");
7271
let mandir_default = datadir_default.join("man");
7372
let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| {
@@ -76,7 +75,7 @@ fn install_sh(
7675
let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
7776
let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default);
7877
let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default);
79-
let bindir = builder.config.bindir.as_ref().unwrap_or(&bindir_default);
78+
let bindir = &builder.config.bindir;
8079
let libdir = builder.config.libdir.as_ref().unwrap_or(&libdir_default);
8180
let mandir = builder.config.mandir.as_ref().unwrap_or(&mandir_default);
8281

src/librustc_interface/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ rustc_plugin = { path = "../librustc_plugin", package = "rustc_plugin_impl" }
3434
rustc_privacy = { path = "../librustc_privacy" }
3535
rustc_resolve = { path = "../librustc_resolve" }
3636
tempfile = "3.0.5"
37+
once_cell = "1"

src/librustc_interface/build.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
println!("cargo:rerun-if-changed=build.rs");
3+
println!("cargo:rerun-if-env-changed=RUSTC_INSTALL_BINDIR");
4+
}

src/librustc_interface/util.rs

+110-74
Original file line numberDiff line numberDiff line change
@@ -289,20 +289,39 @@ pub fn get_codegen_backend(sess: &Session) -> Box<dyn CodegenBackend> {
289289
backend
290290
}
291291

292-
pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box<dyn CodegenBackend> {
293-
// For now we only allow this function to be called once as it'll dlopen a
294-
// few things, which seems to work best if we only do that once. In
295-
// general this assertion never trips due to the once guard in `get_codegen_backend`,
296-
// but there's a few manual calls to this function in this file we protect
297-
// against.
298-
static LOADED: AtomicBool = AtomicBool::new(false);
299-
assert!(!LOADED.fetch_or(true, Ordering::SeqCst),
300-
"cannot load the default codegen backend twice");
292+
// This is used for rustdoc, but it uses similar machinery to codegen backend
293+
// loading, so we leave the code here. It is potentially useful for other tools
294+
// that want to invoke the rustc binary while linking to rustc as well.
295+
pub fn rustc_path<'a>() -> Option<&'a Path> {
296+
static RUSTC_PATH: once_cell::sync::OnceCell<Option<PathBuf>> =
297+
once_cell::sync::OnceCell::new();
301298

299+
const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR");
300+
301+
RUSTC_PATH.get_or_init(|| get_rustc_path_inner(BIN_PATH)).as_ref().map(|v| &**v)
302+
}
303+
304+
fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
305+
sysroot_candidates().iter()
306+
.filter_map(|sysroot| {
307+
let candidate = sysroot.join(bin_path).join(if cfg!(target_os = "windows") {
308+
"rustc.exe"
309+
} else {
310+
"rustc"
311+
});
312+
if candidate.exists() {
313+
Some(candidate)
314+
} else {
315+
None
316+
}
317+
})
318+
.next()
319+
}
320+
321+
fn sysroot_candidates() -> Vec<PathBuf> {
302322
let target = session::config::host_triple();
303323
let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()];
304-
let path = current_dll_path()
305-
.and_then(|s| s.canonicalize().ok());
324+
let path = current_dll_path().and_then(|s| s.canonicalize().ok());
306325
if let Some(dll) = path {
307326
// use `parent` twice to chop off the file name and then also the
308327
// directory containing the dll which should be either `lib` or `bin`.
@@ -327,69 +346,7 @@ pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box<dyn CodegenBackend
327346
}
328347
}
329348

330-
let sysroot = sysroot_candidates.iter()
331-
.map(|sysroot| {
332-
let libdir = filesearch::relative_target_lib_path(&sysroot, &target);
333-
sysroot.join(libdir).with_file_name(
334-
option_env!("CFG_CODEGEN_BACKENDS_DIR").unwrap_or("codegen-backends"))
335-
})
336-
.filter(|f| {
337-
info!("codegen backend candidate: {}", f.display());
338-
f.exists()
339-
})
340-
.next();
341-
let sysroot = sysroot.unwrap_or_else(|| {
342-
let candidates = sysroot_candidates.iter()
343-
.map(|p| p.display().to_string())
344-
.collect::<Vec<_>>()
345-
.join("\n* ");
346-
let err = format!("failed to find a `codegen-backends` folder \
347-
in the sysroot candidates:\n* {}", candidates);
348-
early_error(ErrorOutputType::default(), &err);
349-
});
350-
info!("probing {} for a codegen backend", sysroot.display());
351-
352-
let d = sysroot.read_dir().unwrap_or_else(|e| {
353-
let err = format!("failed to load default codegen backend, couldn't \
354-
read `{}`: {}", sysroot.display(), e);
355-
early_error(ErrorOutputType::default(), &err);
356-
});
357-
358-
let mut file: Option<PathBuf> = None;
359-
360-
let expected_name = format!("rustc_codegen_llvm-{}", backend_name);
361-
for entry in d.filter_map(|e| e.ok()) {
362-
let path = entry.path();
363-
let filename = match path.file_name().and_then(|s| s.to_str()) {
364-
Some(s) => s,
365-
None => continue,
366-
};
367-
if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) {
368-
continue
369-
}
370-
let name = &filename[DLL_PREFIX.len() .. filename.len() - DLL_SUFFIX.len()];
371-
if name != expected_name {
372-
continue
373-
}
374-
if let Some(ref prev) = file {
375-
let err = format!("duplicate codegen backends found\n\
376-
first: {}\n\
377-
second: {}\n\
378-
", prev.display(), path.display());
379-
early_error(ErrorOutputType::default(), &err);
380-
}
381-
file = Some(path.clone());
382-
}
383-
384-
match file {
385-
Some(ref s) => return load_backend_from_dylib(s),
386-
None => {
387-
let err = format!("failed to load default codegen backend for `{}`, \
388-
no appropriate codegen dylib found in `{}`",
389-
backend_name, sysroot.display());
390-
early_error(ErrorOutputType::default(), &err);
391-
}
392-
}
349+
return sysroot_candidates;
393350

394351
#[cfg(unix)]
395352
fn current_dll_path() -> Option<PathBuf> {
@@ -459,6 +416,85 @@ pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box<dyn CodegenBackend
459416
}
460417
}
461418

419+
pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box<dyn CodegenBackend> {
420+
// For now we only allow this function to be called once as it'll dlopen a
421+
// few things, which seems to work best if we only do that once. In
422+
// general this assertion never trips due to the once guard in `get_codegen_backend`,
423+
// but there's a few manual calls to this function in this file we protect
424+
// against.
425+
static LOADED: AtomicBool = AtomicBool::new(false);
426+
assert!(!LOADED.fetch_or(true, Ordering::SeqCst),
427+
"cannot load the default codegen backend twice");
428+
429+
let target = session::config::host_triple();
430+
let sysroot_candidates = sysroot_candidates();
431+
432+
let sysroot = sysroot_candidates.iter()
433+
.map(|sysroot| {
434+
let libdir = filesearch::relative_target_lib_path(&sysroot, &target);
435+
sysroot.join(libdir).with_file_name(
436+
option_env!("CFG_CODEGEN_BACKENDS_DIR").unwrap_or("codegen-backends"))
437+
})
438+
.filter(|f| {
439+
info!("codegen backend candidate: {}", f.display());
440+
f.exists()
441+
})
442+
.next();
443+
let sysroot = sysroot.unwrap_or_else(|| {
444+
let candidates = sysroot_candidates.iter()
445+
.map(|p| p.display().to_string())
446+
.collect::<Vec<_>>()
447+
.join("\n* ");
448+
let err = format!("failed to find a `codegen-backends` folder \
449+
in the sysroot candidates:\n* {}", candidates);
450+
early_error(ErrorOutputType::default(), &err);
451+
});
452+
info!("probing {} for a codegen backend", sysroot.display());
453+
454+
let d = sysroot.read_dir().unwrap_or_else(|e| {
455+
let err = format!("failed to load default codegen backend, couldn't \
456+
read `{}`: {}", sysroot.display(), e);
457+
early_error(ErrorOutputType::default(), &err);
458+
});
459+
460+
let mut file: Option<PathBuf> = None;
461+
462+
let expected_name = format!("rustc_codegen_llvm-{}", backend_name);
463+
for entry in d.filter_map(|e| e.ok()) {
464+
let path = entry.path();
465+
let filename = match path.file_name().and_then(|s| s.to_str()) {
466+
Some(s) => s,
467+
None => continue,
468+
};
469+
if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) {
470+
continue
471+
}
472+
let name = &filename[DLL_PREFIX.len() .. filename.len() - DLL_SUFFIX.len()];
473+
if name != expected_name {
474+
continue
475+
}
476+
if let Some(ref prev) = file {
477+
let err = format!("duplicate codegen backends found\n\
478+
first: {}\n\
479+
second: {}\n\
480+
", prev.display(), path.display());
481+
early_error(ErrorOutputType::default(), &err);
482+
}
483+
file = Some(path.clone());
484+
}
485+
486+
match file {
487+
Some(ref s) => return load_backend_from_dylib(s),
488+
None => {
489+
let err = format!("failed to load default codegen backend for `{}`, \
490+
no appropriate codegen dylib found in `{}`",
491+
backend_name, sysroot.display());
492+
early_error(ErrorOutputType::default(), &err);
493+
}
494+
}
495+
496+
}
497+
462498
pub(crate) fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator {
463499
use std::hash::Hasher;
464500

src/librustdoc/config.rs

+6
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ pub struct Options {
8686
/// contains "foo" as a substring
8787
pub enable_per_target_ignores: bool,
8888

89+
/// The path to a rustc-like binary to build tests with. If not set, we
90+
/// default to loading from $sysroot/bin/rustc.
91+
pub test_builder: Option<PathBuf>,
92+
8993
// Options that affect the documentation process
9094

9195
/// The selected default set of passes to use.
@@ -476,6 +480,7 @@ impl Options {
476480
let generate_search_filter = !matches.opt_present("disable-per-crate-search");
477481
let persist_doctests = matches.opt_str("persist-doctests").map(PathBuf::from);
478482
let generate_redirect_pages = matches.opt_present("generate-redirect-pages");
483+
let test_builder = matches.opt_str("test-builder").map(PathBuf::from);
479484
let codegen_options_strs = matches.opt_strs("C");
480485
let lib_strs = matches.opt_strs("L");
481486
let extern_strs = matches.opt_strs("extern");
@@ -515,6 +520,7 @@ impl Options {
515520
runtool,
516521
runtool_args,
517522
enable_per_target_ignores,
523+
test_builder,
518524
render_options: RenderOptions {
519525
output,
520526
external_html,

src/librustdoc/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,11 @@ fn opts() -> Vec<RustcOptGroup> {
373373
"",
374374
"One (of possibly many) arguments to pass to the runtool")
375375
}),
376+
unstable("test-builder", |o| {
377+
o.optflag("",
378+
"test-builder",
379+
"specified the rustc-like binary to use as the test builder")
380+
}),
376381
]
377382
}
378383

src/librustdoc/test.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,10 @@ fn run_test(
248248
};
249249
let output_file = outdir.path().join("rust_out");
250250

251-
let mut compiler = Command::new(std::env::current_exe().unwrap().with_file_name("rustc"));
251+
let rustc_binary = options.test_builder.as_ref().map(|v| &**v).unwrap_or_else(|| {
252+
rustc_interface::util::rustc_path().expect("found rustc")
253+
});
254+
let mut compiler = Command::new(&rustc_binary);
252255
compiler.arg("--crate-type").arg("bin");
253256
for cfg in &options.cfgs {
254257
compiler.arg("--cfg").arg(&cfg);

0 commit comments

Comments
 (0)