]>
Commit | Line | Data |
---|---|---|
c30ab7b3 SL |
1 | //! Implementation of the install aspects of the compiler. |
2 | //! | |
3 | //! This module is responsible for installing the standard library, | |
4 | //! compiler, and documentation. | |
5 | ||
32a655c1 | 6 | use std::env; |
c30ab7b3 | 7 | use std::fs; |
923072b8 | 8 | use std::path::{Component, Path, PathBuf}; |
c30ab7b3 SL |
9 | use std::process::Command; |
10 | ||
5e7ed085 | 11 | use crate::util::t; |
48663c56 | 12 | |
923072b8 | 13 | use crate::dist; |
fc512014 | 14 | use crate::tarball::GeneratedTarball; |
dc9dc135 | 15 | use crate::Compiler; |
7cac9316 | 16 | |
0731742a | 17 | use crate::builder::{Builder, RunConfig, ShouldRun, Step}; |
3dfed10e | 18 | use crate::config::{Config, TargetSelection}; |
7cac9316 | 19 | |
cdc7bbd5 XL |
20 | #[cfg(target_os = "illumos")] |
21 | const SHELL: &str = "bash"; | |
22 | #[cfg(not(target_os = "illumos"))] | |
23 | const SHELL: &str = "sh"; | |
24 | ||
923072b8 FG |
25 | // We have to run a few shell scripts, which choke quite a bit on both `\` |
26 | // characters and on `C:\` paths, so normalize both of them away. | |
27 | fn sanitize_sh(path: &Path) -> String { | |
28 | let path = path.to_str().unwrap().replace("\\", "/"); | |
29 | return change_drive(unc_to_lfs(&path)).unwrap_or(path); | |
30 | ||
31 | fn unc_to_lfs(s: &str) -> &str { | |
32 | s.strip_prefix("//?/").unwrap_or(s) | |
33 | } | |
34 | ||
35 | fn change_drive(s: &str) -> Option<String> { | |
36 | let mut ch = s.chars(); | |
37 | let drive = ch.next().unwrap_or('C'); | |
38 | if ch.next() != Some(':') { | |
39 | return None; | |
40 | } | |
41 | if ch.next() != Some('/') { | |
42 | return None; | |
43 | } | |
44 | Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..])) | |
45 | } | |
46 | } | |
47 | ||
3b2f2976 | 48 | fn install_sh( |
9fa01778 | 49 | builder: &Builder<'_>, |
3b2f2976 | 50 | package: &str, |
3b2f2976 | 51 | stage: u32, |
3dfed10e | 52 | host: Option<TargetSelection>, |
fc512014 | 53 | tarball: &GeneratedTarball, |
3b2f2976 | 54 | ) { |
83c7162d | 55 | builder.info(&format!("Install {} stage{} ({:?})", package, stage, host)); |
3b2f2976 | 56 | |
fc512014 XL |
57 | let prefix = default_path(&builder.config.prefix, "/usr/local"); |
58 | let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc")); | |
59 | let datadir = prefix.join(default_path(&builder.config.datadir, "share")); | |
60 | let docdir = prefix.join(default_path(&builder.config.docdir, "share/doc/rust")); | |
61 | let mandir = prefix.join(default_path(&builder.config.mandir, "share/man")); | |
62 | let libdir = prefix.join(default_path(&builder.config.libdir, "lib")); | |
63 | let bindir = prefix.join(&builder.config.bindir); // Default in config.rs | |
3b2f2976 | 64 | |
83c7162d | 65 | let empty_dir = builder.out.join("tmp/empty_dir"); |
3b2f2976 | 66 | t!(fs::create_dir_all(&empty_dir)); |
7cac9316 | 67 | |
cdc7bbd5 | 68 | let mut cmd = Command::new(SHELL); |
3b2f2976 | 69 | cmd.current_dir(&empty_dir) |
fc512014 XL |
70 | .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"))) |
71 | .arg(format!("--prefix={}", prepare_dir(prefix))) | |
72 | .arg(format!("--sysconfdir={}", prepare_dir(sysconfdir))) | |
73 | .arg(format!("--datadir={}", prepare_dir(datadir))) | |
74 | .arg(format!("--docdir={}", prepare_dir(docdir))) | |
75 | .arg(format!("--bindir={}", prepare_dir(bindir))) | |
76 | .arg(format!("--libdir={}", prepare_dir(libdir))) | |
77 | .arg(format!("--mandir={}", prepare_dir(mandir))) | |
3b2f2976 | 78 | .arg("--disable-ldconfig"); |
83c7162d | 79 | builder.run(&mut cmd); |
3b2f2976 | 80 | t!(fs::remove_dir_all(&empty_dir)); |
c30ab7b3 | 81 | } |
32a655c1 | 82 | |
fc512014 | 83 | fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf { |
6a06907d | 84 | config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default)) |
fc512014 XL |
85 | } |
86 | ||
87 | fn prepare_dir(mut path: PathBuf) -> String { | |
88 | // The DESTDIR environment variable is a standard way to install software in a subdirectory | |
89 | // while keeping the original directory structure, even if the prefix or other directories | |
90 | // contain absolute paths. | |
91 | // | |
92 | // More information on the environment variable is available here: | |
93 | // https://www.gnu.org/prep/standards/html_node/DESTDIR.html | |
94 | if let Some(destdir) = env::var_os("DESTDIR").map(PathBuf::from) { | |
95 | let without_destdir = path.clone(); | |
96 | path = destdir; | |
97 | // Custom .join() which ignores disk roots. | |
98 | for part in without_destdir.components() { | |
99 | if let Component::Normal(s) = part { | |
100 | path.push(s) | |
101 | } | |
32a655c1 SL |
102 | } |
103 | } | |
fc512014 XL |
104 | |
105 | // The installation command is not executed from the current directory, but from a temporary | |
106 | // directory. To prevent relative paths from breaking this converts relative paths to absolute | |
107 | // paths. std::fs::canonicalize is not used as that requires the path to actually be present. | |
108 | if path.is_relative() { | |
109 | path = std::env::current_dir().expect("failed to get the current directory").join(path); | |
110 | assert!(path.is_absolute(), "could not make the path relative"); | |
111 | } | |
112 | ||
113 | sanitize_sh(&path) | |
32a655c1 | 114 | } |
3b2f2976 XL |
115 | |
116 | macro_rules! install { | |
117 | (($sel:ident, $builder:ident, $_config:ident), | |
118 | $($name:ident, | |
04454e1e | 119 | $condition_name: ident = $path_or_alias: literal, |
3b2f2976 XL |
120 | $default_cond:expr, |
121 | only_hosts: $only_hosts:expr, | |
122 | $run_item:block $(, $c:ident)*;)+) => { | |
123 | $( | |
124 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
125 | pub struct $name { | |
dc9dc135 | 126 | pub compiler: Compiler, |
3dfed10e | 127 | pub target: TargetSelection, |
3b2f2976 XL |
128 | } |
129 | ||
2c00a5a8 XL |
130 | impl $name { |
131 | #[allow(dead_code)] | |
132 | fn should_build(config: &Config) -> bool { | |
133 | config.extended && config.tools.as_ref() | |
04454e1e | 134 | .map_or(true, |t| t.contains($path_or_alias)) |
2c00a5a8 | 135 | } |
2c00a5a8 XL |
136 | } |
137 | ||
3b2f2976 XL |
138 | impl Step for $name { |
139 | type Output = (); | |
140 | const DEFAULT: bool = true; | |
3b2f2976 XL |
141 | const ONLY_HOSTS: bool = $only_hosts; |
142 | $(const $c: bool = true;)* | |
143 | ||
9fa01778 | 144 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
3b2f2976 | 145 | let $_config = &run.builder.config; |
04454e1e | 146 | run.$condition_name($path_or_alias).default_condition($default_cond) |
3b2f2976 XL |
147 | } |
148 | ||
9fa01778 | 149 | fn make_run(run: RunConfig<'_>) { |
3b2f2976 | 150 | run.builder.ensure($name { |
dc9dc135 | 151 | compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), |
3b2f2976 | 152 | target: run.target, |
3b2f2976 XL |
153 | }); |
154 | } | |
155 | ||
9fa01778 | 156 | fn run($sel, $builder: &Builder<'_>) { |
3b2f2976 XL |
157 | $run_item |
158 | } | |
159 | })+ | |
160 | } | |
161 | } | |
162 | ||
163 | install!((self, builder, _config), | |
04454e1e | 164 | Docs, path = "src/doc", _config.docs, only_hosts: false, { |
94222f64 XL |
165 | let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs"); |
166 | install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball); | |
3b2f2976 | 167 | }; |
04454e1e | 168 | Std, path = "library/std", true, only_hosts: false, { |
83c7162d | 169 | for target in &builder.targets { |
17df50a5 XL |
170 | // `expect` should be safe, only None when host != build, but this |
171 | // only runs when host == build | |
fc512014 | 172 | let tarball = builder.ensure(dist::Std { |
dc9dc135 | 173 | compiler: self.compiler, |
abe05a73 | 174 | target: *target |
fc512014 XL |
175 | }).expect("missing std"); |
176 | install_sh(builder, "std", self.compiler.stage, Some(*target), &tarball); | |
abe05a73 | 177 | } |
3b2f2976 | 178 | }; |
04454e1e | 179 | Cargo, alias = "cargo", Self::should_build(_config), only_hosts: true, { |
94222f64 XL |
180 | let tarball = builder |
181 | .ensure(dist::Cargo { compiler: self.compiler, target: self.target }) | |
182 | .expect("missing cargo"); | |
fc512014 | 183 | install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball); |
3b2f2976 | 184 | }; |
04454e1e | 185 | RustAnalyzer, alias = "rust-analyzer", Self::should_build(_config), only_hosts: true, { |
17df50a5 XL |
186 | if let Some(tarball) = |
187 | builder.ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target }) | |
188 | { | |
189 | install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball); | |
190 | } else { | |
191 | builder.info( | |
192 | &format!("skipping Install rust-analyzer stage{} ({})", self.compiler.stage, self.target), | |
193 | ); | |
194 | } | |
f035d41b | 195 | }; |
04454e1e | 196 | Clippy, alias = "clippy", Self::should_build(_config), only_hosts: true, { |
94222f64 XL |
197 | let tarball = builder |
198 | .ensure(dist::Clippy { compiler: self.compiler, target: self.target }) | |
199 | .expect("missing clippy"); | |
fc512014 | 200 | install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball); |
8faf50e0 | 201 | }; |
04454e1e | 202 | Miri, alias = "miri", Self::should_build(_config), only_hosts: true, { |
487cf647 FG |
203 | if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) { |
204 | install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball); | |
205 | } else { | |
206 | // Miri is only available on nightly | |
207 | builder.info( | |
208 | &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target), | |
209 | ); | |
210 | } | |
0731742a | 211 | }; |
f25598a0 FG |
212 | LlvmTools, alias = "llvm-tools", Self::should_build(_config), only_hosts: true, { |
213 | let tarball = builder | |
214 | .ensure(dist::LlvmTools { target: self.target }) | |
215 | .expect("missing llvm-tools"); | |
216 | install_sh(builder, "llvm-tools", self.compiler.stage, Some(self.target), &tarball); | |
217 | }; | |
04454e1e | 218 | Rustfmt, alias = "rustfmt", Self::should_build(_config), only_hosts: true, { |
fc512014 | 219 | if let Some(tarball) = builder.ensure(dist::Rustfmt { |
dc9dc135 XL |
220 | compiler: self.compiler, |
221 | target: self.target | |
fc512014 XL |
222 | }) { |
223 | install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball); | |
abe05a73 | 224 | } else { |
83c7162d | 225 | builder.info( |
dc9dc135 XL |
226 | &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target), |
227 | ); | |
abe05a73 | 228 | } |
3b2f2976 | 229 | }; |
04454e1e | 230 | RustDemangler, alias = "rust-demangler", Self::should_build(_config), only_hosts: true, { |
cdc7bbd5 XL |
231 | // Note: Even though `should_build` may return true for `extended` default tools, |
232 | // dist::RustDemangler may still return None, unless the target-dependent `profiler` config | |
233 | // is also true, or the `tools` array explicitly includes "rust-demangler". | |
234 | if let Some(tarball) = builder.ensure(dist::RustDemangler { | |
235 | compiler: self.compiler, | |
236 | target: self.target | |
237 | }) { | |
238 | install_sh(builder, "rust-demangler", self.compiler.stage, Some(self.target), &tarball); | |
239 | } else { | |
240 | builder.info( | |
241 | &format!("skipping Install RustDemangler stage{} ({})", | |
242 | self.compiler.stage, self.target), | |
243 | ); | |
244 | } | |
245 | }; | |
04454e1e | 246 | Analysis, alias = "analysis", Self::should_build(_config), only_hosts: false, { |
17df50a5 XL |
247 | // `expect` should be safe, only None with host != build, but this |
248 | // only uses the `build` compiler | |
fc512014 | 249 | let tarball = builder.ensure(dist::Analysis { |
dc9dc135 XL |
250 | // Find the actual compiler (handling the full bootstrap option) which |
251 | // produced the save-analysis data because that data isn't copied | |
252 | // through the sysroot uplifting. | |
253 | compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target), | |
3b2f2976 | 254 | target: self.target |
fc512014 XL |
255 | }).expect("missing analysis"); |
256 | install_sh(builder, "analysis", self.compiler.stage, Some(self.target), &tarball); | |
3b2f2976 | 257 | }; |
04454e1e | 258 | Rustc, path = "compiler/rustc", true, only_hosts: true, { |
fc512014 | 259 | let tarball = builder.ensure(dist::Rustc { |
60c5eb7d | 260 | compiler: builder.compiler(builder.top_stage, self.target), |
3b2f2976 | 261 | }); |
fc512014 | 262 | install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball); |
3b2f2976 XL |
263 | }; |
264 | ); | |
0531ce1d XL |
265 | |
266 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
267 | pub struct Src { | |
268 | pub stage: u32, | |
269 | } | |
270 | ||
271 | impl Step for Src { | |
272 | type Output = (); | |
273 | const DEFAULT: bool = true; | |
274 | const ONLY_HOSTS: bool = true; | |
275 | ||
9fa01778 | 276 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
0531ce1d | 277 | let config = &run.builder.config; |
dc9dc135 | 278 | let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src")); |
0531ce1d XL |
279 | run.path("src").default_condition(cond) |
280 | } | |
281 | ||
9fa01778 | 282 | fn make_run(run: RunConfig<'_>) { |
dc9dc135 | 283 | run.builder.ensure(Src { stage: run.builder.top_stage }); |
0531ce1d XL |
284 | } |
285 | ||
9fa01778 | 286 | fn run(self, builder: &Builder<'_>) { |
fc512014 XL |
287 | let tarball = builder.ensure(dist::Src); |
288 | install_sh(builder, "src", self.stage, None, &tarball); | |
0531ce1d XL |
289 | } |
290 | } |