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