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