Skip to main content

bootstrap/core/build_steps/
vendor.rs

1//! Handles the vendoring process for the bootstrap system.
2//!
3//! This module ensures that all required Cargo dependencies are gathered
4//! and stored in the `<src>/<VENDOR_DIR>` directory.
5use std::path::PathBuf;
6
7use crate::core::build_steps::tool::SUBMODULES_FOR_RUSTBOOK;
8use crate::core::builder::{Builder, CommandLineStep, RunConfig, ShouldRun};
9use crate::utils::exec::command;
10
11/// The name of the directory where vendored dependencies are stored.
12pub const VENDOR_DIR: &str = "vendor";
13
14/// Returns the cargo workspaces to vendor for `x vendor` and dist tarballs.
15///
16/// Returns a `Vec` of `(path_to_manifest, submodules_required)` where
17/// `path_to_manifest` is the cargo workspace, and `submodules_required` is
18/// the set of submodules that must be available.
19pub fn default_paths_to_vendor(builder: &Builder<'_>) -> Vec<(PathBuf, Vec<&'static str>)> {
20    [
21        ("src/tools/cargo/Cargo.toml", vec!["src/tools/cargo"]),
22        ("src/tools/clippy/clippy_test_deps/Cargo.toml", vec![]),
23        ("src/tools/rust-analyzer/Cargo.toml", vec![]),
24        ("compiler/rustc_codegen_cranelift/Cargo.toml", vec![]),
25        ("compiler/rustc_codegen_gcc/Cargo.toml", vec![]),
26        ("library/Cargo.toml", vec![]),
27        ("library/stdarch/Cargo.toml", vec![]),
28        ("src/bootstrap/Cargo.toml", vec![]),
29        ("src/tools/rustbook/Cargo.toml", SUBMODULES_FOR_RUSTBOOK.into()),
30        ("src/tools/rustc-perf/Cargo.toml", vec!["src/tools/rustc-perf"]),
31        ("src/tools/opt-dist/Cargo.toml", vec![]),
32        ("src/doc/book/packages/trpl/Cargo.toml", vec![]),
33    ]
34    .into_iter()
35    .map(|(path, submodules)| (builder.src.join(path), submodules))
36    .collect()
37}
38
39/// Defines the vendoring step in the bootstrap process.
40///
41/// This step executes `cargo vendor` to collect all dependencies
42/// and store them in the `<src>/<VENDOR_DIR>` directory.
43#[derive(Debug, Clone, Hash, PartialEq, Eq)]
44pub(crate) struct Vendor {
45    /// Additional paths to synchronize during vendoring.
46    pub(crate) sync_args: Vec<PathBuf>,
47    /// Determines whether vendored dependencies use versioned directories.
48    pub(crate) versioned_dirs: bool,
49    /// The root directory of the source code.
50    ///
51    /// Vendored dependencies will be stored in <root_dir>/vendor and
52    /// <root_dir>/library/vendor unless overridden by `output_dir`.
53    pub(crate) root_dir: PathBuf,
54    /// The root directory for storing vendored dependencies in <output_dir>/vendor
55    /// and <output_dir>/library/vendor.
56    pub(crate) output_dir: Option<PathBuf>,
57    /// Only vendor crates necessary by the library workspace.
58    pub(crate) only_library_workspace: bool,
59}
60
61impl CommandLineStep for Vendor {
62    type Output = VendorOutput;
63    const IS_HOST: bool = true;
64
65    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
66        run.alias("placeholder")
67    }
68
69    fn is_default_step(_builder: &Builder<'_>) -> bool {
70        true
71    }
72
73    fn make_run(run: RunConfig<'_>) {
74        let vendor = run.builder.ensure(Vendor {
75            sync_args: run.builder.config.cmd.vendor_sync_args(),
76            versioned_dirs: run.builder.config.cmd.vendor_versioned_dirs(),
77            root_dir: run.builder.src.clone(),
78            output_dir: None,
79            only_library_workspace: false,
80        });
81        // Print the config.toml contents, so that the user can manually copy it
82        if !run.builder.config.dry_run() {
83            println!("Put the following config into .cargo/config.toml:\n{}", vendor.config);
84            println!(
85                "Put the following config into library/.cargo/config.toml:\n{}",
86                vendor.config_library
87            );
88        }
89    }
90
91    /// Executes the vendoring process.
92    ///
93    /// This function runs `cargo vendor` and ensures all required submodules
94    /// are initialized before vendoring begins.
95    fn run(self, builder: &Builder<'_>) -> Self::Output {
96        let _guard = builder.group(&format!("Vendoring sources to {:?}", self.root_dir));
97
98        let config = if self.only_library_workspace {
99            String::new()
100        } else {
101            let mut cmd = command(&builder.initial_cargo);
102            cmd.arg("vendor");
103
104            if self.versioned_dirs {
105                cmd.arg("--versioned-dirs");
106            }
107
108            let to_vendor = default_paths_to_vendor(builder);
109            // These submodules must be present for `x vendor` to work.
110            for (_, submodules) in &to_vendor {
111                for submodule in submodules {
112                    builder.sess.require_submodule(submodule, None);
113                }
114            }
115
116            // Sync these paths by default.
117            for (p, _) in &to_vendor {
118                cmd.arg("--sync").arg(p);
119            }
120
121            // Also sync explicitly requested paths.
122            for sync_arg in self.sync_args {
123                cmd.arg("--sync").arg(sync_arg);
124            }
125
126            // Reuse vendored dependencies when building source tarball for offline support.
127            if builder.config.vendor {
128                cmd.arg("--respect-source-config")
129                    .arg("--config")
130                    .arg(builder.src.join(".cargo").join("config.toml"));
131            }
132
133            // Will read the libstd Cargo.toml
134            // which uses the unstable `public-dependency` feature.
135            cmd.env("RUSTC_BOOTSTRAP", "1");
136            cmd.env("RUSTC", &builder.initial_rustc);
137
138            cmd.current_dir(&self.root_dir);
139            match &self.output_dir {
140                None => cmd.arg(VENDOR_DIR),
141                Some(output_dir) => cmd.arg(output_dir.join(VENDOR_DIR)),
142            };
143
144            cmd.run_capture_stdout(builder).stdout()
145        };
146
147        let mut cmd = command(&builder.initial_cargo);
148        cmd.arg("vendor");
149
150        if self.versioned_dirs {
151            cmd.arg("--versioned-dirs");
152        }
153
154        // Reuse vendored dependencies when building source tarball for offline support.
155        if builder.config.vendor {
156            cmd.arg("--respect-source-config")
157                .arg("--config")
158                .arg(builder.src.join("library").join(".cargo").join("config.toml"));
159        }
160
161        // Will read the libstd Cargo.toml
162        // which uses the unstable `public-dependency` feature.
163        cmd.env("RUSTC_BOOTSTRAP", "1");
164        cmd.env("RUSTC", &builder.initial_rustc);
165
166        cmd.current_dir(self.root_dir.join("library"));
167        match &self.output_dir {
168            None => cmd.arg(VENDOR_DIR),
169            Some(output_dir) => cmd.arg(output_dir.join("library").join(VENDOR_DIR)),
170        };
171
172        let config_library = cmd.run_capture_stdout(builder).stdout();
173
174        VendorOutput { config, config_library }
175    }
176}
177
178/// Stores the result of the vendoring step.
179#[derive(Debug, Clone)]
180pub(crate) struct VendorOutput {
181    pub(crate) config: String,
182    pub(crate) config_library: String,
183}