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