Skip to main content

bootstrap/core/build_steps/
dist.rs

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