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 {compiler, target});
1579
1580        let etc = builder.src.join("src/etc/installer");
1581
1582        // Avoid producing tarballs during a dry run.
1583        if builder.config.dry_run() {
1584            return;
1585        }
1586
1587        let tarball = Tarball::new(builder, "rust", &target.triple);
1588        let generated = tarball.combine(&tarballs);
1589
1590        let tmp = tmpdir(builder).join("combined-tarball");
1591        let work = generated.work_dir();
1592
1593        let mut license = String::new();
1594        license += &builder.read(&builder.src.join("COPYRIGHT"));
1595        license += &builder.read(&builder.src.join("LICENSE-APACHE"));
1596        license += &builder.read(&builder.src.join("LICENSE-MIT"));
1597        license.push('\n');
1598        license.push('\n');
1599
1600        let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18";
1601        let mut rtf = rtf.to_string();
1602        rtf.push('\n');
1603        for line in license.lines() {
1604            rtf.push_str(line);
1605            rtf.push_str("\\line ");
1606        }
1607        rtf.push('}');
1608
1609        fn filter(contents: &str, marker: &str) -> String {
1610            let start = format!("tool-{marker}-start");
1611            let end = format!("tool-{marker}-end");
1612            let mut lines = Vec::new();
1613            let mut omitted = false;
1614            for line in contents.lines() {
1615                if line.contains(&start) {
1616                    omitted = true;
1617                } else if line.contains(&end) {
1618                    omitted = false;
1619                } else if !omitted {
1620                    lines.push(line);
1621                }
1622            }
1623
1624            lines.join("\n")
1625        }
1626
1627        let xform = |p: &Path| {
1628            let mut contents = t!(fs::read_to_string(p));
1629            for tool in &["miri", "rust-docs"] {
1630                if !built_tools.contains(tool) {
1631                    contents = filter(&contents, tool);
1632                }
1633            }
1634            let ret = tmp.join(p.file_name().unwrap());
1635            t!(fs::write(&ret, &contents));
1636            ret
1637        };
1638
1639        if target.contains("apple-darwin") {
1640            builder.info("building pkg installer");
1641            let pkg = tmp.join("pkg");
1642            let _ = fs::remove_dir_all(&pkg);
1643
1644            let pkgbuild = |component: &str| {
1645                let mut cmd = command("pkgbuild");
1646                cmd.arg("--identifier")
1647                    .arg(format!("org.rust-lang.{component}"))
1648                    .arg("--scripts")
1649                    .arg(pkg.join(component))
1650                    .arg("--nopayload")
1651                    .arg(pkg.join(component).with_extension("pkg"));
1652                cmd.run(builder);
1653            };
1654
1655            let prepare = |name: &str| {
1656                builder.create_dir(&pkg.join(name));
1657                builder.cp_link_r(
1658                    &work.join(format!("{}-{}", pkgname(builder, name), target.triple)),
1659                    &pkg.join(name),
1660                );
1661                builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), FileType::Script);
1662                pkgbuild(name);
1663            };
1664            prepare("rustc");
1665            prepare("cargo");
1666            prepare("rust-std");
1667            prepare("rust-analysis");
1668
1669            for tool in &[
1670                "clippy",
1671                "rustfmt",
1672                "rust-analyzer",
1673                "rust-docs",
1674                "miri",
1675                "rustc-codegen-cranelift",
1676            ] {
1677                if built_tools.contains(tool) {
1678                    prepare(tool);
1679                }
1680            }
1681            // create an 'uninstall' package
1682            builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), FileType::Script);
1683            pkgbuild("uninstall");
1684
1685            builder.create_dir(&pkg.join("res"));
1686            builder.create(&pkg.join("res/LICENSE.txt"), &license);
1687            builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), FileType::Regular);
1688            let mut cmd = command("productbuild");
1689            cmd.arg("--distribution")
1690                .arg(xform(&etc.join("pkg/Distribution.xml")))
1691                .arg("--resources")
1692                .arg(pkg.join("res"))
1693                .arg(distdir(builder).join(format!(
1694                    "{}-{}.pkg",
1695                    pkgname(builder, "rust"),
1696                    target.triple
1697                )))
1698                .arg("--package-path")
1699                .arg(&pkg);
1700            let _time = timeit(builder);
1701            cmd.run(builder);
1702        }
1703
1704        // FIXME(mati865): `gnullvm` here is temporary, remove it once it can host itself
1705        if target.is_windows() && !target.contains("gnullvm") {
1706            let exe = tmp.join("exe");
1707            let _ = fs::remove_dir_all(&exe);
1708
1709            let prepare = |name: &str| {
1710                builder.create_dir(&exe.join(name));
1711                let dir = if name == "rust-std" || name == "rust-analysis" {
1712                    format!("{}-{}", name, target.triple)
1713                } else if name == "rust-analyzer" {
1714                    "rust-analyzer-preview".to_string()
1715                } else if name == "clippy" {
1716                    "clippy-preview".to_string()
1717                } else if name == "rustfmt" {
1718                    "rustfmt-preview".to_string()
1719                } else if name == "miri" {
1720                    "miri-preview".to_string()
1721                } else if name == "rustc-codegen-cranelift" {
1722                    // FIXME add installer support for cg_clif once it is ready to be distributed on
1723                    // windows.
1724                    unreachable!("cg_clif shouldn't be built for windows");
1725                } else {
1726                    name.to_string()
1727                };
1728                builder.cp_link_r(
1729                    &work.join(format!("{}-{}", pkgname(builder, name), target.triple)).join(dir),
1730                    &exe.join(name),
1731                );
1732                builder.remove(&exe.join(name).join("manifest.in"));
1733            };
1734            prepare("rustc");
1735            prepare("cargo");
1736            prepare("rust-analysis");
1737            prepare("rust-std");
1738            for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] {
1739                if built_tools.contains(tool) {
1740                    prepare(tool);
1741                }
1742            }
1743            if target.is_windows_gnu() {
1744                prepare("rust-mingw");
1745            }
1746
1747            builder.install(&etc.join("gfx/rust-logo.ico"), &exe, FileType::Regular);
1748
1749            // Generate msi installer
1750            let wix_path = env::var_os("WIX")
1751                .expect("`WIX` environment variable must be set for generating MSI installer(s).");
1752            let wix = PathBuf::from(wix_path);
1753            let heat = wix.join("bin/heat.exe");
1754            let candle = wix.join("bin/candle.exe");
1755            let light = wix.join("bin/light.exe");
1756
1757            let heat_flags = ["-nologo", "-gg", "-sfrag", "-srd", "-sreg"];
1758            command(&heat)
1759                .current_dir(&exe)
1760                .arg("dir")
1761                .arg("rustc")
1762                .args(heat_flags)
1763                .arg("-cg")
1764                .arg("RustcGroup")
1765                .arg("-dr")
1766                .arg("Rustc")
1767                .arg("-var")
1768                .arg("var.RustcDir")
1769                .arg("-out")
1770                .arg(exe.join("RustcGroup.wxs"))
1771                .run(builder);
1772            if built_tools.contains("rust-docs") {
1773                command(&heat)
1774                    .current_dir(&exe)
1775                    .arg("dir")
1776                    .arg("rust-docs")
1777                    .args(heat_flags)
1778                    .arg("-cg")
1779                    .arg("DocsGroup")
1780                    .arg("-dr")
1781                    .arg("Docs")
1782                    .arg("-var")
1783                    .arg("var.DocsDir")
1784                    .arg("-out")
1785                    .arg(exe.join("DocsGroup.wxs"))
1786                    .arg("-t")
1787                    .arg(etc.join("msi/squash-components.xsl"))
1788                    .run(builder);
1789            }
1790            command(&heat)
1791                .current_dir(&exe)
1792                .arg("dir")
1793                .arg("cargo")
1794                .args(heat_flags)
1795                .arg("-cg")
1796                .arg("CargoGroup")
1797                .arg("-dr")
1798                .arg("Cargo")
1799                .arg("-var")
1800                .arg("var.CargoDir")
1801                .arg("-out")
1802                .arg(exe.join("CargoGroup.wxs"))
1803                .arg("-t")
1804                .arg(etc.join("msi/remove-duplicates.xsl"))
1805                .run(builder);
1806            command(&heat)
1807                .current_dir(&exe)
1808                .arg("dir")
1809                .arg("rust-std")
1810                .args(heat_flags)
1811                .arg("-cg")
1812                .arg("StdGroup")
1813                .arg("-dr")
1814                .arg("Std")
1815                .arg("-var")
1816                .arg("var.StdDir")
1817                .arg("-out")
1818                .arg(exe.join("StdGroup.wxs"))
1819                .run(builder);
1820            if built_tools.contains("rust-analyzer") {
1821                command(&heat)
1822                    .current_dir(&exe)
1823                    .arg("dir")
1824                    .arg("rust-analyzer")
1825                    .args(heat_flags)
1826                    .arg("-cg")
1827                    .arg("RustAnalyzerGroup")
1828                    .arg("-dr")
1829                    .arg("RustAnalyzer")
1830                    .arg("-var")
1831                    .arg("var.RustAnalyzerDir")
1832                    .arg("-out")
1833                    .arg(exe.join("RustAnalyzerGroup.wxs"))
1834                    .arg("-t")
1835                    .arg(etc.join("msi/remove-duplicates.xsl"))
1836                    .run(builder);
1837            }
1838            if built_tools.contains("clippy") {
1839                command(&heat)
1840                    .current_dir(&exe)
1841                    .arg("dir")
1842                    .arg("clippy")
1843                    .args(heat_flags)
1844                    .arg("-cg")
1845                    .arg("ClippyGroup")
1846                    .arg("-dr")
1847                    .arg("Clippy")
1848                    .arg("-var")
1849                    .arg("var.ClippyDir")
1850                    .arg("-out")
1851                    .arg(exe.join("ClippyGroup.wxs"))
1852                    .arg("-t")
1853                    .arg(etc.join("msi/remove-duplicates.xsl"))
1854                    .run(builder);
1855            }
1856            if built_tools.contains("rustfmt") {
1857                command(&heat)
1858                    .current_dir(&exe)
1859                    .arg("dir")
1860                    .arg("rustfmt")
1861                    .args(heat_flags)
1862                    .arg("-cg")
1863                    .arg("RustFmtGroup")
1864                    .arg("-dr")
1865                    .arg("RustFmt")
1866                    .arg("-var")
1867                    .arg("var.RustFmtDir")
1868                    .arg("-out")
1869                    .arg(exe.join("RustFmtGroup.wxs"))
1870                    .arg("-t")
1871                    .arg(etc.join("msi/remove-duplicates.xsl"))
1872                    .run(builder);
1873            }
1874            if built_tools.contains("miri") {
1875                command(&heat)
1876                    .current_dir(&exe)
1877                    .arg("dir")
1878                    .arg("miri")
1879                    .args(heat_flags)
1880                    .arg("-cg")
1881                    .arg("MiriGroup")
1882                    .arg("-dr")
1883                    .arg("Miri")
1884                    .arg("-var")
1885                    .arg("var.MiriDir")
1886                    .arg("-out")
1887                    .arg(exe.join("MiriGroup.wxs"))
1888                    .arg("-t")
1889                    .arg(etc.join("msi/remove-duplicates.xsl"))
1890                    .run(builder);
1891            }
1892            command(&heat)
1893                .current_dir(&exe)
1894                .arg("dir")
1895                .arg("rust-analysis")
1896                .args(heat_flags)
1897                .arg("-cg")
1898                .arg("AnalysisGroup")
1899                .arg("-dr")
1900                .arg("Analysis")
1901                .arg("-var")
1902                .arg("var.AnalysisDir")
1903                .arg("-out")
1904                .arg(exe.join("AnalysisGroup.wxs"))
1905                .arg("-t")
1906                .arg(etc.join("msi/remove-duplicates.xsl"))
1907                .run(builder);
1908            if target.is_windows_gnu() {
1909                command(&heat)
1910                    .current_dir(&exe)
1911                    .arg("dir")
1912                    .arg("rust-mingw")
1913                    .args(heat_flags)
1914                    .arg("-cg")
1915                    .arg("GccGroup")
1916                    .arg("-dr")
1917                    .arg("Gcc")
1918                    .arg("-var")
1919                    .arg("var.GccDir")
1920                    .arg("-out")
1921                    .arg(exe.join("GccGroup.wxs"))
1922                    .run(builder);
1923            }
1924
1925            let candle = |input: &Path| {
1926                let output = exe.join(input.file_stem().unwrap()).with_extension("wixobj");
1927                let arch = if target.contains("x86_64") { "x64" } else { "x86" };
1928                let mut cmd = command(&candle);
1929                cmd.current_dir(&exe)
1930                    .arg("-nologo")
1931                    .arg("-dRustcDir=rustc")
1932                    .arg("-dCargoDir=cargo")
1933                    .arg("-dStdDir=rust-std")
1934                    .arg("-dAnalysisDir=rust-analysis")
1935                    .arg("-arch")
1936                    .arg(arch)
1937                    .arg("-out")
1938                    .arg(&output)
1939                    .arg(input);
1940                add_env(builder, &mut cmd, target, &built_tools);
1941
1942                if built_tools.contains("clippy") {
1943                    cmd.arg("-dClippyDir=clippy");
1944                }
1945                if built_tools.contains("rustfmt") {
1946                    cmd.arg("-dRustFmtDir=rustfmt");
1947                }
1948                if built_tools.contains("rust-docs") {
1949                    cmd.arg("-dDocsDir=rust-docs");
1950                }
1951                if built_tools.contains("rust-analyzer") {
1952                    cmd.arg("-dRustAnalyzerDir=rust-analyzer");
1953                }
1954                if built_tools.contains("miri") {
1955                    cmd.arg("-dMiriDir=miri");
1956                }
1957                if target.is_windows_gnu() {
1958                    cmd.arg("-dGccDir=rust-mingw");
1959                }
1960                cmd.run(builder);
1961            };
1962            candle(&xform(&etc.join("msi/rust.wxs")));
1963            candle(&etc.join("msi/ui.wxs"));
1964            candle(&etc.join("msi/rustwelcomedlg.wxs"));
1965            candle("RustcGroup.wxs".as_ref());
1966            if built_tools.contains("rust-docs") {
1967                candle("DocsGroup.wxs".as_ref());
1968            }
1969            candle("CargoGroup.wxs".as_ref());
1970            candle("StdGroup.wxs".as_ref());
1971            if built_tools.contains("clippy") {
1972                candle("ClippyGroup.wxs".as_ref());
1973            }
1974            if built_tools.contains("rustfmt") {
1975                candle("RustFmtGroup.wxs".as_ref());
1976            }
1977            if built_tools.contains("miri") {
1978                candle("MiriGroup.wxs".as_ref());
1979            }
1980            if built_tools.contains("rust-analyzer") {
1981                candle("RustAnalyzerGroup.wxs".as_ref());
1982            }
1983            candle("AnalysisGroup.wxs".as_ref());
1984
1985            if target.is_windows_gnu() {
1986                candle("GccGroup.wxs".as_ref());
1987            }
1988
1989            builder.create(&exe.join("LICENSE.rtf"), &rtf);
1990            builder.install(&etc.join("gfx/banner.bmp"), &exe, FileType::Regular);
1991            builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, FileType::Regular);
1992
1993            builder.info(&format!("building `msi` installer with {light:?}"));
1994            let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple);
1995            let mut cmd = command(&light);
1996            cmd.arg("-nologo")
1997                .arg("-ext")
1998                .arg("WixUIExtension")
1999                .arg("-ext")
2000                .arg("WixUtilExtension")
2001                .arg("-out")
2002                .arg(exe.join(&filename))
2003                .arg("rust.wixobj")
2004                .arg("ui.wixobj")
2005                .arg("rustwelcomedlg.wixobj")
2006                .arg("RustcGroup.wixobj")
2007                .arg("CargoGroup.wixobj")
2008                .arg("StdGroup.wixobj")
2009                .arg("AnalysisGroup.wixobj")
2010                .current_dir(&exe);
2011
2012            if built_tools.contains("clippy") {
2013                cmd.arg("ClippyGroup.wixobj");
2014            }
2015            if built_tools.contains("rustfmt") {
2016                cmd.arg("RustFmtGroup.wixobj");
2017            }
2018            if built_tools.contains("miri") {
2019                cmd.arg("MiriGroup.wixobj");
2020            }
2021            if built_tools.contains("rust-analyzer") {
2022                cmd.arg("RustAnalyzerGroup.wixobj");
2023            }
2024            if built_tools.contains("rust-docs") {
2025                cmd.arg("DocsGroup.wixobj");
2026            }
2027
2028            if target.is_windows_gnu() {
2029                cmd.arg("GccGroup.wixobj");
2030            }
2031            // ICE57 wrongly complains about the shortcuts
2032            cmd.arg("-sice:ICE57");
2033
2034            let _time = timeit(builder);
2035            cmd.run(builder);
2036
2037            if !builder.config.dry_run() {
2038                t!(move_file(exe.join(&filename), distdir(builder).join(&filename)));
2039            }
2040        }
2041    }
2042}
2043
2044fn add_env(
2045    builder: &Builder<'_>,
2046    cmd: &mut BootstrapCommand,
2047    target: TargetSelection,
2048    built_tools: &HashSet<&'static str>,
2049) {
2050    let mut parts = builder.version.split('.');
2051    cmd.env("CFG_RELEASE_INFO", builder.rust_version())
2052        .env("CFG_RELEASE_NUM", &builder.version)
2053        .env("CFG_RELEASE", builder.rust_release())
2054        .env("CFG_VER_MAJOR", parts.next().unwrap())
2055        .env("CFG_VER_MINOR", parts.next().unwrap())
2056        .env("CFG_VER_PATCH", parts.next().unwrap())
2057        .env("CFG_VER_BUILD", "0") // just needed to build
2058        .env("CFG_PACKAGE_VERS", builder.rust_package_vers())
2059        .env("CFG_PACKAGE_NAME", pkgname(builder, "rust"))
2060        .env("CFG_BUILD", target.triple)
2061        .env("CFG_CHANNEL", &builder.config.channel);
2062
2063    if target.contains("windows-gnullvm") {
2064        cmd.env("CFG_MINGW", "1").env("CFG_ABI", "LLVM");
2065    } else if target.is_windows_gnu() {
2066        cmd.env("CFG_MINGW", "1").env("CFG_ABI", "GNU");
2067    } else {
2068        cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC");
2069    }
2070
2071    // ensure these variables are defined
2072    let mut define_optional_tool = |tool_name: &str, env_name: &str| {
2073        cmd.env(env_name, if built_tools.contains(tool_name) { "1" } else { "0" });
2074    };
2075    define_optional_tool("rustfmt", "CFG_RUSTFMT");
2076    define_optional_tool("clippy", "CFG_CLIPPY");
2077    define_optional_tool("miri", "CFG_MIRI");
2078    define_optional_tool("rust-analyzer", "CFG_RA");
2079}
2080
2081fn install_llvm_file(
2082    builder: &Builder<'_>,
2083    source: &Path,
2084    destination: &Path,
2085    install_symlink: bool,
2086) {
2087    if builder.config.dry_run() {
2088        return;
2089    }
2090
2091    if source.is_symlink() {
2092        // If we have a symlink like libLLVM-18.so -> libLLVM.so.18.1, install the target of the
2093        // symlink, which is what will actually get loaded at runtime.
2094        builder.install(&t!(fs::canonicalize(source)), destination, FileType::NativeLibrary);
2095
2096        let full_dest = destination.join(source.file_name().unwrap());
2097        if install_symlink {
2098            // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a
2099            // symlink is fine here, as this is not a rustup component.
2100            builder.copy_link(source, &full_dest, FileType::NativeLibrary);
2101        } else {
2102            // Otherwise, replace the symlink with an equivalent linker script. This is used when
2103            // projects like miri link against librustc_driver.so. We don't use a symlink, as
2104            // these are not allowed inside rustup components.
2105            let link = t!(fs::read_link(source));
2106            let mut linker_script = t!(fs::File::create(full_dest));
2107            t!(write!(linker_script, "INPUT({})\n", link.display()));
2108
2109            // We also want the linker script to have the same mtime as the source, otherwise it
2110            // can trigger rebuilds.
2111            let meta = t!(fs::metadata(source));
2112            if let Ok(mtime) = meta.modified() {
2113                t!(linker_script.set_modified(mtime));
2114            }
2115        }
2116    } else {
2117        builder.install(source, destination, FileType::NativeLibrary);
2118    }
2119}
2120
2121/// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking.
2122///
2123/// Returns whether the files were actually copied.
2124#[cfg_attr(
2125    feature = "tracing",
2126    instrument(
2127        level = "trace",
2128        name = "maybe_install_llvm",
2129        skip_all,
2130        fields(target = ?target, dst_libdir = ?dst_libdir, install_symlink = install_symlink),
2131    ),
2132)]
2133fn maybe_install_llvm(
2134    builder: &Builder<'_>,
2135    target: TargetSelection,
2136    dst_libdir: &Path,
2137    install_symlink: bool,
2138) -> bool {
2139    // If the LLVM was externally provided, then we don't currently copy
2140    // artifacts into the sysroot. This is not necessarily the right
2141    // choice (in particular, it will require the LLVM dylib to be in
2142    // the linker's load path at runtime), but the common use case for
2143    // external LLVMs is distribution provided LLVMs, and in that case
2144    // they're usually in the standard search path (e.g., /usr/lib) and
2145    // copying them here is going to cause problems as we may end up
2146    // with the wrong files and isn't what distributions want.
2147    //
2148    // This behavior may be revisited in the future though.
2149    //
2150    // NOTE: this intentionally doesn't use `is_rust_llvm`; whether this is patched or not doesn't matter,
2151    // we only care if the shared object itself is managed by bootstrap.
2152    //
2153    // If the LLVM is coming from ourselves (just from CI) though, we
2154    // still want to install it, as it otherwise won't be available.
2155    if builder.config.is_system_llvm(target) {
2156        trace!("system LLVM requested, no install");
2157        return false;
2158    }
2159
2160    // On macOS, rustc (and LLVM tools) link to an unversioned libLLVM.dylib
2161    // instead of libLLVM-11-rust-....dylib, as on linux. It's not entirely
2162    // clear why this is the case, though. llvm-config will emit the versioned
2163    // paths and we don't want those in the sysroot (as we're expecting
2164    // unversioned paths).
2165    if target.contains("apple-darwin") && builder.llvm_link_shared() {
2166        let src_libdir = builder.llvm_out(target).join("lib");
2167        let llvm_dylib_path = src_libdir.join("libLLVM.dylib");
2168        if llvm_dylib_path.exists() {
2169            builder.install(&llvm_dylib_path, dst_libdir, FileType::NativeLibrary);
2170        }
2171        !builder.config.dry_run()
2172    } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) =
2173        llvm::prebuilt_llvm_config(builder, target, true)
2174    {
2175        trace!("LLVM already built, installing LLVM files");
2176        let mut cmd = command(llvm_config);
2177        cmd.arg("--libfiles");
2178        builder.verbose(|| println!("running {cmd:?}"));
2179        let files = cmd.run_capture_stdout(builder).stdout();
2180        let build_llvm_out = &builder.llvm_out(builder.config.host_target);
2181        let target_llvm_out = &builder.llvm_out(target);
2182        for file in files.trim_end().split(' ') {
2183            // If we're not using a custom LLVM, make sure we package for the target.
2184            let file = if let Ok(relative_path) = Path::new(file).strip_prefix(build_llvm_out) {
2185                target_llvm_out.join(relative_path)
2186            } else {
2187                PathBuf::from(file)
2188            };
2189            install_llvm_file(builder, &file, dst_libdir, install_symlink);
2190        }
2191        !builder.config.dry_run()
2192    } else {
2193        false
2194    }
2195}
2196
2197/// Maybe add libLLVM.so to the target lib-dir for linking.
2198#[cfg_attr(
2199    feature = "tracing",
2200    instrument(
2201        level = "trace",
2202        name = "maybe_install_llvm_target",
2203        skip_all,
2204        fields(
2205            llvm_link_shared = ?builder.llvm_link_shared(),
2206            target = ?target,
2207            sysroot = ?sysroot,
2208        ),
2209    ),
2210)]
2211pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
2212    let dst_libdir = sysroot.join("lib/rustlib").join(target).join("lib");
2213    // We do not need to copy LLVM files into the sysroot if it is not
2214    // dynamically linked; it is already included into librustc_llvm
2215    // statically.
2216    if builder.llvm_link_shared() {
2217        maybe_install_llvm(builder, target, &dst_libdir, false);
2218    }
2219}
2220
2221/// Maybe add libLLVM.so to the runtime lib-dir for rustc itself.
2222#[cfg_attr(
2223    feature = "tracing",
2224    instrument(
2225        level = "trace",
2226        name = "maybe_install_llvm_runtime",
2227        skip_all,
2228        fields(
2229            llvm_link_shared = ?builder.llvm_link_shared(),
2230            target = ?target,
2231            sysroot = ?sysroot,
2232        ),
2233    ),
2234)]
2235pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
2236    let dst_libdir = sysroot.join(builder.sysroot_libdir_relative(Compiler::new(1, target)));
2237    // We do not need to copy LLVM files into the sysroot if it is not
2238    // dynamically linked; it is already included into librustc_llvm
2239    // statically.
2240    if builder.llvm_link_shared() {
2241        maybe_install_llvm(builder, target, &dst_libdir, false);
2242    }
2243}
2244
2245#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2246pub struct LlvmTools {
2247    pub target: TargetSelection,
2248}
2249
2250impl Step for LlvmTools {
2251    type Output = Option<GeneratedTarball>;
2252    const ONLY_HOSTS: bool = true;
2253    const DEFAULT: bool = true;
2254
2255    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2256        let default = should_build_extended_tool(run.builder, "llvm-tools");
2257
2258        let mut run = run.alias("llvm-tools");
2259        for tool in LLVM_TOOLS {
2260            run = run.alias(tool);
2261        }
2262
2263        run.default_condition(default)
2264    }
2265
2266    fn make_run(run: RunConfig<'_>) {
2267        run.builder.ensure(LlvmTools { target: run.target });
2268    }
2269
2270    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2271        fn tools_to_install(paths: &[PathBuf]) -> Vec<&'static str> {
2272            let mut tools = vec![];
2273
2274            for path in paths {
2275                let path = path.to_str().unwrap();
2276
2277                // Include all tools if path is 'llvm-tools'.
2278                if path == "llvm-tools" {
2279                    return LLVM_TOOLS.to_owned();
2280                }
2281
2282                for tool in LLVM_TOOLS {
2283                    if path == *tool {
2284                        tools.push(*tool);
2285                    }
2286                }
2287            }
2288
2289            // If no specific tool is requested, include all tools.
2290            if tools.is_empty() {
2291                tools = LLVM_TOOLS.to_owned();
2292            }
2293
2294            tools
2295        }
2296
2297        let target = self.target;
2298
2299        // Run only if a custom llvm-config is not used
2300        if let Some(config) = builder.config.target_config.get(&target)
2301            && !builder.config.llvm_from_ci
2302            && config.llvm_config.is_some()
2303        {
2304            builder.info(&format!("Skipping LlvmTools ({target}): external LLVM"));
2305            return None;
2306        }
2307
2308        if !builder.config.dry_run() {
2309            builder.require_submodule("src/llvm-project", None);
2310        }
2311
2312        builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2313
2314        let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple);
2315        tarball.set_overlay(OverlayKind::Llvm);
2316        tarball.is_preview(true);
2317
2318        if builder.config.llvm_tools_enabled {
2319            // Prepare the image directory
2320            let src_bindir = builder.llvm_out(target).join("bin");
2321            let dst_bindir = format!("lib/rustlib/{}/bin", target.triple);
2322            for tool in tools_to_install(&builder.paths) {
2323                let exe = src_bindir.join(exe(tool, target));
2324                // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them.
2325                if !exe.exists() && builder.config.llvm_from_ci {
2326                    eprintln!("{} does not exist; skipping copy", exe.display());
2327                    continue;
2328                }
2329
2330                tarball.add_file(&exe, &dst_bindir, FileType::Executable);
2331            }
2332        }
2333
2334        // Copy libLLVM.so to the target lib dir as well, so the RPATH like
2335        // `$ORIGIN/../lib` can find it. It may also be used as a dependency
2336        // of `rustc-dev` to support the inherited `-lLLVM` when using the
2337        // compiler libraries.
2338        maybe_install_llvm_target(builder, target, tarball.image_dir());
2339
2340        Some(tarball.generate())
2341    }
2342}
2343
2344#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
2345pub struct LlvmBitcodeLinker {
2346    pub compiler: Compiler,
2347    pub target: TargetSelection,
2348}
2349
2350impl Step for LlvmBitcodeLinker {
2351    type Output = Option<GeneratedTarball>;
2352    const DEFAULT: bool = true;
2353    const ONLY_HOSTS: bool = true;
2354
2355    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2356        let default = should_build_extended_tool(run.builder, "llvm-bitcode-linker");
2357        run.alias("llvm-bitcode-linker").default_condition(default)
2358    }
2359
2360    fn make_run(run: RunConfig<'_>) {
2361        run.builder.ensure(LlvmBitcodeLinker {
2362            compiler: run.builder.compiler_for(
2363                run.builder.top_stage,
2364                run.builder.config.host_target,
2365                run.target,
2366            ),
2367            target: run.target,
2368        });
2369    }
2370
2371    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2372        let compiler = self.compiler;
2373        let target = self.target;
2374
2375        builder.ensure(compile::Rustc::new(compiler, target));
2376
2377        let llbc_linker =
2378            builder.ensure(tool::LlvmBitcodeLinker { compiler, target, extra_features: vec![] });
2379
2380        let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple);
2381
2382        // Prepare the image directory
2383        let mut tarball = Tarball::new(builder, "llvm-bitcode-linker", &target.triple);
2384        tarball.set_overlay(OverlayKind::LlvmBitcodeLinker);
2385        tarball.is_preview(true);
2386
2387        tarball.add_file(&llbc_linker.tool_path, self_contained_bin_dir, FileType::Executable);
2388
2389        Some(tarball.generate())
2390    }
2391}
2392
2393/// Tarball intended for internal consumption to ease rustc/std development.
2394///
2395/// Should not be considered stable by end users.
2396///
2397/// In practice, this is the tarball that gets downloaded and used by
2398/// `llvm.download-ci-llvm`.
2399///
2400/// (Don't confuse this with [`RustcDev`], with a `c`!)
2401#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2402pub struct RustDev {
2403    pub target: TargetSelection,
2404}
2405
2406impl Step for RustDev {
2407    type Output = Option<GeneratedTarball>;
2408    const DEFAULT: bool = true;
2409    const ONLY_HOSTS: bool = true;
2410
2411    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2412        run.alias("rust-dev")
2413    }
2414
2415    fn make_run(run: RunConfig<'_>) {
2416        run.builder.ensure(RustDev { target: run.target });
2417    }
2418
2419    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2420        let target = self.target;
2421
2422        /* run only if llvm-config isn't used */
2423        if let Some(config) = builder.config.target_config.get(&target)
2424            && let Some(ref _s) = config.llvm_config
2425        {
2426            builder.info(&format!("Skipping RustDev ({target}): external LLVM"));
2427            return None;
2428        }
2429
2430        if !builder.config.dry_run() {
2431            builder.require_submodule("src/llvm-project", None);
2432        }
2433
2434        let mut tarball = Tarball::new(builder, "rust-dev", &target.triple);
2435        tarball.set_overlay(OverlayKind::Llvm);
2436        // LLVM requires a shared object symlink to exist on some platforms.
2437        tarball.permit_symlinks(true);
2438
2439        builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2440
2441        let src_bindir = builder.llvm_out(target).join("bin");
2442        // If updating this, you likely want to change
2443        // src/bootstrap/download-ci-llvm-stamp as well, otherwise local users
2444        // will not pick up the extra file until LLVM gets bumped.
2445        // We should include all the build artifacts obtained from a source build,
2446        // so that you can use the downloadable LLVM as if you’ve just run a full source build.
2447        if src_bindir.exists() {
2448            for entry in walkdir::WalkDir::new(&src_bindir) {
2449                let entry = t!(entry);
2450                if entry.file_type().is_file() && !entry.path_is_symlink() {
2451                    let name = entry.file_name().to_str().unwrap();
2452                    tarball.add_file(src_bindir.join(name), "bin", FileType::Executable);
2453                }
2454            }
2455        }
2456
2457        if builder.config.lld_enabled {
2458            // We want to package `lld` to use it with `download-ci-llvm`.
2459            let lld_out = builder.ensure(crate::core::build_steps::llvm::Lld { target });
2460
2461            // We don't build LLD on some platforms, so only add it if it exists
2462            let lld_path = lld_out.join("bin").join(exe("lld", target));
2463            if lld_path.exists() {
2464                tarball.add_file(&lld_path, "bin", FileType::Executable);
2465            }
2466        }
2467
2468        tarball.add_file(builder.llvm_filecheck(target), "bin", FileType::Executable);
2469
2470        // Copy the include directory as well; needed mostly to build
2471        // librustc_llvm properly (e.g., llvm-config.h is in here). But also
2472        // just broadly useful to be able to link against the bundled LLVM.
2473        tarball.add_dir(builder.llvm_out(target).join("include"), "include");
2474
2475        // Copy libLLVM.so to the target lib dir as well, so the RPATH like
2476        // `$ORIGIN/../lib` can find it. It may also be used as a dependency
2477        // of `rustc-dev` to support the inherited `-lLLVM` when using the
2478        // compiler libraries.
2479        let dst_libdir = tarball.image_dir().join("lib");
2480        maybe_install_llvm(builder, target, &dst_libdir, true);
2481        let link_type = if builder.llvm_link_shared() { "dynamic" } else { "static" };
2482        t!(std::fs::write(tarball.image_dir().join("link-type.txt"), link_type), dst_libdir);
2483
2484        // Copy the `compiler-rt` source, so that `library/profiler_builtins`
2485        // can potentially use it to build the profiler runtime without needing
2486        // to check out the LLVM submodule.
2487        copy_src_dirs(
2488            builder,
2489            &builder.src.join("src").join("llvm-project"),
2490            &["compiler-rt"],
2491            // The test subdirectory is much larger than the rest of the source,
2492            // and we currently don't use these test files anyway.
2493            &["compiler-rt/test"],
2494            tarball.image_dir(),
2495        );
2496
2497        Some(tarball.generate())
2498    }
2499}
2500
2501/// Tarball intended for internal consumption to ease rustc/std development.
2502///
2503/// Should not be considered stable by end users.
2504#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2505pub struct Bootstrap {
2506    pub target: TargetSelection,
2507}
2508
2509impl Step for Bootstrap {
2510    type Output = Option<GeneratedTarball>;
2511    const DEFAULT: bool = false;
2512    const ONLY_HOSTS: bool = true;
2513
2514    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2515        run.alias("bootstrap")
2516    }
2517
2518    fn make_run(run: RunConfig<'_>) {
2519        run.builder.ensure(Bootstrap { target: run.target });
2520    }
2521
2522    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2523        let target = self.target;
2524
2525        let tarball = Tarball::new(builder, "bootstrap", &target.triple);
2526
2527        let bootstrap_outdir = &builder.bootstrap_out;
2528        for file in &["bootstrap", "rustc", "rustdoc"] {
2529            tarball.add_file(
2530                bootstrap_outdir.join(exe(file, target)),
2531                "bootstrap/bin",
2532                FileType::Executable,
2533            );
2534        }
2535
2536        Some(tarball.generate())
2537    }
2538}
2539
2540/// Tarball containing a prebuilt version of the build-manifest tool, intended to be used by the
2541/// release process to avoid cloning the monorepo and building stuff.
2542///
2543/// Should not be considered stable by end users.
2544#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2545pub struct BuildManifest {
2546    pub target: TargetSelection,
2547}
2548
2549impl Step for BuildManifest {
2550    type Output = GeneratedTarball;
2551    const DEFAULT: bool = false;
2552    const ONLY_HOSTS: bool = true;
2553
2554    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2555        run.alias("build-manifest")
2556    }
2557
2558    fn make_run(run: RunConfig<'_>) {
2559        run.builder.ensure(BuildManifest { target: run.target });
2560    }
2561
2562    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
2563        let build_manifest = builder.tool_exe(Tool::BuildManifest);
2564
2565        let tarball = Tarball::new(builder, "build-manifest", &self.target.triple);
2566        tarball.add_file(&build_manifest, "bin", FileType::Executable);
2567        tarball.generate()
2568    }
2569}
2570
2571/// Tarball containing artifacts necessary to reproduce the build of rustc.
2572///
2573/// Currently this is the PGO profile data.
2574///
2575/// Should not be considered stable by end users.
2576#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2577pub struct ReproducibleArtifacts {
2578    pub target: TargetSelection,
2579}
2580
2581impl Step for ReproducibleArtifacts {
2582    type Output = Option<GeneratedTarball>;
2583    const DEFAULT: bool = true;
2584    const ONLY_HOSTS: bool = true;
2585
2586    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2587        run.alias("reproducible-artifacts")
2588    }
2589
2590    fn make_run(run: RunConfig<'_>) {
2591        run.builder.ensure(ReproducibleArtifacts { target: run.target });
2592    }
2593
2594    fn run(self, builder: &Builder<'_>) -> Self::Output {
2595        let mut added_anything = false;
2596        let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple);
2597        if let Some(path) = builder.config.rust_profile_use.as_ref() {
2598            tarball.add_file(path, ".", FileType::Regular);
2599            added_anything = true;
2600        }
2601        if let Some(path) = builder.config.llvm_profile_use.as_ref() {
2602            tarball.add_file(path, ".", FileType::Regular);
2603            added_anything = true;
2604        }
2605        for profile in &builder.config.reproducible_artifacts {
2606            tarball.add_file(profile, ".", FileType::Regular);
2607            added_anything = true;
2608        }
2609        if added_anything { Some(tarball.generate()) } else { None }
2610    }
2611}
2612
2613/// Tarball containing a prebuilt version of the libgccjit library,
2614/// needed as a dependency for the GCC codegen backend (similarly to the LLVM
2615/// backend needing a prebuilt libLLVM).
2616#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2617pub struct Gcc {
2618    pub target: TargetSelection,
2619}
2620
2621impl Step for Gcc {
2622    type Output = GeneratedTarball;
2623
2624    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2625        run.alias("gcc")
2626    }
2627
2628    fn make_run(run: RunConfig<'_>) {
2629        run.builder.ensure(Gcc { target: run.target });
2630    }
2631
2632    fn run(self, builder: &Builder<'_>) -> Self::Output {
2633        let tarball = Tarball::new(builder, "gcc", &self.target.triple);
2634        let output = builder.ensure(super::gcc::Gcc { target: self.target });
2635        tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary);
2636        tarball.generate()
2637    }
2638}