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