bootstrap/core/build_steps/
dist.rs

1//! Implementation of the various distribution aspects of the compiler.
2//!
3//! This module is responsible for creating tarballs of the standard library,
4//! compiler, and documentation. This ends up being what we distribute to
5//! everyone as well.
6//!
7//! No tarball is actually created literally in this file, but rather we shell
8//! out to `rust-installer` still. This may one day be replaced with bits and
9//! pieces of `rustup.rs`!
10
11use std::collections::HashSet;
12use std::ffi::OsStr;
13use std::io::Write;
14use std::path::{Path, PathBuf};
15use std::{env, fs};
16
17use object::BinaryFormat;
18use object::read::archive::ArchiveFile;
19#[cfg(feature = "tracing")]
20use tracing::instrument;
21
22use crate::core::build_steps::compile::{get_codegen_backend_file, normalize_codegen_backend_name};
23use crate::core::build_steps::doc::DocumentationFormat;
24use crate::core::build_steps::tool::{self, RustcPrivateCompilers, Tool};
25use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor};
26use crate::core::build_steps::{compile, llvm};
27use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata};
28use crate::core::config::TargetSelection;
29use crate::utils::build_stamp::{self, BuildStamp};
30use crate::utils::channel::{self, Info};
31use crate::utils::exec::{BootstrapCommand, command};
32use crate::utils::helpers::{
33    exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit,
34};
35use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball};
36use crate::{CodegenBackendKind, Compiler, DependencyType, FileType, LLVM_TOOLS, Mode, trace};
37
38pub fn pkgname(builder: &Builder<'_>, component: &str) -> String {
39    format!("{}-{}", component, builder.rust_package_vers())
40}
41
42pub(crate) fn distdir(builder: &Builder<'_>) -> PathBuf {
43    builder.out.join("dist")
44}
45
46pub fn tmpdir(builder: &Builder<'_>) -> PathBuf {
47    builder.out.join("tmp/dist")
48}
49
50fn should_build_extended_tool(builder: &Builder<'_>, tool: &str) -> bool {
51    if !builder.config.extended {
52        return false;
53    }
54    builder.config.tools.as_ref().is_none_or(|tools| tools.contains(tool))
55}
56
57#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
58pub struct Docs {
59    pub host: TargetSelection,
60}
61
62impl Step for Docs {
63    type Output = Option<GeneratedTarball>;
64    const DEFAULT: bool = true;
65
66    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
67        let default = run.builder.config.docs;
68        run.alias("rust-docs").default_condition(default)
69    }
70
71    fn make_run(run: RunConfig<'_>) {
72        run.builder.ensure(Docs { host: run.target });
73    }
74
75    /// Builds the `rust-docs` installer component.
76    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
77        let host = self.host;
78        builder.default_doc(&[]);
79
80        let dest = "share/doc/rust/html";
81
82        let mut tarball = Tarball::new(builder, "rust-docs", &host.triple);
83        tarball.set_product_name("Rust Documentation");
84        tarball.add_bulk_dir(builder.doc_out(host), dest);
85        tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, FileType::Regular);
86        Some(tarball.generate())
87    }
88
89    fn metadata(&self) -> Option<StepMetadata> {
90        Some(StepMetadata::dist("docs", self.host))
91    }
92}
93
94#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
95pub struct JsonDocs {
96    build_compiler: Compiler,
97    target: TargetSelection,
98}
99
100impl Step for JsonDocs {
101    type Output = Option<GeneratedTarball>;
102    const DEFAULT: bool = true;
103
104    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
105        let default = run.builder.config.docs;
106        run.alias("rust-docs-json").default_condition(default)
107    }
108
109    fn make_run(run: RunConfig<'_>) {
110        run.builder.ensure(JsonDocs {
111            build_compiler: run.builder.compiler(run.builder.top_stage, run.builder.host_target),
112            target: run.target,
113        });
114    }
115
116    /// Builds the `rust-docs-json` installer component.
117    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
118        let target = self.target;
119        let directory = builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler(
120            self.build_compiler,
121            target,
122            DocumentationFormat::Json,
123        ));
124
125        let dest = "share/doc/rust/json";
126
127        let mut tarball = Tarball::new(builder, "rust-docs-json", &target.triple);
128        tarball.set_product_name("Rust Documentation In JSON Format");
129        tarball.is_preview(true);
130        tarball.add_bulk_dir(directory, dest);
131        Some(tarball.generate())
132    }
133}
134
135#[derive(Debug, Clone, Hash, PartialEq, Eq)]
136pub struct RustcDocs {
137    pub host: TargetSelection,
138}
139
140impl Step for RustcDocs {
141    type Output = Option<GeneratedTarball>;
142    const DEFAULT: bool = true;
143    const IS_HOST: bool = true;
144
145    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
146        let builder = run.builder;
147        run.alias("rustc-docs").default_condition(builder.config.compiler_docs)
148    }
149
150    fn make_run(run: RunConfig<'_>) {
151        run.builder.ensure(RustcDocs { host: run.target });
152    }
153
154    /// Builds the `rustc-docs` installer component.
155    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
156        let host = self.host;
157        builder.default_doc(&[]);
158
159        let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple);
160        tarball.set_product_name("Rustc Documentation");
161        tarball.add_bulk_dir(builder.compiler_doc_out(host), "share/doc/rust/html/rustc");
162        Some(tarball.generate())
163    }
164}
165
166fn find_files(files: &[&str], path: &[PathBuf]) -> Vec<PathBuf> {
167    let mut found = Vec::with_capacity(files.len());
168
169    for file in files {
170        let file_path = path.iter().map(|dir| dir.join(file)).find(|p| p.exists());
171
172        if let Some(file_path) = file_path {
173            found.push(file_path);
174        } else {
175            panic!("Could not find '{file}' in {path:?}");
176        }
177    }
178
179    found
180}
181
182fn make_win_dist(plat_root: &Path, target: TargetSelection, builder: &Builder<'_>) {
183    if builder.config.dry_run() {
184        return;
185    }
186
187    let (bin_path, lib_path) = get_cc_search_dirs(target, builder);
188
189    let compiler = if target == "i686-pc-windows-gnu" {
190        "i686-w64-mingw32-gcc.exe"
191    } else if target == "x86_64-pc-windows-gnu" {
192        "x86_64-w64-mingw32-gcc.exe"
193    } else {
194        "gcc.exe"
195    };
196    let target_tools = [compiler, "ld.exe", "dlltool.exe", "libwinpthread-1.dll"];
197
198    // Libraries necessary to link the windows-gnu toolchains.
199    // System libraries will be preferred if they are available (see #67429).
200    let target_libs = [
201        //MinGW libs
202        "libgcc.a",
203        "libgcc_eh.a",
204        "libgcc_s.a",
205        "libm.a",
206        "libmingw32.a",
207        "libmingwex.a",
208        "libstdc++.a",
209        "libiconv.a",
210        "libmoldname.a",
211        "libpthread.a",
212        // Windows import libs
213        // This *should* contain only the set of libraries necessary to link the standard library,
214        // however we've had problems with people accidentally depending on extra libs being here,
215        // so we can't easily remove entries.
216        "libadvapi32.a",
217        "libbcrypt.a",
218        "libcomctl32.a",
219        "libcomdlg32.a",
220        "libcredui.a",
221        "libcrypt32.a",
222        "libdbghelp.a",
223        "libgdi32.a",
224        "libimagehlp.a",
225        "libiphlpapi.a",
226        "libkernel32.a",
227        "libmsimg32.a",
228        "libmsvcrt.a",
229        "libntdll.a",
230        "libodbc32.a",
231        "libole32.a",
232        "liboleaut32.a",
233        "libopengl32.a",
234        "libpsapi.a",
235        "librpcrt4.a",
236        "libsecur32.a",
237        "libsetupapi.a",
238        "libshell32.a",
239        "libsynchronization.a",
240        "libuser32.a",
241        "libuserenv.a",
242        "libuuid.a",
243        "libwinhttp.a",
244        "libwinmm.a",
245        "libwinspool.a",
246        "libws2_32.a",
247        "libwsock32.a",
248    ];
249
250    //Find mingw artifacts we want to bundle
251    let target_tools = find_files(&target_tools, &bin_path);
252    let target_libs = find_files(&target_libs, &lib_path);
253
254    //Copy platform tools to platform-specific bin directory
255    let plat_target_bin_self_contained_dir =
256        plat_root.join("lib/rustlib").join(target).join("bin/self-contained");
257    fs::create_dir_all(&plat_target_bin_self_contained_dir)
258        .expect("creating plat_target_bin_self_contained_dir failed");
259    for src in target_tools {
260        builder.copy_link_to_folder(&src, &plat_target_bin_self_contained_dir);
261    }
262
263    // Warn windows-gnu users that the bundled GCC cannot compile C files
264    builder.create(
265        &plat_target_bin_self_contained_dir.join("GCC-WARNING.txt"),
266        "gcc.exe contained in this folder cannot be used for compiling C files - it is only \
267         used as a linker. In order to be able to compile projects containing C code use \
268         the GCC provided by MinGW or Cygwin.",
269    );
270
271    //Copy platform libs to platform-specific lib directory
272    let plat_target_lib_self_contained_dir =
273        plat_root.join("lib/rustlib").join(target).join("lib/self-contained");
274    fs::create_dir_all(&plat_target_lib_self_contained_dir)
275        .expect("creating plat_target_lib_self_contained_dir failed");
276    for src in target_libs {
277        builder.copy_link_to_folder(&src, &plat_target_lib_self_contained_dir);
278    }
279}
280
281fn runtime_dll_dist(rust_root: &Path, target: TargetSelection, builder: &Builder<'_>) {
282    if builder.config.dry_run() {
283        return;
284    }
285
286    let (bin_path, libs_path) = get_cc_search_dirs(target, builder);
287
288    let mut rustc_dlls = vec![];
289    // windows-gnu and windows-gnullvm require different runtime libs
290    if target.ends_with("windows-gnu") {
291        rustc_dlls.push("libwinpthread-1.dll");
292        if target.starts_with("i686-") {
293            rustc_dlls.push("libgcc_s_dw2-1.dll");
294        } else {
295            rustc_dlls.push("libgcc_s_seh-1.dll");
296        }
297    } else if target.ends_with("windows-gnullvm") {
298        rustc_dlls.push("libunwind.dll");
299    } else {
300        panic!("Vendoring of runtime DLLs for `{target}` is not supported`");
301    }
302    // FIXME(#144656): Remove this whole `let ...`
303    let bin_path = if target.ends_with("windows-gnullvm") && builder.host_target != target {
304        bin_path
305            .into_iter()
306            .chain(libs_path.iter().map(|path| path.with_file_name("bin")))
307            .collect()
308    } else {
309        bin_path
310    };
311    let rustc_dlls = find_files(&rustc_dlls, &bin_path);
312
313    // Copy runtime dlls next to rustc.exe
314    let rust_bin_dir = rust_root.join("bin/");
315    fs::create_dir_all(&rust_bin_dir).expect("creating rust_bin_dir failed");
316    for src in &rustc_dlls {
317        builder.copy_link_to_folder(src, &rust_bin_dir);
318    }
319
320    if builder.config.lld_enabled {
321        // rust-lld.exe also needs runtime dlls
322        let rust_target_bin_dir = rust_root.join("lib/rustlib").join(target).join("bin");
323        fs::create_dir_all(&rust_target_bin_dir).expect("creating rust_target_bin_dir failed");
324        for src in &rustc_dlls {
325            builder.copy_link_to_folder(src, &rust_target_bin_dir);
326        }
327    }
328}
329
330fn get_cc_search_dirs(
331    target: TargetSelection,
332    builder: &Builder<'_>,
333) -> (Vec<PathBuf>, Vec<PathBuf>) {
334    //Ask gcc where it keeps its stuff
335    let mut cmd = command(builder.cc(target));
336    cmd.arg("-print-search-dirs");
337    let gcc_out = cmd.run_capture_stdout(builder).stdout();
338
339    let mut bin_path: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect();
340    let mut lib_path = Vec::new();
341
342    for line in gcc_out.lines() {
343        let idx = line.find(':').unwrap();
344        let key = &line[..idx];
345        let trim_chars: &[_] = &[' ', '='];
346        let value = env::split_paths(line[(idx + 1)..].trim_start_matches(trim_chars));
347
348        if key == "programs" {
349            bin_path.extend(value);
350        } else if key == "libraries" {
351            lib_path.extend(value);
352        }
353    }
354    (bin_path, lib_path)
355}
356
357#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
358pub struct Mingw {
359    pub host: TargetSelection,
360}
361
362impl Step for Mingw {
363    type Output = Option<GeneratedTarball>;
364    const DEFAULT: bool = true;
365
366    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
367        run.alias("rust-mingw")
368    }
369
370    fn make_run(run: RunConfig<'_>) {
371        run.builder.ensure(Mingw { host: run.target });
372    }
373
374    /// Builds the `rust-mingw` installer component.
375    ///
376    /// This contains all the bits and pieces to run the MinGW Windows targets
377    /// without any extra installed software (e.g., we bundle gcc, libraries, etc).
378    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
379        let host = self.host;
380        if !host.ends_with("pc-windows-gnu") || !builder.config.dist_include_mingw_linker {
381            return None;
382        }
383
384        let mut tarball = Tarball::new(builder, "rust-mingw", &host.triple);
385        tarball.set_product_name("Rust MinGW");
386
387        make_win_dist(tarball.image_dir(), host, builder);
388
389        Some(tarball.generate())
390    }
391
392    fn metadata(&self) -> Option<StepMetadata> {
393        Some(StepMetadata::dist("mingw", self.host))
394    }
395}
396
397#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
398pub struct Rustc {
399    pub compiler: Compiler,
400}
401
402impl Step for Rustc {
403    type Output = GeneratedTarball;
404    const DEFAULT: bool = true;
405    const IS_HOST: bool = true;
406
407    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
408        run.alias("rustc")
409    }
410
411    fn make_run(run: RunConfig<'_>) {
412        run.builder
413            .ensure(Rustc { compiler: run.builder.compiler(run.builder.top_stage, run.target) });
414    }
415
416    /// Creates the `rustc` installer component.
417    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
418        let compiler = self.compiler;
419        let host = self.compiler.host;
420
421        let tarball = Tarball::new(builder, "rustc", &host.triple);
422
423        // Prepare the rustc "image", what will actually end up getting installed
424        prepare_image(builder, compiler, tarball.image_dir());
425
426        // On MinGW we've got a few runtime DLL dependencies that we need to
427        // include.
428        // On 32-bit MinGW we're always including a DLL which needs some extra
429        // licenses to distribute. On 64-bit MinGW we don't actually distribute
430        // anything requiring us to distribute a license, but it's likely the
431        // install will *also* include the rust-mingw package, which also needs
432        // licenses, so to be safe we just include it here in all MinGW packages.
433        if host.contains("pc-windows-gnu") && builder.config.dist_include_mingw_linker {
434            runtime_dll_dist(tarball.image_dir(), host, builder);
435            tarball.add_dir(builder.src.join("src/etc/third-party"), "share/doc");
436        }
437
438        return tarball.generate();
439
440        fn prepare_image(builder: &Builder<'_>, compiler: Compiler, image: &Path) {
441            let host = compiler.host;
442            let src = builder.sysroot(compiler);
443
444            // Copy rustc binary
445            t!(fs::create_dir_all(image.join("bin")));
446            builder.cp_link_r(&src.join("bin"), &image.join("bin"));
447
448            // If enabled, copy rustdoc binary
449            if builder
450                .config
451                .tools
452                .as_ref()
453                .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc"))
454            {
455                let rustdoc = builder.rustdoc_for_compiler(compiler);
456                builder.install(&rustdoc, &image.join("bin"), FileType::Executable);
457            }
458
459            let ra_proc_macro_srv_compiler =
460                builder.compiler_for(compiler.stage, builder.config.host_target, compiler.host);
461            let compilers = RustcPrivateCompilers::from_build_compiler(
462                builder,
463                ra_proc_macro_srv_compiler,
464                compiler.host,
465            );
466
467            if let Some(ra_proc_macro_srv) = builder.ensure_if_default(
468                tool::RustAnalyzerProcMacroSrv::from_compilers(compilers),
469                builder.kind,
470            ) {
471                let dst = image.join("libexec");
472                builder.install(&ra_proc_macro_srv.tool_path, &dst, FileType::Executable);
473            }
474
475            let libdir_relative = builder.libdir_relative(compiler);
476
477            // Copy runtime DLLs needed by the compiler
478            if libdir_relative.to_str() != Some("bin") {
479                let libdir = builder.rustc_libdir(compiler);
480                for entry in builder.read_dir(&libdir) {
481                    // A safeguard that we will not ship libgccjit.so from the libdir, in case the
482                    // GCC codegen backend is enabled by default.
483                    // Long-term we should probably split the config options for:
484                    // - Include cg_gcc in the rustc sysroot by default
485                    // - Run dist of a specific codegen backend in `x dist` by default
486                    if is_dylib(&entry.path())
487                        && !entry
488                            .path()
489                            .file_name()
490                            .and_then(|n| n.to_str())
491                            .map(|n| n.contains("libgccjit"))
492                            .unwrap_or(false)
493                    {
494                        // Don't use custom libdir here because ^lib/ will be resolved again
495                        // with installer
496                        builder.install(&entry.path(), &image.join("lib"), FileType::NativeLibrary);
497                    }
498                }
499            }
500
501            // Copy libLLVM.so to the lib dir as well, if needed. While not
502            // technically needed by rustc itself it's needed by lots of other
503            // components like the llvm tools and LLD. LLD is included below and
504            // tools/LLDB come later, so let's just throw it in the rustc
505            // component for now.
506            maybe_install_llvm_runtime(builder, host, image);
507
508            let dst_dir = image.join("lib/rustlib").join(host).join("bin");
509            t!(fs::create_dir_all(&dst_dir));
510
511            // Copy over lld if it's there
512            if builder.config.lld_enabled {
513                let src_dir = builder.sysroot_target_bindir(compiler, host);
514                let rust_lld = exe("rust-lld", compiler.host);
515                builder.copy_link(
516                    &src_dir.join(&rust_lld),
517                    &dst_dir.join(&rust_lld),
518                    FileType::Executable,
519                );
520                let self_contained_lld_src_dir = src_dir.join("gcc-ld");
521                let self_contained_lld_dst_dir = dst_dir.join("gcc-ld");
522                t!(fs::create_dir(&self_contained_lld_dst_dir));
523                for name in crate::LLD_FILE_NAMES {
524                    let exe_name = exe(name, compiler.host);
525                    builder.copy_link(
526                        &self_contained_lld_src_dir.join(&exe_name),
527                        &self_contained_lld_dst_dir.join(&exe_name),
528                        FileType::Executable,
529                    );
530                }
531            }
532
533            if builder.config.llvm_enabled(compiler.host) && builder.config.llvm_tools_enabled {
534                let src_dir = builder.sysroot_target_bindir(compiler, host);
535                let llvm_objcopy = exe("llvm-objcopy", compiler.host);
536                let rust_objcopy = exe("rust-objcopy", compiler.host);
537                builder.copy_link(
538                    &src_dir.join(&llvm_objcopy),
539                    &dst_dir.join(&rust_objcopy),
540                    FileType::Executable,
541                );
542            }
543
544            if builder.tool_enabled("wasm-component-ld") {
545                let src_dir = builder.sysroot_target_bindir(compiler, host);
546                let ld = exe("wasm-component-ld", compiler.host);
547                builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld), FileType::Executable);
548            }
549
550            // Man pages
551            t!(fs::create_dir_all(image.join("share/man/man1")));
552            let man_src = builder.src.join("src/doc/man");
553            let man_dst = image.join("share/man/man1");
554
555            // don't use our `bootstrap::{copy_internal, cp_r}`, because those try
556            // to hardlink, and we don't want to edit the source templates
557            for file_entry in builder.read_dir(&man_src) {
558                let page_src = file_entry.path();
559                let page_dst = man_dst.join(file_entry.file_name());
560                let src_text = t!(std::fs::read_to_string(&page_src));
561                let new_text = src_text.replace("<INSERT VERSION HERE>", &builder.version);
562                t!(std::fs::write(&page_dst, &new_text));
563                t!(fs::copy(&page_src, &page_dst));
564            }
565
566            // Debugger scripts
567            builder.ensure(DebuggerScripts { sysroot: image.to_owned(), host });
568
569            // HTML copyright files
570            let file_list = builder.ensure(super::run::GenerateCopyright);
571            for file in file_list {
572                builder.install(&file, &image.join("share/doc/rust"), FileType::Regular);
573            }
574
575            // README
576            builder.install(
577                &builder.src.join("README.md"),
578                &image.join("share/doc/rust"),
579                FileType::Regular,
580            );
581
582            // The REUSE-managed license files
583            let license = |path: &Path| {
584                builder.install(path, &image.join("share/doc/rust/licenses"), FileType::Regular);
585            };
586            for entry in t!(std::fs::read_dir(builder.src.join("LICENSES"))).flatten() {
587                license(&entry.path());
588            }
589        }
590    }
591
592    fn metadata(&self) -> Option<StepMetadata> {
593        Some(StepMetadata::dist("rustc", self.compiler.host))
594    }
595}
596
597#[derive(Debug, Clone, Hash, PartialEq, Eq)]
598pub struct DebuggerScripts {
599    pub sysroot: PathBuf,
600    pub host: TargetSelection,
601}
602
603impl Step for DebuggerScripts {
604    type Output = ();
605
606    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
607        run.never()
608    }
609
610    /// Copies debugger scripts for `target` into the `sysroot` specified.
611    fn run(self, builder: &Builder<'_>) {
612        let host = self.host;
613        let sysroot = self.sysroot;
614        let dst = sysroot.join("lib/rustlib/etc");
615        t!(fs::create_dir_all(&dst));
616        let cp_debugger_script = |file: &str| {
617            builder.install(&builder.src.join("src/etc/").join(file), &dst, FileType::Regular);
618        };
619        if host.contains("windows-msvc") {
620            // windbg debugger scripts
621            builder.install(
622                &builder.src.join("src/etc/rust-windbg.cmd"),
623                &sysroot.join("bin"),
624                FileType::Script,
625            );
626
627            cp_debugger_script("natvis/intrinsic.natvis");
628            cp_debugger_script("natvis/liballoc.natvis");
629            cp_debugger_script("natvis/libcore.natvis");
630            cp_debugger_script("natvis/libstd.natvis");
631        }
632
633        cp_debugger_script("rust_types.py");
634
635        // gdb debugger scripts
636        builder.install(
637            &builder.src.join("src/etc/rust-gdb"),
638            &sysroot.join("bin"),
639            FileType::Script,
640        );
641        builder.install(
642            &builder.src.join("src/etc/rust-gdbgui"),
643            &sysroot.join("bin"),
644            FileType::Script,
645        );
646
647        cp_debugger_script("gdb_load_rust_pretty_printers.py");
648        cp_debugger_script("gdb_lookup.py");
649        cp_debugger_script("gdb_providers.py");
650
651        // lldb debugger scripts
652        builder.install(
653            &builder.src.join("src/etc/rust-lldb"),
654            &sysroot.join("bin"),
655            FileType::Script,
656        );
657
658        cp_debugger_script("lldb_lookup.py");
659        cp_debugger_script("lldb_providers.py");
660        cp_debugger_script("lldb_commands")
661    }
662}
663
664fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool {
665    // The only true set of target libraries came from the build triple, so
666    // let's reduce redundant work by only producing archives from that host.
667    if !builder.config.is_host_target(compiler.host) {
668        builder.info("\tskipping, not a build host");
669        true
670    } else {
671        false
672    }
673}
674
675/// Check that all objects in rlibs for UEFI targets are COFF. This
676/// ensures that the C compiler isn't producing ELF objects, which would
677/// not link correctly with the COFF objects.
678fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &BuildStamp) {
679    if !target.ends_with("-uefi") {
680        return;
681    }
682
683    for (path, _) in builder.read_stamp_file(stamp) {
684        if path.extension() != Some(OsStr::new("rlib")) {
685            continue;
686        }
687
688        let data = t!(fs::read(&path));
689        let data = data.as_slice();
690        let archive = t!(ArchiveFile::parse(data));
691        for member in archive.members() {
692            let member = t!(member);
693            let member_data = t!(member.data(data));
694
695            let is_coff = match object::File::parse(member_data) {
696                Ok(member_file) => member_file.format() == BinaryFormat::Coff,
697                Err(_) => false,
698            };
699
700            if !is_coff {
701                let member_name = String::from_utf8_lossy(member.name());
702                panic!("member {} in {} is not COFF", member_name, path.display());
703            }
704        }
705    }
706}
707
708/// Copy stamped files into an image's `target/lib` directory.
709fn copy_target_libs(
710    builder: &Builder<'_>,
711    target: TargetSelection,
712    image: &Path,
713    stamp: &BuildStamp,
714) {
715    let dst = image.join("lib/rustlib").join(target).join("lib");
716    let self_contained_dst = dst.join("self-contained");
717    t!(fs::create_dir_all(&dst));
718    t!(fs::create_dir_all(&self_contained_dst));
719    for (path, dependency_type) in builder.read_stamp_file(stamp) {
720        if dependency_type == DependencyType::TargetSelfContained {
721            builder.copy_link(
722                &path,
723                &self_contained_dst.join(path.file_name().unwrap()),
724                FileType::NativeLibrary,
725            );
726        } else if dependency_type == DependencyType::Target || builder.config.is_host_target(target)
727        {
728            builder.copy_link(&path, &dst.join(path.file_name().unwrap()), FileType::NativeLibrary);
729        }
730    }
731}
732
733#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
734pub struct Std {
735    pub compiler: Compiler,
736    pub target: TargetSelection,
737}
738
739impl Step for Std {
740    type Output = Option<GeneratedTarball>;
741    const DEFAULT: bool = true;
742
743    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
744        run.alias("rust-std")
745    }
746
747    fn make_run(run: RunConfig<'_>) {
748        run.builder.ensure(Std {
749            compiler: run.builder.compiler_for(
750                run.builder.top_stage,
751                run.builder.config.host_target,
752                run.target,
753            ),
754            target: run.target,
755        });
756    }
757
758    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
759        let compiler = self.compiler;
760        let target = self.target;
761
762        if skip_host_target_lib(builder, compiler) {
763            return None;
764        }
765
766        builder.std(compiler, target);
767
768        let mut tarball = Tarball::new(builder, "rust-std", &target.triple);
769        tarball.include_target_in_component_name(true);
770
771        let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
772        let stamp = build_stamp::libstd_stamp(builder, compiler_to_use, target);
773        verify_uefi_rlib_format(builder, target, &stamp);
774        copy_target_libs(builder, target, tarball.image_dir(), &stamp);
775
776        Some(tarball.generate())
777    }
778
779    fn metadata(&self) -> Option<StepMetadata> {
780        Some(StepMetadata::dist("std", self.target).built_by(self.compiler))
781    }
782}
783
784/// Tarball containing the compiler that gets downloaded and used by
785/// `rust.download-rustc`.
786///
787/// (Don't confuse this with [`RustDev`], without the `c`!)
788#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
789pub struct RustcDev {
790    pub compiler: Compiler,
791    pub target: TargetSelection,
792}
793
794impl Step for RustcDev {
795    type Output = Option<GeneratedTarball>;
796    const DEFAULT: bool = true;
797    const IS_HOST: bool = true;
798
799    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
800        run.alias("rustc-dev")
801    }
802
803    fn make_run(run: RunConfig<'_>) {
804        run.builder.ensure(RustcDev {
805            compiler: run.builder.compiler_for(
806                run.builder.top_stage,
807                run.builder.config.host_target,
808                run.target,
809            ),
810            target: run.target,
811        });
812    }
813
814    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
815        let compiler = self.compiler;
816        let target = self.target;
817        if skip_host_target_lib(builder, compiler) {
818            return None;
819        }
820
821        builder.ensure(compile::Rustc::new(compiler, target));
822
823        let tarball = Tarball::new(builder, "rustc-dev", &target.triple);
824
825        let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
826        let stamp = build_stamp::librustc_stamp(builder, compiler_to_use, target);
827        copy_target_libs(builder, target, tarball.image_dir(), &stamp);
828
829        let src_files = &["Cargo.lock"];
830        // This is the reduced set of paths which will become the rustc-dev component
831        // (essentially the compiler crates and all of their path dependencies).
832        copy_src_dirs(
833            builder,
834            &builder.src,
835            // The compiler has a path dependency on proc_macro, so make sure to include it.
836            &["compiler", "library/proc_macro"],
837            &[],
838            &tarball.image_dir().join("lib/rustlib/rustc-src/rust"),
839        );
840        for file in src_files {
841            tarball.add_file(
842                builder.src.join(file),
843                "lib/rustlib/rustc-src/rust",
844                FileType::Regular,
845            );
846        }
847
848        Some(tarball.generate())
849    }
850}
851
852#[derive(Debug, Clone, Hash, PartialEq, Eq)]
853pub struct Analysis {
854    pub compiler: Compiler,
855    pub target: TargetSelection,
856}
857
858impl Step for Analysis {
859    type Output = Option<GeneratedTarball>;
860    const DEFAULT: bool = true;
861
862    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
863        let default = should_build_extended_tool(run.builder, "analysis");
864        run.alias("rust-analysis").default_condition(default)
865    }
866
867    fn make_run(run: RunConfig<'_>) {
868        run.builder.ensure(Analysis {
869            // Find the actual compiler (handling the full bootstrap option) which
870            // produced the save-analysis data because that data isn't copied
871            // through the sysroot uplifting.
872            compiler: run.builder.compiler_for(
873                run.builder.top_stage,
874                run.builder.config.host_target,
875                run.target,
876            ),
877            target: run.target,
878        });
879    }
880
881    /// Creates a tarball of (degenerate) save-analysis metadata, if available.
882    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
883        let compiler = self.compiler;
884        let target = self.target;
885        if !builder.config.is_host_target(compiler.host) {
886            return None;
887        }
888
889        let src = builder
890            .stage_out(compiler, Mode::Std)
891            .join(target)
892            .join(builder.cargo_dir())
893            .join("deps")
894            .join("save-analysis");
895
896        // Write a file indicating that this component has been removed.
897        t!(std::fs::create_dir_all(&src));
898        let mut removed = src.clone();
899        removed.push("removed.json");
900        let mut f = t!(std::fs::File::create(removed));
901        t!(write!(f, r#"{{ "warning": "The `rust-analysis` component has been removed." }}"#));
902
903        let mut tarball = Tarball::new(builder, "rust-analysis", &target.triple);
904        tarball.include_target_in_component_name(true);
905        tarball.add_dir(src, format!("lib/rustlib/{}/analysis", target.triple));
906        Some(tarball.generate())
907    }
908}
909
910/// Use the `builder` to make a filtered copy of `base`/X for X in (`src_dirs` - `exclude_dirs`) to
911/// `dst_dir`.
912fn copy_src_dirs(
913    builder: &Builder<'_>,
914    base: &Path,
915    src_dirs: &[&str],
916    exclude_dirs: &[&str],
917    dst_dir: &Path,
918) {
919    // The src directories should be relative to `base`, we depend on them not being absolute
920    // paths below.
921    for src_dir in src_dirs {
922        assert!(Path::new(src_dir).is_relative());
923    }
924
925    // Iterating, filtering and copying a large number of directories can be quite slow.
926    // Avoid doing it in dry run (and thus also tests).
927    if builder.config.dry_run() {
928        return;
929    }
930
931    fn filter_fn(exclude_dirs: &[&str], dir: &str, path: &Path) -> bool {
932        // The paths are relative, e.g. `llvm-project/...`.
933        let spath = match path.to_str() {
934            Some(path) => path,
935            None => return false,
936        };
937        if spath.ends_with('~') || spath.ends_with(".pyc") {
938            return false;
939        }
940        // Normalize slashes
941        let spath = spath.replace("\\", "/");
942
943        static LLVM_PROJECTS: &[&str] = &[
944            "llvm-project/clang",
945            "llvm-project/libunwind",
946            "llvm-project/lld",
947            "llvm-project/lldb",
948            "llvm-project/llvm",
949            "llvm-project/compiler-rt",
950            "llvm-project/cmake",
951            "llvm-project/runtimes",
952            "llvm-project/third-party",
953        ];
954        if spath.starts_with("llvm-project") && spath != "llvm-project" {
955            if !LLVM_PROJECTS.iter().any(|path| spath.starts_with(path)) {
956                return false;
957            }
958
959            // Keep siphash third-party dependency
960            if spath.starts_with("llvm-project/third-party")
961                && spath != "llvm-project/third-party"
962                && !spath.starts_with("llvm-project/third-party/siphash")
963            {
964                return false;
965            }
966
967            if spath.starts_with("llvm-project/llvm/test")
968                && (spath.ends_with(".ll") || spath.ends_with(".td") || spath.ends_with(".s"))
969            {
970                return false;
971            }
972        }
973
974        // Cargo tests use some files like `.gitignore` that we would otherwise exclude.
975        if spath.starts_with("tools/cargo/tests") {
976            return true;
977        }
978
979        if !exclude_dirs.is_empty() {
980            let full_path = Path::new(dir).join(path);
981            if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) {
982                return false;
983            }
984        }
985
986        static EXCLUDES: &[&str] = &[
987            "CVS",
988            "RCS",
989            "SCCS",
990            ".git",
991            ".gitignore",
992            ".gitmodules",
993            ".gitattributes",
994            ".cvsignore",
995            ".svn",
996            ".arch-ids",
997            "{arch}",
998            "=RELEASE-ID",
999            "=meta-update",
1000            "=update",
1001            ".bzr",
1002            ".bzrignore",
1003            ".bzrtags",
1004            ".hg",
1005            ".hgignore",
1006            ".hgrags",
1007            "_darcs",
1008        ];
1009
1010        // We want to check if any component of `path` doesn't contain the strings in `EXCLUDES`.
1011        // However, since we traverse directories top-down in `Builder::cp_link_filtered`,
1012        // it is enough to always check only the last component:
1013        // - If the path is a file, we will iterate to it and then check it's filename
1014        // - If the path is a dir, if it's dir name contains an excluded string, we will not even
1015        //   recurse into it.
1016        let last_component = path.iter().next_back().map(|s| s.to_str().unwrap()).unwrap();
1017        !EXCLUDES.contains(&last_component)
1018    }
1019
1020    // Copy the directories using our filter
1021    for item in src_dirs {
1022        let dst = &dst_dir.join(item);
1023        t!(fs::create_dir_all(dst));
1024        builder
1025            .cp_link_filtered(&base.join(item), dst, &|path| filter_fn(exclude_dirs, item, path));
1026    }
1027}
1028
1029#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1030pub struct Src;
1031
1032impl Step for Src {
1033    /// The output path of the src installer tarball
1034    type Output = GeneratedTarball;
1035    const DEFAULT: bool = true;
1036    const IS_HOST: bool = true;
1037
1038    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1039        run.alias("rust-src")
1040    }
1041
1042    fn make_run(run: RunConfig<'_>) {
1043        run.builder.ensure(Src);
1044    }
1045
1046    /// Creates the `rust-src` installer component
1047    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
1048        if !builder.config.dry_run() {
1049            builder.require_submodule("src/llvm-project", None);
1050        }
1051
1052        let tarball = Tarball::new_targetless(builder, "rust-src");
1053
1054        // A lot of tools expect the rust-src component to be entirely in this directory, so if you
1055        // change that (e.g. by adding another directory `lib/rustlib/src/foo` or
1056        // `lib/rustlib/src/rust/foo`), you will need to go around hunting for implicit assumptions
1057        // and fix them...
1058        //
1059        // NOTE: if you update the paths here, you also should update the "virtual" path
1060        // translation code in `imported_source_files` in `src/librustc_metadata/rmeta/decoder.rs`
1061        let dst_src = tarball.image_dir().join("lib/rustlib/src/rust");
1062
1063        // This is the reduced set of paths which will become the rust-src component
1064        // (essentially libstd and all of its path dependencies).
1065        copy_src_dirs(
1066            builder,
1067            &builder.src,
1068            &["library", "src/llvm-project/libunwind"],
1069            &[
1070                // not needed and contains symlinks which rustup currently
1071                // chokes on when unpacking.
1072                "library/backtrace/crates",
1073                // these are 30MB combined and aren't necessary for building
1074                // the standard library.
1075                "library/stdarch/Cargo.toml",
1076                "library/stdarch/crates/stdarch-verify",
1077                "library/stdarch/crates/intrinsic-test",
1078            ],
1079            &dst_src,
1080        );
1081
1082        tarball.generate()
1083    }
1084
1085    fn metadata(&self) -> Option<StepMetadata> {
1086        Some(StepMetadata::dist("src", TargetSelection::default()))
1087    }
1088}
1089
1090#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1091pub struct PlainSourceTarball;
1092
1093impl Step for PlainSourceTarball {
1094    /// Produces the location of the tarball generated
1095    type Output = GeneratedTarball;
1096    const DEFAULT: bool = true;
1097    const IS_HOST: bool = true;
1098
1099    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1100        let builder = run.builder;
1101        run.alias("rustc-src").default_condition(builder.config.rust_dist_src)
1102    }
1103
1104    fn make_run(run: RunConfig<'_>) {
1105        run.builder.ensure(PlainSourceTarball);
1106    }
1107
1108    /// Creates the plain source tarball
1109    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
1110        // NOTE: This is a strange component in a lot of ways. It uses `src` as the target, which
1111        // means neither rustup nor rustup-toolchain-install-master know how to download it.
1112        // It also contains symbolic links, unlike other any other dist tarball.
1113        // It's used for distros building rustc from source in a pre-vendored environment.
1114        let mut tarball = Tarball::new(builder, "rustc", "src");
1115        tarball.permit_symlinks(true);
1116        let plain_dst_src = tarball.image_dir();
1117
1118        // This is the set of root paths which will become part of the source package
1119        let src_files = [
1120            // tidy-alphabetical-start
1121            ".gitmodules",
1122            "CONTRIBUTING.md",
1123            "COPYRIGHT",
1124            "Cargo.lock",
1125            "Cargo.toml",
1126            "LICENSE-APACHE",
1127            "LICENSE-MIT",
1128            "README.md",
1129            "RELEASES.md",
1130            "REUSE.toml",
1131            "bootstrap.example.toml",
1132            "configure",
1133            "license-metadata.json",
1134            "package-lock.json",
1135            "package.json",
1136            "x",
1137            "x.ps1",
1138            "x.py",
1139            // tidy-alphabetical-end
1140        ];
1141        let src_dirs = ["src", "compiler", "library", "tests", "LICENSES"];
1142
1143        copy_src_dirs(
1144            builder,
1145            &builder.src,
1146            &src_dirs,
1147            &[
1148                // We don't currently use the GCC source code for building any official components,
1149                // it is very big, and has unclear licensing implications due to being GPL licensed.
1150                // We thus exclude it from the source tarball from now.
1151                "src/gcc",
1152            ],
1153            plain_dst_src,
1154        );
1155        // We keep something in src/gcc because it is a registered submodule,
1156        // and if it misses completely it can cause issues elsewhere
1157        // (see https://github.com/rust-lang/rust/issues/137332).
1158        // We can also let others know why is the source code missing.
1159        if !builder.config.dry_run() {
1160            builder.create_dir(&plain_dst_src.join("src/gcc"));
1161            t!(std::fs::write(
1162                plain_dst_src.join("src/gcc/notice.txt"),
1163                "The GCC source code is not included due to unclear licensing implications\n"
1164            ));
1165        }
1166
1167        // Copy the files normally
1168        for item in &src_files {
1169            builder.copy_link(
1170                &builder.src.join(item),
1171                &plain_dst_src.join(item),
1172                FileType::Regular,
1173            );
1174        }
1175
1176        // Create the version file
1177        builder.create(&plain_dst_src.join("version"), &builder.rust_version());
1178
1179        // Create the files containing git info, to ensure --version outputs the same.
1180        let write_git_info = |info: Option<&Info>, path: &Path| {
1181            if let Some(info) = info {
1182                t!(std::fs::create_dir_all(path));
1183                channel::write_commit_hash_file(path, &info.sha);
1184                channel::write_commit_info_file(path, info);
1185            }
1186        };
1187        write_git_info(builder.rust_info().info(), plain_dst_src);
1188        write_git_info(builder.cargo_info.info(), &plain_dst_src.join("./src/tools/cargo"));
1189
1190        if builder.config.dist_vendor {
1191            builder.require_and_update_all_submodules();
1192
1193            // Vendor packages that are required by opt-dist to collect PGO profiles.
1194            let pkgs_for_pgo_training = build_helper::LLVM_PGO_CRATES
1195                .iter()
1196                .chain(build_helper::RUSTC_PGO_CRATES)
1197                .map(|pkg| {
1198                    let mut manifest_path =
1199                        builder.src.join("./src/tools/rustc-perf/collector/compile-benchmarks");
1200                    manifest_path.push(pkg);
1201                    manifest_path.push("Cargo.toml");
1202                    manifest_path
1203                });
1204
1205            // Vendor all Cargo dependencies
1206            let vendor = builder.ensure(Vendor {
1207                sync_args: pkgs_for_pgo_training.collect(),
1208                versioned_dirs: true,
1209                root_dir: plain_dst_src.into(),
1210                output_dir: VENDOR_DIR.into(),
1211            });
1212
1213            let cargo_config_dir = plain_dst_src.join(".cargo");
1214            builder.create_dir(&cargo_config_dir);
1215            builder.create(&cargo_config_dir.join("config.toml"), &vendor.config);
1216        }
1217
1218        // Delete extraneous directories
1219        // FIXME: if we're managed by git, we should probably instead ask git if the given path
1220        // is managed by it?
1221        for entry in walkdir::WalkDir::new(tarball.image_dir())
1222            .follow_links(true)
1223            .into_iter()
1224            .filter_map(|e| e.ok())
1225        {
1226            if entry.path().is_dir() && entry.path().file_name() == Some(OsStr::new("__pycache__"))
1227            {
1228                t!(fs::remove_dir_all(entry.path()));
1229            }
1230        }
1231
1232        tarball.bare()
1233    }
1234}
1235
1236#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1237pub struct Cargo {
1238    pub build_compiler: Compiler,
1239    pub target: TargetSelection,
1240}
1241
1242impl Step for Cargo {
1243    type Output = Option<GeneratedTarball>;
1244    const DEFAULT: bool = true;
1245    const IS_HOST: bool = true;
1246
1247    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1248        let default = should_build_extended_tool(run.builder, "cargo");
1249        run.alias("cargo").default_condition(default)
1250    }
1251
1252    fn make_run(run: RunConfig<'_>) {
1253        run.builder.ensure(Cargo {
1254            build_compiler: run.builder.compiler_for(
1255                run.builder.top_stage,
1256                run.builder.config.host_target,
1257                run.target,
1258            ),
1259            target: run.target,
1260        });
1261    }
1262
1263    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1264        let build_compiler = self.build_compiler;
1265        let target = self.target;
1266
1267        let cargo = builder.ensure(tool::Cargo::from_build_compiler(build_compiler, target));
1268        let src = builder.src.join("src/tools/cargo");
1269        let etc = src.join("src/etc");
1270
1271        // Prepare the image directory
1272        let mut tarball = Tarball::new(builder, "cargo", &target.triple);
1273        tarball.set_overlay(OverlayKind::Cargo);
1274
1275        tarball.add_file(&cargo.tool_path, "bin", FileType::Executable);
1276        tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", FileType::Regular);
1277        tarball.add_renamed_file(
1278            etc.join("cargo.bashcomp.sh"),
1279            "etc/bash_completion.d",
1280            "cargo",
1281            FileType::Regular,
1282        );
1283        tarball.add_dir(etc.join("man"), "share/man/man1");
1284        tarball.add_legal_and_readme_to("share/doc/cargo");
1285
1286        Some(tarball.generate())
1287    }
1288}
1289
1290#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1291pub struct RustAnalyzer {
1292    pub build_compiler: Compiler,
1293    pub target: TargetSelection,
1294}
1295
1296impl Step for RustAnalyzer {
1297    type Output = Option<GeneratedTarball>;
1298    const DEFAULT: bool = true;
1299    const IS_HOST: bool = true;
1300
1301    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1302        let default = should_build_extended_tool(run.builder, "rust-analyzer");
1303        run.alias("rust-analyzer").default_condition(default)
1304    }
1305
1306    fn make_run(run: RunConfig<'_>) {
1307        run.builder.ensure(RustAnalyzer {
1308            build_compiler: run.builder.compiler_for(
1309                run.builder.top_stage,
1310                run.builder.config.host_target,
1311                run.target,
1312            ),
1313            target: run.target,
1314        });
1315    }
1316
1317    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1318        let target = self.target;
1319        let compilers =
1320            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target);
1321
1322        let rust_analyzer = builder.ensure(tool::RustAnalyzer::from_compilers(compilers));
1323
1324        let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple);
1325        tarball.set_overlay(OverlayKind::RustAnalyzer);
1326        tarball.is_preview(true);
1327        tarball.add_file(&rust_analyzer.tool_path, "bin", FileType::Executable);
1328        tarball.add_legal_and_readme_to("share/doc/rust-analyzer");
1329        Some(tarball.generate())
1330    }
1331}
1332
1333#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1334pub struct Clippy {
1335    pub build_compiler: Compiler,
1336    pub target: TargetSelection,
1337}
1338
1339impl Step for Clippy {
1340    type Output = Option<GeneratedTarball>;
1341    const DEFAULT: bool = true;
1342    const IS_HOST: bool = true;
1343
1344    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1345        let default = should_build_extended_tool(run.builder, "clippy");
1346        run.alias("clippy").default_condition(default)
1347    }
1348
1349    fn make_run(run: RunConfig<'_>) {
1350        run.builder.ensure(Clippy {
1351            build_compiler: run.builder.compiler_for(
1352                run.builder.top_stage,
1353                run.builder.config.host_target,
1354                run.target,
1355            ),
1356            target: run.target,
1357        });
1358    }
1359
1360    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1361        let target = self.target;
1362        let compilers =
1363            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target);
1364
1365        // Prepare the image directory
1366        // We expect clippy to build, because we've exited this step above if tool
1367        // state for clippy isn't testing.
1368        let clippy = builder.ensure(tool::Clippy::from_compilers(compilers));
1369        let cargoclippy = builder.ensure(tool::CargoClippy::from_compilers(compilers));
1370
1371        let mut tarball = Tarball::new(builder, "clippy", &target.triple);
1372        tarball.set_overlay(OverlayKind::Clippy);
1373        tarball.is_preview(true);
1374        tarball.add_file(&clippy.tool_path, "bin", FileType::Executable);
1375        tarball.add_file(&cargoclippy.tool_path, "bin", FileType::Executable);
1376        tarball.add_legal_and_readme_to("share/doc/clippy");
1377        Some(tarball.generate())
1378    }
1379}
1380
1381#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1382pub struct Miri {
1383    pub build_compiler: Compiler,
1384    pub target: TargetSelection,
1385}
1386
1387impl Step for Miri {
1388    type Output = Option<GeneratedTarball>;
1389    const DEFAULT: bool = true;
1390    const IS_HOST: bool = true;
1391
1392    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1393        let default = should_build_extended_tool(run.builder, "miri");
1394        run.alias("miri").default_condition(default)
1395    }
1396
1397    fn make_run(run: RunConfig<'_>) {
1398        run.builder.ensure(Miri {
1399            build_compiler: run.builder.compiler_for(
1400                run.builder.top_stage,
1401                run.builder.config.host_target,
1402                run.target,
1403            ),
1404            target: run.target,
1405        });
1406    }
1407
1408    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1409        // This prevents miri from being built for "dist" or "install"
1410        // on the stable/beta channels. It is a nightly-only tool and should
1411        // not be included.
1412        if !builder.build.unstable_features() {
1413            return None;
1414        }
1415
1416        let compilers =
1417            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target);
1418        let miri = builder.ensure(tool::Miri::from_compilers(compilers));
1419        let cargomiri = builder.ensure(tool::CargoMiri::from_compilers(compilers));
1420
1421        let mut tarball = Tarball::new(builder, "miri", &self.target.triple);
1422        tarball.set_overlay(OverlayKind::Miri);
1423        tarball.is_preview(true);
1424        tarball.add_file(&miri.tool_path, "bin", FileType::Executable);
1425        tarball.add_file(&cargomiri.tool_path, "bin", FileType::Executable);
1426        tarball.add_legal_and_readme_to("share/doc/miri");
1427        Some(tarball.generate())
1428    }
1429}
1430
1431#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1432pub struct CraneliftCodegenBackend {
1433    pub build_compiler: Compiler,
1434    pub target: TargetSelection,
1435}
1436
1437impl Step for CraneliftCodegenBackend {
1438    type Output = Option<GeneratedTarball>;
1439    const DEFAULT: bool = true;
1440    const IS_HOST: bool = true;
1441
1442    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1443        // We only want to build the cranelift backend in `x dist` if the backend was enabled
1444        // in rust.codegen-backends.
1445        // Sadly, we don't have access to the actual target for which we're disting clif here..
1446        // So we just use the host target.
1447        let clif_enabled_by_default = run
1448            .builder
1449            .config
1450            .enabled_codegen_backends(run.builder.host_target)
1451            .contains(&CodegenBackendKind::Cranelift);
1452        run.alias("rustc_codegen_cranelift").default_condition(clif_enabled_by_default)
1453    }
1454
1455    fn make_run(run: RunConfig<'_>) {
1456        run.builder.ensure(CraneliftCodegenBackend {
1457            build_compiler: run.builder.compiler_for(
1458                run.builder.top_stage,
1459                run.builder.config.host_target,
1460                run.target,
1461            ),
1462            target: run.target,
1463        });
1464    }
1465
1466    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1467        // This prevents rustc_codegen_cranelift from being built for "dist"
1468        // or "install" on the stable/beta channels. It is not yet stable and
1469        // should not be included.
1470        if !builder.build.unstable_features() {
1471            return None;
1472        }
1473
1474        let target = self.target;
1475        let compilers =
1476            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target);
1477        if !target_supports_cranelift_backend(target) {
1478            builder.info("target not supported by rustc_codegen_cranelift. skipping");
1479            return None;
1480        }
1481
1482        let mut tarball = Tarball::new(builder, "rustc-codegen-cranelift", &target.triple);
1483        tarball.set_overlay(OverlayKind::RustcCodegenCranelift);
1484        tarball.is_preview(true);
1485        tarball.add_legal_and_readme_to("share/doc/rustc_codegen_cranelift");
1486
1487        let stamp = builder.ensure(compile::CraneliftCodegenBackend { compilers });
1488
1489        if builder.config.dry_run() {
1490            return None;
1491        }
1492
1493        // Get the relative path of where the codegen backend should be stored.
1494        let backends_dst = builder.sysroot_codegen_backends(compilers.target_compiler());
1495        let backends_rel = backends_dst
1496            .strip_prefix(builder.sysroot(compilers.target_compiler()))
1497            .unwrap()
1498            .strip_prefix(builder.sysroot_libdir_relative(compilers.target_compiler()))
1499            .unwrap();
1500        // Don't use custom libdir here because ^lib/ will be resolved again with installer
1501        let backends_dst = PathBuf::from("lib").join(backends_rel);
1502
1503        let codegen_backend_dylib = get_codegen_backend_file(&stamp);
1504        tarball.add_renamed_file(
1505            &codegen_backend_dylib,
1506            &backends_dst,
1507            &normalize_codegen_backend_name(builder, &codegen_backend_dylib),
1508            FileType::NativeLibrary,
1509        );
1510
1511        Some(tarball.generate())
1512    }
1513
1514    fn metadata(&self) -> Option<StepMetadata> {
1515        Some(
1516            StepMetadata::dist("rustc_codegen_cranelift", self.build_compiler.host)
1517                .built_by(self.build_compiler),
1518        )
1519    }
1520}
1521
1522#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1523pub struct Rustfmt {
1524    pub build_compiler: Compiler,
1525    pub target: TargetSelection,
1526}
1527
1528impl Step for Rustfmt {
1529    type Output = Option<GeneratedTarball>;
1530    const DEFAULT: bool = true;
1531    const IS_HOST: bool = true;
1532
1533    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1534        let default = should_build_extended_tool(run.builder, "rustfmt");
1535        run.alias("rustfmt").default_condition(default)
1536    }
1537
1538    fn make_run(run: RunConfig<'_>) {
1539        run.builder.ensure(Rustfmt {
1540            build_compiler: run.builder.compiler_for(
1541                run.builder.top_stage,
1542                run.builder.config.host_target,
1543                run.target,
1544            ),
1545            target: run.target,
1546        });
1547    }
1548
1549    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1550        let compilers =
1551            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target);
1552
1553        let rustfmt = builder.ensure(tool::Rustfmt::from_compilers(compilers));
1554        let cargofmt = builder.ensure(tool::Cargofmt::from_compilers(compilers));
1555
1556        let mut tarball = Tarball::new(builder, "rustfmt", &self.target.triple);
1557        tarball.set_overlay(OverlayKind::Rustfmt);
1558        tarball.is_preview(true);
1559        tarball.add_file(&rustfmt.tool_path, "bin", FileType::Executable);
1560        tarball.add_file(&cargofmt.tool_path, "bin", FileType::Executable);
1561        tarball.add_legal_and_readme_to("share/doc/rustfmt");
1562        Some(tarball.generate())
1563    }
1564}
1565
1566#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1567pub struct Extended {
1568    stage: u32,
1569    host: TargetSelection,
1570    target: TargetSelection,
1571}
1572
1573impl Step for Extended {
1574    type Output = ();
1575    const DEFAULT: bool = true;
1576    const IS_HOST: bool = true;
1577
1578    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1579        let builder = run.builder;
1580        run.alias("extended").default_condition(builder.config.extended)
1581    }
1582
1583    fn make_run(run: RunConfig<'_>) {
1584        run.builder.ensure(Extended {
1585            stage: run.builder.top_stage,
1586            host: run.builder.config.host_target,
1587            target: run.target,
1588        });
1589    }
1590
1591    /// Creates a combined installer for the specified target in the provided stage.
1592    fn run(self, builder: &Builder<'_>) {
1593        let target = self.target;
1594        let stage = self.stage;
1595        let compiler = builder.compiler_for(self.stage, self.host, self.target);
1596
1597        builder.info(&format!("Dist extended stage{} ({})", compiler.stage, target));
1598
1599        let mut tarballs = Vec::new();
1600        let mut built_tools = HashSet::new();
1601        macro_rules! add_component {
1602            ($name:expr => $step:expr) => {
1603                if let Some(Some(tarball)) = builder.ensure_if_default($step, Kind::Dist) {
1604                    tarballs.push(tarball);
1605                    built_tools.insert($name);
1606                }
1607            };
1608        }
1609
1610        let target_compiler = builder.compiler(stage, target);
1611        // When rust-std package split from rustc, we needed to ensure that during
1612        // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering
1613        // the std files during uninstall. To do this ensure that rustc comes
1614        // before rust-std in the list below.
1615        tarballs.push(builder.ensure(Rustc { compiler: target_compiler }));
1616        tarballs.push(builder.ensure(Std { compiler, target }).expect("missing std"));
1617
1618        if target.is_windows_gnu() {
1619            tarballs.push(builder.ensure(Mingw { host: target }).expect("missing mingw"));
1620        }
1621
1622        add_component!("rust-docs" => Docs { host: target });
1623        // Std stage N is documented with compiler stage N
1624        add_component!("rust-json-docs" => JsonDocs { build_compiler: target_compiler, target });
1625        add_component!("cargo" => Cargo { build_compiler: compiler, target });
1626        add_component!("rustfmt" => Rustfmt { build_compiler: compiler, target });
1627        add_component!("rust-analyzer" => RustAnalyzer { build_compiler: compiler, target });
1628        add_component!("llvm-components" => LlvmTools { target });
1629        add_component!("clippy" => Clippy { build_compiler: compiler, target });
1630        add_component!("miri" => Miri { build_compiler: compiler, target });
1631        add_component!("analysis" => Analysis { compiler, target });
1632        add_component!("rustc-codegen-cranelift" => CraneliftCodegenBackend {
1633            build_compiler: compiler,
1634            target
1635        });
1636        add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker {
1637            build_compiler: compiler,
1638            target
1639        });
1640
1641        let etc = builder.src.join("src/etc/installer");
1642
1643        // Avoid producing tarballs during a dry run.
1644        if builder.config.dry_run() {
1645            return;
1646        }
1647
1648        let tarball = Tarball::new(builder, "rust", &target.triple);
1649        let generated = tarball.combine(&tarballs);
1650
1651        let tmp = tmpdir(builder).join("combined-tarball");
1652        let work = generated.work_dir();
1653
1654        let mut license = String::new();
1655        license += &builder.read(&builder.src.join("COPYRIGHT"));
1656        license += &builder.read(&builder.src.join("LICENSE-APACHE"));
1657        license += &builder.read(&builder.src.join("LICENSE-MIT"));
1658        license.push('\n');
1659        license.push('\n');
1660
1661        let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18";
1662        let mut rtf = rtf.to_string();
1663        rtf.push('\n');
1664        for line in license.lines() {
1665            rtf.push_str(line);
1666            rtf.push_str("\\line ");
1667        }
1668        rtf.push('}');
1669
1670        fn filter(contents: &str, marker: &str) -> String {
1671            let start = format!("tool-{marker}-start");
1672            let end = format!("tool-{marker}-end");
1673            let mut lines = Vec::new();
1674            let mut omitted = false;
1675            for line in contents.lines() {
1676                if line.contains(&start) {
1677                    omitted = true;
1678                } else if line.contains(&end) {
1679                    omitted = false;
1680                } else if !omitted {
1681                    lines.push(line);
1682                }
1683            }
1684
1685            lines.join("\n")
1686        }
1687
1688        let xform = |p: &Path| {
1689            let mut contents = t!(fs::read_to_string(p));
1690            for tool in &["miri", "rust-docs"] {
1691                if !built_tools.contains(tool) {
1692                    contents = filter(&contents, tool);
1693                }
1694            }
1695            let ret = tmp.join(p.file_name().unwrap());
1696            t!(fs::write(&ret, &contents));
1697            ret
1698        };
1699
1700        if target.contains("apple-darwin") {
1701            builder.info("building pkg installer");
1702            let pkg = tmp.join("pkg");
1703            let _ = fs::remove_dir_all(&pkg);
1704
1705            let pkgbuild = |component: &str| {
1706                let mut cmd = command("pkgbuild");
1707                cmd.arg("--identifier")
1708                    .arg(format!("org.rust-lang.{component}"))
1709                    .arg("--scripts")
1710                    .arg(pkg.join(component))
1711                    .arg("--nopayload")
1712                    .arg(pkg.join(component).with_extension("pkg"));
1713                cmd.run(builder);
1714            };
1715
1716            let prepare = |name: &str| {
1717                builder.create_dir(&pkg.join(name));
1718                builder.cp_link_r(
1719                    &work.join(format!("{}-{}", pkgname(builder, name), target.triple)),
1720                    &pkg.join(name),
1721                );
1722                builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), FileType::Script);
1723                pkgbuild(name);
1724            };
1725            prepare("rustc");
1726            prepare("cargo");
1727            prepare("rust-std");
1728            prepare("rust-analysis");
1729
1730            for tool in &[
1731                "clippy",
1732                "rustfmt",
1733                "rust-analyzer",
1734                "rust-docs",
1735                "miri",
1736                "rustc-codegen-cranelift",
1737            ] {
1738                if built_tools.contains(tool) {
1739                    prepare(tool);
1740                }
1741            }
1742            // create an 'uninstall' package
1743            builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), FileType::Script);
1744            pkgbuild("uninstall");
1745
1746            builder.create_dir(&pkg.join("res"));
1747            builder.create(&pkg.join("res/LICENSE.txt"), &license);
1748            builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), FileType::Regular);
1749            let mut cmd = command("productbuild");
1750            cmd.arg("--distribution")
1751                .arg(xform(&etc.join("pkg/Distribution.xml")))
1752                .arg("--resources")
1753                .arg(pkg.join("res"))
1754                .arg(distdir(builder).join(format!(
1755                    "{}-{}.pkg",
1756                    pkgname(builder, "rust"),
1757                    target.triple
1758                )))
1759                .arg("--package-path")
1760                .arg(&pkg);
1761            let _time = timeit(builder);
1762            cmd.run(builder);
1763        }
1764
1765        // FIXME(mati865): `gnullvm` here is temporary, remove it once it can host itself
1766        if target.is_windows() && !target.contains("gnullvm") {
1767            let exe = tmp.join("exe");
1768            let _ = fs::remove_dir_all(&exe);
1769
1770            let prepare = |name: &str| {
1771                builder.create_dir(&exe.join(name));
1772                let dir = if name == "rust-std" || name == "rust-analysis" {
1773                    format!("{}-{}", name, target.triple)
1774                } else if name == "rust-analyzer" {
1775                    "rust-analyzer-preview".to_string()
1776                } else if name == "clippy" {
1777                    "clippy-preview".to_string()
1778                } else if name == "rustfmt" {
1779                    "rustfmt-preview".to_string()
1780                } else if name == "miri" {
1781                    "miri-preview".to_string()
1782                } else if name == "rustc-codegen-cranelift" {
1783                    // FIXME add installer support for cg_clif once it is ready to be distributed on
1784                    // windows.
1785                    unreachable!("cg_clif shouldn't be built for windows");
1786                } else {
1787                    name.to_string()
1788                };
1789                builder.cp_link_r(
1790                    &work.join(format!("{}-{}", pkgname(builder, name), target.triple)).join(dir),
1791                    &exe.join(name),
1792                );
1793                builder.remove(&exe.join(name).join("manifest.in"));
1794            };
1795            prepare("rustc");
1796            prepare("cargo");
1797            prepare("rust-analysis");
1798            prepare("rust-std");
1799            for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] {
1800                if built_tools.contains(tool) {
1801                    prepare(tool);
1802                }
1803            }
1804            if target.is_windows_gnu() {
1805                prepare("rust-mingw");
1806            }
1807
1808            builder.install(&etc.join("gfx/rust-logo.ico"), &exe, FileType::Regular);
1809
1810            // Generate msi installer
1811            let wix_path = env::var_os("WIX")
1812                .expect("`WIX` environment variable must be set for generating MSI installer(s).");
1813            let wix = PathBuf::from(wix_path);
1814            let heat = wix.join("bin/heat.exe");
1815            let candle = wix.join("bin/candle.exe");
1816            let light = wix.join("bin/light.exe");
1817
1818            let heat_flags = ["-nologo", "-gg", "-sfrag", "-srd", "-sreg"];
1819            command(&heat)
1820                .current_dir(&exe)
1821                .arg("dir")
1822                .arg("rustc")
1823                .args(heat_flags)
1824                .arg("-cg")
1825                .arg("RustcGroup")
1826                .arg("-dr")
1827                .arg("Rustc")
1828                .arg("-var")
1829                .arg("var.RustcDir")
1830                .arg("-out")
1831                .arg(exe.join("RustcGroup.wxs"))
1832                .run(builder);
1833            if built_tools.contains("rust-docs") {
1834                command(&heat)
1835                    .current_dir(&exe)
1836                    .arg("dir")
1837                    .arg("rust-docs")
1838                    .args(heat_flags)
1839                    .arg("-cg")
1840                    .arg("DocsGroup")
1841                    .arg("-dr")
1842                    .arg("Docs")
1843                    .arg("-var")
1844                    .arg("var.DocsDir")
1845                    .arg("-out")
1846                    .arg(exe.join("DocsGroup.wxs"))
1847                    .arg("-t")
1848                    .arg(etc.join("msi/squash-components.xsl"))
1849                    .run(builder);
1850            }
1851            command(&heat)
1852                .current_dir(&exe)
1853                .arg("dir")
1854                .arg("cargo")
1855                .args(heat_flags)
1856                .arg("-cg")
1857                .arg("CargoGroup")
1858                .arg("-dr")
1859                .arg("Cargo")
1860                .arg("-var")
1861                .arg("var.CargoDir")
1862                .arg("-out")
1863                .arg(exe.join("CargoGroup.wxs"))
1864                .arg("-t")
1865                .arg(etc.join("msi/remove-duplicates.xsl"))
1866                .run(builder);
1867            command(&heat)
1868                .current_dir(&exe)
1869                .arg("dir")
1870                .arg("rust-std")
1871                .args(heat_flags)
1872                .arg("-cg")
1873                .arg("StdGroup")
1874                .arg("-dr")
1875                .arg("Std")
1876                .arg("-var")
1877                .arg("var.StdDir")
1878                .arg("-out")
1879                .arg(exe.join("StdGroup.wxs"))
1880                .run(builder);
1881            if built_tools.contains("rust-analyzer") {
1882                command(&heat)
1883                    .current_dir(&exe)
1884                    .arg("dir")
1885                    .arg("rust-analyzer")
1886                    .args(heat_flags)
1887                    .arg("-cg")
1888                    .arg("RustAnalyzerGroup")
1889                    .arg("-dr")
1890                    .arg("RustAnalyzer")
1891                    .arg("-var")
1892                    .arg("var.RustAnalyzerDir")
1893                    .arg("-out")
1894                    .arg(exe.join("RustAnalyzerGroup.wxs"))
1895                    .arg("-t")
1896                    .arg(etc.join("msi/remove-duplicates.xsl"))
1897                    .run(builder);
1898            }
1899            if built_tools.contains("clippy") {
1900                command(&heat)
1901                    .current_dir(&exe)
1902                    .arg("dir")
1903                    .arg("clippy")
1904                    .args(heat_flags)
1905                    .arg("-cg")
1906                    .arg("ClippyGroup")
1907                    .arg("-dr")
1908                    .arg("Clippy")
1909                    .arg("-var")
1910                    .arg("var.ClippyDir")
1911                    .arg("-out")
1912                    .arg(exe.join("ClippyGroup.wxs"))
1913                    .arg("-t")
1914                    .arg(etc.join("msi/remove-duplicates.xsl"))
1915                    .run(builder);
1916            }
1917            if built_tools.contains("rustfmt") {
1918                command(&heat)
1919                    .current_dir(&exe)
1920                    .arg("dir")
1921                    .arg("rustfmt")
1922                    .args(heat_flags)
1923                    .arg("-cg")
1924                    .arg("RustFmtGroup")
1925                    .arg("-dr")
1926                    .arg("RustFmt")
1927                    .arg("-var")
1928                    .arg("var.RustFmtDir")
1929                    .arg("-out")
1930                    .arg(exe.join("RustFmtGroup.wxs"))
1931                    .arg("-t")
1932                    .arg(etc.join("msi/remove-duplicates.xsl"))
1933                    .run(builder);
1934            }
1935            if built_tools.contains("miri") {
1936                command(&heat)
1937                    .current_dir(&exe)
1938                    .arg("dir")
1939                    .arg("miri")
1940                    .args(heat_flags)
1941                    .arg("-cg")
1942                    .arg("MiriGroup")
1943                    .arg("-dr")
1944                    .arg("Miri")
1945                    .arg("-var")
1946                    .arg("var.MiriDir")
1947                    .arg("-out")
1948                    .arg(exe.join("MiriGroup.wxs"))
1949                    .arg("-t")
1950                    .arg(etc.join("msi/remove-duplicates.xsl"))
1951                    .run(builder);
1952            }
1953            command(&heat)
1954                .current_dir(&exe)
1955                .arg("dir")
1956                .arg("rust-analysis")
1957                .args(heat_flags)
1958                .arg("-cg")
1959                .arg("AnalysisGroup")
1960                .arg("-dr")
1961                .arg("Analysis")
1962                .arg("-var")
1963                .arg("var.AnalysisDir")
1964                .arg("-out")
1965                .arg(exe.join("AnalysisGroup.wxs"))
1966                .arg("-t")
1967                .arg(etc.join("msi/remove-duplicates.xsl"))
1968                .run(builder);
1969            if target.is_windows_gnu() {
1970                command(&heat)
1971                    .current_dir(&exe)
1972                    .arg("dir")
1973                    .arg("rust-mingw")
1974                    .args(heat_flags)
1975                    .arg("-cg")
1976                    .arg("GccGroup")
1977                    .arg("-dr")
1978                    .arg("Gcc")
1979                    .arg("-var")
1980                    .arg("var.GccDir")
1981                    .arg("-out")
1982                    .arg(exe.join("GccGroup.wxs"))
1983                    .run(builder);
1984            }
1985
1986            let candle = |input: &Path| {
1987                let output = exe.join(input.file_stem().unwrap()).with_extension("wixobj");
1988                let arch = if target.contains("x86_64") { "x64" } else { "x86" };
1989                let mut cmd = command(&candle);
1990                cmd.current_dir(&exe)
1991                    .arg("-nologo")
1992                    .arg("-dRustcDir=rustc")
1993                    .arg("-dCargoDir=cargo")
1994                    .arg("-dStdDir=rust-std")
1995                    .arg("-dAnalysisDir=rust-analysis")
1996                    .arg("-arch")
1997                    .arg(arch)
1998                    .arg("-out")
1999                    .arg(&output)
2000                    .arg(input);
2001                add_env(builder, &mut cmd, target, &built_tools);
2002
2003                if built_tools.contains("clippy") {
2004                    cmd.arg("-dClippyDir=clippy");
2005                }
2006                if built_tools.contains("rustfmt") {
2007                    cmd.arg("-dRustFmtDir=rustfmt");
2008                }
2009                if built_tools.contains("rust-docs") {
2010                    cmd.arg("-dDocsDir=rust-docs");
2011                }
2012                if built_tools.contains("rust-analyzer") {
2013                    cmd.arg("-dRustAnalyzerDir=rust-analyzer");
2014                }
2015                if built_tools.contains("miri") {
2016                    cmd.arg("-dMiriDir=miri");
2017                }
2018                if target.is_windows_gnu() {
2019                    cmd.arg("-dGccDir=rust-mingw");
2020                }
2021                cmd.run(builder);
2022            };
2023            candle(&xform(&etc.join("msi/rust.wxs")));
2024            candle(&etc.join("msi/ui.wxs"));
2025            candle(&etc.join("msi/rustwelcomedlg.wxs"));
2026            candle("RustcGroup.wxs".as_ref());
2027            if built_tools.contains("rust-docs") {
2028                candle("DocsGroup.wxs".as_ref());
2029            }
2030            candle("CargoGroup.wxs".as_ref());
2031            candle("StdGroup.wxs".as_ref());
2032            if built_tools.contains("clippy") {
2033                candle("ClippyGroup.wxs".as_ref());
2034            }
2035            if built_tools.contains("rustfmt") {
2036                candle("RustFmtGroup.wxs".as_ref());
2037            }
2038            if built_tools.contains("miri") {
2039                candle("MiriGroup.wxs".as_ref());
2040            }
2041            if built_tools.contains("rust-analyzer") {
2042                candle("RustAnalyzerGroup.wxs".as_ref());
2043            }
2044            candle("AnalysisGroup.wxs".as_ref());
2045
2046            if target.is_windows_gnu() {
2047                candle("GccGroup.wxs".as_ref());
2048            }
2049
2050            builder.create(&exe.join("LICENSE.rtf"), &rtf);
2051            builder.install(&etc.join("gfx/banner.bmp"), &exe, FileType::Regular);
2052            builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, FileType::Regular);
2053
2054            builder.info(&format!("building `msi` installer with {light:?}"));
2055            let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple);
2056            let mut cmd = command(&light);
2057            cmd.arg("-nologo")
2058                .arg("-ext")
2059                .arg("WixUIExtension")
2060                .arg("-ext")
2061                .arg("WixUtilExtension")
2062                .arg("-out")
2063                .arg(exe.join(&filename))
2064                .arg("rust.wixobj")
2065                .arg("ui.wixobj")
2066                .arg("rustwelcomedlg.wixobj")
2067                .arg("RustcGroup.wixobj")
2068                .arg("CargoGroup.wixobj")
2069                .arg("StdGroup.wixobj")
2070                .arg("AnalysisGroup.wixobj")
2071                .current_dir(&exe);
2072
2073            if built_tools.contains("clippy") {
2074                cmd.arg("ClippyGroup.wixobj");
2075            }
2076            if built_tools.contains("rustfmt") {
2077                cmd.arg("RustFmtGroup.wixobj");
2078            }
2079            if built_tools.contains("miri") {
2080                cmd.arg("MiriGroup.wixobj");
2081            }
2082            if built_tools.contains("rust-analyzer") {
2083                cmd.arg("RustAnalyzerGroup.wixobj");
2084            }
2085            if built_tools.contains("rust-docs") {
2086                cmd.arg("DocsGroup.wixobj");
2087            }
2088
2089            if target.is_windows_gnu() {
2090                cmd.arg("GccGroup.wixobj");
2091            }
2092            // ICE57 wrongly complains about the shortcuts
2093            cmd.arg("-sice:ICE57");
2094
2095            let _time = timeit(builder);
2096            cmd.run(builder);
2097
2098            if !builder.config.dry_run() {
2099                t!(move_file(exe.join(&filename), distdir(builder).join(&filename)));
2100            }
2101        }
2102    }
2103}
2104
2105fn add_env(
2106    builder: &Builder<'_>,
2107    cmd: &mut BootstrapCommand,
2108    target: TargetSelection,
2109    built_tools: &HashSet<&'static str>,
2110) {
2111    let mut parts = builder.version.split('.');
2112    cmd.env("CFG_RELEASE_INFO", builder.rust_version())
2113        .env("CFG_RELEASE_NUM", &builder.version)
2114        .env("CFG_RELEASE", builder.rust_release())
2115        .env("CFG_VER_MAJOR", parts.next().unwrap())
2116        .env("CFG_VER_MINOR", parts.next().unwrap())
2117        .env("CFG_VER_PATCH", parts.next().unwrap())
2118        .env("CFG_VER_BUILD", "0") // just needed to build
2119        .env("CFG_PACKAGE_VERS", builder.rust_package_vers())
2120        .env("CFG_PACKAGE_NAME", pkgname(builder, "rust"))
2121        .env("CFG_BUILD", target.triple)
2122        .env("CFG_CHANNEL", &builder.config.channel);
2123
2124    if target.contains("windows-gnullvm") {
2125        cmd.env("CFG_MINGW", "1").env("CFG_ABI", "LLVM");
2126    } else if target.is_windows_gnu() {
2127        cmd.env("CFG_MINGW", "1").env("CFG_ABI", "GNU");
2128    } else {
2129        cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC");
2130    }
2131
2132    // ensure these variables are defined
2133    let mut define_optional_tool = |tool_name: &str, env_name: &str| {
2134        cmd.env(env_name, if built_tools.contains(tool_name) { "1" } else { "0" });
2135    };
2136    define_optional_tool("rustfmt", "CFG_RUSTFMT");
2137    define_optional_tool("clippy", "CFG_CLIPPY");
2138    define_optional_tool("miri", "CFG_MIRI");
2139    define_optional_tool("rust-analyzer", "CFG_RA");
2140}
2141
2142fn install_llvm_file(
2143    builder: &Builder<'_>,
2144    source: &Path,
2145    destination: &Path,
2146    install_symlink: bool,
2147) {
2148    if builder.config.dry_run() {
2149        return;
2150    }
2151
2152    if source.is_symlink() {
2153        // If we have a symlink like libLLVM-18.so -> libLLVM.so.18.1, install the target of the
2154        // symlink, which is what will actually get loaded at runtime.
2155        builder.install(&t!(fs::canonicalize(source)), destination, FileType::NativeLibrary);
2156
2157        let full_dest = destination.join(source.file_name().unwrap());
2158        if install_symlink {
2159            // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a
2160            // symlink is fine here, as this is not a rustup component.
2161            builder.copy_link(source, &full_dest, FileType::NativeLibrary);
2162        } else {
2163            // Otherwise, replace the symlink with an equivalent linker script. This is used when
2164            // projects like miri link against librustc_driver.so. We don't use a symlink, as
2165            // these are not allowed inside rustup components.
2166            let link = t!(fs::read_link(source));
2167            let mut linker_script = t!(fs::File::create(full_dest));
2168            t!(write!(linker_script, "INPUT({})\n", link.display()));
2169
2170            // We also want the linker script to have the same mtime as the source, otherwise it
2171            // can trigger rebuilds.
2172            let meta = t!(fs::metadata(source));
2173            if let Ok(mtime) = meta.modified() {
2174                t!(linker_script.set_modified(mtime));
2175            }
2176        }
2177    } else {
2178        builder.install(source, destination, FileType::NativeLibrary);
2179    }
2180}
2181
2182/// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking.
2183///
2184/// Returns whether the files were actually copied.
2185#[cfg_attr(
2186    feature = "tracing",
2187    instrument(
2188        level = "trace",
2189        name = "maybe_install_llvm",
2190        skip_all,
2191        fields(target = ?target, dst_libdir = ?dst_libdir, install_symlink = install_symlink),
2192    ),
2193)]
2194fn maybe_install_llvm(
2195    builder: &Builder<'_>,
2196    target: TargetSelection,
2197    dst_libdir: &Path,
2198    install_symlink: bool,
2199) -> bool {
2200    // If the LLVM was externally provided, then we don't currently copy
2201    // artifacts into the sysroot. This is not necessarily the right
2202    // choice (in particular, it will require the LLVM dylib to be in
2203    // the linker's load path at runtime), but the common use case for
2204    // external LLVMs is distribution provided LLVMs, and in that case
2205    // they're usually in the standard search path (e.g., /usr/lib) and
2206    // copying them here is going to cause problems as we may end up
2207    // with the wrong files and isn't what distributions want.
2208    //
2209    // This behavior may be revisited in the future though.
2210    //
2211    // NOTE: this intentionally doesn't use `is_rust_llvm`; whether this is patched or not doesn't matter,
2212    // we only care if the shared object itself is managed by bootstrap.
2213    //
2214    // If the LLVM is coming from ourselves (just from CI) though, we
2215    // still want to install it, as it otherwise won't be available.
2216    if builder.config.is_system_llvm(target) {
2217        trace!("system LLVM requested, no install");
2218        return false;
2219    }
2220
2221    // On macOS, rustc (and LLVM tools) link to an unversioned libLLVM.dylib
2222    // instead of libLLVM-11-rust-....dylib, as on linux. It's not entirely
2223    // clear why this is the case, though. llvm-config will emit the versioned
2224    // paths and we don't want those in the sysroot (as we're expecting
2225    // unversioned paths).
2226    if target.contains("apple-darwin") && builder.llvm_link_shared() {
2227        let src_libdir = builder.llvm_out(target).join("lib");
2228        let llvm_dylib_path = src_libdir.join("libLLVM.dylib");
2229        if llvm_dylib_path.exists() {
2230            builder.install(&llvm_dylib_path, dst_libdir, FileType::NativeLibrary);
2231        }
2232        !builder.config.dry_run()
2233    } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) =
2234        llvm::prebuilt_llvm_config(builder, target, true)
2235    {
2236        trace!("LLVM already built, installing LLVM files");
2237        let mut cmd = command(llvm_config);
2238        cmd.arg("--libfiles");
2239        builder.verbose(|| println!("running {cmd:?}"));
2240        let files = cmd.run_capture_stdout(builder).stdout();
2241        let build_llvm_out = &builder.llvm_out(builder.config.host_target);
2242        let target_llvm_out = &builder.llvm_out(target);
2243        for file in files.trim_end().split(' ') {
2244            // If we're not using a custom LLVM, make sure we package for the target.
2245            let file = if let Ok(relative_path) = Path::new(file).strip_prefix(build_llvm_out) {
2246                target_llvm_out.join(relative_path)
2247            } else {
2248                PathBuf::from(file)
2249            };
2250            install_llvm_file(builder, &file, dst_libdir, install_symlink);
2251        }
2252        !builder.config.dry_run()
2253    } else {
2254        false
2255    }
2256}
2257
2258/// Maybe add libLLVM.so to the target lib-dir for linking.
2259#[cfg_attr(
2260    feature = "tracing",
2261    instrument(
2262        level = "trace",
2263        name = "maybe_install_llvm_target",
2264        skip_all,
2265        fields(
2266            llvm_link_shared = ?builder.llvm_link_shared(),
2267            target = ?target,
2268            sysroot = ?sysroot,
2269        ),
2270    ),
2271)]
2272pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
2273    let dst_libdir = sysroot.join("lib/rustlib").join(target).join("lib");
2274    // We do not need to copy LLVM files into the sysroot if it is not
2275    // dynamically linked; it is already included into librustc_llvm
2276    // statically.
2277    if builder.llvm_link_shared() {
2278        maybe_install_llvm(builder, target, &dst_libdir, false);
2279    }
2280}
2281
2282/// Maybe add libLLVM.so to the runtime lib-dir for rustc itself.
2283#[cfg_attr(
2284    feature = "tracing",
2285    instrument(
2286        level = "trace",
2287        name = "maybe_install_llvm_runtime",
2288        skip_all,
2289        fields(
2290            llvm_link_shared = ?builder.llvm_link_shared(),
2291            target = ?target,
2292            sysroot = ?sysroot,
2293        ),
2294    ),
2295)]
2296pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
2297    let dst_libdir = sysroot.join(builder.sysroot_libdir_relative(Compiler::new(1, target)));
2298    // We do not need to copy LLVM files into the sysroot if it is not
2299    // dynamically linked; it is already included into librustc_llvm
2300    // statically.
2301    if builder.llvm_link_shared() {
2302        maybe_install_llvm(builder, target, &dst_libdir, false);
2303    }
2304}
2305
2306#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2307pub struct LlvmTools {
2308    pub target: TargetSelection,
2309}
2310
2311impl Step for LlvmTools {
2312    type Output = Option<GeneratedTarball>;
2313    const IS_HOST: bool = true;
2314    const DEFAULT: bool = true;
2315
2316    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2317        let default = should_build_extended_tool(run.builder, "llvm-tools");
2318
2319        let mut run = run.alias("llvm-tools");
2320        for tool in LLVM_TOOLS {
2321            run = run.alias(tool);
2322        }
2323
2324        run.default_condition(default)
2325    }
2326
2327    fn make_run(run: RunConfig<'_>) {
2328        run.builder.ensure(LlvmTools { target: run.target });
2329    }
2330
2331    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2332        fn tools_to_install(paths: &[PathBuf]) -> Vec<&'static str> {
2333            let mut tools = vec![];
2334
2335            for path in paths {
2336                let path = path.to_str().unwrap();
2337
2338                // Include all tools if path is 'llvm-tools'.
2339                if path == "llvm-tools" {
2340                    return LLVM_TOOLS.to_owned();
2341                }
2342
2343                for tool in LLVM_TOOLS {
2344                    if path == *tool {
2345                        tools.push(*tool);
2346                    }
2347                }
2348            }
2349
2350            // If no specific tool is requested, include all tools.
2351            if tools.is_empty() {
2352                tools = LLVM_TOOLS.to_owned();
2353            }
2354
2355            tools
2356        }
2357
2358        let target = self.target;
2359
2360        // Run only if a custom llvm-config is not used
2361        if let Some(config) = builder.config.target_config.get(&target)
2362            && !builder.config.llvm_from_ci
2363            && config.llvm_config.is_some()
2364        {
2365            builder.info(&format!("Skipping LlvmTools ({target}): external LLVM"));
2366            return None;
2367        }
2368
2369        if !builder.config.dry_run() {
2370            builder.require_submodule("src/llvm-project", None);
2371        }
2372
2373        builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2374
2375        let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple);
2376        tarball.set_overlay(OverlayKind::Llvm);
2377        tarball.is_preview(true);
2378
2379        if builder.config.llvm_tools_enabled {
2380            // Prepare the image directory
2381            let src_bindir = builder.llvm_out(target).join("bin");
2382            let dst_bindir = format!("lib/rustlib/{}/bin", target.triple);
2383            for tool in tools_to_install(&builder.paths) {
2384                let exe = src_bindir.join(exe(tool, target));
2385                // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them.
2386                if !exe.exists() && builder.config.llvm_from_ci {
2387                    eprintln!("{} does not exist; skipping copy", exe.display());
2388                    continue;
2389                }
2390
2391                tarball.add_file(&exe, &dst_bindir, FileType::Executable);
2392            }
2393        }
2394
2395        // Copy libLLVM.so to the target lib dir as well, so the RPATH like
2396        // `$ORIGIN/../lib` can find it. It may also be used as a dependency
2397        // of `rustc-dev` to support the inherited `-lLLVM` when using the
2398        // compiler libraries.
2399        maybe_install_llvm_target(builder, target, tarball.image_dir());
2400
2401        Some(tarball.generate())
2402    }
2403}
2404
2405/// Distributes the `llvm-bitcode-linker` tool so that it can be used by a compiler whose host
2406/// is `target`.
2407#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
2408pub struct LlvmBitcodeLinker {
2409    /// The linker will be compiled by this compiler.
2410    pub build_compiler: Compiler,
2411    /// The linker will by usable by rustc on this host.
2412    pub target: TargetSelection,
2413}
2414
2415impl Step for LlvmBitcodeLinker {
2416    type Output = Option<GeneratedTarball>;
2417    const DEFAULT: bool = true;
2418    const IS_HOST: bool = true;
2419
2420    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2421        let default = should_build_extended_tool(run.builder, "llvm-bitcode-linker");
2422        run.alias("llvm-bitcode-linker").default_condition(default)
2423    }
2424
2425    fn make_run(run: RunConfig<'_>) {
2426        run.builder.ensure(LlvmBitcodeLinker {
2427            build_compiler: tool::LlvmBitcodeLinker::get_build_compiler_for_target(
2428                run.builder,
2429                run.target,
2430            ),
2431            target: run.target,
2432        });
2433    }
2434
2435    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2436        let target = self.target;
2437
2438        let llbc_linker = builder
2439            .ensure(tool::LlvmBitcodeLinker::from_build_compiler(self.build_compiler, target));
2440
2441        let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple);
2442
2443        // Prepare the image directory
2444        let mut tarball = Tarball::new(builder, "llvm-bitcode-linker", &target.triple);
2445        tarball.set_overlay(OverlayKind::LlvmBitcodeLinker);
2446        tarball.is_preview(true);
2447
2448        tarball.add_file(&llbc_linker.tool_path, self_contained_bin_dir, FileType::Executable);
2449
2450        Some(tarball.generate())
2451    }
2452}
2453
2454/// Tarball intended for internal consumption to ease rustc/std development.
2455///
2456/// Should not be considered stable by end users.
2457///
2458/// In practice, this is the tarball that gets downloaded and used by
2459/// `llvm.download-ci-llvm`.
2460///
2461/// (Don't confuse this with [`RustcDev`], with a `c`!)
2462#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2463pub struct RustDev {
2464    pub target: TargetSelection,
2465}
2466
2467impl Step for RustDev {
2468    type Output = Option<GeneratedTarball>;
2469    const DEFAULT: bool = true;
2470    const IS_HOST: bool = true;
2471
2472    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2473        run.alias("rust-dev")
2474    }
2475
2476    fn make_run(run: RunConfig<'_>) {
2477        run.builder.ensure(RustDev { target: run.target });
2478    }
2479
2480    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2481        let target = self.target;
2482
2483        /* run only if llvm-config isn't used */
2484        if let Some(config) = builder.config.target_config.get(&target)
2485            && let Some(ref _s) = config.llvm_config
2486        {
2487            builder.info(&format!("Skipping RustDev ({target}): external LLVM"));
2488            return None;
2489        }
2490
2491        if !builder.config.dry_run() {
2492            builder.require_submodule("src/llvm-project", None);
2493        }
2494
2495        let mut tarball = Tarball::new(builder, "rust-dev", &target.triple);
2496        tarball.set_overlay(OverlayKind::Llvm);
2497        // LLVM requires a shared object symlink to exist on some platforms.
2498        tarball.permit_symlinks(true);
2499
2500        builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2501
2502        let src_bindir = builder.llvm_out(target).join("bin");
2503        // If updating this, you likely want to change
2504        // src/bootstrap/download-ci-llvm-stamp as well, otherwise local users
2505        // will not pick up the extra file until LLVM gets bumped.
2506        // We should include all the build artifacts obtained from a source build,
2507        // so that you can use the downloadable LLVM as if you’ve just run a full source build.
2508        if src_bindir.exists() {
2509            for entry in walkdir::WalkDir::new(&src_bindir) {
2510                let entry = t!(entry);
2511                if entry.file_type().is_file() && !entry.path_is_symlink() {
2512                    let name = entry.file_name().to_str().unwrap();
2513                    tarball.add_file(src_bindir.join(name), "bin", FileType::Executable);
2514                }
2515            }
2516        }
2517
2518        if builder.config.lld_enabled {
2519            // We want to package `lld` to use it with `download-ci-llvm`.
2520            let lld_out = builder.ensure(crate::core::build_steps::llvm::Lld { target });
2521
2522            // We don't build LLD on some platforms, so only add it if it exists
2523            let lld_path = lld_out.join("bin").join(exe("lld", target));
2524            if lld_path.exists() {
2525                tarball.add_file(&lld_path, "bin", FileType::Executable);
2526            }
2527        }
2528
2529        tarball.add_file(builder.llvm_filecheck(target), "bin", FileType::Executable);
2530
2531        // Copy the include directory as well; needed mostly to build
2532        // librustc_llvm properly (e.g., llvm-config.h is in here). But also
2533        // just broadly useful to be able to link against the bundled LLVM.
2534        tarball.add_dir(builder.llvm_out(target).join("include"), "include");
2535
2536        // Copy libLLVM.so to the target lib dir as well, so the RPATH like
2537        // `$ORIGIN/../lib` can find it. It may also be used as a dependency
2538        // of `rustc-dev` to support the inherited `-lLLVM` when using the
2539        // compiler libraries.
2540        let dst_libdir = tarball.image_dir().join("lib");
2541        maybe_install_llvm(builder, target, &dst_libdir, true);
2542        let link_type = if builder.llvm_link_shared() { "dynamic" } else { "static" };
2543        t!(std::fs::write(tarball.image_dir().join("link-type.txt"), link_type), dst_libdir);
2544
2545        // Copy the `compiler-rt` source, so that `library/profiler_builtins`
2546        // can potentially use it to build the profiler runtime without needing
2547        // to check out the LLVM submodule.
2548        copy_src_dirs(
2549            builder,
2550            &builder.src.join("src").join("llvm-project"),
2551            &["compiler-rt"],
2552            // The test subdirectory is much larger than the rest of the source,
2553            // and we currently don't use these test files anyway.
2554            &["compiler-rt/test"],
2555            tarball.image_dir(),
2556        );
2557
2558        Some(tarball.generate())
2559    }
2560}
2561
2562/// Tarball intended for internal consumption to ease rustc/std development.
2563///
2564/// Should not be considered stable by end users.
2565#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2566pub struct Bootstrap {
2567    pub target: TargetSelection,
2568}
2569
2570impl Step for Bootstrap {
2571    type Output = Option<GeneratedTarball>;
2572    const DEFAULT: bool = false;
2573    const IS_HOST: bool = true;
2574
2575    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2576        run.alias("bootstrap")
2577    }
2578
2579    fn make_run(run: RunConfig<'_>) {
2580        run.builder.ensure(Bootstrap { target: run.target });
2581    }
2582
2583    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2584        let target = self.target;
2585
2586        let tarball = Tarball::new(builder, "bootstrap", &target.triple);
2587
2588        let bootstrap_outdir = &builder.bootstrap_out;
2589        for file in &["bootstrap", "rustc", "rustdoc"] {
2590            tarball.add_file(
2591                bootstrap_outdir.join(exe(file, target)),
2592                "bootstrap/bin",
2593                FileType::Executable,
2594            );
2595        }
2596
2597        Some(tarball.generate())
2598    }
2599}
2600
2601/// Tarball containing a prebuilt version of the build-manifest tool, intended to be used by the
2602/// release process to avoid cloning the monorepo and building stuff.
2603///
2604/// Should not be considered stable by end users.
2605#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2606pub struct BuildManifest {
2607    pub target: TargetSelection,
2608}
2609
2610impl Step for BuildManifest {
2611    type Output = GeneratedTarball;
2612    const DEFAULT: bool = false;
2613    const IS_HOST: bool = true;
2614
2615    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2616        run.alias("build-manifest")
2617    }
2618
2619    fn make_run(run: RunConfig<'_>) {
2620        run.builder.ensure(BuildManifest { target: run.target });
2621    }
2622
2623    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
2624        let build_manifest = builder.tool_exe(Tool::BuildManifest);
2625
2626        let tarball = Tarball::new(builder, "build-manifest", &self.target.triple);
2627        tarball.add_file(&build_manifest, "bin", FileType::Executable);
2628        tarball.generate()
2629    }
2630}
2631
2632/// Tarball containing artifacts necessary to reproduce the build of rustc.
2633///
2634/// Currently this is the PGO profile data.
2635///
2636/// Should not be considered stable by end users.
2637#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2638pub struct ReproducibleArtifacts {
2639    pub target: TargetSelection,
2640}
2641
2642impl Step for ReproducibleArtifacts {
2643    type Output = Option<GeneratedTarball>;
2644    const DEFAULT: bool = true;
2645    const IS_HOST: bool = true;
2646
2647    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2648        run.alias("reproducible-artifacts")
2649    }
2650
2651    fn make_run(run: RunConfig<'_>) {
2652        run.builder.ensure(ReproducibleArtifacts { target: run.target });
2653    }
2654
2655    fn run(self, builder: &Builder<'_>) -> Self::Output {
2656        let mut added_anything = false;
2657        let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple);
2658        if let Some(path) = builder.config.rust_profile_use.as_ref() {
2659            tarball.add_file(path, ".", FileType::Regular);
2660            added_anything = true;
2661        }
2662        if let Some(path) = builder.config.llvm_profile_use.as_ref() {
2663            tarball.add_file(path, ".", FileType::Regular);
2664            added_anything = true;
2665        }
2666        for profile in &builder.config.reproducible_artifacts {
2667            tarball.add_file(profile, ".", FileType::Regular);
2668            added_anything = true;
2669        }
2670        if added_anything { Some(tarball.generate()) } else { None }
2671    }
2672}
2673
2674/// Tarball containing a prebuilt version of the libgccjit library,
2675/// needed as a dependency for the GCC codegen backend (similarly to the LLVM
2676/// backend needing a prebuilt libLLVM).
2677#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2678pub struct Gcc {
2679    pub target: TargetSelection,
2680}
2681
2682impl Step for Gcc {
2683    type Output = GeneratedTarball;
2684
2685    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2686        run.alias("gcc")
2687    }
2688
2689    fn make_run(run: RunConfig<'_>) {
2690        run.builder.ensure(Gcc { target: run.target });
2691    }
2692
2693    fn run(self, builder: &Builder<'_>) -> Self::Output {
2694        let tarball = Tarball::new(builder, "gcc", &self.target.triple);
2695        let output = builder.ensure(super::gcc::Gcc { target: self.target });
2696        tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary);
2697        tarball.generate()
2698    }
2699}