1 //! Implementation of the install aspects of the compiler.
3 //! This module is responsible for installing the standard library,
4 //! compiler, and documentation.
8 use std::path::{Component, PathBuf};
9 use std::process::Command;
13 use crate::dist::{self, sanitize_sh};
14 use crate::tarball::GeneratedTarball;
17 use crate::builder::{Builder, RunConfig, ShouldRun, Step};
18 use crate::config::{Config, TargetSelection};
21 builder: &Builder<'_>,
24 host: Option<TargetSelection>,
25 tarball: &GeneratedTarball,
27 builder.info(&format!("Install {} stage{} ({:?})", package, stage, host));
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
37 let empty_dir = builder.out.join("tmp/empty_dir");
38 t!(fs::create_dir_all(&empty_dir));
40 let mut cmd = Command::new("sh");
41 cmd.current_dir(&empty_dir)
42 .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
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)))
50 .arg("--disable-ldconfig");
51 builder.run(&mut cmd);
52 t!(fs::remove_dir_all(&empty_dir));
55 fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf {
56 PathBuf::from(config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default)))
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.
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();
69 // Custom .join() which ignores disk roots.
70 for part in without_destdir.components() {
71 if let Component::Normal(s) = part {
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");
88 macro_rules! install {
89 (($sel:ident, $builder:ident, $_config:ident),
93 only_hosts: $only_hosts:expr,
94 $run_item:block $(, $c:ident)*;)+) => {
96 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
98 pub compiler: Compiler,
99 pub target: TargetSelection,
104 fn should_build(config: &Config) -> bool {
105 config.extended && config.tools.as_ref()
106 .map_or(true, |t| t.contains($path))
110 impl Step for $name {
112 const DEFAULT: bool = true;
113 const ONLY_HOSTS: bool = $only_hosts;
114 $(const $c: bool = true;)*
116 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
117 let $_config = &run.builder.config;
118 run.path($path).default_condition($default_cond)
121 fn make_run(run: RunConfig<'_>) {
122 run.builder.ensure($name {
123 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
128 fn run($sel, $builder: &Builder<'_>) {
135 install!((self, builder, _config),
136 Docs, "src/doc", _config.docs, only_hosts: false, {
137 let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs");
138 install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
140 Std, "library/std", true, only_hosts: false, {
141 for target in &builder.targets {
142 let tarball = builder.ensure(dist::Std {
143 compiler: self.compiler,
145 }).expect("missing std");
146 install_sh(builder, "std", self.compiler.stage, Some(*target), &tarball);
149 Cargo, "cargo", Self::should_build(_config), only_hosts: true, {
150 let tarball = builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target });
151 install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball);
153 Rls, "rls", Self::should_build(_config), only_hosts: true, {
154 if let Some(tarball) = builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }) {
155 install_sh(builder, "rls", self.compiler.stage, Some(self.target), &tarball);
158 &format!("skipping Install RLS stage{} ({})", self.compiler.stage, self.target),
162 RustAnalyzer, "rust-analyzer", Self::should_build(_config), only_hosts: true, {
163 let tarball = builder
164 .ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target })
165 .expect("missing rust-analyzer");
166 install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball);
168 Clippy, "clippy", Self::should_build(_config), only_hosts: true, {
169 let tarball = builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target });
170 install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball);
172 Miri, "miri", Self::should_build(_config), only_hosts: true, {
173 if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) {
174 install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
177 &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target),
181 Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
182 if let Some(tarball) = builder.ensure(dist::Rustfmt {
183 compiler: self.compiler,
186 install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball);
189 &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target),
193 Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
194 let tarball = builder.ensure(dist::Analysis {
195 // Find the actual compiler (handling the full bootstrap option) which
196 // produced the save-analysis data because that data isn't copied
197 // through the sysroot uplifting.
198 compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target),
200 }).expect("missing analysis");
201 install_sh(builder, "analysis", self.compiler.stage, Some(self.target), &tarball);
203 Rustc, "src/librustc", true, only_hosts: true, {
204 let tarball = builder.ensure(dist::Rustc {
205 compiler: builder.compiler(builder.top_stage, self.target),
207 install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball);
211 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
218 const DEFAULT: bool = true;
219 const ONLY_HOSTS: bool = true;
221 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
222 let config = &run.builder.config;
223 let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src"));
224 run.path("src").default_condition(cond)
227 fn make_run(run: RunConfig<'_>) {
228 run.builder.ensure(Src { stage: run.builder.top_stage });
231 fn run(self, builder: &Builder<'_>) {
232 let tarball = builder.ensure(dist::Src);
233 install_sh(builder, "src", self.stage, None, &tarball);