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    // Iterating, filtering and copying a large number of directories can be quite slow.
920    // Avoid doing it in dry run (and thus also tests).
921    if builder.config.dry_run() {
922        return;
923    }
924
925    fn filter_fn(exclude_dirs: &[&str], dir: &str, path: &Path) -> bool {
926        let spath = match path.to_str() {
927            Some(path) => path,
928            None => return false,
929        };
930        if spath.ends_with('~') || spath.ends_with(".pyc") {
931            return false;
932        }
933
934        const LLVM_PROJECTS: &[&str] = &[
935            "llvm-project/clang",
936            "llvm-project\\clang",
937            "llvm-project/libunwind",
938            "llvm-project\\libunwind",
939            "llvm-project/lld",
940            "llvm-project\\lld",
941            "llvm-project/lldb",
942            "llvm-project\\lldb",
943            "llvm-project/llvm",
944            "llvm-project\\llvm",
945            "llvm-project/compiler-rt",
946            "llvm-project\\compiler-rt",
947            "llvm-project/cmake",
948            "llvm-project\\cmake",
949            "llvm-project/runtimes",
950            "llvm-project\\runtimes",
951            "llvm-project/third-party",
952            "llvm-project\\third-party",
953        ];
954        if spath.contains("llvm-project")
955            && !spath.ends_with("llvm-project")
956            && !LLVM_PROJECTS.iter().any(|path| spath.contains(path))
957        {
958            return false;
959        }
960
961        // Keep only these third party libraries
962        const LLVM_THIRD_PARTY: &[&str] =
963            &["llvm-project/third-party/siphash", "llvm-project\\third-party\\siphash"];
964        if (spath.starts_with("llvm-project/third-party")
965            || spath.starts_with("llvm-project\\third-party"))
966            && !(spath.ends_with("llvm-project/third-party")
967                || spath.ends_with("llvm-project\\third-party"))
968            && !LLVM_THIRD_PARTY.iter().any(|path| spath.contains(path))
969        {
970            return false;
971        }
972
973        const LLVM_TEST: &[&str] = &["llvm-project/llvm/test", "llvm-project\\llvm\\test"];
974        if LLVM_TEST.iter().any(|path| spath.contains(path))
975            && (spath.ends_with(".ll") || spath.ends_with(".td") || spath.ends_with(".s"))
976        {
977            return false;
978        }
979
980        // Cargo tests use some files like `.gitignore` that we would otherwise exclude.
981        const CARGO_TESTS: &[&str] = &["tools/cargo/tests", "tools\\cargo\\tests"];
982        if CARGO_TESTS.iter().any(|path| spath.contains(path)) {
983            return true;
984        }
985
986        let full_path = Path::new(dir).join(path);
987        if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) {
988            return false;
989        }
990
991        let excludes = [
992            "CVS",
993            "RCS",
994            "SCCS",
995            ".git",
996            ".gitignore",
997            ".gitmodules",
998            ".gitattributes",
999            ".cvsignore",
1000            ".svn",
1001            ".arch-ids",
1002            "{arch}",
1003            "=RELEASE-ID",
1004            "=meta-update",
1005            "=update",
1006            ".bzr",
1007            ".bzrignore",
1008            ".bzrtags",
1009            ".hg",
1010            ".hgignore",
1011            ".hgrags",
1012            "_darcs",
1013        ];
1014        !path.iter().map(|s| s.to_str().unwrap()).any(|s| excludes.contains(&s))
1015    }
1016
1017    // Copy the directories using our filter
1018    for item in src_dirs {
1019        let dst = &dst_dir.join(item);
1020        t!(fs::create_dir_all(dst));
1021        builder
1022            .cp_link_filtered(&base.join(item), dst, &|path| filter_fn(exclude_dirs, item, path));
1023    }
1024}
1025
1026#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1027pub struct Src;
1028
1029impl Step for Src {
1030    /// The output path of the src installer tarball
1031    type Output = GeneratedTarball;
1032    const DEFAULT: bool = true;
1033    const IS_HOST: bool = true;
1034
1035    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1036        run.alias("rust-src")
1037    }
1038
1039    fn make_run(run: RunConfig<'_>) {
1040        run.builder.ensure(Src);
1041    }
1042
1043    /// Creates the `rust-src` installer component
1044    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
1045        if !builder.config.dry_run() {
1046            builder.require_submodule("src/llvm-project", None);
1047        }
1048
1049        let tarball = Tarball::new_targetless(builder, "rust-src");
1050
1051        // A lot of tools expect the rust-src component to be entirely in this directory, so if you
1052        // change that (e.g. by adding another directory `lib/rustlib/src/foo` or
1053        // `lib/rustlib/src/rust/foo`), you will need to go around hunting for implicit assumptions
1054        // and fix them...
1055        //
1056        // NOTE: if you update the paths here, you also should update the "virtual" path
1057        // translation code in `imported_source_files` in `src/librustc_metadata/rmeta/decoder.rs`
1058        let dst_src = tarball.image_dir().join("lib/rustlib/src/rust");
1059
1060        // This is the reduced set of paths which will become the rust-src component
1061        // (essentially libstd and all of its path dependencies).
1062        copy_src_dirs(
1063            builder,
1064            &builder.src,
1065            &["library", "src/llvm-project/libunwind"],
1066            &[
1067                // not needed and contains symlinks which rustup currently
1068                // chokes on when unpacking.
1069                "library/backtrace/crates",
1070                // these are 30MB combined and aren't necessary for building
1071                // the standard library.
1072                "library/stdarch/Cargo.toml",
1073                "library/stdarch/crates/stdarch-verify",
1074                "library/stdarch/crates/intrinsic-test",
1075            ],
1076            &dst_src,
1077        );
1078
1079        tarball.generate()
1080    }
1081
1082    fn metadata(&self) -> Option<StepMetadata> {
1083        Some(StepMetadata::dist("src", TargetSelection::default()))
1084    }
1085}
1086
1087#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1088pub struct PlainSourceTarball;
1089
1090impl Step for PlainSourceTarball {
1091    /// Produces the location of the tarball generated
1092    type Output = GeneratedTarball;
1093    const DEFAULT: bool = true;
1094    const IS_HOST: bool = true;
1095
1096    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1097        let builder = run.builder;
1098        run.alias("rustc-src").default_condition(builder.config.rust_dist_src)
1099    }
1100
1101    fn make_run(run: RunConfig<'_>) {
1102        run.builder.ensure(PlainSourceTarball);
1103    }
1104
1105    /// Creates the plain source tarball
1106    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
1107        // NOTE: This is a strange component in a lot of ways. It uses `src` as the target, which
1108        // means neither rustup nor rustup-toolchain-install-master know how to download it.
1109        // It also contains symbolic links, unlike other any other dist tarball.
1110        // It's used for distros building rustc from source in a pre-vendored environment.
1111        let mut tarball = Tarball::new(builder, "rustc", "src");
1112        tarball.permit_symlinks(true);
1113        let plain_dst_src = tarball.image_dir();
1114
1115        // This is the set of root paths which will become part of the source package
1116        let src_files = [
1117            // tidy-alphabetical-start
1118            ".gitmodules",
1119            "CONTRIBUTING.md",
1120            "COPYRIGHT",
1121            "Cargo.lock",
1122            "Cargo.toml",
1123            "LICENSE-APACHE",
1124            "LICENSE-MIT",
1125            "README.md",
1126            "RELEASES.md",
1127            "REUSE.toml",
1128            "bootstrap.example.toml",
1129            "configure",
1130            "license-metadata.json",
1131            "package-lock.json",
1132            "package.json",
1133            "x",
1134            "x.ps1",
1135            "x.py",
1136            // tidy-alphabetical-end
1137        ];
1138        let src_dirs = ["src", "compiler", "library", "tests", "LICENSES"];
1139
1140        copy_src_dirs(
1141            builder,
1142            &builder.src,
1143            &src_dirs,
1144            &[
1145                // We don't currently use the GCC source code for building any official components,
1146                // it is very big, and has unclear licensing implications due to being GPL licensed.
1147                // We thus exclude it from the source tarball from now.
1148                "src/gcc",
1149            ],
1150            plain_dst_src,
1151        );
1152        // We keep something in src/gcc because it is a registered submodule,
1153        // and if it misses completely it can cause issues elsewhere
1154        // (see https://github.com/rust-lang/rust/issues/137332).
1155        // We can also let others know why is the source code missing.
1156        if !builder.config.dry_run() {
1157            builder.create_dir(&plain_dst_src.join("src/gcc"));
1158            t!(std::fs::write(
1159                plain_dst_src.join("src/gcc/notice.txt"),
1160                "The GCC source code is not included due to unclear licensing implications\n"
1161            ));
1162        }
1163
1164        // Copy the files normally
1165        for item in &src_files {
1166            builder.copy_link(
1167                &builder.src.join(item),
1168                &plain_dst_src.join(item),
1169                FileType::Regular,
1170            );
1171        }
1172
1173        // Create the version file
1174        builder.create(&plain_dst_src.join("version"), &builder.rust_version());
1175
1176        // Create the files containing git info, to ensure --version outputs the same.
1177        let write_git_info = |info: Option<&Info>, path: &Path| {
1178            if let Some(info) = info {
1179                t!(std::fs::create_dir_all(path));
1180                channel::write_commit_hash_file(path, &info.sha);
1181                channel::write_commit_info_file(path, info);
1182            }
1183        };
1184        write_git_info(builder.rust_info().info(), plain_dst_src);
1185        write_git_info(builder.cargo_info.info(), &plain_dst_src.join("./src/tools/cargo"));
1186
1187        if builder.config.dist_vendor {
1188            builder.require_and_update_all_submodules();
1189
1190            // Vendor packages that are required by opt-dist to collect PGO profiles.
1191            let pkgs_for_pgo_training = build_helper::LLVM_PGO_CRATES
1192                .iter()
1193                .chain(build_helper::RUSTC_PGO_CRATES)
1194                .map(|pkg| {
1195                    let mut manifest_path =
1196                        builder.src.join("./src/tools/rustc-perf/collector/compile-benchmarks");
1197                    manifest_path.push(pkg);
1198                    manifest_path.push("Cargo.toml");
1199                    manifest_path
1200                });
1201
1202            // Vendor all Cargo dependencies
1203            let vendor = builder.ensure(Vendor {
1204                sync_args: pkgs_for_pgo_training.collect(),
1205                versioned_dirs: true,
1206                root_dir: plain_dst_src.into(),
1207                output_dir: VENDOR_DIR.into(),
1208            });
1209
1210            let cargo_config_dir = plain_dst_src.join(".cargo");
1211            builder.create_dir(&cargo_config_dir);
1212            builder.create(&cargo_config_dir.join("config.toml"), &vendor.config);
1213        }
1214
1215        // Delete extraneous directories
1216        // FIXME: if we're managed by git, we should probably instead ask git if the given path
1217        // is managed by it?
1218        for entry in walkdir::WalkDir::new(tarball.image_dir())
1219            .follow_links(true)
1220            .into_iter()
1221            .filter_map(|e| e.ok())
1222        {
1223            if entry.path().is_dir() && entry.path().file_name() == Some(OsStr::new("__pycache__"))
1224            {
1225                t!(fs::remove_dir_all(entry.path()));
1226            }
1227        }
1228
1229        tarball.bare()
1230    }
1231}
1232
1233#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1234pub struct Cargo {
1235    pub build_compiler: Compiler,
1236    pub target: TargetSelection,
1237}
1238
1239impl Step for Cargo {
1240    type Output = Option<GeneratedTarball>;
1241    const DEFAULT: bool = true;
1242    const IS_HOST: bool = true;
1243
1244    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1245        let default = should_build_extended_tool(run.builder, "cargo");
1246        run.alias("cargo").default_condition(default)
1247    }
1248
1249    fn make_run(run: RunConfig<'_>) {
1250        run.builder.ensure(Cargo {
1251            build_compiler: run.builder.compiler_for(
1252                run.builder.top_stage,
1253                run.builder.config.host_target,
1254                run.target,
1255            ),
1256            target: run.target,
1257        });
1258    }
1259
1260    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1261        let build_compiler = self.build_compiler;
1262        let target = self.target;
1263
1264        let cargo = builder.ensure(tool::Cargo::from_build_compiler(build_compiler, target));
1265        let src = builder.src.join("src/tools/cargo");
1266        let etc = src.join("src/etc");
1267
1268        // Prepare the image directory
1269        let mut tarball = Tarball::new(builder, "cargo", &target.triple);
1270        tarball.set_overlay(OverlayKind::Cargo);
1271
1272        tarball.add_file(&cargo.tool_path, "bin", FileType::Executable);
1273        tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", FileType::Regular);
1274        tarball.add_renamed_file(
1275            etc.join("cargo.bashcomp.sh"),
1276            "etc/bash_completion.d",
1277            "cargo",
1278            FileType::Regular,
1279        );
1280        tarball.add_dir(etc.join("man"), "share/man/man1");
1281        tarball.add_legal_and_readme_to("share/doc/cargo");
1282
1283        Some(tarball.generate())
1284    }
1285}
1286
1287#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1288pub struct RustAnalyzer {
1289    pub build_compiler: Compiler,
1290    pub target: TargetSelection,
1291}
1292
1293impl Step for RustAnalyzer {
1294    type Output = Option<GeneratedTarball>;
1295    const DEFAULT: bool = true;
1296    const IS_HOST: bool = true;
1297
1298    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1299        let default = should_build_extended_tool(run.builder, "rust-analyzer");
1300        run.alias("rust-analyzer").default_condition(default)
1301    }
1302
1303    fn make_run(run: RunConfig<'_>) {
1304        run.builder.ensure(RustAnalyzer {
1305            build_compiler: run.builder.compiler_for(
1306                run.builder.top_stage,
1307                run.builder.config.host_target,
1308                run.target,
1309            ),
1310            target: run.target,
1311        });
1312    }
1313
1314    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1315        let target = self.target;
1316        let compilers =
1317            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target);
1318
1319        let rust_analyzer = builder.ensure(tool::RustAnalyzer::from_compilers(compilers));
1320
1321        let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple);
1322        tarball.set_overlay(OverlayKind::RustAnalyzer);
1323        tarball.is_preview(true);
1324        tarball.add_file(&rust_analyzer.tool_path, "bin", FileType::Executable);
1325        tarball.add_legal_and_readme_to("share/doc/rust-analyzer");
1326        Some(tarball.generate())
1327    }
1328}
1329
1330#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1331pub struct Clippy {
1332    pub build_compiler: Compiler,
1333    pub target: TargetSelection,
1334}
1335
1336impl Step for Clippy {
1337    type Output = Option<GeneratedTarball>;
1338    const DEFAULT: bool = true;
1339    const IS_HOST: bool = true;
1340
1341    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1342        let default = should_build_extended_tool(run.builder, "clippy");
1343        run.alias("clippy").default_condition(default)
1344    }
1345
1346    fn make_run(run: RunConfig<'_>) {
1347        run.builder.ensure(Clippy {
1348            build_compiler: run.builder.compiler_for(
1349                run.builder.top_stage,
1350                run.builder.config.host_target,
1351                run.target,
1352            ),
1353            target: run.target,
1354        });
1355    }
1356
1357    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1358        let target = self.target;
1359        let compilers =
1360            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target);
1361
1362        // Prepare the image directory
1363        // We expect clippy to build, because we've exited this step above if tool
1364        // state for clippy isn't testing.
1365        let clippy = builder.ensure(tool::Clippy::from_compilers(compilers));
1366        let cargoclippy = builder.ensure(tool::CargoClippy::from_compilers(compilers));
1367
1368        let mut tarball = Tarball::new(builder, "clippy", &target.triple);
1369        tarball.set_overlay(OverlayKind::Clippy);
1370        tarball.is_preview(true);
1371        tarball.add_file(&clippy.tool_path, "bin", FileType::Executable);
1372        tarball.add_file(&cargoclippy.tool_path, "bin", FileType::Executable);
1373        tarball.add_legal_and_readme_to("share/doc/clippy");
1374        Some(tarball.generate())
1375    }
1376}
1377
1378#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1379pub struct Miri {
1380    pub build_compiler: Compiler,
1381    pub target: TargetSelection,
1382}
1383
1384impl Step for Miri {
1385    type Output = Option<GeneratedTarball>;
1386    const DEFAULT: bool = true;
1387    const IS_HOST: bool = true;
1388
1389    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1390        let default = should_build_extended_tool(run.builder, "miri");
1391        run.alias("miri").default_condition(default)
1392    }
1393
1394    fn make_run(run: RunConfig<'_>) {
1395        run.builder.ensure(Miri {
1396            build_compiler: run.builder.compiler_for(
1397                run.builder.top_stage,
1398                run.builder.config.host_target,
1399                run.target,
1400            ),
1401            target: run.target,
1402        });
1403    }
1404
1405    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1406        // This prevents miri from being built for "dist" or "install"
1407        // on the stable/beta channels. It is a nightly-only tool and should
1408        // not be included.
1409        if !builder.build.unstable_features() {
1410            return None;
1411        }
1412
1413        let compilers =
1414            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target);
1415        let miri = builder.ensure(tool::Miri::from_compilers(compilers));
1416        let cargomiri = builder.ensure(tool::CargoMiri::from_compilers(compilers));
1417
1418        let mut tarball = Tarball::new(builder, "miri", &self.target.triple);
1419        tarball.set_overlay(OverlayKind::Miri);
1420        tarball.is_preview(true);
1421        tarball.add_file(&miri.tool_path, "bin", FileType::Executable);
1422        tarball.add_file(&cargomiri.tool_path, "bin", FileType::Executable);
1423        tarball.add_legal_and_readme_to("share/doc/miri");
1424        Some(tarball.generate())
1425    }
1426}
1427
1428#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1429pub struct CraneliftCodegenBackend {
1430    pub build_compiler: Compiler,
1431    pub target: TargetSelection,
1432}
1433
1434impl Step for CraneliftCodegenBackend {
1435    type Output = Option<GeneratedTarball>;
1436    const DEFAULT: bool = true;
1437    const IS_HOST: bool = true;
1438
1439    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1440        // We only want to build the cranelift backend in `x dist` if the backend was enabled
1441        // in rust.codegen-backends.
1442        // Sadly, we don't have access to the actual target for which we're disting clif here..
1443        // So we just use the host target.
1444        let clif_enabled_by_default = run
1445            .builder
1446            .config
1447            .enabled_codegen_backends(run.builder.host_target)
1448            .contains(&CodegenBackendKind::Cranelift);
1449        run.alias("rustc_codegen_cranelift").default_condition(clif_enabled_by_default)
1450    }
1451
1452    fn make_run(run: RunConfig<'_>) {
1453        run.builder.ensure(CraneliftCodegenBackend {
1454            build_compiler: run.builder.compiler_for(
1455                run.builder.top_stage,
1456                run.builder.config.host_target,
1457                run.target,
1458            ),
1459            target: run.target,
1460        });
1461    }
1462
1463    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1464        // This prevents rustc_codegen_cranelift from being built for "dist"
1465        // or "install" on the stable/beta channels. It is not yet stable and
1466        // should not be included.
1467        if !builder.build.unstable_features() {
1468            return None;
1469        }
1470
1471        let target = self.target;
1472        let compilers =
1473            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target);
1474        if !target_supports_cranelift_backend(target) {
1475            builder.info("target not supported by rustc_codegen_cranelift. skipping");
1476            return None;
1477        }
1478
1479        let mut tarball = Tarball::new(builder, "rustc-codegen-cranelift", &target.triple);
1480        tarball.set_overlay(OverlayKind::RustcCodegenCranelift);
1481        tarball.is_preview(true);
1482        tarball.add_legal_and_readme_to("share/doc/rustc_codegen_cranelift");
1483
1484        let stamp = builder.ensure(compile::CraneliftCodegenBackend { compilers });
1485
1486        if builder.config.dry_run() {
1487            return None;
1488        }
1489
1490        // Get the relative path of where the codegen backend should be stored.
1491        let backends_dst = builder.sysroot_codegen_backends(compilers.target_compiler());
1492        let backends_rel = backends_dst
1493            .strip_prefix(builder.sysroot(compilers.target_compiler()))
1494            .unwrap()
1495            .strip_prefix(builder.sysroot_libdir_relative(compilers.target_compiler()))
1496            .unwrap();
1497        // Don't use custom libdir here because ^lib/ will be resolved again with installer
1498        let backends_dst = PathBuf::from("lib").join(backends_rel);
1499
1500        let codegen_backend_dylib = get_codegen_backend_file(&stamp);
1501        tarball.add_renamed_file(
1502            &codegen_backend_dylib,
1503            &backends_dst,
1504            &normalize_codegen_backend_name(builder, &codegen_backend_dylib),
1505            FileType::NativeLibrary,
1506        );
1507
1508        Some(tarball.generate())
1509    }
1510
1511    fn metadata(&self) -> Option<StepMetadata> {
1512        Some(
1513            StepMetadata::dist("rustc_codegen_cranelift", self.build_compiler.host)
1514                .built_by(self.build_compiler),
1515        )
1516    }
1517}
1518
1519#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1520pub struct Rustfmt {
1521    pub build_compiler: Compiler,
1522    pub target: TargetSelection,
1523}
1524
1525impl Step for Rustfmt {
1526    type Output = Option<GeneratedTarball>;
1527    const DEFAULT: bool = true;
1528    const IS_HOST: bool = true;
1529
1530    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1531        let default = should_build_extended_tool(run.builder, "rustfmt");
1532        run.alias("rustfmt").default_condition(default)
1533    }
1534
1535    fn make_run(run: RunConfig<'_>) {
1536        run.builder.ensure(Rustfmt {
1537            build_compiler: run.builder.compiler_for(
1538                run.builder.top_stage,
1539                run.builder.config.host_target,
1540                run.target,
1541            ),
1542            target: run.target,
1543        });
1544    }
1545
1546    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1547        let compilers =
1548            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target);
1549
1550        let rustfmt = builder.ensure(tool::Rustfmt::from_compilers(compilers));
1551        let cargofmt = builder.ensure(tool::Cargofmt::from_compilers(compilers));
1552
1553        let mut tarball = Tarball::new(builder, "rustfmt", &self.target.triple);
1554        tarball.set_overlay(OverlayKind::Rustfmt);
1555        tarball.is_preview(true);
1556        tarball.add_file(&rustfmt.tool_path, "bin", FileType::Executable);
1557        tarball.add_file(&cargofmt.tool_path, "bin", FileType::Executable);
1558        tarball.add_legal_and_readme_to("share/doc/rustfmt");
1559        Some(tarball.generate())
1560    }
1561}
1562
1563#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1564pub struct Extended {
1565    stage: u32,
1566    host: TargetSelection,
1567    target: TargetSelection,
1568}
1569
1570impl Step for Extended {
1571    type Output = ();
1572    const DEFAULT: bool = true;
1573    const IS_HOST: bool = true;
1574
1575    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1576        let builder = run.builder;
1577        run.alias("extended").default_condition(builder.config.extended)
1578    }
1579
1580    fn make_run(run: RunConfig<'_>) {
1581        run.builder.ensure(Extended {
1582            stage: run.builder.top_stage,
1583            host: run.builder.config.host_target,
1584            target: run.target,
1585        });
1586    }
1587
1588    /// Creates a combined installer for the specified target in the provided stage.
1589    fn run(self, builder: &Builder<'_>) {
1590        let target = self.target;
1591        let stage = self.stage;
1592        let compiler = builder.compiler_for(self.stage, self.host, self.target);
1593
1594        builder.info(&format!("Dist extended stage{} ({})", compiler.stage, target));
1595
1596        let mut tarballs = Vec::new();
1597        let mut built_tools = HashSet::new();
1598        macro_rules! add_component {
1599            ($name:expr => $step:expr) => {
1600                if let Some(Some(tarball)) = builder.ensure_if_default($step, Kind::Dist) {
1601                    tarballs.push(tarball);
1602                    built_tools.insert($name);
1603                }
1604            };
1605        }
1606
1607        let target_compiler = builder.compiler(stage, target);
1608        // When rust-std package split from rustc, we needed to ensure that during
1609        // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering
1610        // the std files during uninstall. To do this ensure that rustc comes
1611        // before rust-std in the list below.
1612        tarballs.push(builder.ensure(Rustc { compiler: target_compiler }));
1613        tarballs.push(builder.ensure(Std { compiler, target }).expect("missing std"));
1614
1615        if target.is_windows_gnu() {
1616            tarballs.push(builder.ensure(Mingw { host: target }).expect("missing mingw"));
1617        }
1618
1619        add_component!("rust-docs" => Docs { host: target });
1620        // Std stage N is documented with compiler stage N
1621        add_component!("rust-json-docs" => JsonDocs { build_compiler: target_compiler, target });
1622        add_component!("cargo" => Cargo { build_compiler: compiler, target });
1623        add_component!("rustfmt" => Rustfmt { build_compiler: compiler, target });
1624        add_component!("rust-analyzer" => RustAnalyzer { build_compiler: compiler, target });
1625        add_component!("llvm-components" => LlvmTools { target });
1626        add_component!("clippy" => Clippy { build_compiler: compiler, target });
1627        add_component!("miri" => Miri { build_compiler: compiler, target });
1628        add_component!("analysis" => Analysis { compiler, target });
1629        add_component!("rustc-codegen-cranelift" => CraneliftCodegenBackend {
1630            build_compiler: compiler,
1631            target
1632        });
1633        add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker {
1634            build_compiler: compiler,
1635            target
1636        });
1637
1638        let etc = builder.src.join("src/etc/installer");
1639
1640        // Avoid producing tarballs during a dry run.
1641        if builder.config.dry_run() {
1642            return;
1643        }
1644
1645        let tarball = Tarball::new(builder, "rust", &target.triple);
1646        let generated = tarball.combine(&tarballs);
1647
1648        let tmp = tmpdir(builder).join("combined-tarball");
1649        let work = generated.work_dir();
1650
1651        let mut license = String::new();
1652        license += &builder.read(&builder.src.join("COPYRIGHT"));
1653        license += &builder.read(&builder.src.join("LICENSE-APACHE"));
1654        license += &builder.read(&builder.src.join("LICENSE-MIT"));
1655        license.push('\n');
1656        license.push('\n');
1657
1658        let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18";
1659        let mut rtf = rtf.to_string();
1660        rtf.push('\n');
1661        for line in license.lines() {
1662            rtf.push_str(line);
1663            rtf.push_str("\\line ");
1664        }
1665        rtf.push('}');
1666
1667        fn filter(contents: &str, marker: &str) -> String {
1668            let start = format!("tool-{marker}-start");
1669            let end = format!("tool-{marker}-end");
1670            let mut lines = Vec::new();
1671            let mut omitted = false;
1672            for line in contents.lines() {
1673                if line.contains(&start) {
1674                    omitted = true;
1675                } else if line.contains(&end) {
1676                    omitted = false;
1677                } else if !omitted {
1678                    lines.push(line);
1679                }
1680            }
1681
1682            lines.join("\n")
1683        }
1684
1685        let xform = |p: &Path| {
1686            let mut contents = t!(fs::read_to_string(p));
1687            for tool in &["miri", "rust-docs"] {
1688                if !built_tools.contains(tool) {
1689                    contents = filter(&contents, tool);
1690                }
1691            }
1692            let ret = tmp.join(p.file_name().unwrap());
1693            t!(fs::write(&ret, &contents));
1694            ret
1695        };
1696
1697        if target.contains("apple-darwin") {
1698            builder.info("building pkg installer");
1699            let pkg = tmp.join("pkg");
1700            let _ = fs::remove_dir_all(&pkg);
1701
1702            let pkgbuild = |component: &str| {
1703                let mut cmd = command("pkgbuild");
1704                cmd.arg("--identifier")
1705                    .arg(format!("org.rust-lang.{component}"))
1706                    .arg("--scripts")
1707                    .arg(pkg.join(component))
1708                    .arg("--nopayload")
1709                    .arg(pkg.join(component).with_extension("pkg"));
1710                cmd.run(builder);
1711            };
1712
1713            let prepare = |name: &str| {
1714                builder.create_dir(&pkg.join(name));
1715                builder.cp_link_r(
1716                    &work.join(format!("{}-{}", pkgname(builder, name), target.triple)),
1717                    &pkg.join(name),
1718                );
1719                builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), FileType::Script);
1720                pkgbuild(name);
1721            };
1722            prepare("rustc");
1723            prepare("cargo");
1724            prepare("rust-std");
1725            prepare("rust-analysis");
1726
1727            for tool in &[
1728                "clippy",
1729                "rustfmt",
1730                "rust-analyzer",
1731                "rust-docs",
1732                "miri",
1733                "rustc-codegen-cranelift",
1734            ] {
1735                if built_tools.contains(tool) {
1736                    prepare(tool);
1737                }
1738            }
1739            // create an 'uninstall' package
1740            builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), FileType::Script);
1741            pkgbuild("uninstall");
1742
1743            builder.create_dir(&pkg.join("res"));
1744            builder.create(&pkg.join("res/LICENSE.txt"), &license);
1745            builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), FileType::Regular);
1746            let mut cmd = command("productbuild");
1747            cmd.arg("--distribution")
1748                .arg(xform(&etc.join("pkg/Distribution.xml")))
1749                .arg("--resources")
1750                .arg(pkg.join("res"))
1751                .arg(distdir(builder).join(format!(
1752                    "{}-{}.pkg",
1753                    pkgname(builder, "rust"),
1754                    target.triple
1755                )))
1756                .arg("--package-path")
1757                .arg(&pkg);
1758            let _time = timeit(builder);
1759            cmd.run(builder);
1760        }
1761
1762        // FIXME(mati865): `gnullvm` here is temporary, remove it once it can host itself
1763        if target.is_windows() && !target.contains("gnullvm") {
1764            let exe = tmp.join("exe");
1765            let _ = fs::remove_dir_all(&exe);
1766
1767            let prepare = |name: &str| {
1768                builder.create_dir(&exe.join(name));
1769                let dir = if name == "rust-std" || name == "rust-analysis" {
1770                    format!("{}-{}", name, target.triple)
1771                } else if name == "rust-analyzer" {
1772                    "rust-analyzer-preview".to_string()
1773                } else if name == "clippy" {
1774                    "clippy-preview".to_string()
1775                } else if name == "rustfmt" {
1776                    "rustfmt-preview".to_string()
1777                } else if name == "miri" {
1778                    "miri-preview".to_string()
1779                } else if name == "rustc-codegen-cranelift" {
1780                    // FIXME add installer support for cg_clif once it is ready to be distributed on
1781                    // windows.
1782                    unreachable!("cg_clif shouldn't be built for windows");
1783                } else {
1784                    name.to_string()
1785                };
1786                builder.cp_link_r(
1787                    &work.join(format!("{}-{}", pkgname(builder, name), target.triple)).join(dir),
1788                    &exe.join(name),
1789                );
1790                builder.remove(&exe.join(name).join("manifest.in"));
1791            };
1792            prepare("rustc");
1793            prepare("cargo");
1794            prepare("rust-analysis");
1795            prepare("rust-std");
1796            for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] {
1797                if built_tools.contains(tool) {
1798                    prepare(tool);
1799                }
1800            }
1801            if target.is_windows_gnu() {
1802                prepare("rust-mingw");
1803            }
1804
1805            builder.install(&etc.join("gfx/rust-logo.ico"), &exe, FileType::Regular);
1806
1807            // Generate msi installer
1808            let wix_path = env::var_os("WIX")
1809                .expect("`WIX` environment variable must be set for generating MSI installer(s).");
1810            let wix = PathBuf::from(wix_path);
1811            let heat = wix.join("bin/heat.exe");
1812            let candle = wix.join("bin/candle.exe");
1813            let light = wix.join("bin/light.exe");
1814
1815            let heat_flags = ["-nologo", "-gg", "-sfrag", "-srd", "-sreg"];
1816            command(&heat)
1817                .current_dir(&exe)
1818                .arg("dir")
1819                .arg("rustc")
1820                .args(heat_flags)
1821                .arg("-cg")
1822                .arg("RustcGroup")
1823                .arg("-dr")
1824                .arg("Rustc")
1825                .arg("-var")
1826                .arg("var.RustcDir")
1827                .arg("-out")
1828                .arg(exe.join("RustcGroup.wxs"))
1829                .run(builder);
1830            if built_tools.contains("rust-docs") {
1831                command(&heat)
1832                    .current_dir(&exe)
1833                    .arg("dir")
1834                    .arg("rust-docs")
1835                    .args(heat_flags)
1836                    .arg("-cg")
1837                    .arg("DocsGroup")
1838                    .arg("-dr")
1839                    .arg("Docs")
1840                    .arg("-var")
1841                    .arg("var.DocsDir")
1842                    .arg("-out")
1843                    .arg(exe.join("DocsGroup.wxs"))
1844                    .arg("-t")
1845                    .arg(etc.join("msi/squash-components.xsl"))
1846                    .run(builder);
1847            }
1848            command(&heat)
1849                .current_dir(&exe)
1850                .arg("dir")
1851                .arg("cargo")
1852                .args(heat_flags)
1853                .arg("-cg")
1854                .arg("CargoGroup")
1855                .arg("-dr")
1856                .arg("Cargo")
1857                .arg("-var")
1858                .arg("var.CargoDir")
1859                .arg("-out")
1860                .arg(exe.join("CargoGroup.wxs"))
1861                .arg("-t")
1862                .arg(etc.join("msi/remove-duplicates.xsl"))
1863                .run(builder);
1864            command(&heat)
1865                .current_dir(&exe)
1866                .arg("dir")
1867                .arg("rust-std")
1868                .args(heat_flags)
1869                .arg("-cg")
1870                .arg("StdGroup")
1871                .arg("-dr")
1872                .arg("Std")
1873                .arg("-var")
1874                .arg("var.StdDir")
1875                .arg("-out")
1876                .arg(exe.join("StdGroup.wxs"))
1877                .run(builder);
1878            if built_tools.contains("rust-analyzer") {
1879                command(&heat)
1880                    .current_dir(&exe)
1881                    .arg("dir")
1882                    .arg("rust-analyzer")
1883                    .args(heat_flags)
1884                    .arg("-cg")
1885                    .arg("RustAnalyzerGroup")
1886                    .arg("-dr")
1887                    .arg("RustAnalyzer")
1888                    .arg("-var")
1889                    .arg("var.RustAnalyzerDir")
1890                    .arg("-out")
1891                    .arg(exe.join("RustAnalyzerGroup.wxs"))
1892                    .arg("-t")
1893                    .arg(etc.join("msi/remove-duplicates.xsl"))
1894                    .run(builder);
1895            }
1896            if built_tools.contains("clippy") {
1897                command(&heat)
1898                    .current_dir(&exe)
1899                    .arg("dir")
1900                    .arg("clippy")
1901                    .args(heat_flags)
1902                    .arg("-cg")
1903                    .arg("ClippyGroup")
1904                    .arg("-dr")
1905                    .arg("Clippy")
1906                    .arg("-var")
1907                    .arg("var.ClippyDir")
1908                    .arg("-out")
1909                    .arg(exe.join("ClippyGroup.wxs"))
1910                    .arg("-t")
1911                    .arg(etc.join("msi/remove-duplicates.xsl"))
1912                    .run(builder);
1913            }
1914            if built_tools.contains("rustfmt") {
1915                command(&heat)
1916                    .current_dir(&exe)
1917                    .arg("dir")
1918                    .arg("rustfmt")
1919                    .args(heat_flags)
1920                    .arg("-cg")
1921                    .arg("RustFmtGroup")
1922                    .arg("-dr")
1923                    .arg("RustFmt")
1924                    .arg("-var")
1925                    .arg("var.RustFmtDir")
1926                    .arg("-out")
1927                    .arg(exe.join("RustFmtGroup.wxs"))
1928                    .arg("-t")
1929                    .arg(etc.join("msi/remove-duplicates.xsl"))
1930                    .run(builder);
1931            }
1932            if built_tools.contains("miri") {
1933                command(&heat)
1934                    .current_dir(&exe)
1935                    .arg("dir")
1936                    .arg("miri")
1937                    .args(heat_flags)
1938                    .arg("-cg")
1939                    .arg("MiriGroup")
1940                    .arg("-dr")
1941                    .arg("Miri")
1942                    .arg("-var")
1943                    .arg("var.MiriDir")
1944                    .arg("-out")
1945                    .arg(exe.join("MiriGroup.wxs"))
1946                    .arg("-t")
1947                    .arg(etc.join("msi/remove-duplicates.xsl"))
1948                    .run(builder);
1949            }
1950            command(&heat)
1951                .current_dir(&exe)
1952                .arg("dir")
1953                .arg("rust-analysis")
1954                .args(heat_flags)
1955                .arg("-cg")
1956                .arg("AnalysisGroup")
1957                .arg("-dr")
1958                .arg("Analysis")
1959                .arg("-var")
1960                .arg("var.AnalysisDir")
1961                .arg("-out")
1962                .arg(exe.join("AnalysisGroup.wxs"))
1963                .arg("-t")
1964                .arg(etc.join("msi/remove-duplicates.xsl"))
1965                .run(builder);
1966            if target.is_windows_gnu() {
1967                command(&heat)
1968                    .current_dir(&exe)
1969                    .arg("dir")
1970                    .arg("rust-mingw")
1971                    .args(heat_flags)
1972                    .arg("-cg")
1973                    .arg("GccGroup")
1974                    .arg("-dr")
1975                    .arg("Gcc")
1976                    .arg("-var")
1977                    .arg("var.GccDir")
1978                    .arg("-out")
1979                    .arg(exe.join("GccGroup.wxs"))
1980                    .run(builder);
1981            }
1982
1983            let candle = |input: &Path| {
1984                let output = exe.join(input.file_stem().unwrap()).with_extension("wixobj");
1985                let arch = if target.contains("x86_64") { "x64" } else { "x86" };
1986                let mut cmd = command(&candle);
1987                cmd.current_dir(&exe)
1988                    .arg("-nologo")
1989                    .arg("-dRustcDir=rustc")
1990                    .arg("-dCargoDir=cargo")
1991                    .arg("-dStdDir=rust-std")
1992                    .arg("-dAnalysisDir=rust-analysis")
1993                    .arg("-arch")
1994                    .arg(arch)
1995                    .arg("-out")
1996                    .arg(&output)
1997                    .arg(input);
1998                add_env(builder, &mut cmd, target, &built_tools);
1999
2000                if built_tools.contains("clippy") {
2001                    cmd.arg("-dClippyDir=clippy");
2002                }
2003                if built_tools.contains("rustfmt") {
2004                    cmd.arg("-dRustFmtDir=rustfmt");
2005                }
2006                if built_tools.contains("rust-docs") {
2007                    cmd.arg("-dDocsDir=rust-docs");
2008                }
2009                if built_tools.contains("rust-analyzer") {
2010                    cmd.arg("-dRustAnalyzerDir=rust-analyzer");
2011                }
2012                if built_tools.contains("miri") {
2013                    cmd.arg("-dMiriDir=miri");
2014                }
2015                if target.is_windows_gnu() {
2016                    cmd.arg("-dGccDir=rust-mingw");
2017                }
2018                cmd.run(builder);
2019            };
2020            candle(&xform(&etc.join("msi/rust.wxs")));
2021            candle(&etc.join("msi/ui.wxs"));
2022            candle(&etc.join("msi/rustwelcomedlg.wxs"));
2023            candle("RustcGroup.wxs".as_ref());
2024            if built_tools.contains("rust-docs") {
2025                candle("DocsGroup.wxs".as_ref());
2026            }
2027            candle("CargoGroup.wxs".as_ref());
2028            candle("StdGroup.wxs".as_ref());
2029            if built_tools.contains("clippy") {
2030                candle("ClippyGroup.wxs".as_ref());
2031            }
2032            if built_tools.contains("rustfmt") {
2033                candle("RustFmtGroup.wxs".as_ref());
2034            }
2035            if built_tools.contains("miri") {
2036                candle("MiriGroup.wxs".as_ref());
2037            }
2038            if built_tools.contains("rust-analyzer") {
2039                candle("RustAnalyzerGroup.wxs".as_ref());
2040            }
2041            candle("AnalysisGroup.wxs".as_ref());
2042
2043            if target.is_windows_gnu() {
2044                candle("GccGroup.wxs".as_ref());
2045            }
2046
2047            builder.create(&exe.join("LICENSE.rtf"), &rtf);
2048            builder.install(&etc.join("gfx/banner.bmp"), &exe, FileType::Regular);
2049            builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, FileType::Regular);
2050
2051            builder.info(&format!("building `msi` installer with {light:?}"));
2052            let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple);
2053            let mut cmd = command(&light);
2054            cmd.arg("-nologo")
2055                .arg("-ext")
2056                .arg("WixUIExtension")
2057                .arg("-ext")
2058                .arg("WixUtilExtension")
2059                .arg("-out")
2060                .arg(exe.join(&filename))
2061                .arg("rust.wixobj")
2062                .arg("ui.wixobj")
2063                .arg("rustwelcomedlg.wixobj")
2064                .arg("RustcGroup.wixobj")
2065                .arg("CargoGroup.wixobj")
2066                .arg("StdGroup.wixobj")
2067                .arg("AnalysisGroup.wixobj")
2068                .current_dir(&exe);
2069
2070            if built_tools.contains("clippy") {
2071                cmd.arg("ClippyGroup.wixobj");
2072            }
2073            if built_tools.contains("rustfmt") {
2074                cmd.arg("RustFmtGroup.wixobj");
2075            }
2076            if built_tools.contains("miri") {
2077                cmd.arg("MiriGroup.wixobj");
2078            }
2079            if built_tools.contains("rust-analyzer") {
2080                cmd.arg("RustAnalyzerGroup.wixobj");
2081            }
2082            if built_tools.contains("rust-docs") {
2083                cmd.arg("DocsGroup.wixobj");
2084            }
2085
2086            if target.is_windows_gnu() {
2087                cmd.arg("GccGroup.wixobj");
2088            }
2089            // ICE57 wrongly complains about the shortcuts
2090            cmd.arg("-sice:ICE57");
2091
2092            let _time = timeit(builder);
2093            cmd.run(builder);
2094
2095            if !builder.config.dry_run() {
2096                t!(move_file(exe.join(&filename), distdir(builder).join(&filename)));
2097            }
2098        }
2099    }
2100}
2101
2102fn add_env(
2103    builder: &Builder<'_>,
2104    cmd: &mut BootstrapCommand,
2105    target: TargetSelection,
2106    built_tools: &HashSet<&'static str>,
2107) {
2108    let mut parts = builder.version.split('.');
2109    cmd.env("CFG_RELEASE_INFO", builder.rust_version())
2110        .env("CFG_RELEASE_NUM", &builder.version)
2111        .env("CFG_RELEASE", builder.rust_release())
2112        .env("CFG_VER_MAJOR", parts.next().unwrap())
2113        .env("CFG_VER_MINOR", parts.next().unwrap())
2114        .env("CFG_VER_PATCH", parts.next().unwrap())
2115        .env("CFG_VER_BUILD", "0") // just needed to build
2116        .env("CFG_PACKAGE_VERS", builder.rust_package_vers())
2117        .env("CFG_PACKAGE_NAME", pkgname(builder, "rust"))
2118        .env("CFG_BUILD", target.triple)
2119        .env("CFG_CHANNEL", &builder.config.channel);
2120
2121    if target.contains("windows-gnullvm") {
2122        cmd.env("CFG_MINGW", "1").env("CFG_ABI", "LLVM");
2123    } else if target.is_windows_gnu() {
2124        cmd.env("CFG_MINGW", "1").env("CFG_ABI", "GNU");
2125    } else {
2126        cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC");
2127    }
2128
2129    // ensure these variables are defined
2130    let mut define_optional_tool = |tool_name: &str, env_name: &str| {
2131        cmd.env(env_name, if built_tools.contains(tool_name) { "1" } else { "0" });
2132    };
2133    define_optional_tool("rustfmt", "CFG_RUSTFMT");
2134    define_optional_tool("clippy", "CFG_CLIPPY");
2135    define_optional_tool("miri", "CFG_MIRI");
2136    define_optional_tool("rust-analyzer", "CFG_RA");
2137}
2138
2139fn install_llvm_file(
2140    builder: &Builder<'_>,
2141    source: &Path,
2142    destination: &Path,
2143    install_symlink: bool,
2144) {
2145    if builder.config.dry_run() {
2146        return;
2147    }
2148
2149    if source.is_symlink() {
2150        // If we have a symlink like libLLVM-18.so -> libLLVM.so.18.1, install the target of the
2151        // symlink, which is what will actually get loaded at runtime.
2152        builder.install(&t!(fs::canonicalize(source)), destination, FileType::NativeLibrary);
2153
2154        let full_dest = destination.join(source.file_name().unwrap());
2155        if install_symlink {
2156            // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a
2157            // symlink is fine here, as this is not a rustup component.
2158            builder.copy_link(source, &full_dest, FileType::NativeLibrary);
2159        } else {
2160            // Otherwise, replace the symlink with an equivalent linker script. This is used when
2161            // projects like miri link against librustc_driver.so. We don't use a symlink, as
2162            // these are not allowed inside rustup components.
2163            let link = t!(fs::read_link(source));
2164            let mut linker_script = t!(fs::File::create(full_dest));
2165            t!(write!(linker_script, "INPUT({})\n", link.display()));
2166
2167            // We also want the linker script to have the same mtime as the source, otherwise it
2168            // can trigger rebuilds.
2169            let meta = t!(fs::metadata(source));
2170            if let Ok(mtime) = meta.modified() {
2171                t!(linker_script.set_modified(mtime));
2172            }
2173        }
2174    } else {
2175        builder.install(source, destination, FileType::NativeLibrary);
2176    }
2177}
2178
2179/// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking.
2180///
2181/// Returns whether the files were actually copied.
2182#[cfg_attr(
2183    feature = "tracing",
2184    instrument(
2185        level = "trace",
2186        name = "maybe_install_llvm",
2187        skip_all,
2188        fields(target = ?target, dst_libdir = ?dst_libdir, install_symlink = install_symlink),
2189    ),
2190)]
2191fn maybe_install_llvm(
2192    builder: &Builder<'_>,
2193    target: TargetSelection,
2194    dst_libdir: &Path,
2195    install_symlink: bool,
2196) -> bool {
2197    // If the LLVM was externally provided, then we don't currently copy
2198    // artifacts into the sysroot. This is not necessarily the right
2199    // choice (in particular, it will require the LLVM dylib to be in
2200    // the linker's load path at runtime), but the common use case for
2201    // external LLVMs is distribution provided LLVMs, and in that case
2202    // they're usually in the standard search path (e.g., /usr/lib) and
2203    // copying them here is going to cause problems as we may end up
2204    // with the wrong files and isn't what distributions want.
2205    //
2206    // This behavior may be revisited in the future though.
2207    //
2208    // NOTE: this intentionally doesn't use `is_rust_llvm`; whether this is patched or not doesn't matter,
2209    // we only care if the shared object itself is managed by bootstrap.
2210    //
2211    // If the LLVM is coming from ourselves (just from CI) though, we
2212    // still want to install it, as it otherwise won't be available.
2213    if builder.config.is_system_llvm(target) {
2214        trace!("system LLVM requested, no install");
2215        return false;
2216    }
2217
2218    // On macOS, rustc (and LLVM tools) link to an unversioned libLLVM.dylib
2219    // instead of libLLVM-11-rust-....dylib, as on linux. It's not entirely
2220    // clear why this is the case, though. llvm-config will emit the versioned
2221    // paths and we don't want those in the sysroot (as we're expecting
2222    // unversioned paths).
2223    if target.contains("apple-darwin") && builder.llvm_link_shared() {
2224        let src_libdir = builder.llvm_out(target).join("lib");
2225        let llvm_dylib_path = src_libdir.join("libLLVM.dylib");
2226        if llvm_dylib_path.exists() {
2227            builder.install(&llvm_dylib_path, dst_libdir, FileType::NativeLibrary);
2228        }
2229        !builder.config.dry_run()
2230    } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) =
2231        llvm::prebuilt_llvm_config(builder, target, true)
2232    {
2233        trace!("LLVM already built, installing LLVM files");
2234        let mut cmd = command(llvm_config);
2235        cmd.arg("--libfiles");
2236        builder.verbose(|| println!("running {cmd:?}"));
2237        let files = cmd.run_capture_stdout(builder).stdout();
2238        let build_llvm_out = &builder.llvm_out(builder.config.host_target);
2239        let target_llvm_out = &builder.llvm_out(target);
2240        for file in files.trim_end().split(' ') {
2241            // If we're not using a custom LLVM, make sure we package for the target.
2242            let file = if let Ok(relative_path) = Path::new(file).strip_prefix(build_llvm_out) {
2243                target_llvm_out.join(relative_path)
2244            } else {
2245                PathBuf::from(file)
2246            };
2247            install_llvm_file(builder, &file, dst_libdir, install_symlink);
2248        }
2249        !builder.config.dry_run()
2250    } else {
2251        false
2252    }
2253}
2254
2255/// Maybe add libLLVM.so to the target lib-dir for linking.
2256#[cfg_attr(
2257    feature = "tracing",
2258    instrument(
2259        level = "trace",
2260        name = "maybe_install_llvm_target",
2261        skip_all,
2262        fields(
2263            llvm_link_shared = ?builder.llvm_link_shared(),
2264            target = ?target,
2265            sysroot = ?sysroot,
2266        ),
2267    ),
2268)]
2269pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
2270    let dst_libdir = sysroot.join("lib/rustlib").join(target).join("lib");
2271    // We do not need to copy LLVM files into the sysroot if it is not
2272    // dynamically linked; it is already included into librustc_llvm
2273    // statically.
2274    if builder.llvm_link_shared() {
2275        maybe_install_llvm(builder, target, &dst_libdir, false);
2276    }
2277}
2278
2279/// Maybe add libLLVM.so to the runtime lib-dir for rustc itself.
2280#[cfg_attr(
2281    feature = "tracing",
2282    instrument(
2283        level = "trace",
2284        name = "maybe_install_llvm_runtime",
2285        skip_all,
2286        fields(
2287            llvm_link_shared = ?builder.llvm_link_shared(),
2288            target = ?target,
2289            sysroot = ?sysroot,
2290        ),
2291    ),
2292)]
2293pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
2294    let dst_libdir = sysroot.join(builder.sysroot_libdir_relative(Compiler::new(1, target)));
2295    // We do not need to copy LLVM files into the sysroot if it is not
2296    // dynamically linked; it is already included into librustc_llvm
2297    // statically.
2298    if builder.llvm_link_shared() {
2299        maybe_install_llvm(builder, target, &dst_libdir, false);
2300    }
2301}
2302
2303#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2304pub struct LlvmTools {
2305    pub target: TargetSelection,
2306}
2307
2308impl Step for LlvmTools {
2309    type Output = Option<GeneratedTarball>;
2310    const IS_HOST: bool = true;
2311    const DEFAULT: bool = true;
2312
2313    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2314        let default = should_build_extended_tool(run.builder, "llvm-tools");
2315
2316        let mut run = run.alias("llvm-tools");
2317        for tool in LLVM_TOOLS {
2318            run = run.alias(tool);
2319        }
2320
2321        run.default_condition(default)
2322    }
2323
2324    fn make_run(run: RunConfig<'_>) {
2325        run.builder.ensure(LlvmTools { target: run.target });
2326    }
2327
2328    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2329        fn tools_to_install(paths: &[PathBuf]) -> Vec<&'static str> {
2330            let mut tools = vec![];
2331
2332            for path in paths {
2333                let path = path.to_str().unwrap();
2334
2335                // Include all tools if path is 'llvm-tools'.
2336                if path == "llvm-tools" {
2337                    return LLVM_TOOLS.to_owned();
2338                }
2339
2340                for tool in LLVM_TOOLS {
2341                    if path == *tool {
2342                        tools.push(*tool);
2343                    }
2344                }
2345            }
2346
2347            // If no specific tool is requested, include all tools.
2348            if tools.is_empty() {
2349                tools = LLVM_TOOLS.to_owned();
2350            }
2351
2352            tools
2353        }
2354
2355        let target = self.target;
2356
2357        // Run only if a custom llvm-config is not used
2358        if let Some(config) = builder.config.target_config.get(&target)
2359            && !builder.config.llvm_from_ci
2360            && config.llvm_config.is_some()
2361        {
2362            builder.info(&format!("Skipping LlvmTools ({target}): external LLVM"));
2363            return None;
2364        }
2365
2366        if !builder.config.dry_run() {
2367            builder.require_submodule("src/llvm-project", None);
2368        }
2369
2370        builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2371
2372        let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple);
2373        tarball.set_overlay(OverlayKind::Llvm);
2374        tarball.is_preview(true);
2375
2376        if builder.config.llvm_tools_enabled {
2377            // Prepare the image directory
2378            let src_bindir = builder.llvm_out(target).join("bin");
2379            let dst_bindir = format!("lib/rustlib/{}/bin", target.triple);
2380            for tool in tools_to_install(&builder.paths) {
2381                let exe = src_bindir.join(exe(tool, target));
2382                // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them.
2383                if !exe.exists() && builder.config.llvm_from_ci {
2384                    eprintln!("{} does not exist; skipping copy", exe.display());
2385                    continue;
2386                }
2387
2388                tarball.add_file(&exe, &dst_bindir, FileType::Executable);
2389            }
2390        }
2391
2392        // Copy libLLVM.so to the target lib dir as well, so the RPATH like
2393        // `$ORIGIN/../lib` can find it. It may also be used as a dependency
2394        // of `rustc-dev` to support the inherited `-lLLVM` when using the
2395        // compiler libraries.
2396        maybe_install_llvm_target(builder, target, tarball.image_dir());
2397
2398        Some(tarball.generate())
2399    }
2400}
2401
2402/// Distributes the `llvm-bitcode-linker` tool so that it can be used by a compiler whose host
2403/// is `target`.
2404#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
2405pub struct LlvmBitcodeLinker {
2406    /// The linker will be compiled by this compiler.
2407    pub build_compiler: Compiler,
2408    /// The linker will by usable by rustc on this host.
2409    pub target: TargetSelection,
2410}
2411
2412impl Step for LlvmBitcodeLinker {
2413    type Output = Option<GeneratedTarball>;
2414    const DEFAULT: bool = true;
2415    const IS_HOST: bool = true;
2416
2417    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2418        let default = should_build_extended_tool(run.builder, "llvm-bitcode-linker");
2419        run.alias("llvm-bitcode-linker").default_condition(default)
2420    }
2421
2422    fn make_run(run: RunConfig<'_>) {
2423        run.builder.ensure(LlvmBitcodeLinker {
2424            build_compiler: tool::LlvmBitcodeLinker::get_build_compiler_for_target(
2425                run.builder,
2426                run.target,
2427            ),
2428            target: run.target,
2429        });
2430    }
2431
2432    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2433        let target = self.target;
2434
2435        let llbc_linker = builder
2436            .ensure(tool::LlvmBitcodeLinker::from_build_compiler(self.build_compiler, target));
2437
2438        let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple);
2439
2440        // Prepare the image directory
2441        let mut tarball = Tarball::new(builder, "llvm-bitcode-linker", &target.triple);
2442        tarball.set_overlay(OverlayKind::LlvmBitcodeLinker);
2443        tarball.is_preview(true);
2444
2445        tarball.add_file(&llbc_linker.tool_path, self_contained_bin_dir, FileType::Executable);
2446
2447        Some(tarball.generate())
2448    }
2449}
2450
2451/// Tarball intended for internal consumption to ease rustc/std development.
2452///
2453/// Should not be considered stable by end users.
2454///
2455/// In practice, this is the tarball that gets downloaded and used by
2456/// `llvm.download-ci-llvm`.
2457///
2458/// (Don't confuse this with [`RustcDev`], with a `c`!)
2459#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2460pub struct RustDev {
2461    pub target: TargetSelection,
2462}
2463
2464impl Step for RustDev {
2465    type Output = Option<GeneratedTarball>;
2466    const DEFAULT: bool = true;
2467    const IS_HOST: bool = true;
2468
2469    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2470        run.alias("rust-dev")
2471    }
2472
2473    fn make_run(run: RunConfig<'_>) {
2474        run.builder.ensure(RustDev { target: run.target });
2475    }
2476
2477    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2478        let target = self.target;
2479
2480        /* run only if llvm-config isn't used */
2481        if let Some(config) = builder.config.target_config.get(&target)
2482            && let Some(ref _s) = config.llvm_config
2483        {
2484            builder.info(&format!("Skipping RustDev ({target}): external LLVM"));
2485            return None;
2486        }
2487
2488        if !builder.config.dry_run() {
2489            builder.require_submodule("src/llvm-project", None);
2490        }
2491
2492        let mut tarball = Tarball::new(builder, "rust-dev", &target.triple);
2493        tarball.set_overlay(OverlayKind::Llvm);
2494        // LLVM requires a shared object symlink to exist on some platforms.
2495        tarball.permit_symlinks(true);
2496
2497        builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2498
2499        let src_bindir = builder.llvm_out(target).join("bin");
2500        // If updating this, you likely want to change
2501        // src/bootstrap/download-ci-llvm-stamp as well, otherwise local users
2502        // will not pick up the extra file until LLVM gets bumped.
2503        // We should include all the build artifacts obtained from a source build,
2504        // so that you can use the downloadable LLVM as if you’ve just run a full source build.
2505        if src_bindir.exists() {
2506            for entry in walkdir::WalkDir::new(&src_bindir) {
2507                let entry = t!(entry);
2508                if entry.file_type().is_file() && !entry.path_is_symlink() {
2509                    let name = entry.file_name().to_str().unwrap();
2510                    tarball.add_file(src_bindir.join(name), "bin", FileType::Executable);
2511                }
2512            }
2513        }
2514
2515        if builder.config.lld_enabled {
2516            // We want to package `lld` to use it with `download-ci-llvm`.
2517            let lld_out = builder.ensure(crate::core::build_steps::llvm::Lld { target });
2518
2519            // We don't build LLD on some platforms, so only add it if it exists
2520            let lld_path = lld_out.join("bin").join(exe("lld", target));
2521            if lld_path.exists() {
2522                tarball.add_file(&lld_path, "bin", FileType::Executable);
2523            }
2524        }
2525
2526        tarball.add_file(builder.llvm_filecheck(target), "bin", FileType::Executable);
2527
2528        // Copy the include directory as well; needed mostly to build
2529        // librustc_llvm properly (e.g., llvm-config.h is in here). But also
2530        // just broadly useful to be able to link against the bundled LLVM.
2531        tarball.add_dir(builder.llvm_out(target).join("include"), "include");
2532
2533        // Copy libLLVM.so to the target lib dir as well, so the RPATH like
2534        // `$ORIGIN/../lib` can find it. It may also be used as a dependency
2535        // of `rustc-dev` to support the inherited `-lLLVM` when using the
2536        // compiler libraries.
2537        let dst_libdir = tarball.image_dir().join("lib");
2538        maybe_install_llvm(builder, target, &dst_libdir, true);
2539        let link_type = if builder.llvm_link_shared() { "dynamic" } else { "static" };
2540        t!(std::fs::write(tarball.image_dir().join("link-type.txt"), link_type), dst_libdir);
2541
2542        // Copy the `compiler-rt` source, so that `library/profiler_builtins`
2543        // can potentially use it to build the profiler runtime without needing
2544        // to check out the LLVM submodule.
2545        copy_src_dirs(
2546            builder,
2547            &builder.src.join("src").join("llvm-project"),
2548            &["compiler-rt"],
2549            // The test subdirectory is much larger than the rest of the source,
2550            // and we currently don't use these test files anyway.
2551            &["compiler-rt/test"],
2552            tarball.image_dir(),
2553        );
2554
2555        Some(tarball.generate())
2556    }
2557}
2558
2559/// Tarball intended for internal consumption to ease rustc/std development.
2560///
2561/// Should not be considered stable by end users.
2562#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2563pub struct Bootstrap {
2564    pub target: TargetSelection,
2565}
2566
2567impl Step for Bootstrap {
2568    type Output = Option<GeneratedTarball>;
2569    const DEFAULT: bool = false;
2570    const IS_HOST: bool = true;
2571
2572    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2573        run.alias("bootstrap")
2574    }
2575
2576    fn make_run(run: RunConfig<'_>) {
2577        run.builder.ensure(Bootstrap { target: run.target });
2578    }
2579
2580    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2581        let target = self.target;
2582
2583        let tarball = Tarball::new(builder, "bootstrap", &target.triple);
2584
2585        let bootstrap_outdir = &builder.bootstrap_out;
2586        for file in &["bootstrap", "rustc", "rustdoc"] {
2587            tarball.add_file(
2588                bootstrap_outdir.join(exe(file, target)),
2589                "bootstrap/bin",
2590                FileType::Executable,
2591            );
2592        }
2593
2594        Some(tarball.generate())
2595    }
2596}
2597
2598/// Tarball containing a prebuilt version of the build-manifest tool, intended to be used by the
2599/// release process to avoid cloning the monorepo and building stuff.
2600///
2601/// Should not be considered stable by end users.
2602#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2603pub struct BuildManifest {
2604    pub target: TargetSelection,
2605}
2606
2607impl Step for BuildManifest {
2608    type Output = GeneratedTarball;
2609    const DEFAULT: bool = false;
2610    const IS_HOST: bool = true;
2611
2612    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2613        run.alias("build-manifest")
2614    }
2615
2616    fn make_run(run: RunConfig<'_>) {
2617        run.builder.ensure(BuildManifest { target: run.target });
2618    }
2619
2620    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
2621        let build_manifest = builder.tool_exe(Tool::BuildManifest);
2622
2623        let tarball = Tarball::new(builder, "build-manifest", &self.target.triple);
2624        tarball.add_file(&build_manifest, "bin", FileType::Executable);
2625        tarball.generate()
2626    }
2627}
2628
2629/// Tarball containing artifacts necessary to reproduce the build of rustc.
2630///
2631/// Currently this is the PGO profile data.
2632///
2633/// Should not be considered stable by end users.
2634#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2635pub struct ReproducibleArtifacts {
2636    pub target: TargetSelection,
2637}
2638
2639impl Step for ReproducibleArtifacts {
2640    type Output = Option<GeneratedTarball>;
2641    const DEFAULT: bool = true;
2642    const IS_HOST: bool = true;
2643
2644    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2645        run.alias("reproducible-artifacts")
2646    }
2647
2648    fn make_run(run: RunConfig<'_>) {
2649        run.builder.ensure(ReproducibleArtifacts { target: run.target });
2650    }
2651
2652    fn run(self, builder: &Builder<'_>) -> Self::Output {
2653        let mut added_anything = false;
2654        let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple);
2655        if let Some(path) = builder.config.rust_profile_use.as_ref() {
2656            tarball.add_file(path, ".", FileType::Regular);
2657            added_anything = true;
2658        }
2659        if let Some(path) = builder.config.llvm_profile_use.as_ref() {
2660            tarball.add_file(path, ".", FileType::Regular);
2661            added_anything = true;
2662        }
2663        for profile in &builder.config.reproducible_artifacts {
2664            tarball.add_file(profile, ".", FileType::Regular);
2665            added_anything = true;
2666        }
2667        if added_anything { Some(tarball.generate()) } else { None }
2668    }
2669}
2670
2671/// Tarball containing a prebuilt version of the libgccjit library,
2672/// needed as a dependency for the GCC codegen backend (similarly to the LLVM
2673/// backend needing a prebuilt libLLVM).
2674#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2675pub struct Gcc {
2676    pub target: TargetSelection,
2677}
2678
2679impl Step for Gcc {
2680    type Output = GeneratedTarball;
2681
2682    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2683        run.alias("gcc")
2684    }
2685
2686    fn make_run(run: RunConfig<'_>) {
2687        run.builder.ensure(Gcc { target: run.target });
2688    }
2689
2690    fn run(self, builder: &Builder<'_>) -> Self::Output {
2691        let tarball = Tarball::new(builder, "gcc", &self.target.triple);
2692        let output = builder.ensure(super::gcc::Gcc { target: self.target });
2693        tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary);
2694        tarball.generate()
2695    }
2696}