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