bootstrap/core/builder/
cli_paths.rs1use std::fmt::{self, Debug};
6use std::path::PathBuf;
7
8use crate::core::builder::{Builder, Kind, PathSet, ShouldRun, StepDescription};
9
10#[cfg(test)]
11mod tests;
12
13pub(crate) const PATH_REMAP: &[(&str, &[&str])] = &[
14 ("rust-analyzer-proc-macro-srv", &["src/tools/rust-analyzer/crates/proc-macro-srv-cli"]),
17 (
19 "tests",
20 &[
21 "tests/assembly-llvm",
23 "tests/build-std",
24 "tests/codegen-llvm",
25 "tests/codegen-units",
26 "tests/coverage",
27 "tests/coverage-run-rustdoc",
28 "tests/crashes",
29 "tests/debuginfo",
30 "tests/incremental",
31 "tests/mir-opt",
32 "tests/pretty",
33 "tests/run-make",
34 "tests/run-make-cargo",
35 "tests/rustdoc-gui",
36 "tests/rustdoc-html",
37 "tests/rustdoc-js",
38 "tests/rustdoc-js-std",
39 "tests/rustdoc-json",
40 "tests/rustdoc-ui",
41 "tests/ui",
42 "tests/ui-fulldeps",
43 ],
45 ),
46];
47
48pub(crate) fn remap_paths(paths: &mut Vec<PathBuf>) {
49 let mut remove = vec![];
50 let mut add = vec![];
51 for (i, path) in paths.iter().enumerate().filter_map(|(i, path)| path.to_str().map(|s| (i, s)))
52 {
53 for &(search, replace) in PATH_REMAP {
54 if path.trim_matches(std::path::is_separator) == search {
56 remove.push(i);
57 add.extend(replace.iter().map(PathBuf::from));
58 break;
59 }
60 }
61 }
62 remove.sort();
63 remove.dedup();
64 for idx in remove.into_iter().rev() {
65 paths.remove(idx);
66 }
67 paths.append(&mut add);
68}
69
70#[derive(Clone, PartialEq)]
71pub(crate) struct CLIStepPath {
72 pub(crate) path: PathBuf,
73 pub(crate) will_be_executed: bool,
74}
75
76#[cfg(test)]
77impl CLIStepPath {
78 pub(crate) fn will_be_executed(mut self, will_be_executed: bool) -> Self {
79 self.will_be_executed = will_be_executed;
80 self
81 }
82}
83
84impl Debug for CLIStepPath {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 write!(f, "{}", self.path.display())
87 }
88}
89
90impl From<PathBuf> for CLIStepPath {
91 fn from(path: PathBuf) -> Self {
92 Self { path, will_be_executed: false }
93 }
94}
95
96struct StepExtra<'a> {
98 desc: &'a StepDescription,
99 should_run: ShouldRun<'a>,
100}
101
102struct StepToRun<'a> {
103 sort_index: usize,
104 desc: &'a StepDescription,
105 pathsets: Vec<PathSet>,
106}
107
108pub(crate) fn match_paths_to_steps_and_run(
109 builder: &Builder<'_>,
110 step_descs: &[StepDescription],
111 paths: &[PathBuf],
112) {
113 let steps = step_descs
116 .iter()
117 .map(|desc| StepExtra {
118 desc,
119 should_run: (desc.should_run)(ShouldRun::new(builder, desc.kind)),
120 })
121 .collect::<Vec<_>>();
122
123 if builder.download_rustc() && (builder.kind == Kind::Dist || builder.kind == Kind::Install) {
126 eprintln!(
127 "ERROR: '{}' subcommand is incompatible with `rust.download-rustc`.",
128 builder.kind.as_str()
129 );
130 crate::exit!(1);
131 }
132
133 for StepExtra { desc, should_run } in &steps {
135 assert!(!should_run.paths.is_empty(), "{:?} should have at least one pathset", desc.name);
136 }
137
138 if paths.is_empty() || builder.config.include_default_paths {
139 for StepExtra { desc, should_run } in &steps {
140 if (desc.is_default_step_fn)(builder) {
141 let default_pathsets = should_run.default_pathsets();
142 desc.maybe_run(builder, default_pathsets);
143 }
144 }
145 }
146
147 let mut paths: Vec<PathBuf> = paths
149 .iter()
150 .map(|original_path| {
151 let mut path = original_path.clone();
152
153 if !path.is_absolute() {
159 path = builder.src.join(path);
160 }
161
162 if !path.exists() {
164 return original_path.clone();
166 }
167
168 match std::path::absolute(&path) {
170 Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(),
171 Err(e) => {
172 eprintln!("ERROR: {e:?}");
173 panic!("Due to the above error, failed to resolve path: {path:?}");
174 }
175 }
176 })
177 .collect();
178
179 remap_paths(&mut paths);
180
181 paths.retain(|path| {
184 for StepExtra { desc, should_run } in &steps {
185 if let Some(suite) = should_run.is_suite_path(path) {
186 desc.maybe_run(builder, vec![suite.clone()]);
187 return false;
188 }
189 }
190 true
191 });
192
193 if paths.is_empty() {
194 return;
195 }
196
197 let mut paths: Vec<CLIStepPath> = paths.into_iter().map(|p| p.into()).collect();
198 let mut path_lookup: Vec<(CLIStepPath, bool)> =
199 paths.clone().into_iter().map(|p| (p, false)).collect();
200
201 let mut steps_to_run = vec![];
204
205 for StepExtra { desc, should_run } in &steps {
206 let pathsets = should_run.pathset_for_paths_removing_matches(&mut paths, desc.kind);
207
208 let mut closest_index = usize::MAX;
214
215 for (index, (path, is_used)) in path_lookup.iter_mut().enumerate() {
217 if !*is_used && !paths.contains(path) {
218 closest_index = index;
219 *is_used = true;
220 break;
221 }
222 }
223
224 steps_to_run.push(StepToRun { sort_index: closest_index, desc, pathsets });
225 }
226
227 steps_to_run.sort_by_key(|step| step.sort_index);
229
230 for StepToRun { sort_index: _, desc, pathsets } in steps_to_run {
232 if !pathsets.is_empty() {
233 desc.maybe_run(builder, pathsets);
234 }
235 }
236
237 paths.retain(|p| !p.will_be_executed);
238
239 if !paths.is_empty() {
240 eprintln!("ERROR: no `{}` rules matched {:?}", builder.kind.as_str(), paths);
241 eprintln!(
242 "HELP: run `x.py {} --help --verbose` to show a list of available paths",
243 builder.kind.as_str()
244 );
245 eprintln!(
246 "NOTE: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`"
247 );
248 crate::exit!(1);
249 }
250}