1use std::path::{Component, Path, PathBuf};
7use std::{env, fs};
8
9use crate::core::build_steps::dist;
10use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
11use crate::core::config::{Config, TargetSelection};
12use crate::utils::exec::command;
13use crate::utils::helpers::t;
14use crate::utils::tarball::GeneratedTarball;
15use crate::{Compiler, Kind};
16
17#[cfg(target_os = "illumos")]
18const SHELL: &str = "bash";
19#[cfg(not(target_os = "illumos"))]
20const SHELL: &str = "sh";
21
22fn sanitize_sh(path: &Path, is_cygwin: bool) -> String {
25 let path = path.to_str().unwrap().replace('\\', "/");
26 return if is_cygwin { path } else { change_drive(unc_to_lfs(&path)).unwrap_or(path) };
27
28 fn unc_to_lfs(s: &str) -> &str {
29 s.strip_prefix("//?/").unwrap_or(s)
30 }
31
32 fn change_drive(s: &str) -> Option<String> {
33 let mut ch = s.chars();
34 let drive = ch.next().unwrap_or('C');
35 if ch.next() != Some(':') {
36 return None;
37 }
38 if ch.next() != Some('/') {
39 return None;
40 }
41 Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..]))
42 }
43}
44
45fn is_dir_writable_for_user(dir: &Path) -> bool {
46 let tmp = dir.join(".tmp");
47 match fs::create_dir_all(&tmp) {
48 Ok(_) => {
49 fs::remove_dir_all(tmp).unwrap();
50 true
51 }
52 Err(e) => {
53 if e.kind() == std::io::ErrorKind::PermissionDenied {
54 false
55 } else {
56 panic!("Failed the write access check for the current user. {}", e);
57 }
58 }
59 }
60}
61
62fn install_sh(
63 builder: &Builder<'_>,
64 package: &str,
65 stage: u32,
66 host: Option<TargetSelection>,
67 tarball: &GeneratedTarball,
68) {
69 let _guard = builder.msg(Kind::Install, stage, package, host, host);
70
71 let prefix = default_path(&builder.config.prefix, "/usr/local");
72 let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc"));
73 let destdir_env = env::var_os("DESTDIR").map(PathBuf::from);
74 let is_cygwin = builder.config.build.is_cygwin();
75
76 if let Some(destdir) = &destdir_env {
84 assert!(is_dir_writable_for_user(destdir), "User doesn't have write access on DESTDIR.");
85 } else {
86 assert!(
87 is_dir_writable_for_user(&prefix),
88 "User doesn't have write access on `install.prefix` path in the `config.toml`.",
89 );
90 assert!(
91 is_dir_writable_for_user(&sysconfdir),
92 "User doesn't have write access on `install.sysconfdir` path in `config.toml`."
93 );
94 }
95
96 let datadir = prefix.join(default_path(&builder.config.datadir, "share"));
97 let docdir = prefix.join(default_path(&builder.config.docdir, &format!("share/doc/{package}")));
98 let mandir = prefix.join(default_path(&builder.config.mandir, "share/man"));
99 let libdir = prefix.join(default_path(&builder.config.libdir, "lib"));
100 let bindir = prefix.join(&builder.config.bindir); let empty_dir = builder.out.join("tmp/empty_dir");
103 t!(fs::create_dir_all(&empty_dir));
104
105 let mut cmd = command(SHELL);
106 cmd.current_dir(&empty_dir)
107 .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"), is_cygwin))
108 .arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix, is_cygwin)))
109 .arg(format!("--sysconfdir={}", prepare_dir(&destdir_env, sysconfdir, is_cygwin)))
110 .arg(format!("--datadir={}", prepare_dir(&destdir_env, datadir, is_cygwin)))
111 .arg(format!("--docdir={}", prepare_dir(&destdir_env, docdir, is_cygwin)))
112 .arg(format!("--bindir={}", prepare_dir(&destdir_env, bindir, is_cygwin)))
113 .arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir, is_cygwin)))
114 .arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir, is_cygwin)))
115 .arg("--disable-ldconfig");
116 cmd.run(builder);
117 t!(fs::remove_dir_all(&empty_dir));
118}
119
120fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf {
121 config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default))
122}
123
124fn prepare_dir(destdir_env: &Option<PathBuf>, mut path: PathBuf, is_cygwin: bool) -> String {
125 if let Some(destdir) = destdir_env {
132 let without_destdir = path.clone();
133 path.clone_from(destdir);
134 for part in without_destdir.components() {
136 if let Component::Normal(s) = part {
137 path.push(s)
138 }
139 }
140 }
141
142 if path.is_relative() {
146 path = std::env::current_dir().expect("failed to get the current directory").join(path);
147 assert!(path.is_absolute(), "could not make the path relative");
148 }
149
150 sanitize_sh(&path, is_cygwin)
151}
152
153macro_rules! install {
154 (($sel:ident, $builder:ident, $_config:ident),
155 $($name:ident,
156 $condition_name: ident = $path_or_alias: literal,
157 $default_cond:expr,
158 only_hosts: $only_hosts:expr,
159 $run_item:block $(, $c:ident)*;)+) => {
160 $(
161 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
162 pub struct $name {
163 pub compiler: Compiler,
164 pub target: TargetSelection,
165 }
166
167 impl $name {
168 #[allow(dead_code)]
169 fn should_build(config: &Config) -> bool {
170 config.extended && config.tools.as_ref()
171 .map_or(true, |t| t.contains($path_or_alias))
172 }
173 }
174
175 impl Step for $name {
176 type Output = ();
177 const DEFAULT: bool = true;
178 const ONLY_HOSTS: bool = $only_hosts;
179 $(const $c: bool = true;)*
180
181 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
182 let $_config = &run.builder.config;
183 run.$condition_name($path_or_alias).default_condition($default_cond)
184 }
185
186 fn make_run(run: RunConfig<'_>) {
187 run.builder.ensure($name {
188 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
189 target: run.target,
190 });
191 }
192
193 fn run($sel, $builder: &Builder<'_>) {
194 $run_item
195 }
196 })+
197 }
198}
199
200install!((self, builder, _config),
201 Docs, path = "src/doc", _config.docs, only_hosts: false, {
202 let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs");
203 install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
204 };
205 Std, path = "library/std", true, only_hosts: false, {
206 let tarball = builder.ensure(dist::Std {
209 compiler: self.compiler,
210 target: self.target
211 }).expect("missing std");
212 install_sh(builder, "std", self.compiler.stage, Some(self.target), &tarball);
213 };
214 Cargo, alias = "cargo", Self::should_build(_config), only_hosts: true, {
215 let tarball = builder
216 .ensure(dist::Cargo { compiler: self.compiler, target: self.target })
217 .expect("missing cargo");
218 install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball);
219 };
220 RustAnalyzer, alias = "rust-analyzer", Self::should_build(_config), only_hosts: true, {
221 if let Some(tarball) =
222 builder.ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target })
223 {
224 install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball);
225 } else {
226 builder.info(
227 &format!("skipping Install rust-analyzer stage{} ({})", self.compiler.stage, self.target),
228 );
229 }
230 };
231 Clippy, alias = "clippy", Self::should_build(_config), only_hosts: true, {
232 let tarball = builder
233 .ensure(dist::Clippy { compiler: self.compiler, target: self.target })
234 .expect("missing clippy");
235 install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball);
236 };
237 Miri, alias = "miri", Self::should_build(_config), only_hosts: true, {
238 if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) {
239 install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
240 } else {
241 builder.info(
243 &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target),
244 );
245 }
246 };
247 LlvmTools, alias = "llvm-tools", Self::should_build(_config), only_hosts: true, {
248 if let Some(tarball) = builder.ensure(dist::LlvmTools { target: self.target }) {
249 install_sh(builder, "llvm-tools", self.compiler.stage, Some(self.target), &tarball);
250 } else {
251 builder.info(
252 &format!("skipping llvm-tools stage{} ({}): external LLVM", self.compiler.stage, self.target),
253 );
254 }
255 };
256 Rustfmt, alias = "rustfmt", Self::should_build(_config), only_hosts: true, {
257 if let Some(tarball) = builder.ensure(dist::Rustfmt {
258 compiler: self.compiler,
259 target: self.target
260 }) {
261 install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball);
262 } else {
263 builder.info(
264 &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target),
265 );
266 }
267 };
268 Rustc, path = "compiler/rustc", true, only_hosts: true, {
269 let tarball = builder.ensure(dist::Rustc {
270 compiler: builder.compiler(builder.top_stage, self.target),
271 });
272 install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball);
273 };
274 RustcCodegenCranelift, alias = "rustc-codegen-cranelift", Self::should_build(_config), only_hosts: true, {
275 if let Some(tarball) = builder.ensure(dist::CodegenBackend {
276 compiler: self.compiler,
277 backend: "cranelift".to_string(),
278 }) {
279 install_sh(builder, "rustc-codegen-cranelift", self.compiler.stage, Some(self.target), &tarball);
280 } else {
281 builder.info(
282 &format!("skipping Install CodegenBackend(\"cranelift\") stage{} ({})",
283 self.compiler.stage, self.target),
284 );
285 }
286 };
287 LlvmBitcodeLinker, alias = "llvm-bitcode-linker", Self::should_build(_config), only_hosts: true, {
288 if let Some(tarball) = builder.ensure(dist::LlvmBitcodeLinker { compiler: self.compiler, target: self.target }) {
289 install_sh(builder, "llvm-bitcode-linker", self.compiler.stage, Some(self.target), &tarball);
290 } else {
291 builder.info(
292 &format!("skipping llvm-bitcode-linker stage{} ({})", self.compiler.stage, self.target),
293 );
294 }
295 };
296);
297
298#[derive(Debug, Clone, Hash, PartialEq, Eq)]
299pub struct Src {
300 pub stage: u32,
301}
302
303impl Step for Src {
304 type Output = ();
305 const DEFAULT: bool = true;
306 const ONLY_HOSTS: bool = true;
307
308 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
309 let config = &run.builder.config;
310 let cond = config.extended && config.tools.as_ref().is_none_or(|t| t.contains("src"));
311 run.path("src").default_condition(cond)
312 }
313
314 fn make_run(run: RunConfig<'_>) {
315 run.builder.ensure(Src { stage: run.builder.top_stage });
316 }
317
318 fn run(self, builder: &Builder<'_>) {
319 let tarball = builder.ensure(dist::Src);
320 install_sh(builder, "src", self.stage, None, &tarball);
321 }
322}