Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 477cae3

Browse files
jyn514Mark-Simulacrum
authored andcommittedMar 7, 2022
copy over std::path::absolute instead of adding canonicalize hacks
this also fixes a bug where bootstrap would try to use the fake `rustc` binary built by bootstrap - cargo puts it in a different directory when using `cargo run` instead of x.py
1 parent 984527f commit 477cae3

File tree

8 files changed

+138
-26
lines changed

8 files changed

+138
-26
lines changed
 

‎src/bootstrap/bin/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use bootstrap::{Build, Config, Subcommand, VERSION};
1111

1212
fn main() {
1313
let args = env::args().skip(1).collect::<Vec<_>>();
14-
let config = Config::parse(&args, false);
14+
let config = Config::parse(&args);
1515

1616
// check_version warnings are not printed during setup
1717
let changelog_suggestion =

‎src/bootstrap/builder.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ impl<'a> Builder<'a> {
883883
}
884884

885885
pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command {
886-
let mut cmd = Command::new(&self.out.join("bootstrap/debug/rustdoc"));
886+
let mut cmd = Command::new(&self.bootstrap_out.join("rustdoc"));
887887
cmd.env("RUSTC_STAGE", compiler.stage.to_string())
888888
.env("RUSTC_SYSROOT", self.sysroot(compiler))
889889
// Note that this is *not* the sysroot_libdir because rustdoc must be linked
@@ -1249,7 +1249,7 @@ impl<'a> Builder<'a> {
12491249
.env("RUSTC_STAGE", stage.to_string())
12501250
.env("RUSTC_SYSROOT", &sysroot)
12511251
.env("RUSTC_LIBDIR", &libdir)
1252-
.env("RUSTDOC", self.out.join("bootstrap/debug/rustdoc"))
1252+
.env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
12531253
.env(
12541254
"RUSTDOC_REAL",
12551255
if cmd == "doc" || cmd == "rustdoc" || (cmd == "test" && want_rustdoc) {
@@ -1263,7 +1263,7 @@ impl<'a> Builder<'a> {
12631263
// Clippy support is a hack and uses the default `cargo-clippy` in path.
12641264
// Don't override RUSTC so that the `cargo-clippy` in path will be run.
12651265
if cmd != "clippy" {
1266-
cargo.env("RUSTC", self.out.join("bootstrap/debug/rustc"));
1266+
cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
12671267
}
12681268

12691269
// Dealing with rpath here is a little special, so let's go into some

‎src/bootstrap/builder/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::config::{Config, TargetSelection};
33
use std::thread;
44

55
fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
6-
let mut config = Config::parse(&[cmd.to_owned()], true);
6+
let mut config = Config::parse(&[cmd.to_owned()]);
77
// don't save toolstates
88
config.save_toolstates = None;
99
config.dry_run = true;

‎src/bootstrap/config.rs

+6-19
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ impl Config {
619619
config
620620
}
621621

622-
pub fn parse(args: &[String], unit_test: bool) -> Config {
622+
pub fn parse(args: &[String]) -> Config {
623623
let flags = Flags::parse(&args);
624624

625625
let mut config = Config::default_opts();
@@ -681,26 +681,13 @@ impl Config {
681681

682682
let build = toml.build.unwrap_or_default();
683683

684-
set(&mut config.out, build.build_dir.map(String::into));
684+
set(&mut config.initial_rustc, build.rustc.map(PathBuf::from));
685+
set(&mut config.out, build.build_dir.map(PathBuf::from));
685686
// NOTE: Bootstrap spawns various commands with different working directories.
686687
// To avoid writing to random places on the file system, `config.out` needs to be an absolute path.
687-
688-
// FIXME: using `canonicalize()` makes this a lot more complicated than it needs to be -
689-
// if/when `std::path::absolute` lands, we should use that instead.
690-
691-
// HACK: in tests, we override the build directory manually.
692-
// Avoid creating a directory we won't actually need.
693-
// (The original motivation for this is that CI uses read-only directories.)
694-
if !config.out.is_absolute() && !unit_test {
695-
// canonicalize() gives a hard error if the directory doesn't exist
696-
t!(
697-
fs::create_dir_all(&config.out),
698-
format!("failed to create build dir: {}", config.out.display())
699-
);
700-
config.out = t!(
701-
config.out.canonicalize(),
702-
format!("failed to canonicalize {}", config.out.display())
703-
);
688+
if !config.out.is_absolute() {
689+
// `canonicalize` requires the path to already exist. Use our vendored copy of `absolute` instead.
690+
config.out = crate::util::absolute(&config.out);
704691
}
705692

706693
if config.dry_run {

‎src/bootstrap/flags.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
343343

344344
// All subcommands except `clean` can have an optional "Available paths" section
345345
if verbose {
346-
let config = Config::parse(&["build".to_string()], false);
346+
let config = Config::parse(&["build".to_string()]);
347347
let build = Build::new(config);
348348

349349
let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());

‎src/bootstrap/lib.rs

+16
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ pub struct Build {
261261
// Properties derived from the above configuration
262262
src: PathBuf,
263263
out: PathBuf,
264+
bootstrap_out: PathBuf,
264265
rust_info: channel::GitInfo,
265266
cargo_info: channel::GitInfo,
266267
rls_info: channel::GitInfo,
@@ -435,6 +436,20 @@ impl Build {
435436
.expect("failed to read src/version");
436437
let version = version.trim();
437438

439+
let bootstrap_out = if std::env::var("BOOTSTRAP_PYTHON").is_ok() {
440+
out.join("bootstrap").join("debug")
441+
} else {
442+
let workspace_target_dir = std::env::var("CARGO_TARGET_DIR")
443+
.map(PathBuf::from)
444+
.unwrap_or_else(|_| src.join("target"));
445+
let bootstrap_out = workspace_target_dir.join("debug");
446+
if !bootstrap_out.join("rustc").exists() {
447+
// this restriction can be lifted whenever https://github.com/rust-lang/rfcs/pull/3028 is implemented
448+
panic!("run `cargo build --bins` before `cargo run`")
449+
}
450+
bootstrap_out
451+
};
452+
438453
let mut build = Build {
439454
initial_rustc: config.initial_rustc.clone(),
440455
initial_cargo: config.initial_cargo.clone(),
@@ -453,6 +468,7 @@ impl Build {
453468
version: version.to_string(),
454469
src,
455470
out,
471+
bootstrap_out,
456472

457473
rust_info,
458474
cargo_info,

‎src/bootstrap/test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ impl Step for RustdocTheme {
730730
}
731731

732732
fn run(self, builder: &Builder<'_>) {
733-
let rustdoc = builder.out.join("bootstrap/debug/rustdoc");
733+
let rustdoc = builder.bootstrap_out.join("rustdoc");
734734
let mut cmd = builder.tool_cmd(Tool::RustdocTheme);
735735
cmd.arg(rustdoc.to_str().unwrap())
736736
.arg(builder.src.join("src/librustdoc/html/static/css/themes").to_str().unwrap())

‎src/bootstrap/util.rs

+109
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,112 @@ fn fail(s: &str) -> ! {
440440
println!("\n\n{}\n\n", s);
441441
std::process::exit(1);
442442
}
443+
444+
/// Copied from `std::path::absolute` until it stabilizes.
445+
///
446+
/// FIXME: this shouldn't exist.
447+
pub(crate) fn absolute(path: &Path) -> PathBuf {
448+
if path.as_os_str().is_empty() {
449+
panic!("can't make empty path absolute");
450+
}
451+
#[cfg(unix)]
452+
{
453+
t!(absolute_unix(path), format!("could not make path absolute: {}", path.display()))
454+
}
455+
#[cfg(windows)]
456+
{
457+
t!(absolute_windows(path), format!("could not make path absolute: {}", path.display()))
458+
}
459+
#[cfg(not(any(unix, windows)))]
460+
{
461+
println!("warning: bootstrap is not supported on non-unix platforms");
462+
t!(std::fs::canonicalize(t!(std::env::current_dir()))).join(path)
463+
}
464+
}
465+
466+
#[cfg(unix)]
467+
/// Make a POSIX path absolute without changing its semantics.
468+
fn absolute_unix(path: &Path) -> io::Result<PathBuf> {
469+
// This is mostly a wrapper around collecting `Path::components`, with
470+
// exceptions made where this conflicts with the POSIX specification.
471+
// See 4.13 Pathname Resolution, IEEE Std 1003.1-2017
472+
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
473+
474+
use std::os::unix::prelude::OsStrExt;
475+
let mut components = path.components();
476+
let path_os = path.as_os_str().as_bytes();
477+
478+
let mut normalized = if path.is_absolute() {
479+
// "If a pathname begins with two successive <slash> characters, the
480+
// first component following the leading <slash> characters may be
481+
// interpreted in an implementation-defined manner, although more than
482+
// two leading <slash> characters shall be treated as a single <slash>
483+
// character."
484+
if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
485+
components.next();
486+
PathBuf::from("//")
487+
} else {
488+
PathBuf::new()
489+
}
490+
} else {
491+
env::current_dir()?
492+
};
493+
normalized.extend(components);
494+
495+
// "Interfaces using pathname resolution may specify additional constraints
496+
// when a pathname that does not name an existing directory contains at
497+
// least one non- <slash> character and contains one or more trailing
498+
// <slash> characters".
499+
// A trailing <slash> is also meaningful if "a symbolic link is
500+
// encountered during pathname resolution".
501+
502+
if path_os.ends_with(b"/") {
503+
normalized.push("");
504+
}
505+
506+
Ok(normalized)
507+
}
508+
509+
#[cfg(windows)]
510+
fn absolute_windows(path: &std::path::Path) -> std::io::Result<std::path::PathBuf> {
511+
use std::ffi::OsString;
512+
use std::io::Error;
513+
use std::os::windows::ffi::{OsStrExt, OsStringExt};
514+
use std::ptr::null_mut;
515+
#[link(name = "kernel32")]
516+
extern "system" {
517+
fn GetFullPathNameW(
518+
lpFileName: *const u16,
519+
nBufferLength: u32,
520+
lpBuffer: *mut u16,
521+
lpFilePart: *mut *const u16,
522+
) -> u32;
523+
}
524+
525+
unsafe {
526+
// encode the path as UTF-16
527+
let path: Vec<u16> = path.as_os_str().encode_wide().chain([0]).collect();
528+
let mut buffer = Vec::new();
529+
// Loop until either success or failure.
530+
loop {
531+
// Try to get the absolute path
532+
let len = GetFullPathNameW(
533+
path.as_ptr(),
534+
buffer.len().try_into().unwrap(),
535+
buffer.as_mut_ptr(),
536+
null_mut(),
537+
);
538+
match len as usize {
539+
// Failure
540+
0 => return Err(Error::last_os_error()),
541+
// Buffer is too small, resize.
542+
len if len > buffer.len() => buffer.resize(len, 0),
543+
// Success!
544+
len => {
545+
buffer.truncate(len);
546+
return Ok(OsString::from_wide(&buffer).into());
547+
}
548+
}
549+
}
550+
}
551+
}

0 commit comments

Comments
 (0)
Failed to load comments.