bootstrap/core/builder/
cli_paths.rs1use std::fmt::{self, Debug};
6use std::path::PathBuf;
7
8use crate::core::builder::{Builder, CommandLineStepDescription, Kind, PathSet, ShouldRun};
9
10#[cfg(test)]
11mod tests;
12
13#[derive(Clone, PartialEq)]
14pub(crate) struct CLIStepPath {
15 pub(crate) path: PathBuf,
16 pub(crate) will_be_executed: bool,
17}
18
19impl Debug for CLIStepPath {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(f, "{}", self.path.display())
22 }
23}
24
25impl From<PathBuf> for CLIStepPath {
26 fn from(path: PathBuf) -> Self {
27 Self { path, will_be_executed: false }
28 }
29}
30
31struct StepExtra<'a> {
33 desc: &'a CommandLineStepDescription,
34 should_run: ShouldRun<'a>,
35}
36
37struct StepToRun<'a> {
38 sort_index: usize,
39 desc: &'a CommandLineStepDescription,
40 pathsets: Vec<PathSet>,
41}
42
43pub(crate) fn match_paths_to_steps_and_run(
44 builder: &Builder<'_>,
45 step_descs: &[CommandLineStepDescription],
46 paths: &[PathBuf],
47) {
48 let steps = step_descs
51 .iter()
52 .map(|desc| StepExtra { desc, should_run: (desc.should_run)(ShouldRun::new(builder)) })
53 .collect::<Vec<_>>();
54
55 if builder.download_rustc() && (builder.kind == Kind::Dist || builder.kind == Kind::Install) {
58 eprintln!(
59 "ERROR: '{}' subcommand is incompatible with `rust.download-rustc`.",
60 builder.kind.as_str()
61 );
62 crate::exit!(1);
63 }
64
65 for StepExtra { desc, should_run } in &steps {
67 assert!(!should_run.paths.is_empty(), "{:?} should have at least one pathset", desc.name);
68 }
69
70 if paths.is_empty() || builder.config.include_default_paths {
71 for StepExtra { desc, should_run } in &steps {
72 if (desc.is_default_step_fn)(builder) {
73 let default_pathsets = should_run.default_pathsets();
74 desc.maybe_run(builder, default_pathsets);
75 }
76 }
77 }
78
79 let mut paths = paths
87 .iter()
88 .map(|path| {
89 if path.is_absolute()
90 && path.exists()
91 && let Ok(relative) = path.strip_prefix(&builder.src)
92 {
93 relative
94 } else {
95 path
96 }
97 })
98 .map(|p| p.to_owned())
99 .collect::<Vec<_>>();
100
101 let bad_abs_paths = paths.iter().filter(|path| path.is_absolute()).collect::<Vec<_>>();
103 if !bad_abs_paths.is_empty() {
104 eprintln!("ERROR: failed to resolve absolute paths: {bad_abs_paths:#?}");
105 crate::exit!(1);
106 }
107
108 paths.retain(|path| {
111 for StepExtra { desc, should_run } in &steps {
112 if let Some(suite) = should_run.is_suite_path(path) {
113 desc.maybe_run(builder, vec![suite.clone()]);
114 return false;
115 }
116 }
117 true
118 });
119
120 if paths.is_empty() {
121 return;
122 }
123
124 let mut paths: Vec<CLIStepPath> = paths.into_iter().map(|p| p.into()).collect();
125 let mut path_lookup: Vec<(CLIStepPath, bool)> =
126 paths.clone().into_iter().map(|p| (p, false)).collect();
127
128 let mut steps_to_run = vec![];
131
132 for StepExtra { desc, should_run } in &steps {
133 let pathsets = should_run.pathsets_for_paths_flagging_matches(&mut paths);
134
135 let mut closest_index = usize::MAX;
141
142 for (index, (path, is_used)) in path_lookup.iter_mut().enumerate() {
144 if !*is_used && !paths.contains(path) {
145 closest_index = index;
146 *is_used = true;
147 break;
148 }
149 }
150
151 steps_to_run.push(StepToRun { sort_index: closest_index, desc, pathsets });
152 }
153
154 steps_to_run.sort_by_key(|step| step.sort_index);
156
157 for StepToRun { sort_index: _, desc, pathsets } in steps_to_run {
159 if !pathsets.is_empty() {
160 desc.maybe_run(builder, pathsets);
161 }
162 }
163
164 paths.retain(|p| !p.will_be_executed);
165
166 if !paths.is_empty() {
167 eprintln!("ERROR: no `{}` rules matched {:?}", builder.kind.as_str(), paths);
168 eprintln!(
169 "HELP: run `x.py {} --help --verbose` to show a list of available paths",
170 builder.kind.as_str()
171 );
172 eprintln!(
173 "NOTE: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`"
174 );
175 crate::exit!(1);
176 }
177}