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