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