Skip to content

Commit 46c35c7

Browse files
committed
Auto merge of rust-lang#80797 - pietroalbini:fix-relative-install, r=Mark-Simulacrum
Fix x.py install not working with relative prefix The code powering `./x.py install` did not handle relative paths well: the installation script is executed inside a temporary directory, so all the relative paths specified in `config.toml` and in the `DESTDIR` environment variable were relative to that temporary directory. The original code fixed the problem for `config.toml` paths by canonicalizing the prefix, but breaking `DESTDIR`. rust-lang#80240 fixed the `DESTDIR` problem, but also regressed `config.toml` paths (rust-lang#80683). This PR refactors the installation code to generate paths that *in my understanding* are correct, adding comments in the meantime to explain what each step does. There was no documentation on why choices were made before, so my understanding could actually be wrong. Regardless, executed `./x.py install` with various combinations of `config.toml` and `DESTDIR` paths, and everything seems to work according to my understanding. Still, I'd love if `@vext01` and `@yshui` could test these changes. r? `@Mark-Simulacrum` `@rustbot` modify labels: beta-nominated T-infra
2 parents 1f9dc9a + 2caf9bc commit 46c35c7

File tree

1 file changed

+44
-55
lines changed

1 file changed

+44
-55
lines changed

src/bootstrap/install.rs

+44-55
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use std::env;
77
use std::fs;
8-
use std::path::{Component, Path, PathBuf};
8+
use std::path::{Component, PathBuf};
99
use std::process::Command;
1010

1111
use build_helper::t;
@@ -26,74 +26,63 @@ fn install_sh(
2626
) {
2727
builder.info(&format!("Install {} stage{} ({:?})", package, stage, host));
2828

29-
let prefix_default = PathBuf::from("/usr/local");
30-
let sysconfdir_default = PathBuf::from("/etc");
31-
let datadir_default = PathBuf::from("share");
32-
let docdir_default = datadir_default.join("doc/rust");
33-
let libdir_default = PathBuf::from("lib");
34-
let mandir_default = datadir_default.join("man");
35-
let prefix = builder.config.prefix.as_ref().unwrap_or(&prefix_default);
36-
let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
37-
let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default);
38-
let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default);
39-
let bindir = &builder.config.bindir;
40-
let libdir = builder.config.libdir.as_ref().unwrap_or(&libdir_default);
41-
let mandir = builder.config.mandir.as_ref().unwrap_or(&mandir_default);
42-
43-
let sysconfdir = prefix.join(sysconfdir);
44-
let datadir = prefix.join(datadir);
45-
let docdir = prefix.join(docdir);
46-
let bindir = prefix.join(bindir);
47-
let libdir = prefix.join(libdir);
48-
let mandir = prefix.join(mandir);
49-
50-
let destdir = env::var_os("DESTDIR").map(PathBuf::from);
51-
52-
let prefix = add_destdir(&prefix, &destdir);
53-
let sysconfdir = add_destdir(&sysconfdir, &destdir);
54-
let datadir = add_destdir(&datadir, &destdir);
55-
let docdir = add_destdir(&docdir, &destdir);
56-
let bindir = add_destdir(&bindir, &destdir);
57-
let libdir = add_destdir(&libdir, &destdir);
58-
let mandir = add_destdir(&mandir, &destdir);
59-
60-
let prefix = {
61-
fs::create_dir_all(&prefix)
62-
.unwrap_or_else(|err| panic!("could not create {}: {}", prefix.display(), err));
63-
fs::canonicalize(&prefix)
64-
.unwrap_or_else(|err| panic!("could not canonicalize {}: {}", prefix.display(), err))
65-
};
29+
let prefix = default_path(&builder.config.prefix, "/usr/local");
30+
let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc"));
31+
let datadir = prefix.join(default_path(&builder.config.datadir, "share"));
32+
let docdir = prefix.join(default_path(&builder.config.docdir, "share/doc"));
33+
let mandir = prefix.join(default_path(&builder.config.mandir, "share/man"));
34+
let libdir = prefix.join(default_path(&builder.config.libdir, "lib"));
35+
let bindir = prefix.join(&builder.config.bindir); // Default in config.rs
6636

6737
let empty_dir = builder.out.join("tmp/empty_dir");
68-
6938
t!(fs::create_dir_all(&empty_dir));
7039

7140
let mut cmd = Command::new("sh");
7241
cmd.current_dir(&empty_dir)
7342
.arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
74-
.arg(format!("--prefix={}", sanitize_sh(&prefix)))
75-
.arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir)))
76-
.arg(format!("--datadir={}", sanitize_sh(&datadir)))
77-
.arg(format!("--docdir={}", sanitize_sh(&docdir)))
78-
.arg(format!("--bindir={}", sanitize_sh(&bindir)))
79-
.arg(format!("--libdir={}", sanitize_sh(&libdir)))
80-
.arg(format!("--mandir={}", sanitize_sh(&mandir)))
43+
.arg(format!("--prefix={}", prepare_dir(prefix)))
44+
.arg(format!("--sysconfdir={}", prepare_dir(sysconfdir)))
45+
.arg(format!("--datadir={}", prepare_dir(datadir)))
46+
.arg(format!("--docdir={}", prepare_dir(docdir)))
47+
.arg(format!("--bindir={}", prepare_dir(bindir)))
48+
.arg(format!("--libdir={}", prepare_dir(libdir)))
49+
.arg(format!("--mandir={}", prepare_dir(mandir)))
8150
.arg("--disable-ldconfig");
8251
builder.run(&mut cmd);
8352
t!(fs::remove_dir_all(&empty_dir));
8453
}
8554

86-
fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
87-
let mut ret = match *destdir {
88-
Some(ref dest) => dest.clone(),
89-
None => return path.to_path_buf(),
90-
};
91-
for part in path.components() {
92-
if let Component::Normal(s) = part {
93-
ret.push(s)
55+
fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf {
56+
PathBuf::from(config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default)))
57+
}
58+
59+
fn prepare_dir(mut path: PathBuf) -> String {
60+
// The DESTDIR environment variable is a standard way to install software in a subdirectory
61+
// while keeping the original directory structure, even if the prefix or other directories
62+
// contain absolute paths.
63+
//
64+
// More information on the environment variable is available here:
65+
// https://www.gnu.org/prep/standards/html_node/DESTDIR.html
66+
if let Some(destdir) = env::var_os("DESTDIR").map(PathBuf::from) {
67+
let without_destdir = path.clone();
68+
path = destdir;
69+
// Custom .join() which ignores disk roots.
70+
for part in without_destdir.components() {
71+
if let Component::Normal(s) = part {
72+
path.push(s)
73+
}
9474
}
9575
}
96-
ret
76+
77+
// The installation command is not executed from the current directory, but from a temporary
78+
// directory. To prevent relative paths from breaking this converts relative paths to absolute
79+
// paths. std::fs::canonicalize is not used as that requires the path to actually be present.
80+
if path.is_relative() {
81+
path = std::env::current_dir().expect("failed to get the current directory").join(path);
82+
assert!(path.is_absolute(), "could not make the path relative");
83+
}
84+
85+
sanitize_sh(&path)
9786
}
9887

9988
macro_rules! install {

0 commit comments

Comments
 (0)