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, Path, PathBuf}
;
9 use std
::process
::Command
;
14 use crate::tarball
::GeneratedTarball
;
17 use crate::builder
::{Builder, RunConfig, ShouldRun, Step}
;
18 use crate::config
::{Config, TargetSelection}
;
20 #[cfg(target_os = "illumos")]
21 const SHELL
: &str = "bash";
22 #[cfg(not(target_os = "illumos"))]
23 const SHELL
: &str = "sh";
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
);
31 fn unc_to_lfs(s
: &str) -> &str {
32 s
.strip_prefix("//?/").unwrap_or(s
)
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('
:'
) {
41 if ch
.next() != Some('
/'
) {
44 Some(format
!("/{}/{}", drive
, &s
[drive
.len_utf8() + 2..]))
49 builder
: &Builder
<'_
>,
52 host
: Option
<TargetSelection
>,
53 tarball
: &GeneratedTarball
,
55 builder
.info(&format
!("Install {} stage{} ({:?})", package
, stage
, host
));
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
65 let empty_dir
= builder
.out
.join("tmp/empty_dir");
66 t
!(fs
::create_dir_all(&empty_dir
));
68 let mut cmd
= Command
::new(SHELL
);
69 cmd
.current_dir(&empty_dir
)
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
)))
78 .arg("--disable-ldconfig");
79 builder
.run(&mut cmd
);
80 t
!(fs
::remove_dir_all(&empty_dir
));
83 fn default_path(config
: &Option
<PathBuf
>, default: &str) -> PathBuf
{
84 config
.as_ref().cloned().unwrap_or_else(|| PathBuf
::from(default))
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.
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();
97 // Custom .join() which ignores disk roots.
98 for part
in without_destdir
.components() {
99 if let Component
::Normal(s
) = part
{
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");
116 macro_rules
! install
{
117 (($sel
:ident
, $builder
:ident
, $_config
:ident
),
119 $condition_name
: ident
= $path_or_alias
: literal
,
121 only_hosts
: $only_hosts
:expr
,
122 $run_item
:block $
(, $c
:ident
)*;)+) => {
124 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
126 pub compiler
: Compiler
,
127 pub target
: TargetSelection
,
132 fn should_build(config
: &Config
) -> bool
{
133 config
.extended
&& config
.tools
.as_ref()
134 .map_or(true, |t
| t
.contains($path_or_alias
))
138 impl Step
for $name
{
140 const DEFAULT
: bool
= true;
141 const ONLY_HOSTS
: bool
= $only_hosts
;
142 $
(const $c
: bool
= true;)*
144 fn should_run(run
: ShouldRun
<'_
>) -> ShouldRun
<'_
> {
145 let $_config
= &run
.builder
.config
;
146 run
.$
condition_name($path_or_alias
).default_condition($default_cond
)
149 fn make_run(run
: RunConfig
<'_
>) {
150 run
.builder
.ensure($name
{
151 compiler
: run
.builder
.compiler(run
.builder
.top_stage
, run
.builder
.config
.build
),
156 fn run($sel
, $builder
: &Builder
<'_
>) {
163 install
!((self, builder
, _config
),
164 Docs
, path
= "src/doc", _config
.docs
, only_hosts
: false, {
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
);
168 Std
, path
= "library/std", true, only_hosts
: false, {
169 for target
in &builder
.targets
{
170 // `expect` should be safe, only None when host != build, but this
171 // only runs when host == build
172 let tarball
= builder
.ensure(dist
::Std
{
173 compiler
: self.compiler
,
175 }).expect("missing std");
176 install_sh(builder
, "std", self.compiler
.stage
, Some(*target
), &tarball
);
179 Cargo
, alias
= "cargo", Self::should_build(_config
), only_hosts
: true, {
180 let tarball
= builder
181 .ensure(dist
::Cargo { compiler: self.compiler, target: self.target }
)
182 .expect("missing cargo");
183 install_sh(builder
, "cargo", self.compiler
.stage
, Some(self.target
), &tarball
);
185 RustAnalyzer
, alias
= "rust-analyzer", Self::should_build(_config
), only_hosts
: true, {
186 if let Some(tarball
) =
187 builder
.ensure(dist
::RustAnalyzer { compiler: self.compiler, target: self.target }
)
189 install_sh(builder
, "rust-analyzer", self.compiler
.stage
, Some(self.target
), &tarball
);
192 &format
!("skipping Install rust-analyzer stage{} ({})", self.compiler
.stage
, self.target
),
196 Clippy
, alias
= "clippy", Self::should_build(_config
), only_hosts
: true, {
197 let tarball
= builder
198 .ensure(dist
::Clippy { compiler: self.compiler, target: self.target }
)
199 .expect("missing clippy");
200 install_sh(builder
, "clippy", self.compiler
.stage
, Some(self.target
), &tarball
);
202 Miri
, alias
= "miri", Self::should_build(_config
), only_hosts
: true, {
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
);
207 &format
!("skipping Install miri stage{} ({})", self.compiler
.stage
, self.target
),
211 Rustfmt
, alias
= "rustfmt", Self::should_build(_config
), only_hosts
: true, {
212 if let Some(tarball
) = builder
.ensure(dist
::Rustfmt
{
213 compiler
: self.compiler
,
216 install_sh(builder
, "rustfmt", self.compiler
.stage
, Some(self.target
), &tarball
);
219 &format
!("skipping Install Rustfmt stage{} ({})", self.compiler
.stage
, self.target
),
223 RustDemangler
, alias
= "rust-demangler", Self::should_build(_config
), only_hosts
: true, {
224 // Note: Even though `should_build` may return true for `extended` default tools,
225 // dist::RustDemangler may still return None, unless the target-dependent `profiler` config
226 // is also true, or the `tools` array explicitly includes "rust-demangler".
227 if let Some(tarball
) = builder
.ensure(dist
::RustDemangler
{
228 compiler
: self.compiler
,
231 install_sh(builder
, "rust-demangler", self.compiler
.stage
, Some(self.target
), &tarball
);
234 &format
!("skipping Install RustDemangler stage{} ({})",
235 self.compiler
.stage
, self.target
),
239 Analysis
, alias
= "analysis", Self::should_build(_config
), only_hosts
: false, {
240 // `expect` should be safe, only None with host != build, but this
241 // only uses the `build` compiler
242 let tarball
= builder
.ensure(dist
::Analysis
{
243 // Find the actual compiler (handling the full bootstrap option) which
244 // produced the save-analysis data because that data isn't copied
245 // through the sysroot uplifting.
246 compiler
: builder
.compiler_for(builder
.top_stage
, builder
.config
.build
, self.target
),
248 }).expect("missing analysis");
249 install_sh(builder
, "analysis", self.compiler
.stage
, Some(self.target
), &tarball
);
251 Rustc
, path
= "compiler/rustc", true, only_hosts
: true, {
252 let tarball
= builder
.ensure(dist
::Rustc
{
253 compiler
: builder
.compiler(builder
.top_stage
, self.target
),
255 install_sh(builder
, "rustc", self.compiler
.stage
, Some(self.target
), &tarball
);
259 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
266 const DEFAULT
: bool
= true;
267 const ONLY_HOSTS
: bool
= true;
269 fn should_run(run
: ShouldRun
<'_
>) -> ShouldRun
<'_
> {
270 let config
= &run
.builder
.config
;
271 let cond
= config
.extended
&& config
.tools
.as_ref().map_or(true, |t
| t
.contains("src"));
272 run
.path("src").default_condition(cond
)
275 fn make_run(run
: RunConfig
<'_
>) {
276 run
.builder
.ensure(Src { stage: run.builder.top_stage }
);
279 fn run(self, builder
: &Builder
<'_
>) {
280 let tarball
= builder
.ensure(dist
::Src
);
281 install_sh(builder
, "src", self.stage
, None
, &tarball
);