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