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::build_steps::doc::DocumentationFormat;
23use crate::core::build_steps::tool::{self, Tool};
24use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor};
25use crate::core::build_steps::{compile, llvm};
26use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
27use crate::core::config::TargetSelection;
28use crate::utils::build_stamp::{self, BuildStamp};
29use crate::utils::channel::{self, Info};
30use crate::utils::exec::{BootstrapCommand, command};
31use crate::utils::helpers::{
32    exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit,
33};
34use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball};
35use crate::{Compiler, DependencyType, FileType, LLVM_TOOLS, Mode, trace};
36
37pub fn pkgname(builder: &Builder<'_>, component: &str) -> String {
38    format!("{}-{}", component, builder.rust_package_vers())
39}
40
41pub(crate) fn distdir(builder: &Builder<'_>) -> PathBuf {
42    builder.out.join("dist")
43}
44
45pub fn tmpdir(builder: &Builder<'_>) -> PathBuf {
46    builder.out.join("tmp/dist")
47}
48
49fn should_build_extended_tool(builder: &Builder<'_>, tool: &str) -> bool {
50    if !builder.config.extended {
51        return false;
52    }
53    builder.config.tools.as_ref().is_none_or(|tools| tools.contains(tool))
54}
55
56#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
57pub struct Docs {
58    pub host: TargetSelection,
59}
60
61impl Step for Docs {
62    type Output = Option<GeneratedTarball>;
63    const DEFAULT: bool = true;
64
65    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
66        let default = run.builder.config.docs;
67        run.alias("rust-docs").default_condition(default)
68    }
69
70    fn make_run(run: RunConfig<'_>) {
71        run.builder.ensure(Docs { host: run.target });
72    }
73
74    /// Builds the `rust-docs` installer component.
75    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
76        let host = self.host;
77        builder.default_doc(&[]);
78
79        let dest = "share/doc/rust/html";
80
81        let mut tarball = Tarball::new(builder, "rust-docs", &host.triple);
82        tarball.set_product_name("Rust Documentation");
83        tarball.add_bulk_dir(builder.doc_out(host), dest);
84        tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, FileType::Regular);
85        Some(tarball.generate())
86    }
87}
88
89#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
90pub struct JsonDocs {
91    pub host: TargetSelection,
92}
93
94impl Step for JsonDocs {
95    type Output = Option<GeneratedTarball>;
96    const DEFAULT: bool = true;
97
98    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
99        let default = run.builder.config.docs;
100        run.alias("rust-docs-json").default_condition(default)
101    }
102
103    fn make_run(run: RunConfig<'_>) {
104        run.builder.ensure(JsonDocs { host: run.target });
105    }
106
107    /// Builds the `rust-docs-json` installer component.
108    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
109        let host = self.host;
110        builder.ensure(crate::core::build_steps::doc::Std::new(
111            builder.top_stage,
112            host,
113            DocumentationFormat::Json,
114        ));
115
116        let dest = "share/doc/rust/json";
117
118        let mut tarball = Tarball::new(builder, "rust-docs-json", &host.triple);
119        tarball.set_product_name("Rust Documentation In JSON Format");
120        tarball.is_preview(true);
121        tarball.add_bulk_dir(builder.json_doc_out(host), dest);
122        Some(tarball.generate())
123    }
124}
125
126#[derive(Debug, Clone, Hash, PartialEq, Eq)]
127pub struct RustcDocs {
128    pub host: TargetSelection,
129}
130
131impl Step for RustcDocs {
132    type Output = Option<GeneratedTarball>;
133    const DEFAULT: bool = true;
134    const ONLY_HOSTS: bool = true;
135
136    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
137        let builder = run.builder;
138        run.alias("rustc-docs").default_condition(builder.config.compiler_docs)
139    }
140
141    fn make_run(run: RunConfig<'_>) {
142        run.builder.ensure(RustcDocs { host: run.target });
143    }
144
145    /// Builds the `rustc-docs` installer component.
146    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
147        let host = self.host;
148        builder.default_doc(&[]);
149
150        let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple);
151        tarball.set_product_name("Rustc Documentation");
152        tarball.add_bulk_dir(builder.compiler_doc_out(host), "share/doc/rust/html/rustc");
153        Some(tarball.generate())
154    }
155}
156
157fn find_files(files: &[&str], path: &[PathBuf]) -> Vec<PathBuf> {
158    let mut found = Vec::with_capacity(files.len());
159
160    for file in files {
161        let file_path = path.iter().map(|dir| dir.join(file)).find(|p| p.exists());
162
163        if let Some(file_path) = file_path {
164            found.push(file_path);
165        } else {
166            panic!("Could not find '{file}' in {path:?}");
167        }
168    }
169
170    found
171}
172
173fn make_win_dist(
174    rust_root: &Path,
175    plat_root: &Path,
176    target: TargetSelection,
177    builder: &Builder<'_>,
178) {
179    if builder.config.dry_run() {
180        return;
181    }
182
183    //Ask gcc where it keeps its stuff
184    let mut cmd = command(builder.cc(target));
185    cmd.arg("-print-search-dirs");
186    let gcc_out = cmd.run_capture_stdout(builder).stdout();
187
188    let mut bin_path: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect();
189    let mut lib_path = Vec::new();
190
191    for line in gcc_out.lines() {
192        let idx = line.find(':').unwrap();
193        let key = &line[..idx];
194        let trim_chars: &[_] = &[' ', '='];
195        let value = env::split_paths(line[(idx + 1)..].trim_start_matches(trim_chars));
196
197        if key == "programs" {
198            bin_path.extend(value);
199        } else if key == "libraries" {
200            lib_path.extend(value);
201        }
202    }
203
204    let compiler = if target == "i686-pc-windows-gnu" {
205        "i686-w64-mingw32-gcc.exe"
206    } else if target == "x86_64-pc-windows-gnu" {
207        "x86_64-w64-mingw32-gcc.exe"
208    } else {
209        "gcc.exe"
210    };
211    let target_tools = [compiler, "ld.exe", "dlltool.exe", "libwinpthread-1.dll"];
212    let mut rustc_dlls = vec!["libwinpthread-1.dll"];
213    if target.starts_with("i686-") {
214        rustc_dlls.push("libgcc_s_dw2-1.dll");
215    } else {
216        rustc_dlls.push("libgcc_s_seh-1.dll");
217    }
218
219    // Libraries necessary to link the windows-gnu toolchains.
220    // System libraries will be preferred if they are available (see #67429).
221    let target_libs = [
222        //MinGW libs
223        "libgcc.a",
224        "libgcc_eh.a",
225        "libgcc_s.a",
226        "libm.a",
227        "libmingw32.a",
228        "libmingwex.a",
229        "libstdc++.a",
230        "libiconv.a",
231        "libmoldname.a",
232        "libpthread.a",
233        // Windows import libs
234        // This *should* contain only the set of libraries necessary to link the standard library,
235        // however we've had problems with people accidentally depending on extra libs being here,
236        // so we can't easily remove entries.
237        "libadvapi32.a",
238        "libbcrypt.a",
239        "libcomctl32.a",
240        "libcomdlg32.a",
241        "libcredui.a",
242        "libcrypt32.a",
243        "libdbghelp.a",
244        "libgdi32.a",
245        "libimagehlp.a",
246        "libiphlpapi.a",
247        "libkernel32.a",
248        "libmsimg32.a",
249        "libmsvcrt.a",
250        "libntdll.a",
251        "libodbc32.a",
252        "libole32.a",
253        "liboleaut32.a",
254        "libopengl32.a",
255        "libpsapi.a",
256        "librpcrt4.a",
257        "libsecur32.a",
258        "libsetupapi.a",
259        "libshell32.a",
260        "libsynchronization.a",
261        "libuser32.a",
262        "libuserenv.a",
263        "libuuid.a",
264        "libwinhttp.a",
265        "libwinmm.a",
266        "libwinspool.a",
267        "libws2_32.a",
268        "libwsock32.a",
269    ];
270
271    //Find mingw artifacts we want to bundle
272    let target_tools = find_files(&target_tools, &bin_path);
273    let rustc_dlls = find_files(&rustc_dlls, &bin_path);
274    let target_libs = find_files(&target_libs, &lib_path);
275
276    // Copy runtime dlls next to rustc.exe
277    let rust_bin_dir = rust_root.join("bin/");
278    fs::create_dir_all(&rust_bin_dir).expect("creating rust_bin_dir failed");
279    for src in &rustc_dlls {
280        builder.copy_link_to_folder(src, &rust_bin_dir);
281    }
282
283    if builder.config.lld_enabled {
284        // rust-lld.exe also needs runtime dlls
285        let rust_target_bin_dir = rust_root.join("lib/rustlib").join(target).join("bin");
286        fs::create_dir_all(&rust_target_bin_dir).expect("creating rust_target_bin_dir failed");
287        for src in &rustc_dlls {
288            builder.copy_link_to_folder(src, &rust_target_bin_dir);
289        }
290    }
291
292    //Copy platform tools to platform-specific bin directory
293    let plat_target_bin_self_contained_dir =
294        plat_root.join("lib/rustlib").join(target).join("bin/self-contained");
295    fs::create_dir_all(&plat_target_bin_self_contained_dir)
296        .expect("creating plat_target_bin_self_contained_dir failed");
297    for src in target_tools {
298        builder.copy_link_to_folder(&src, &plat_target_bin_self_contained_dir);
299    }
300
301    // Warn windows-gnu users that the bundled GCC cannot compile C files
302    builder.create(
303        &plat_target_bin_self_contained_dir.join("GCC-WARNING.txt"),
304        "gcc.exe contained in this folder cannot be used for compiling C files - it is only \
305         used as a linker. In order to be able to compile projects containing C code use \
306         the GCC provided by MinGW or Cygwin.",
307    );
308
309    //Copy platform libs to platform-specific lib directory
310    let plat_target_lib_self_contained_dir =
311        plat_root.join("lib/rustlib").join(target).join("lib/self-contained");
312    fs::create_dir_all(&plat_target_lib_self_contained_dir)
313        .expect("creating plat_target_lib_self_contained_dir failed");
314    for src in target_libs {
315        builder.copy_link_to_folder(&src, &plat_target_lib_self_contained_dir);
316    }
317}
318
319#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
320pub struct Mingw {
321    pub host: TargetSelection,
322}
323
324impl Step for Mingw {
325    type Output = Option<GeneratedTarball>;
326    const DEFAULT: bool = true;
327
328    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
329        run.alias("rust-mingw")
330    }
331
332    fn make_run(run: RunConfig<'_>) {
333        run.builder.ensure(Mingw { host: run.target });
334    }
335
336    /// Builds the `rust-mingw` installer component.
337    ///
338    /// This contains all the bits and pieces to run the MinGW Windows targets
339    /// without any extra installed software (e.g., we bundle gcc, libraries, etc).
340    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
341        let host = self.host;
342        if !host.ends_with("pc-windows-gnu") || !builder.config.dist_include_mingw_linker {
343            return None;
344        }
345
346        let mut tarball = Tarball::new(builder, "rust-mingw", &host.triple);
347        tarball.set_product_name("Rust MinGW");
348
349        // The first argument is a "temporary directory" which is just
350        // thrown away (this contains the runtime DLLs included in the rustc package
351        // above) and the second argument is where to place all the MinGW components
352        // (which is what we want).
353        make_win_dist(&tmpdir(builder), tarball.image_dir(), host, builder);
354
355        Some(tarball.generate())
356    }
357}
358
359#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
360pub struct Rustc {
361    pub compiler: Compiler,
362}
363
364impl Step for Rustc {
365    type Output = GeneratedTarball;
366    const DEFAULT: bool = true;
367    const ONLY_HOSTS: bool = true;
368
369    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
370        run.alias("rustc")
371    }
372
373    fn make_run(run: RunConfig<'_>) {
374        run.builder
375            .ensure(Rustc { compiler: run.builder.compiler(run.builder.top_stage, run.target) });
376    }
377
378    /// Creates the `rustc` installer component.
379    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
380        let compiler = self.compiler;
381        let host = self.compiler.host;
382
383        let tarball = Tarball::new(builder, "rustc", &host.triple);
384
385        // Prepare the rustc "image", what will actually end up getting installed
386        prepare_image(builder, compiler, tarball.image_dir());
387
388        // On MinGW we've got a few runtime DLL dependencies that we need to
389        // include. The first argument to this script is where to put these DLLs
390        // (the image we're creating), and the second argument is a junk directory
391        // to ignore all other MinGW stuff the script creates.
392        //
393        // On 32-bit MinGW we're always including a DLL which needs some extra
394        // licenses to distribute. On 64-bit MinGW we don't actually distribute
395        // anything requiring us to distribute a license, but it's likely the
396        // install will *also* include the rust-mingw package, which also needs
397        // licenses, so to be safe we just include it here in all MinGW packages.
398        if host.ends_with("pc-windows-gnu") && builder.config.dist_include_mingw_linker {
399            make_win_dist(tarball.image_dir(), &tmpdir(builder), host, builder);
400            tarball.add_dir(builder.src.join("src/etc/third-party"), "share/doc");
401        }
402
403        return tarball.generate();
404
405        fn prepare_image(builder: &Builder<'_>, compiler: Compiler, image: &Path) {
406            let host = compiler.host;
407            let src = builder.sysroot(compiler);
408
409            // Copy rustc binary
410            t!(fs::create_dir_all(image.join("bin")));
411            builder.cp_link_r(&src.join("bin"), &image.join("bin"));
412
413            // If enabled, copy rustdoc binary
414            if builder
415                .config
416                .tools
417                .as_ref()
418                .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc"))
419            {
420                let rustdoc = builder.rustdoc(compiler);
421                builder.install(&rustdoc, &image.join("bin"), FileType::Executable);
422            }
423
424            if let Some(ra_proc_macro_srv) = builder.ensure_if_default(
425                tool::RustAnalyzerProcMacroSrv {
426                    compiler: builder.compiler_for(
427                        compiler.stage,
428                        builder.config.build,
429                        compiler.host,
430                    ),
431                    target: compiler.host,
432                },
433                builder.kind,
434            ) {
435                let dst = image.join("libexec");
436                builder.install(&ra_proc_macro_srv.tool_path, &dst, FileType::Executable);
437            }
438
439            let libdir_relative = builder.libdir_relative(compiler);
440
441            // Copy runtime DLLs needed by the compiler
442            if libdir_relative.to_str() != Some("bin") {
443                let libdir = builder.rustc_libdir(compiler);
444                for entry in builder.read_dir(&libdir) {
445                    if is_dylib(&entry.path()) {
446                        // Don't use custom libdir here because ^lib/ will be resolved again
447                        // with installer
448                        builder.install(&entry.path(), &image.join("lib"), FileType::NativeLibrary);
449                    }
450                }
451            }
452
453            // Copy libLLVM.so to the lib dir as well, if needed. While not
454            // technically needed by rustc itself it's needed by lots of other
455            // components like the llvm tools and LLD. LLD is included below and
456            // tools/LLDB come later, so let's just throw it in the rustc
457            // component for now.
458            maybe_install_llvm_runtime(builder, host, image);
459
460            let dst_dir = image.join("lib/rustlib").join(host).join("bin");
461            t!(fs::create_dir_all(&dst_dir));
462
463            // Copy over lld if it's there
464            if builder.config.lld_enabled {
465                let src_dir = builder.sysroot_target_bindir(compiler, host);
466                let rust_lld = exe("rust-lld", compiler.host);
467                builder.copy_link(
468                    &src_dir.join(&rust_lld),
469                    &dst_dir.join(&rust_lld),
470                    FileType::Executable,
471                );
472                let self_contained_lld_src_dir = src_dir.join("gcc-ld");
473                let self_contained_lld_dst_dir = dst_dir.join("gcc-ld");
474                t!(fs::create_dir(&self_contained_lld_dst_dir));
475                for name in crate::LLD_FILE_NAMES {
476                    let exe_name = exe(name, compiler.host);
477                    builder.copy_link(
478                        &self_contained_lld_src_dir.join(&exe_name),
479                        &self_contained_lld_dst_dir.join(&exe_name),
480                        FileType::Executable,
481                    );
482                }
483            }
484
485            if builder.config.llvm_enabled(compiler.host) && builder.config.llvm_tools_enabled {
486                let src_dir = builder.sysroot_target_bindir(compiler, host);
487                let llvm_objcopy = exe("llvm-objcopy", compiler.host);
488                let rust_objcopy = exe("rust-objcopy", compiler.host);
489                builder.copy_link(
490                    &src_dir.join(&llvm_objcopy),
491                    &dst_dir.join(&rust_objcopy),
492                    FileType::Executable,
493                );
494            }
495
496            if builder.tool_enabled("wasm-component-ld") {
497                let src_dir = builder.sysroot_target_bindir(compiler, host);
498                let ld = exe("wasm-component-ld", compiler.host);
499                builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld), FileType::Executable);
500            }
501
502            // Man pages
503            t!(fs::create_dir_all(image.join("share/man/man1")));
504            let man_src = builder.src.join("src/doc/man");
505            let man_dst = image.join("share/man/man1");
506
507            // don't use our `bootstrap::{copy_internal, cp_r}`, because those try
508            // to hardlink, and we don't want to edit the source templates
509            for file_entry in builder.read_dir(&man_src) {
510                let page_src = file_entry.path();
511                let page_dst = man_dst.join(file_entry.file_name());
512                let src_text = t!(std::fs::read_to_string(&page_src));
513                let new_text = src_text.replace("<INSERT VERSION HERE>", &builder.version);
514                t!(std::fs::write(&page_dst, &new_text));
515                t!(fs::copy(&page_src, &page_dst));
516            }
517
518            // Debugger scripts
519            builder.ensure(DebuggerScripts { sysroot: image.to_owned(), host });
520
521            // HTML copyright files
522            let file_list = builder.ensure(super::run::GenerateCopyright);
523            for file in file_list {
524                builder.install(&file, &image.join("share/doc/rust"), FileType::Regular);
525            }
526
527            // README
528            builder.install(
529                &builder.src.join("README.md"),
530                &image.join("share/doc/rust"),
531                FileType::Regular,
532            );
533
534            // The REUSE-managed license files
535            let license = |path: &Path| {
536                builder.install(path, &image.join("share/doc/rust/licenses"), FileType::Regular);
537            };
538            for entry in t!(std::fs::read_dir(builder.src.join("LICENSES"))).flatten() {
539                license(&entry.path());
540            }
541        }
542    }
543}
544
545#[derive(Debug, Clone, Hash, PartialEq, Eq)]
546pub struct DebuggerScripts {
547    pub sysroot: PathBuf,
548    pub host: TargetSelection,
549}
550
551impl Step for DebuggerScripts {
552    type Output = ();
553
554    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
555        run.never()
556    }
557
558    /// Copies debugger scripts for `target` into the `sysroot` specified.
559    fn run(self, builder: &Builder<'_>) {
560        let host = self.host;
561        let sysroot = self.sysroot;
562        let dst = sysroot.join("lib/rustlib/etc");
563        t!(fs::create_dir_all(&dst));
564        let cp_debugger_script = |file: &str| {
565            builder.install(&builder.src.join("src/etc/").join(file), &dst, FileType::Regular);
566        };
567        if host.contains("windows-msvc") {
568            // windbg debugger scripts
569            builder.install(
570                &builder.src.join("src/etc/rust-windbg.cmd"),
571                &sysroot.join("bin"),
572                FileType::Script,
573            );
574
575            cp_debugger_script("natvis/intrinsic.natvis");
576            cp_debugger_script("natvis/liballoc.natvis");
577            cp_debugger_script("natvis/libcore.natvis");
578            cp_debugger_script("natvis/libstd.natvis");
579        }
580
581        cp_debugger_script("rust_types.py");
582
583        // gdb debugger scripts
584        builder.install(
585            &builder.src.join("src/etc/rust-gdb"),
586            &sysroot.join("bin"),
587            FileType::Script,
588        );
589        builder.install(
590            &builder.src.join("src/etc/rust-gdbgui"),
591            &sysroot.join("bin"),
592            FileType::Script,
593        );
594
595        cp_debugger_script("gdb_load_rust_pretty_printers.py");
596        cp_debugger_script("gdb_lookup.py");
597        cp_debugger_script("gdb_providers.py");
598
599        // lldb debugger scripts
600        builder.install(
601            &builder.src.join("src/etc/rust-lldb"),
602            &sysroot.join("bin"),
603            FileType::Script,
604        );
605
606        cp_debugger_script("lldb_lookup.py");
607        cp_debugger_script("lldb_providers.py");
608        cp_debugger_script("lldb_commands")
609    }
610}
611
612fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool {
613    // The only true set of target libraries came from the build triple, so
614    // let's reduce redundant work by only producing archives from that host.
615    if !builder.is_builder_target(compiler.host) {
616        builder.info("\tskipping, not a build host");
617        true
618    } else {
619        false
620    }
621}
622
623/// Check that all objects in rlibs for UEFI targets are COFF. This
624/// ensures that the C compiler isn't producing ELF objects, which would
625/// not link correctly with the COFF objects.
626fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &BuildStamp) {
627    if !target.ends_with("-uefi") {
628        return;
629    }
630
631    for (path, _) in builder.read_stamp_file(stamp) {
632        if path.extension() != Some(OsStr::new("rlib")) {
633            continue;
634        }
635
636        let data = t!(fs::read(&path));
637        let data = data.as_slice();
638        let archive = t!(ArchiveFile::parse(data));
639        for member in archive.members() {
640            let member = t!(member);
641            let member_data = t!(member.data(data));
642
643            let is_coff = match object::File::parse(member_data) {
644                Ok(member_file) => member_file.format() == BinaryFormat::Coff,
645                Err(_) => false,
646            };
647
648            if !is_coff {
649                let member_name = String::from_utf8_lossy(member.name());
650                panic!("member {} in {} is not COFF", member_name, path.display());
651            }
652        }
653    }
654}
655
656/// Copy stamped files into an image's `target/lib` directory.
657fn copy_target_libs(
658    builder: &Builder<'_>,
659    target: TargetSelection,
660    image: &Path,
661    stamp: &BuildStamp,
662) {
663    let dst = image.join("lib/rustlib").join(target).join("lib");
664    let self_contained_dst = dst.join("self-contained");
665    t!(fs::create_dir_all(&dst));
666    t!(fs::create_dir_all(&self_contained_dst));
667    for (path, dependency_type) in builder.read_stamp_file(stamp) {
668        if dependency_type == DependencyType::TargetSelfContained {
669            builder.copy_link(
670                &path,
671                &self_contained_dst.join(path.file_name().unwrap()),
672                FileType::NativeLibrary,
673            );
674        } else if dependency_type == DependencyType::Target || builder.is_builder_target(target) {
675            builder.copy_link(&path, &dst.join(path.file_name().unwrap()), FileType::NativeLibrary);
676        }
677    }
678}
679
680#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
681pub struct Std {
682    pub compiler: Compiler,
683    pub target: TargetSelection,
684}
685
686impl Step for Std {
687    type Output = Option<GeneratedTarball>;
688    const DEFAULT: bool = true;
689
690    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
691        run.alias("rust-std")
692    }
693
694    fn make_run(run: RunConfig<'_>) {
695        run.builder.ensure(Std {
696            compiler: run.builder.compiler_for(
697                run.builder.top_stage,
698                run.builder.config.build,
699                run.target,
700            ),
701            target: run.target,
702        });
703    }
704
705    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
706        let compiler = self.compiler;
707        let target = self.target;
708
709        if skip_host_target_lib(builder, compiler) {
710            return None;
711        }
712
713        builder.ensure(compile::Std::new(compiler, target));
714
715        let mut tarball = Tarball::new(builder, "rust-std", &target.triple);
716        tarball.include_target_in_component_name(true);
717
718        let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
719        let stamp = build_stamp::libstd_stamp(builder, compiler_to_use, target);
720        verify_uefi_rlib_format(builder, target, &stamp);
721        copy_target_libs(builder, target, tarball.image_dir(), &stamp);
722
723        Some(tarball.generate())
724    }
725}
726
727/// Tarball containing the compiler that gets downloaded and used by
728/// `rust.download-rustc`.
729///
730/// (Don't confuse this with [`RustDev`], without the `c`!)
731#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
732pub struct RustcDev {
733    pub compiler: Compiler,
734    pub target: TargetSelection,
735}
736
737impl Step for RustcDev {
738    type Output = Option<GeneratedTarball>;
739    const DEFAULT: bool = true;
740    const ONLY_HOSTS: bool = true;
741
742    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
743        run.alias("rustc-dev")
744    }
745
746    fn make_run(run: RunConfig<'_>) {
747        run.builder.ensure(RustcDev {
748            compiler: run.builder.compiler_for(
749                run.builder.top_stage,
750                run.builder.config.build,
751                run.target,
752            ),
753            target: run.target,
754        });
755    }
756
757    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
758        let compiler = self.compiler;
759        let target = self.target;
760        if skip_host_target_lib(builder, compiler) {
761            return None;
762        }
763
764        builder.ensure(compile::Rustc::new(compiler, target));
765
766        let tarball = Tarball::new(builder, "rustc-dev", &target.triple);
767
768        let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
769        let stamp = build_stamp::librustc_stamp(builder, compiler_to_use, target);
770        copy_target_libs(builder, target, tarball.image_dir(), &stamp);
771
772        let src_files = &["Cargo.lock"];
773        // This is the reduced set of paths which will become the rustc-dev component
774        // (essentially the compiler crates and all of their path dependencies).
775        copy_src_dirs(
776            builder,
777            &builder.src,
778            &["compiler"],
779            &[],
780            &tarball.image_dir().join("lib/rustlib/rustc-src/rust"),
781        );
782        for file in src_files {
783            tarball.add_file(
784                builder.src.join(file),
785                "lib/rustlib/rustc-src/rust",
786                FileType::Regular,
787            );
788        }
789
790        Some(tarball.generate())
791    }
792}
793
794#[derive(Debug, Clone, Hash, PartialEq, Eq)]
795pub struct Analysis {
796    pub compiler: Compiler,
797    pub target: TargetSelection,
798}
799
800impl Step for Analysis {
801    type Output = Option<GeneratedTarball>;
802    const DEFAULT: bool = true;
803
804    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
805        let default = should_build_extended_tool(run.builder, "analysis");
806        run.alias("rust-analysis").default_condition(default)
807    }
808
809    fn make_run(run: RunConfig<'_>) {
810        run.builder.ensure(Analysis {
811            // Find the actual compiler (handling the full bootstrap option) which
812            // produced the save-analysis data because that data isn't copied
813            // through the sysroot uplifting.
814            compiler: run.builder.compiler_for(
815                run.builder.top_stage,
816                run.builder.config.build,
817                run.target,
818            ),
819            target: run.target,
820        });
821    }
822
823    /// Creates a tarball of (degenerate) save-analysis metadata, if available.
824    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
825        let compiler = self.compiler;
826        let target = self.target;
827        if !builder.is_builder_target(compiler.host) {
828            return None;
829        }
830
831        let src = builder
832            .stage_out(compiler, Mode::Std)
833            .join(target)
834            .join(builder.cargo_dir())
835            .join("deps")
836            .join("save-analysis");
837
838        // Write a file indicating that this component has been removed.
839        t!(std::fs::create_dir_all(&src));
840        let mut removed = src.clone();
841        removed.push("removed.json");
842        let mut f = t!(std::fs::File::create(removed));
843        t!(write!(f, r#"{{ "warning": "The `rust-analysis` component has been removed." }}"#));
844
845        let mut tarball = Tarball::new(builder, "rust-analysis", &target.triple);
846        tarball.include_target_in_component_name(true);
847        tarball.add_dir(src, format!("lib/rustlib/{}/analysis", target.triple));
848        Some(tarball.generate())
849    }
850}
851
852/// Use the `builder` to make a filtered copy of `base`/X for X in (`src_dirs` - `exclude_dirs`) to
853/// `dst_dir`.
854fn copy_src_dirs(
855    builder: &Builder<'_>,
856    base: &Path,
857    src_dirs: &[&str],
858    exclude_dirs: &[&str],
859    dst_dir: &Path,
860) {
861    fn filter_fn(exclude_dirs: &[&str], dir: &str, path: &Path) -> bool {
862        let spath = match path.to_str() {
863            Some(path) => path,
864            None => return false,
865        };
866        if spath.ends_with('~') || spath.ends_with(".pyc") {
867            return false;
868        }
869
870        const LLVM_PROJECTS: &[&str] = &[
871            "llvm-project/clang",
872            "llvm-project\\clang",
873            "llvm-project/libunwind",
874            "llvm-project\\libunwind",
875            "llvm-project/lld",
876            "llvm-project\\lld",
877            "llvm-project/lldb",
878            "llvm-project\\lldb",
879            "llvm-project/llvm",
880            "llvm-project\\llvm",
881            "llvm-project/compiler-rt",
882            "llvm-project\\compiler-rt",
883            "llvm-project/cmake",
884            "llvm-project\\cmake",
885            "llvm-project/runtimes",
886            "llvm-project\\runtimes",
887        ];
888        if spath.contains("llvm-project")
889            && !spath.ends_with("llvm-project")
890            && !LLVM_PROJECTS.iter().any(|path| spath.contains(path))
891        {
892            return false;
893        }
894
895        const LLVM_TEST: &[&str] = &["llvm-project/llvm/test", "llvm-project\\llvm\\test"];
896        if LLVM_TEST.iter().any(|path| spath.contains(path))
897            && (spath.ends_with(".ll") || spath.ends_with(".td") || spath.ends_with(".s"))
898        {
899            return false;
900        }
901
902        // Cargo tests use some files like `.gitignore` that we would otherwise exclude.
903        const CARGO_TESTS: &[&str] = &["tools/cargo/tests", "tools\\cargo\\tests"];
904        if CARGO_TESTS.iter().any(|path| spath.contains(path)) {
905            return true;
906        }
907
908        let full_path = Path::new(dir).join(path);
909        if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) {
910            return false;
911        }
912
913        let excludes = [
914            "CVS",
915            "RCS",
916            "SCCS",
917            ".git",
918            ".gitignore",
919            ".gitmodules",
920            ".gitattributes",
921            ".cvsignore",
922            ".svn",
923            ".arch-ids",
924            "{arch}",
925            "=RELEASE-ID",
926            "=meta-update",
927            "=update",
928            ".bzr",
929            ".bzrignore",
930            ".bzrtags",
931            ".hg",
932            ".hgignore",
933            ".hgrags",
934            "_darcs",
935        ];
936        !path.iter().map(|s| s.to_str().unwrap()).any(|s| excludes.contains(&s))
937    }
938
939    // Copy the directories using our filter
940    for item in src_dirs {
941        let dst = &dst_dir.join(item);
942        t!(fs::create_dir_all(dst));
943        builder
944            .cp_link_filtered(&base.join(item), dst, &|path| filter_fn(exclude_dirs, item, path));
945    }
946}
947
948#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
949pub struct Src;
950
951impl Step for Src {
952    /// The output path of the src installer tarball
953    type Output = GeneratedTarball;
954    const DEFAULT: bool = true;
955    const ONLY_HOSTS: bool = true;
956
957    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
958        run.alias("rust-src")
959    }
960
961    fn make_run(run: RunConfig<'_>) {
962        run.builder.ensure(Src);
963    }
964
965    /// Creates the `rust-src` installer component
966    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
967        if !builder.config.dry_run() {
968            builder.require_submodule("src/llvm-project", None);
969        }
970
971        let tarball = Tarball::new_targetless(builder, "rust-src");
972
973        // A lot of tools expect the rust-src component to be entirely in this directory, so if you
974        // change that (e.g. by adding another directory `lib/rustlib/src/foo` or
975        // `lib/rustlib/src/rust/foo`), you will need to go around hunting for implicit assumptions
976        // and fix them...
977        //
978        // NOTE: if you update the paths here, you also should update the "virtual" path
979        // translation code in `imported_source_files` in `src/librustc_metadata/rmeta/decoder.rs`
980        let dst_src = tarball.image_dir().join("lib/rustlib/src/rust");
981
982        // This is the reduced set of paths which will become the rust-src component
983        // (essentially libstd and all of its path dependencies).
984        copy_src_dirs(
985            builder,
986            &builder.src,
987            &["library", "src/llvm-project/libunwind"],
988            &[
989                // not needed and contains symlinks which rustup currently
990                // chokes on when unpacking.
991                "library/backtrace/crates",
992                // these are 30MB combined and aren't necessary for building
993                // the standard library.
994                "library/stdarch/Cargo.toml",
995                "library/stdarch/crates/stdarch-verify",
996                "library/stdarch/crates/intrinsic-test",
997            ],
998            &dst_src,
999        );
1000
1001        tarball.generate()
1002    }
1003}
1004
1005#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1006pub struct PlainSourceTarball;
1007
1008impl Step for PlainSourceTarball {
1009    /// Produces the location of the tarball generated
1010    type Output = GeneratedTarball;
1011    const DEFAULT: bool = true;
1012    const ONLY_HOSTS: bool = true;
1013
1014    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1015        let builder = run.builder;
1016        run.alias("rustc-src").default_condition(builder.config.rust_dist_src)
1017    }
1018
1019    fn make_run(run: RunConfig<'_>) {
1020        run.builder.ensure(PlainSourceTarball);
1021    }
1022
1023    /// Creates the plain source tarball
1024    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
1025        // NOTE: This is a strange component in a lot of ways. It uses `src` as the target, which
1026        // means neither rustup nor rustup-toolchain-install-master know how to download it.
1027        // It also contains symbolic links, unlike other any other dist tarball.
1028        // It's used for distros building rustc from source in a pre-vendored environment.
1029        let mut tarball = Tarball::new(builder, "rustc", "src");
1030        tarball.permit_symlinks(true);
1031        let plain_dst_src = tarball.image_dir();
1032
1033        // This is the set of root paths which will become part of the source package
1034        let src_files = [
1035            // tidy-alphabetical-start
1036            ".gitmodules",
1037            "bootstrap.example.toml",
1038            "Cargo.lock",
1039            "Cargo.toml",
1040            "configure",
1041            "CONTRIBUTING.md",
1042            "COPYRIGHT",
1043            "LICENSE-APACHE",
1044            "license-metadata.json",
1045            "LICENSE-MIT",
1046            "README.md",
1047            "RELEASES.md",
1048            "REUSE.toml",
1049            "x",
1050            "x.ps1",
1051            "x.py",
1052            // tidy-alphabetical-end
1053        ];
1054        let src_dirs = ["src", "compiler", "library", "tests", "LICENSES"];
1055
1056        copy_src_dirs(
1057            builder,
1058            &builder.src,
1059            &src_dirs,
1060            &[
1061                // We don't currently use the GCC source code for building any official components,
1062                // it is very big, and has unclear licensing implications due to being GPL licensed.
1063                // We thus exclude it from the source tarball from now.
1064                "src/gcc",
1065            ],
1066            plain_dst_src,
1067        );
1068        // We keep something in src/gcc because it is a registered submodule,
1069        // and if it misses completely it can cause issues elsewhere
1070        // (see https://github.com/rust-lang/rust/issues/137332).
1071        // We can also let others know why is the source code missing.
1072        if !builder.config.dry_run() {
1073            builder.create_dir(&plain_dst_src.join("src/gcc"));
1074            t!(std::fs::write(
1075                plain_dst_src.join("src/gcc/notice.txt"),
1076                "The GCC source code is not included due to unclear licensing implications\n"
1077            ));
1078        }
1079
1080        // Copy the files normally
1081        for item in &src_files {
1082            builder.copy_link(
1083                &builder.src.join(item),
1084                &plain_dst_src.join(item),
1085                FileType::Regular,
1086            );
1087        }
1088
1089        // Create the version file
1090        builder.create(&plain_dst_src.join("version"), &builder.rust_version());
1091
1092        // Create the files containing git info, to ensure --version outputs the same.
1093        let write_git_info = |info: Option<&Info>, path: &Path| {
1094            if let Some(info) = info {
1095                t!(std::fs::create_dir_all(path));
1096                channel::write_commit_hash_file(path, &info.sha);
1097                channel::write_commit_info_file(path, info);
1098            }
1099        };
1100        write_git_info(builder.rust_info().info(), plain_dst_src);
1101        write_git_info(builder.cargo_info.info(), &plain_dst_src.join("./src/tools/cargo"));
1102
1103        if builder.config.dist_vendor {
1104            builder.require_and_update_all_submodules();
1105
1106            // Vendor packages that are required by opt-dist to collect PGO profiles.
1107            let pkgs_for_pgo_training = build_helper::LLVM_PGO_CRATES
1108                .iter()
1109                .chain(build_helper::RUSTC_PGO_CRATES)
1110                .map(|pkg| {
1111                    let mut manifest_path =
1112                        builder.src.join("./src/tools/rustc-perf/collector/compile-benchmarks");
1113                    manifest_path.push(pkg);
1114                    manifest_path.push("Cargo.toml");
1115                    manifest_path
1116                });
1117
1118            // Vendor all Cargo dependencies
1119            let vendor = builder.ensure(Vendor {
1120                sync_args: pkgs_for_pgo_training.collect(),
1121                versioned_dirs: true,
1122                root_dir: plain_dst_src.into(),
1123                output_dir: VENDOR_DIR.into(),
1124            });
1125
1126            let cargo_config_dir = plain_dst_src.join(".cargo");
1127            builder.create_dir(&cargo_config_dir);
1128            builder.create(&cargo_config_dir.join("config.toml"), &vendor.config);
1129        }
1130
1131        // Delete extraneous directories
1132        // FIXME: if we're managed by git, we should probably instead ask git if the given path
1133        // is managed by it?
1134        for entry in walkdir::WalkDir::new(tarball.image_dir())
1135            .follow_links(true)
1136            .into_iter()
1137            .filter_map(|e| e.ok())
1138        {
1139            if entry.path().is_dir() && entry.path().file_name() == Some(OsStr::new("__pycache__"))
1140            {
1141                t!(fs::remove_dir_all(entry.path()));
1142            }
1143        }
1144
1145        tarball.bare()
1146    }
1147}
1148
1149#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1150pub struct Cargo {
1151    pub compiler: Compiler,
1152    pub target: TargetSelection,
1153}
1154
1155impl Step for Cargo {
1156    type Output = Option<GeneratedTarball>;
1157    const DEFAULT: bool = true;
1158    const ONLY_HOSTS: bool = true;
1159
1160    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1161        let default = should_build_extended_tool(run.builder, "cargo");
1162        run.alias("cargo").default_condition(default)
1163    }
1164
1165    fn make_run(run: RunConfig<'_>) {
1166        run.builder.ensure(Cargo {
1167            compiler: run.builder.compiler_for(
1168                run.builder.top_stage,
1169                run.builder.config.build,
1170                run.target,
1171            ),
1172            target: run.target,
1173        });
1174    }
1175
1176    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1177        let compiler = self.compiler;
1178        let target = self.target;
1179
1180        let cargo = builder.ensure(tool::Cargo { compiler, target });
1181        let src = builder.src.join("src/tools/cargo");
1182        let etc = src.join("src/etc");
1183
1184        // Prepare the image directory
1185        let mut tarball = Tarball::new(builder, "cargo", &target.triple);
1186        tarball.set_overlay(OverlayKind::Cargo);
1187
1188        tarball.add_file(&cargo.tool_path, "bin", FileType::Executable);
1189        tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", FileType::Regular);
1190        tarball.add_renamed_file(
1191            etc.join("cargo.bashcomp.sh"),
1192            "etc/bash_completion.d",
1193            "cargo",
1194            FileType::Regular,
1195        );
1196        tarball.add_dir(etc.join("man"), "share/man/man1");
1197        tarball.add_legal_and_readme_to("share/doc/cargo");
1198
1199        Some(tarball.generate())
1200    }
1201}
1202
1203#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1204pub struct RustAnalyzer {
1205    pub compiler: Compiler,
1206    pub target: TargetSelection,
1207}
1208
1209impl Step for RustAnalyzer {
1210    type Output = Option<GeneratedTarball>;
1211    const DEFAULT: bool = true;
1212    const ONLY_HOSTS: bool = true;
1213
1214    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1215        let default = should_build_extended_tool(run.builder, "rust-analyzer");
1216        run.alias("rust-analyzer").default_condition(default)
1217    }
1218
1219    fn make_run(run: RunConfig<'_>) {
1220        run.builder.ensure(RustAnalyzer {
1221            compiler: run.builder.compiler_for(
1222                run.builder.top_stage,
1223                run.builder.config.build,
1224                run.target,
1225            ),
1226            target: run.target,
1227        });
1228    }
1229
1230    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1231        let compiler = self.compiler;
1232        let target = self.target;
1233
1234        let rust_analyzer = builder.ensure(tool::RustAnalyzer { compiler, target });
1235
1236        let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple);
1237        tarball.set_overlay(OverlayKind::RustAnalyzer);
1238        tarball.is_preview(true);
1239        tarball.add_file(&rust_analyzer.tool_path, "bin", FileType::Executable);
1240        tarball.add_legal_and_readme_to("share/doc/rust-analyzer");
1241        Some(tarball.generate())
1242    }
1243}
1244
1245#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1246pub struct Clippy {
1247    pub compiler: Compiler,
1248    pub target: TargetSelection,
1249}
1250
1251impl Step for Clippy {
1252    type Output = Option<GeneratedTarball>;
1253    const DEFAULT: bool = true;
1254    const ONLY_HOSTS: bool = true;
1255
1256    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1257        let default = should_build_extended_tool(run.builder, "clippy");
1258        run.alias("clippy").default_condition(default)
1259    }
1260
1261    fn make_run(run: RunConfig<'_>) {
1262        run.builder.ensure(Clippy {
1263            compiler: run.builder.compiler_for(
1264                run.builder.top_stage,
1265                run.builder.config.build,
1266                run.target,
1267            ),
1268            target: run.target,
1269        });
1270    }
1271
1272    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1273        let compiler = self.compiler;
1274        let target = self.target;
1275
1276        // Prepare the image directory
1277        // We expect clippy to build, because we've exited this step above if tool
1278        // state for clippy isn't testing.
1279        let clippy = builder.ensure(tool::Clippy { compiler, target });
1280        let cargoclippy = builder.ensure(tool::CargoClippy { compiler, target });
1281
1282        let mut tarball = Tarball::new(builder, "clippy", &target.triple);
1283        tarball.set_overlay(OverlayKind::Clippy);
1284        tarball.is_preview(true);
1285        tarball.add_file(&clippy.tool_path, "bin", FileType::Executable);
1286        tarball.add_file(&cargoclippy.tool_path, "bin", FileType::Executable);
1287        tarball.add_legal_and_readme_to("share/doc/clippy");
1288        Some(tarball.generate())
1289    }
1290}
1291
1292#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1293pub struct Miri {
1294    pub compiler: Compiler,
1295    pub target: TargetSelection,
1296}
1297
1298impl Step for Miri {
1299    type Output = Option<GeneratedTarball>;
1300    const DEFAULT: bool = true;
1301    const ONLY_HOSTS: bool = true;
1302
1303    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1304        let default = should_build_extended_tool(run.builder, "miri");
1305        run.alias("miri").default_condition(default)
1306    }
1307
1308    fn make_run(run: RunConfig<'_>) {
1309        run.builder.ensure(Miri {
1310            compiler: run.builder.compiler_for(
1311                run.builder.top_stage,
1312                run.builder.config.build,
1313                run.target,
1314            ),
1315            target: run.target,
1316        });
1317    }
1318
1319    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1320        // This prevents miri from being built for "dist" or "install"
1321        // on the stable/beta channels. It is a nightly-only tool and should
1322        // not be included.
1323        if !builder.build.unstable_features() {
1324            return None;
1325        }
1326        let compiler = self.compiler;
1327        let target = self.target;
1328
1329        let miri = builder.ensure(tool::Miri { compiler, target });
1330        let cargomiri = builder.ensure(tool::CargoMiri { compiler, target });
1331
1332        let mut tarball = Tarball::new(builder, "miri", &target.triple);
1333        tarball.set_overlay(OverlayKind::Miri);
1334        tarball.is_preview(true);
1335        tarball.add_file(&miri.tool_path, "bin", FileType::Executable);
1336        tarball.add_file(&cargomiri.tool_path, "bin", FileType::Executable);
1337        tarball.add_legal_and_readme_to("share/doc/miri");
1338        Some(tarball.generate())
1339    }
1340}
1341
1342#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1343pub struct CodegenBackend {
1344    pub compiler: Compiler,
1345    pub backend: String,
1346}
1347
1348impl Step for CodegenBackend {
1349    type Output = Option<GeneratedTarball>;
1350    const DEFAULT: bool = true;
1351    const ONLY_HOSTS: bool = true;
1352
1353    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1354        run.path("compiler/rustc_codegen_cranelift")
1355    }
1356
1357    fn make_run(run: RunConfig<'_>) {
1358        for backend in run.builder.config.codegen_backends(run.target) {
1359            if backend == "llvm" {
1360                continue; // Already built as part of rustc
1361            }
1362
1363            run.builder.ensure(CodegenBackend {
1364                compiler: run.builder.compiler(run.builder.top_stage, run.target),
1365                backend: backend.clone(),
1366            });
1367        }
1368    }
1369
1370    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1371        if builder.config.dry_run() {
1372            return None;
1373        }
1374
1375        // This prevents rustc_codegen_cranelift from being built for "dist"
1376        // or "install" on the stable/beta channels. It is not yet stable and
1377        // should not be included.
1378        if !builder.build.unstable_features() {
1379            return None;
1380        }
1381
1382        if !builder.config.codegen_backends(self.compiler.host).contains(&self.backend.to_string())
1383        {
1384            return None;
1385        }
1386
1387        if self.backend == "cranelift" && !target_supports_cranelift_backend(self.compiler.host) {
1388            builder.info("target not supported by rustc_codegen_cranelift. skipping");
1389            return None;
1390        }
1391
1392        let compiler = self.compiler;
1393        let backend = self.backend;
1394
1395        let mut tarball =
1396            Tarball::new(builder, &format!("rustc-codegen-{}", backend), &compiler.host.triple);
1397        if backend == "cranelift" {
1398            tarball.set_overlay(OverlayKind::RustcCodegenCranelift);
1399        } else {
1400            panic!("Unknown backend rustc_codegen_{}", backend);
1401        }
1402        tarball.is_preview(true);
1403        tarball.add_legal_and_readme_to(format!("share/doc/rustc_codegen_{}", backend));
1404
1405        let src = builder.sysroot(compiler);
1406        let backends_src = builder.sysroot_codegen_backends(compiler);
1407        let backends_rel = backends_src
1408            .strip_prefix(src)
1409            .unwrap()
1410            .strip_prefix(builder.sysroot_libdir_relative(compiler))
1411            .unwrap();
1412        // Don't use custom libdir here because ^lib/ will be resolved again with installer
1413        let backends_dst = PathBuf::from("lib").join(backends_rel);
1414
1415        let backend_name = format!("rustc_codegen_{}", backend);
1416        let mut found_backend = false;
1417        for backend in fs::read_dir(&backends_src).unwrap() {
1418            let file_name = backend.unwrap().file_name();
1419            if file_name.to_str().unwrap().contains(&backend_name) {
1420                tarball.add_file(
1421                    backends_src.join(file_name),
1422                    &backends_dst,
1423                    FileType::NativeLibrary,
1424                );
1425                found_backend = true;
1426            }
1427        }
1428        assert!(found_backend);
1429
1430        Some(tarball.generate())
1431    }
1432}
1433
1434#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1435pub struct Rustfmt {
1436    pub compiler: Compiler,
1437    pub target: TargetSelection,
1438}
1439
1440impl Step for Rustfmt {
1441    type Output = Option<GeneratedTarball>;
1442    const DEFAULT: bool = true;
1443    const ONLY_HOSTS: bool = true;
1444
1445    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1446        let default = should_build_extended_tool(run.builder, "rustfmt");
1447        run.alias("rustfmt").default_condition(default)
1448    }
1449
1450    fn make_run(run: RunConfig<'_>) {
1451        run.builder.ensure(Rustfmt {
1452            compiler: run.builder.compiler_for(
1453                run.builder.top_stage,
1454                run.builder.config.build,
1455                run.target,
1456            ),
1457            target: run.target,
1458        });
1459    }
1460
1461    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
1462        let compiler = self.compiler;
1463        let target = self.target;
1464
1465        let rustfmt = builder.ensure(tool::Rustfmt { compiler, target });
1466        let cargofmt = builder.ensure(tool::Cargofmt { compiler, target });
1467        let mut tarball = Tarball::new(builder, "rustfmt", &target.triple);
1468        tarball.set_overlay(OverlayKind::Rustfmt);
1469        tarball.is_preview(true);
1470        tarball.add_file(&rustfmt.tool_path, "bin", FileType::Executable);
1471        tarball.add_file(&cargofmt.tool_path, "bin", FileType::Executable);
1472        tarball.add_legal_and_readme_to("share/doc/rustfmt");
1473        Some(tarball.generate())
1474    }
1475}
1476
1477#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
1478pub struct Extended {
1479    stage: u32,
1480    host: TargetSelection,
1481    target: TargetSelection,
1482}
1483
1484impl Step for Extended {
1485    type Output = ();
1486    const DEFAULT: bool = true;
1487    const ONLY_HOSTS: bool = true;
1488
1489    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1490        let builder = run.builder;
1491        run.alias("extended").default_condition(builder.config.extended)
1492    }
1493
1494    fn make_run(run: RunConfig<'_>) {
1495        run.builder.ensure(Extended {
1496            stage: run.builder.top_stage,
1497            host: run.builder.config.build,
1498            target: run.target,
1499        });
1500    }
1501
1502    /// Creates a combined installer for the specified target in the provided stage.
1503    fn run(self, builder: &Builder<'_>) {
1504        let target = self.target;
1505        let stage = self.stage;
1506        let compiler = builder.compiler_for(self.stage, self.host, self.target);
1507
1508        builder.info(&format!("Dist extended stage{} ({})", compiler.stage, target));
1509
1510        let mut tarballs = Vec::new();
1511        let mut built_tools = HashSet::new();
1512        macro_rules! add_component {
1513            ($name:expr => $step:expr) => {
1514                if let Some(tarball) = builder.ensure_if_default($step, Kind::Dist) {
1515                    tarballs.push(tarball);
1516                    built_tools.insert($name);
1517                }
1518            };
1519        }
1520
1521        // When rust-std package split from rustc, we needed to ensure that during
1522        // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering
1523        // the std files during uninstall. To do this ensure that rustc comes
1524        // before rust-std in the list below.
1525        tarballs.push(builder.ensure(Rustc { compiler: builder.compiler(stage, target) }));
1526        tarballs.push(builder.ensure(Std { compiler, target }).expect("missing std"));
1527
1528        if target.is_windows_gnu() {
1529            tarballs.push(builder.ensure(Mingw { host: target }).expect("missing mingw"));
1530        }
1531
1532        add_component!("rust-docs" => Docs { host: target });
1533        add_component!("rust-json-docs" => JsonDocs { host: target });
1534        add_component!("cargo" => Cargo { compiler, target });
1535        add_component!("rustfmt" => Rustfmt { compiler, target });
1536        add_component!("rust-analyzer" => RustAnalyzer { compiler, target });
1537        add_component!("llvm-components" => LlvmTools { target });
1538        add_component!("clippy" => Clippy { compiler, target });
1539        add_component!("miri" => Miri { compiler, target });
1540        add_component!("analysis" => Analysis { compiler, target });
1541        add_component!("rustc-codegen-cranelift" => CodegenBackend {
1542            compiler: builder.compiler(stage, target),
1543            backend: "cranelift".to_string(),
1544        });
1545        add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker {compiler, target});
1546
1547        let etc = builder.src.join("src/etc/installer");
1548
1549        // Avoid producing tarballs during a dry run.
1550        if builder.config.dry_run() {
1551            return;
1552        }
1553
1554        let tarball = Tarball::new(builder, "rust", &target.triple);
1555        let generated = tarball.combine(&tarballs);
1556
1557        let tmp = tmpdir(builder).join("combined-tarball");
1558        let work = generated.work_dir();
1559
1560        let mut license = String::new();
1561        license += &builder.read(&builder.src.join("COPYRIGHT"));
1562        license += &builder.read(&builder.src.join("LICENSE-APACHE"));
1563        license += &builder.read(&builder.src.join("LICENSE-MIT"));
1564        license.push('\n');
1565        license.push('\n');
1566
1567        let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18";
1568        let mut rtf = rtf.to_string();
1569        rtf.push('\n');
1570        for line in license.lines() {
1571            rtf.push_str(line);
1572            rtf.push_str("\\line ");
1573        }
1574        rtf.push('}');
1575
1576        fn filter(contents: &str, marker: &str) -> String {
1577            let start = format!("tool-{marker}-start");
1578            let end = format!("tool-{marker}-end");
1579            let mut lines = Vec::new();
1580            let mut omitted = false;
1581            for line in contents.lines() {
1582                if line.contains(&start) {
1583                    omitted = true;
1584                } else if line.contains(&end) {
1585                    omitted = false;
1586                } else if !omitted {
1587                    lines.push(line);
1588                }
1589            }
1590
1591            lines.join("\n")
1592        }
1593
1594        let xform = |p: &Path| {
1595            let mut contents = t!(fs::read_to_string(p));
1596            for tool in &["miri", "rust-docs"] {
1597                if !built_tools.contains(tool) {
1598                    contents = filter(&contents, tool);
1599                }
1600            }
1601            let ret = tmp.join(p.file_name().unwrap());
1602            t!(fs::write(&ret, &contents));
1603            ret
1604        };
1605
1606        if target.contains("apple-darwin") {
1607            builder.info("building pkg installer");
1608            let pkg = tmp.join("pkg");
1609            let _ = fs::remove_dir_all(&pkg);
1610
1611            let pkgbuild = |component: &str| {
1612                let mut cmd = command("pkgbuild");
1613                cmd.arg("--identifier")
1614                    .arg(format!("org.rust-lang.{}", component))
1615                    .arg("--scripts")
1616                    .arg(pkg.join(component))
1617                    .arg("--nopayload")
1618                    .arg(pkg.join(component).with_extension("pkg"));
1619                cmd.run(builder);
1620            };
1621
1622            let prepare = |name: &str| {
1623                builder.create_dir(&pkg.join(name));
1624                builder.cp_link_r(
1625                    &work.join(format!("{}-{}", pkgname(builder, name), target.triple)),
1626                    &pkg.join(name),
1627                );
1628                builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), FileType::Script);
1629                pkgbuild(name);
1630            };
1631            prepare("rustc");
1632            prepare("cargo");
1633            prepare("rust-std");
1634            prepare("rust-analysis");
1635
1636            for tool in &[
1637                "clippy",
1638                "rustfmt",
1639                "rust-analyzer",
1640                "rust-docs",
1641                "miri",
1642                "rustc-codegen-cranelift",
1643            ] {
1644                if built_tools.contains(tool) {
1645                    prepare(tool);
1646                }
1647            }
1648            // create an 'uninstall' package
1649            builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), FileType::Script);
1650            pkgbuild("uninstall");
1651
1652            builder.create_dir(&pkg.join("res"));
1653            builder.create(&pkg.join("res/LICENSE.txt"), &license);
1654            builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), FileType::Regular);
1655            let mut cmd = command("productbuild");
1656            cmd.arg("--distribution")
1657                .arg(xform(&etc.join("pkg/Distribution.xml")))
1658                .arg("--resources")
1659                .arg(pkg.join("res"))
1660                .arg(distdir(builder).join(format!(
1661                    "{}-{}.pkg",
1662                    pkgname(builder, "rust"),
1663                    target.triple
1664                )))
1665                .arg("--package-path")
1666                .arg(&pkg);
1667            let _time = timeit(builder);
1668            cmd.run(builder);
1669        }
1670
1671        if target.is_windows() {
1672            let exe = tmp.join("exe");
1673            let _ = fs::remove_dir_all(&exe);
1674
1675            let prepare = |name: &str| {
1676                builder.create_dir(&exe.join(name));
1677                let dir = if name == "rust-std" || name == "rust-analysis" {
1678                    format!("{}-{}", name, target.triple)
1679                } else if name == "rust-analyzer" {
1680                    "rust-analyzer-preview".to_string()
1681                } else if name == "clippy" {
1682                    "clippy-preview".to_string()
1683                } else if name == "rustfmt" {
1684                    "rustfmt-preview".to_string()
1685                } else if name == "miri" {
1686                    "miri-preview".to_string()
1687                } else if name == "rustc-codegen-cranelift" {
1688                    // FIXME add installer support for cg_clif once it is ready to be distributed on
1689                    // windows.
1690                    unreachable!("cg_clif shouldn't be built for windows");
1691                } else {
1692                    name.to_string()
1693                };
1694                builder.cp_link_r(
1695                    &work.join(format!("{}-{}", pkgname(builder, name), target.triple)).join(dir),
1696                    &exe.join(name),
1697                );
1698                builder.remove(&exe.join(name).join("manifest.in"));
1699            };
1700            prepare("rustc");
1701            prepare("cargo");
1702            prepare("rust-analysis");
1703            prepare("rust-std");
1704            for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] {
1705                if built_tools.contains(tool) {
1706                    prepare(tool);
1707                }
1708            }
1709            if target.is_windows_gnu() {
1710                prepare("rust-mingw");
1711            }
1712
1713            builder.install(&etc.join("gfx/rust-logo.ico"), &exe, FileType::Regular);
1714
1715            // Generate msi installer
1716            let wix_path = env::var_os("WIX")
1717                .expect("`WIX` environment variable must be set for generating MSI installer(s).");
1718            let wix = PathBuf::from(wix_path);
1719            let heat = wix.join("bin/heat.exe");
1720            let candle = wix.join("bin/candle.exe");
1721            let light = wix.join("bin/light.exe");
1722
1723            let heat_flags = ["-nologo", "-gg", "-sfrag", "-srd", "-sreg"];
1724            command(&heat)
1725                .current_dir(&exe)
1726                .arg("dir")
1727                .arg("rustc")
1728                .args(heat_flags)
1729                .arg("-cg")
1730                .arg("RustcGroup")
1731                .arg("-dr")
1732                .arg("Rustc")
1733                .arg("-var")
1734                .arg("var.RustcDir")
1735                .arg("-out")
1736                .arg(exe.join("RustcGroup.wxs"))
1737                .run(builder);
1738            if built_tools.contains("rust-docs") {
1739                command(&heat)
1740                    .current_dir(&exe)
1741                    .arg("dir")
1742                    .arg("rust-docs")
1743                    .args(heat_flags)
1744                    .arg("-cg")
1745                    .arg("DocsGroup")
1746                    .arg("-dr")
1747                    .arg("Docs")
1748                    .arg("-var")
1749                    .arg("var.DocsDir")
1750                    .arg("-out")
1751                    .arg(exe.join("DocsGroup.wxs"))
1752                    .arg("-t")
1753                    .arg(etc.join("msi/squash-components.xsl"))
1754                    .run(builder);
1755            }
1756            command(&heat)
1757                .current_dir(&exe)
1758                .arg("dir")
1759                .arg("cargo")
1760                .args(heat_flags)
1761                .arg("-cg")
1762                .arg("CargoGroup")
1763                .arg("-dr")
1764                .arg("Cargo")
1765                .arg("-var")
1766                .arg("var.CargoDir")
1767                .arg("-out")
1768                .arg(exe.join("CargoGroup.wxs"))
1769                .arg("-t")
1770                .arg(etc.join("msi/remove-duplicates.xsl"))
1771                .run(builder);
1772            command(&heat)
1773                .current_dir(&exe)
1774                .arg("dir")
1775                .arg("rust-std")
1776                .args(heat_flags)
1777                .arg("-cg")
1778                .arg("StdGroup")
1779                .arg("-dr")
1780                .arg("Std")
1781                .arg("-var")
1782                .arg("var.StdDir")
1783                .arg("-out")
1784                .arg(exe.join("StdGroup.wxs"))
1785                .run(builder);
1786            if built_tools.contains("rust-analyzer") {
1787                command(&heat)
1788                    .current_dir(&exe)
1789                    .arg("dir")
1790                    .arg("rust-analyzer")
1791                    .args(heat_flags)
1792                    .arg("-cg")
1793                    .arg("RustAnalyzerGroup")
1794                    .arg("-dr")
1795                    .arg("RustAnalyzer")
1796                    .arg("-var")
1797                    .arg("var.RustAnalyzerDir")
1798                    .arg("-out")
1799                    .arg(exe.join("RustAnalyzerGroup.wxs"))
1800                    .arg("-t")
1801                    .arg(etc.join("msi/remove-duplicates.xsl"))
1802                    .run(builder);
1803            }
1804            if built_tools.contains("clippy") {
1805                command(&heat)
1806                    .current_dir(&exe)
1807                    .arg("dir")
1808                    .arg("clippy")
1809                    .args(heat_flags)
1810                    .arg("-cg")
1811                    .arg("ClippyGroup")
1812                    .arg("-dr")
1813                    .arg("Clippy")
1814                    .arg("-var")
1815                    .arg("var.ClippyDir")
1816                    .arg("-out")
1817                    .arg(exe.join("ClippyGroup.wxs"))
1818                    .arg("-t")
1819                    .arg(etc.join("msi/remove-duplicates.xsl"))
1820                    .run(builder);
1821            }
1822            if built_tools.contains("rustfmt") {
1823                command(&heat)
1824                    .current_dir(&exe)
1825                    .arg("dir")
1826                    .arg("rustfmt")
1827                    .args(heat_flags)
1828                    .arg("-cg")
1829                    .arg("RustFmtGroup")
1830                    .arg("-dr")
1831                    .arg("RustFmt")
1832                    .arg("-var")
1833                    .arg("var.RustFmtDir")
1834                    .arg("-out")
1835                    .arg(exe.join("RustFmtGroup.wxs"))
1836                    .arg("-t")
1837                    .arg(etc.join("msi/remove-duplicates.xsl"))
1838                    .run(builder);
1839            }
1840            if built_tools.contains("miri") {
1841                command(&heat)
1842                    .current_dir(&exe)
1843                    .arg("dir")
1844                    .arg("miri")
1845                    .args(heat_flags)
1846                    .arg("-cg")
1847                    .arg("MiriGroup")
1848                    .arg("-dr")
1849                    .arg("Miri")
1850                    .arg("-var")
1851                    .arg("var.MiriDir")
1852                    .arg("-out")
1853                    .arg(exe.join("MiriGroup.wxs"))
1854                    .arg("-t")
1855                    .arg(etc.join("msi/remove-duplicates.xsl"))
1856                    .run(builder);
1857            }
1858            command(&heat)
1859                .current_dir(&exe)
1860                .arg("dir")
1861                .arg("rust-analysis")
1862                .args(heat_flags)
1863                .arg("-cg")
1864                .arg("AnalysisGroup")
1865                .arg("-dr")
1866                .arg("Analysis")
1867                .arg("-var")
1868                .arg("var.AnalysisDir")
1869                .arg("-out")
1870                .arg(exe.join("AnalysisGroup.wxs"))
1871                .arg("-t")
1872                .arg(etc.join("msi/remove-duplicates.xsl"))
1873                .run(builder);
1874            if target.is_windows_gnu() {
1875                command(&heat)
1876                    .current_dir(&exe)
1877                    .arg("dir")
1878                    .arg("rust-mingw")
1879                    .args(heat_flags)
1880                    .arg("-cg")
1881                    .arg("GccGroup")
1882                    .arg("-dr")
1883                    .arg("Gcc")
1884                    .arg("-var")
1885                    .arg("var.GccDir")
1886                    .arg("-out")
1887                    .arg(exe.join("GccGroup.wxs"))
1888                    .run(builder);
1889            }
1890
1891            let candle = |input: &Path| {
1892                let output = exe.join(input.file_stem().unwrap()).with_extension("wixobj");
1893                let arch = if target.contains("x86_64") { "x64" } else { "x86" };
1894                let mut cmd = command(&candle);
1895                cmd.current_dir(&exe)
1896                    .arg("-nologo")
1897                    .arg("-dRustcDir=rustc")
1898                    .arg("-dCargoDir=cargo")
1899                    .arg("-dStdDir=rust-std")
1900                    .arg("-dAnalysisDir=rust-analysis")
1901                    .arg("-arch")
1902                    .arg(arch)
1903                    .arg("-out")
1904                    .arg(&output)
1905                    .arg(input);
1906                add_env(builder, &mut cmd, target, &built_tools);
1907
1908                if built_tools.contains("clippy") {
1909                    cmd.arg("-dClippyDir=clippy");
1910                }
1911                if built_tools.contains("rustfmt") {
1912                    cmd.arg("-dRustFmtDir=rustfmt");
1913                }
1914                if built_tools.contains("rust-docs") {
1915                    cmd.arg("-dDocsDir=rust-docs");
1916                }
1917                if built_tools.contains("rust-analyzer") {
1918                    cmd.arg("-dRustAnalyzerDir=rust-analyzer");
1919                }
1920                if built_tools.contains("miri") {
1921                    cmd.arg("-dMiriDir=miri");
1922                }
1923                if target.is_windows_gnu() {
1924                    cmd.arg("-dGccDir=rust-mingw");
1925                }
1926                cmd.run(builder);
1927            };
1928            candle(&xform(&etc.join("msi/rust.wxs")));
1929            candle(&etc.join("msi/ui.wxs"));
1930            candle(&etc.join("msi/rustwelcomedlg.wxs"));
1931            candle("RustcGroup.wxs".as_ref());
1932            if built_tools.contains("rust-docs") {
1933                candle("DocsGroup.wxs".as_ref());
1934            }
1935            candle("CargoGroup.wxs".as_ref());
1936            candle("StdGroup.wxs".as_ref());
1937            if built_tools.contains("clippy") {
1938                candle("ClippyGroup.wxs".as_ref());
1939            }
1940            if built_tools.contains("rustfmt") {
1941                candle("RustFmtGroup.wxs".as_ref());
1942            }
1943            if built_tools.contains("miri") {
1944                candle("MiriGroup.wxs".as_ref());
1945            }
1946            if built_tools.contains("rust-analyzer") {
1947                candle("RustAnalyzerGroup.wxs".as_ref());
1948            }
1949            candle("AnalysisGroup.wxs".as_ref());
1950
1951            if target.is_windows_gnu() {
1952                candle("GccGroup.wxs".as_ref());
1953            }
1954
1955            builder.create(&exe.join("LICENSE.rtf"), &rtf);
1956            builder.install(&etc.join("gfx/banner.bmp"), &exe, FileType::Regular);
1957            builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, FileType::Regular);
1958
1959            builder.info(&format!("building `msi` installer with {light:?}"));
1960            let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple);
1961            let mut cmd = command(&light);
1962            cmd.arg("-nologo")
1963                .arg("-ext")
1964                .arg("WixUIExtension")
1965                .arg("-ext")
1966                .arg("WixUtilExtension")
1967                .arg("-out")
1968                .arg(exe.join(&filename))
1969                .arg("rust.wixobj")
1970                .arg("ui.wixobj")
1971                .arg("rustwelcomedlg.wixobj")
1972                .arg("RustcGroup.wixobj")
1973                .arg("CargoGroup.wixobj")
1974                .arg("StdGroup.wixobj")
1975                .arg("AnalysisGroup.wixobj")
1976                .current_dir(&exe);
1977
1978            if built_tools.contains("clippy") {
1979                cmd.arg("ClippyGroup.wixobj");
1980            }
1981            if built_tools.contains("rustfmt") {
1982                cmd.arg("RustFmtGroup.wixobj");
1983            }
1984            if built_tools.contains("miri") {
1985                cmd.arg("MiriGroup.wixobj");
1986            }
1987            if built_tools.contains("rust-analyzer") {
1988                cmd.arg("RustAnalyzerGroup.wixobj");
1989            }
1990            if built_tools.contains("rust-docs") {
1991                cmd.arg("DocsGroup.wixobj");
1992            }
1993
1994            if target.is_windows_gnu() {
1995                cmd.arg("GccGroup.wixobj");
1996            }
1997            // ICE57 wrongly complains about the shortcuts
1998            cmd.arg("-sice:ICE57");
1999
2000            let _time = timeit(builder);
2001            cmd.run(builder);
2002
2003            if !builder.config.dry_run() {
2004                t!(move_file(exe.join(&filename), distdir(builder).join(&filename)));
2005            }
2006        }
2007    }
2008}
2009
2010fn add_env(
2011    builder: &Builder<'_>,
2012    cmd: &mut BootstrapCommand,
2013    target: TargetSelection,
2014    built_tools: &HashSet<&'static str>,
2015) {
2016    let mut parts = builder.version.split('.');
2017    cmd.env("CFG_RELEASE_INFO", builder.rust_version())
2018        .env("CFG_RELEASE_NUM", &builder.version)
2019        .env("CFG_RELEASE", builder.rust_release())
2020        .env("CFG_VER_MAJOR", parts.next().unwrap())
2021        .env("CFG_VER_MINOR", parts.next().unwrap())
2022        .env("CFG_VER_PATCH", parts.next().unwrap())
2023        .env("CFG_VER_BUILD", "0") // just needed to build
2024        .env("CFG_PACKAGE_VERS", builder.rust_package_vers())
2025        .env("CFG_PACKAGE_NAME", pkgname(builder, "rust"))
2026        .env("CFG_BUILD", target.triple)
2027        .env("CFG_CHANNEL", &builder.config.channel);
2028
2029    if target.contains("windows-gnullvm") {
2030        cmd.env("CFG_MINGW", "1").env("CFG_ABI", "LLVM");
2031    } else if target.is_windows_gnu() {
2032        cmd.env("CFG_MINGW", "1").env("CFG_ABI", "GNU");
2033    } else {
2034        cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC");
2035    }
2036
2037    // ensure these variables are defined
2038    let mut define_optional_tool = |tool_name: &str, env_name: &str| {
2039        cmd.env(env_name, if built_tools.contains(tool_name) { "1" } else { "0" });
2040    };
2041    define_optional_tool("rustfmt", "CFG_RUSTFMT");
2042    define_optional_tool("clippy", "CFG_CLIPPY");
2043    define_optional_tool("miri", "CFG_MIRI");
2044    define_optional_tool("rust-analyzer", "CFG_RA");
2045}
2046
2047fn install_llvm_file(
2048    builder: &Builder<'_>,
2049    source: &Path,
2050    destination: &Path,
2051    install_symlink: bool,
2052) {
2053    if builder.config.dry_run() {
2054        return;
2055    }
2056
2057    if source.is_symlink() {
2058        // If we have a symlink like libLLVM-18.so -> libLLVM.so.18.1, install the target of the
2059        // symlink, which is what will actually get loaded at runtime.
2060        builder.install(&t!(fs::canonicalize(source)), destination, FileType::NativeLibrary);
2061
2062        let full_dest = destination.join(source.file_name().unwrap());
2063        if install_symlink {
2064            // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a
2065            // symlink is fine here, as this is not a rustup component.
2066            builder.copy_link(source, &full_dest, FileType::NativeLibrary);
2067        } else {
2068            // Otherwise, replace the symlink with an equivalent linker script. This is used when
2069            // projects like miri link against librustc_driver.so. We don't use a symlink, as
2070            // these are not allowed inside rustup components.
2071            let link = t!(fs::read_link(source));
2072            let mut linker_script = t!(fs::File::create(full_dest));
2073            t!(write!(linker_script, "INPUT({})\n", link.display()));
2074
2075            // We also want the linker script to have the same mtime as the source, otherwise it
2076            // can trigger rebuilds.
2077            let meta = t!(fs::metadata(source));
2078            if let Ok(mtime) = meta.modified() {
2079                t!(linker_script.set_modified(mtime));
2080            }
2081        }
2082    } else {
2083        builder.install(source, destination, FileType::NativeLibrary);
2084    }
2085}
2086
2087/// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking.
2088///
2089/// Returns whether the files were actually copied.
2090#[cfg_attr(
2091    feature = "tracing",
2092    instrument(
2093        level = "trace",
2094        name = "maybe_install_llvm",
2095        skip_all,
2096        fields(target = ?target, dst_libdir = ?dst_libdir, install_symlink = install_symlink),
2097    ),
2098)]
2099fn maybe_install_llvm(
2100    builder: &Builder<'_>,
2101    target: TargetSelection,
2102    dst_libdir: &Path,
2103    install_symlink: bool,
2104) -> bool {
2105    // If the LLVM was externally provided, then we don't currently copy
2106    // artifacts into the sysroot. This is not necessarily the right
2107    // choice (in particular, it will require the LLVM dylib to be in
2108    // the linker's load path at runtime), but the common use case for
2109    // external LLVMs is distribution provided LLVMs, and in that case
2110    // they're usually in the standard search path (e.g., /usr/lib) and
2111    // copying them here is going to cause problems as we may end up
2112    // with the wrong files and isn't what distributions want.
2113    //
2114    // This behavior may be revisited in the future though.
2115    //
2116    // NOTE: this intentionally doesn't use `is_rust_llvm`; whether this is patched or not doesn't matter,
2117    // we only care if the shared object itself is managed by bootstrap.
2118    //
2119    // If the LLVM is coming from ourselves (just from CI) though, we
2120    // still want to install it, as it otherwise won't be available.
2121    if builder.is_system_llvm(target) {
2122        trace!("system LLVM requested, no install");
2123        return false;
2124    }
2125
2126    // On macOS, rustc (and LLVM tools) link to an unversioned libLLVM.dylib
2127    // instead of libLLVM-11-rust-....dylib, as on linux. It's not entirely
2128    // clear why this is the case, though. llvm-config will emit the versioned
2129    // paths and we don't want those in the sysroot (as we're expecting
2130    // unversioned paths).
2131    if target.contains("apple-darwin") && builder.llvm_link_shared() {
2132        let src_libdir = builder.llvm_out(target).join("lib");
2133        let llvm_dylib_path = src_libdir.join("libLLVM.dylib");
2134        if llvm_dylib_path.exists() {
2135            builder.install(&llvm_dylib_path, dst_libdir, FileType::NativeLibrary);
2136        }
2137        !builder.config.dry_run()
2138    } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) =
2139        llvm::prebuilt_llvm_config(builder, target, true)
2140    {
2141        trace!("LLVM already built, installing LLVM files");
2142        let mut cmd = command(llvm_config);
2143        cmd.arg("--libfiles");
2144        builder.verbose(|| println!("running {cmd:?}"));
2145        let files = cmd.run_capture_stdout(builder).stdout();
2146        let build_llvm_out = &builder.llvm_out(builder.config.build);
2147        let target_llvm_out = &builder.llvm_out(target);
2148        for file in files.trim_end().split(' ') {
2149            // If we're not using a custom LLVM, make sure we package for the target.
2150            let file = if let Ok(relative_path) = Path::new(file).strip_prefix(build_llvm_out) {
2151                target_llvm_out.join(relative_path)
2152            } else {
2153                PathBuf::from(file)
2154            };
2155            install_llvm_file(builder, &file, dst_libdir, install_symlink);
2156        }
2157        !builder.config.dry_run()
2158    } else {
2159        false
2160    }
2161}
2162
2163/// Maybe add libLLVM.so to the target lib-dir for linking.
2164#[cfg_attr(
2165    feature = "tracing",
2166    instrument(
2167        level = "trace",
2168        name = "maybe_install_llvm_target",
2169        skip_all,
2170        fields(
2171            llvm_link_shared = ?builder.llvm_link_shared(),
2172            target = ?target,
2173            sysroot = ?sysroot,
2174        ),
2175    ),
2176)]
2177pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
2178    let dst_libdir = sysroot.join("lib/rustlib").join(target).join("lib");
2179    // We do not need to copy LLVM files into the sysroot if it is not
2180    // dynamically linked; it is already included into librustc_llvm
2181    // statically.
2182    if builder.llvm_link_shared() {
2183        maybe_install_llvm(builder, target, &dst_libdir, false);
2184    }
2185}
2186
2187/// Maybe add libLLVM.so to the runtime lib-dir for rustc itself.
2188#[cfg_attr(
2189    feature = "tracing",
2190    instrument(
2191        level = "trace",
2192        name = "maybe_install_llvm_runtime",
2193        skip_all,
2194        fields(
2195            llvm_link_shared = ?builder.llvm_link_shared(),
2196            target = ?target,
2197            sysroot = ?sysroot,
2198        ),
2199    ),
2200)]
2201pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) {
2202    let dst_libdir = sysroot.join(builder.sysroot_libdir_relative(Compiler::new(1, target)));
2203    // We do not need to copy LLVM files into the sysroot if it is not
2204    // dynamically linked; it is already included into librustc_llvm
2205    // statically.
2206    if builder.llvm_link_shared() {
2207        maybe_install_llvm(builder, target, &dst_libdir, false);
2208    }
2209}
2210
2211#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2212pub struct LlvmTools {
2213    pub target: TargetSelection,
2214}
2215
2216impl Step for LlvmTools {
2217    type Output = Option<GeneratedTarball>;
2218    const ONLY_HOSTS: bool = true;
2219    const DEFAULT: bool = true;
2220
2221    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2222        let default = should_build_extended_tool(run.builder, "llvm-tools");
2223
2224        let mut run = run.alias("llvm-tools");
2225        for tool in LLVM_TOOLS {
2226            run = run.alias(tool);
2227        }
2228
2229        run.default_condition(default)
2230    }
2231
2232    fn make_run(run: RunConfig<'_>) {
2233        run.builder.ensure(LlvmTools { target: run.target });
2234    }
2235
2236    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2237        fn tools_to_install(paths: &[PathBuf]) -> Vec<&'static str> {
2238            let mut tools = vec![];
2239
2240            for path in paths {
2241                let path = path.to_str().unwrap();
2242
2243                // Include all tools if path is 'llvm-tools'.
2244                if path == "llvm-tools" {
2245                    return LLVM_TOOLS.to_owned();
2246                }
2247
2248                for tool in LLVM_TOOLS {
2249                    if path == *tool {
2250                        tools.push(*tool);
2251                    }
2252                }
2253            }
2254
2255            // If no specific tool is requested, include all tools.
2256            if tools.is_empty() {
2257                tools = LLVM_TOOLS.to_owned();
2258            }
2259
2260            tools
2261        }
2262
2263        let target = self.target;
2264
2265        /* run only if llvm-config isn't used */
2266        if let Some(config) = builder.config.target_config.get(&target) {
2267            if let Some(ref _s) = config.llvm_config {
2268                builder.info(&format!("Skipping LlvmTools ({target}): external LLVM"));
2269                return None;
2270            }
2271        }
2272
2273        builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2274
2275        let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple);
2276        tarball.set_overlay(OverlayKind::Llvm);
2277        tarball.is_preview(true);
2278
2279        if builder.config.llvm_tools_enabled {
2280            // Prepare the image directory
2281            let src_bindir = builder.llvm_out(target).join("bin");
2282            let dst_bindir = format!("lib/rustlib/{}/bin", target.triple);
2283            for tool in tools_to_install(&builder.paths) {
2284                let exe = src_bindir.join(exe(tool, target));
2285                tarball.add_file(&exe, &dst_bindir, FileType::Executable);
2286            }
2287        }
2288
2289        // Copy libLLVM.so to the target lib dir as well, so the RPATH like
2290        // `$ORIGIN/../lib` can find it. It may also be used as a dependency
2291        // of `rustc-dev` to support the inherited `-lLLVM` when using the
2292        // compiler libraries.
2293        maybe_install_llvm_target(builder, target, tarball.image_dir());
2294
2295        Some(tarball.generate())
2296    }
2297}
2298
2299#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
2300pub struct LlvmBitcodeLinker {
2301    pub compiler: Compiler,
2302    pub target: TargetSelection,
2303}
2304
2305impl Step for LlvmBitcodeLinker {
2306    type Output = Option<GeneratedTarball>;
2307    const DEFAULT: bool = true;
2308    const ONLY_HOSTS: bool = true;
2309
2310    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2311        let default = should_build_extended_tool(run.builder, "llvm-bitcode-linker");
2312        run.alias("llvm-bitcode-linker").default_condition(default)
2313    }
2314
2315    fn make_run(run: RunConfig<'_>) {
2316        run.builder.ensure(LlvmBitcodeLinker {
2317            compiler: run.builder.compiler_for(
2318                run.builder.top_stage,
2319                run.builder.config.build,
2320                run.target,
2321            ),
2322            target: run.target,
2323        });
2324    }
2325
2326    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2327        let compiler = self.compiler;
2328        let target = self.target;
2329
2330        let llbc_linker =
2331            builder.ensure(tool::LlvmBitcodeLinker { compiler, target, extra_features: vec![] });
2332
2333        let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple);
2334
2335        // Prepare the image directory
2336        let mut tarball = Tarball::new(builder, "llvm-bitcode-linker", &target.triple);
2337        tarball.set_overlay(OverlayKind::LlvmBitcodeLinker);
2338        tarball.is_preview(true);
2339
2340        tarball.add_file(&llbc_linker.tool_path, self_contained_bin_dir, FileType::Executable);
2341
2342        Some(tarball.generate())
2343    }
2344}
2345
2346/// Tarball intended for internal consumption to ease rustc/std development.
2347///
2348/// Should not be considered stable by end users.
2349///
2350/// In practice, this is the tarball that gets downloaded and used by
2351/// `llvm.download-ci-llvm`.
2352///
2353/// (Don't confuse this with [`RustcDev`], with a `c`!)
2354#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2355pub struct RustDev {
2356    pub target: TargetSelection,
2357}
2358
2359impl Step for RustDev {
2360    type Output = Option<GeneratedTarball>;
2361    const DEFAULT: bool = true;
2362    const ONLY_HOSTS: bool = true;
2363
2364    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2365        run.alias("rust-dev")
2366    }
2367
2368    fn make_run(run: RunConfig<'_>) {
2369        run.builder.ensure(RustDev { target: run.target });
2370    }
2371
2372    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2373        let target = self.target;
2374
2375        /* run only if llvm-config isn't used */
2376        if let Some(config) = builder.config.target_config.get(&target) {
2377            if let Some(ref _s) = config.llvm_config {
2378                builder.info(&format!("Skipping RustDev ({target}): external LLVM"));
2379                return None;
2380            }
2381        }
2382
2383        let mut tarball = Tarball::new(builder, "rust-dev", &target.triple);
2384        tarball.set_overlay(OverlayKind::Llvm);
2385        // LLVM requires a shared object symlink to exist on some platforms.
2386        tarball.permit_symlinks(true);
2387
2388        builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2389
2390        let src_bindir = builder.llvm_out(target).join("bin");
2391        // If updating this, you likely want to change
2392        // src/bootstrap/download-ci-llvm-stamp as well, otherwise local users
2393        // will not pick up the extra file until LLVM gets bumped.
2394        // We should include all the build artifacts obtained from a source build,
2395        // so that you can use the downloadable LLVM as if you’ve just run a full source build.
2396        if src_bindir.exists() {
2397            for entry in walkdir::WalkDir::new(&src_bindir) {
2398                let entry = t!(entry);
2399                if entry.file_type().is_file() && !entry.path_is_symlink() {
2400                    let name = entry.file_name().to_str().unwrap();
2401                    tarball.add_file(src_bindir.join(name), "bin", FileType::Executable);
2402                }
2403            }
2404        }
2405
2406        if builder.config.lld_enabled {
2407            // We want to package `lld` to use it with `download-ci-llvm`.
2408            let lld_out = builder.ensure(crate::core::build_steps::llvm::Lld { target });
2409
2410            // We don't build LLD on some platforms, so only add it if it exists
2411            let lld_path = lld_out.join("bin").join(exe("lld", target));
2412            if lld_path.exists() {
2413                tarball.add_file(&lld_path, "bin", FileType::Executable);
2414            }
2415        }
2416
2417        tarball.add_file(builder.llvm_filecheck(target), "bin", FileType::Executable);
2418
2419        // Copy the include directory as well; needed mostly to build
2420        // librustc_llvm properly (e.g., llvm-config.h is in here). But also
2421        // just broadly useful to be able to link against the bundled LLVM.
2422        tarball.add_dir(builder.llvm_out(target).join("include"), "include");
2423
2424        // Copy libLLVM.so to the target lib dir as well, so the RPATH like
2425        // `$ORIGIN/../lib` can find it. It may also be used as a dependency
2426        // of `rustc-dev` to support the inherited `-lLLVM` when using the
2427        // compiler libraries.
2428        let dst_libdir = tarball.image_dir().join("lib");
2429        maybe_install_llvm(builder, target, &dst_libdir, true);
2430        let link_type = if builder.llvm_link_shared() { "dynamic" } else { "static" };
2431        t!(std::fs::write(tarball.image_dir().join("link-type.txt"), link_type), dst_libdir);
2432
2433        // Copy the `compiler-rt` source, so that `library/profiler_builtins`
2434        // can potentially use it to build the profiler runtime without needing
2435        // to check out the LLVM submodule.
2436        copy_src_dirs(
2437            builder,
2438            &builder.src.join("src").join("llvm-project"),
2439            &["compiler-rt"],
2440            // The test subdirectory is much larger than the rest of the source,
2441            // and we currently don't use these test files anyway.
2442            &["compiler-rt/test"],
2443            tarball.image_dir(),
2444        );
2445
2446        Some(tarball.generate())
2447    }
2448}
2449
2450/// Tarball intended for internal consumption to ease rustc/std development.
2451///
2452/// Should not be considered stable by end users.
2453#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2454pub struct Bootstrap {
2455    pub target: TargetSelection,
2456}
2457
2458impl Step for Bootstrap {
2459    type Output = Option<GeneratedTarball>;
2460    const DEFAULT: bool = false;
2461    const ONLY_HOSTS: bool = true;
2462
2463    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2464        run.alias("bootstrap")
2465    }
2466
2467    fn make_run(run: RunConfig<'_>) {
2468        run.builder.ensure(Bootstrap { target: run.target });
2469    }
2470
2471    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
2472        let target = self.target;
2473
2474        let tarball = Tarball::new(builder, "bootstrap", &target.triple);
2475
2476        let bootstrap_outdir = &builder.bootstrap_out;
2477        for file in &["bootstrap", "rustc", "rustdoc"] {
2478            tarball.add_file(
2479                bootstrap_outdir.join(exe(file, target)),
2480                "bootstrap/bin",
2481                FileType::Executable,
2482            );
2483        }
2484
2485        Some(tarball.generate())
2486    }
2487}
2488
2489/// Tarball containing a prebuilt version of the build-manifest tool, intended to be used by the
2490/// release process to avoid cloning the monorepo and building stuff.
2491///
2492/// Should not be considered stable by end users.
2493#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2494pub struct BuildManifest {
2495    pub target: TargetSelection,
2496}
2497
2498impl Step for BuildManifest {
2499    type Output = GeneratedTarball;
2500    const DEFAULT: bool = false;
2501    const ONLY_HOSTS: bool = true;
2502
2503    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2504        run.alias("build-manifest")
2505    }
2506
2507    fn make_run(run: RunConfig<'_>) {
2508        run.builder.ensure(BuildManifest { target: run.target });
2509    }
2510
2511    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
2512        let build_manifest = builder.tool_exe(Tool::BuildManifest);
2513
2514        let tarball = Tarball::new(builder, "build-manifest", &self.target.triple);
2515        tarball.add_file(&build_manifest, "bin", FileType::Executable);
2516        tarball.generate()
2517    }
2518}
2519
2520/// Tarball containing artifacts necessary to reproduce the build of rustc.
2521///
2522/// Currently this is the PGO profile data.
2523///
2524/// Should not be considered stable by end users.
2525#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2526pub struct ReproducibleArtifacts {
2527    pub target: TargetSelection,
2528}
2529
2530impl Step for ReproducibleArtifacts {
2531    type Output = Option<GeneratedTarball>;
2532    const DEFAULT: bool = true;
2533    const ONLY_HOSTS: bool = true;
2534
2535    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2536        run.alias("reproducible-artifacts")
2537    }
2538
2539    fn make_run(run: RunConfig<'_>) {
2540        run.builder.ensure(ReproducibleArtifacts { target: run.target });
2541    }
2542
2543    fn run(self, builder: &Builder<'_>) -> Self::Output {
2544        let mut added_anything = false;
2545        let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple);
2546        if let Some(path) = builder.config.rust_profile_use.as_ref() {
2547            tarball.add_file(path, ".", FileType::Regular);
2548            added_anything = true;
2549        }
2550        if let Some(path) = builder.config.llvm_profile_use.as_ref() {
2551            tarball.add_file(path, ".", FileType::Regular);
2552            added_anything = true;
2553        }
2554        for profile in &builder.config.reproducible_artifacts {
2555            tarball.add_file(profile, ".", FileType::Regular);
2556            added_anything = true;
2557        }
2558        if added_anything { Some(tarball.generate()) } else { None }
2559    }
2560}
2561
2562/// Tarball containing a prebuilt version of the libgccjit library,
2563/// needed as a dependency for the GCC codegen backend (similarly to the LLVM
2564/// backend needing a prebuilt libLLVM).
2565#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2566pub struct Gcc {
2567    pub target: TargetSelection,
2568}
2569
2570impl Step for Gcc {
2571    type Output = GeneratedTarball;
2572
2573    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2574        run.alias("gcc")
2575    }
2576
2577    fn make_run(run: RunConfig<'_>) {
2578        run.builder.ensure(Gcc { target: run.target });
2579    }
2580
2581    fn run(self, builder: &Builder<'_>) -> Self::Output {
2582        let tarball = Tarball::new(builder, "gcc", &self.target.triple);
2583        let output = builder.ensure(super::gcc::Gcc { target: self.target });
2584        tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary);
2585        tarball.generate()
2586    }
2587}