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