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