Skip to content

Commit b3ad405

Browse files
committed
allow excluding paths only from a single module
x.py has support for excluding some steps from the invocation, but unfortunately that's not granular enough: some steps have the same name in different modules, and that prevents excluding only *some* of them. As a practical example, let's say you need to run everything in `./x.py test` except for the standard library tests, as those tests require IPv6 and need to be executed on a separate machine. Before this commit, if you were to just run this: ./x.py test --exclude library/std ...the execution would fail, as that would not only exclude running the tests for the standard library, it would also exclude generating its documentation (breaking linkchecker). This commit adds support for an optional module annotation in --exclude paths, allowing the user to choose which module to exclude from: ./x.py test --exclude test::library/std This maintains backward compatibility, but also allows for more ganular exclusion. More examples on how this works: | `--exclude` | Docs | Tests | | ------------------- | ------- | ------- | | `library/std` | Skipped | Skipped | | `doc::library/std` | Skipped | Run | | `test::library/std` | Run | Skipped | Note that the new behavior only works in the `--exclude` flag, and not in other x.py arguments or flags yet.
1 parent b27d59d commit b3ad405

File tree

5 files changed

+128
-41
lines changed

5 files changed

+128
-41
lines changed

src/bootstrap/builder.rs

Lines changed: 117 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::fmt::Debug;
77
use std::fs;
88
use std::hash::Hash;
99
use std::ops::Deref;
10-
use std::path::{Path, PathBuf};
10+
use std::path::{Component, Path, PathBuf};
1111
use std::process::Command;
1212
use std::time::{Duration, Instant};
1313

@@ -105,17 +105,43 @@ struct StepDescription {
105105
should_run: fn(ShouldRun<'_>) -> ShouldRun<'_>,
106106
make_run: fn(RunConfig<'_>),
107107
name: &'static str,
108+
kind: Kind,
108109
}
109110

110-
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
111+
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
111112
pub struct TaskPath {
112113
pub path: PathBuf,
113-
pub module: Option<String>,
114+
pub kind: Option<Kind>,
114115
}
115116

116117
impl TaskPath {
117118
pub fn parse(path: impl Into<PathBuf>) -> TaskPath {
118-
TaskPath { path: path.into(), module: None }
119+
let mut kind = None;
120+
let mut path = path.into();
121+
122+
let mut components = path.components();
123+
if let Some(Component::Normal(os_str)) = components.next() {
124+
if let Some(str) = os_str.to_str() {
125+
if let Some((found_kind, found_prefix)) = str.split_once("::") {
126+
if found_kind.is_empty() {
127+
panic!("empty kind in task path {}", path.display());
128+
}
129+
kind = Some(Kind::parse(found_kind));
130+
path = Path::new(found_prefix).join(components.as_path());
131+
}
132+
}
133+
}
134+
135+
TaskPath { path, kind }
136+
}
137+
}
138+
139+
impl Debug for TaskPath {
140+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141+
if let Some(kind) = &self.kind {
142+
write!(f, "{}::", kind.as_str())?;
143+
}
144+
write!(f, "{}", self.path.display())
119145
}
120146
}
121147

@@ -142,16 +168,24 @@ impl PathSet {
142168
PathSet::Set(BTreeSet::new())
143169
}
144170

145-
fn one<P: Into<PathBuf>>(path: P) -> PathSet {
171+
fn one<P: Into<PathBuf>>(path: P, kind: Kind) -> PathSet {
146172
let mut set = BTreeSet::new();
147-
set.insert(TaskPath::parse(path));
173+
set.insert(TaskPath { path: path.into(), kind: Some(kind.into()) });
148174
PathSet::Set(set)
149175
}
150176

151-
fn has(&self, needle: &Path) -> bool {
177+
fn has(&self, needle: &Path, module: Option<Kind>) -> bool {
178+
let check = |p: &TaskPath| {
179+
if let (Some(p_kind), Some(kind)) = (&p.kind, module) {
180+
p.path.ends_with(needle) && *p_kind == kind
181+
} else {
182+
p.path.ends_with(needle)
183+
}
184+
};
185+
152186
match self {
153-
PathSet::Set(set) => set.iter().any(|p| p.path.ends_with(needle)),
154-
PathSet::Suite(suite) => suite.path.ends_with(needle),
187+
PathSet::Set(set) => set.iter().any(check),
188+
PathSet::Suite(suite) => check(suite),
155189
}
156190
}
157191

@@ -166,13 +200,14 @@ impl PathSet {
166200
}
167201

168202
impl StepDescription {
169-
fn from<S: Step>() -> StepDescription {
203+
fn from<S: Step>(kind: Kind) -> StepDescription {
170204
StepDescription {
171205
default: S::DEFAULT,
172206
only_hosts: S::ONLY_HOSTS,
173207
should_run: S::should_run,
174208
make_run: S::make_run,
175209
name: std::any::type_name::<S>(),
210+
kind,
176211
}
177212
}
178213

@@ -191,7 +226,7 @@ impl StepDescription {
191226
}
192227

193228
fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool {
194-
if builder.config.exclude.iter().any(|e| pathset.has(e)) {
229+
if builder.config.exclude.iter().any(|e| pathset.has(&e.path, e.kind)) {
195230
eprintln!("Skipping {:?} because it is excluded", pathset);
196231
return true;
197232
}
@@ -206,8 +241,10 @@ impl StepDescription {
206241
}
207242

208243
fn run(v: &[StepDescription], builder: &Builder<'_>, paths: &[PathBuf]) {
209-
let should_runs =
210-
v.iter().map(|desc| (desc.should_run)(ShouldRun::new(builder))).collect::<Vec<_>>();
244+
let should_runs = v
245+
.iter()
246+
.map(|desc| (desc.should_run)(ShouldRun::new(builder, desc.kind)))
247+
.collect::<Vec<_>>();
211248

212249
// sanity checks on rules
213250
for (desc, should_run) in v.iter().zip(&should_runs) {
@@ -240,7 +277,7 @@ impl StepDescription {
240277
if let Some(suite) = should_run.is_suite_path(path) {
241278
attempted_run = true;
242279
desc.maybe_run(builder, suite);
243-
} else if let Some(pathset) = should_run.pathset_for_path(path) {
280+
} else if let Some(pathset) = should_run.pathset_for_path(path, desc.kind) {
244281
attempted_run = true;
245282
desc.maybe_run(builder, pathset);
246283
}
@@ -260,6 +297,8 @@ enum ReallyDefault<'a> {
260297

261298
pub struct ShouldRun<'a> {
262299
pub builder: &'a Builder<'a>,
300+
kind: Kind,
301+
263302
// use a BTreeSet to maintain sort order
264303
paths: BTreeSet<PathSet>,
265304

@@ -269,9 +308,10 @@ pub struct ShouldRun<'a> {
269308
}
270309

271310
impl<'a> ShouldRun<'a> {
272-
fn new(builder: &'a Builder<'_>) -> ShouldRun<'a> {
311+
fn new(builder: &'a Builder<'_>, kind: Kind) -> ShouldRun<'a> {
273312
ShouldRun {
274313
builder,
314+
kind,
275315
paths: BTreeSet::new(),
276316
is_really_default: ReallyDefault::Bool(true), // by default no additional conditions
277317
}
@@ -307,7 +347,7 @@ impl<'a> ShouldRun<'a> {
307347
let mut set = BTreeSet::new();
308348
for krate in self.builder.in_tree_crates(name, None) {
309349
let path = krate.local_path(self.builder);
310-
set.insert(TaskPath::parse(path));
350+
set.insert(TaskPath { path, kind: Some(self.kind) });
311351
}
312352
self.paths.insert(PathSet::Set(set));
313353
self
@@ -320,7 +360,7 @@ impl<'a> ShouldRun<'a> {
320360
pub fn krate(mut self, name: &str) -> Self {
321361
for krate in self.builder.in_tree_crates(name, None) {
322362
let path = krate.local_path(self.builder);
323-
self.paths.insert(PathSet::one(path));
363+
self.paths.insert(PathSet::one(path, self.kind));
324364
}
325365
self
326366
}
@@ -332,7 +372,12 @@ impl<'a> ShouldRun<'a> {
332372

333373
// multiple aliases for the same job
334374
pub fn paths(mut self, paths: &[&str]) -> Self {
335-
self.paths.insert(PathSet::Set(paths.iter().map(|p| TaskPath::parse(p)).collect()));
375+
self.paths.insert(PathSet::Set(
376+
paths
377+
.iter()
378+
.map(|p| TaskPath { path: p.into(), kind: Some(self.kind.into()) })
379+
.collect(),
380+
));
336381
self
337382
}
338383

@@ -344,7 +389,8 @@ impl<'a> ShouldRun<'a> {
344389
}
345390

346391
pub fn suite_path(mut self, suite: &str) -> Self {
347-
self.paths.insert(PathSet::Suite(TaskPath::parse(suite)));
392+
self.paths
393+
.insert(PathSet::Suite(TaskPath { path: suite.into(), kind: Some(self.kind.into()) }));
348394
self
349395
}
350396

@@ -354,12 +400,12 @@ impl<'a> ShouldRun<'a> {
354400
self
355401
}
356402

357-
fn pathset_for_path(&self, path: &Path) -> Option<&PathSet> {
358-
self.paths.iter().find(|pathset| pathset.has(path))
403+
fn pathset_for_path(&self, path: &Path, kind: Kind) -> Option<&PathSet> {
404+
self.paths.iter().find(|pathset| pathset.has(path, Some(kind)))
359405
}
360406
}
361407

362-
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
408+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
363409
pub enum Kind {
364410
Build,
365411
Check,
@@ -373,11 +419,44 @@ pub enum Kind {
373419
Run,
374420
}
375421

422+
impl Kind {
423+
fn parse(string: &str) -> Kind {
424+
match string {
425+
"build" => Kind::Build,
426+
"check" => Kind::Check,
427+
"clippy" => Kind::Clippy,
428+
"fix" => Kind::Fix,
429+
"test" => Kind::Test,
430+
"bench" => Kind::Bench,
431+
"dist" => Kind::Dist,
432+
"doc" => Kind::Doc,
433+
"install" => Kind::Install,
434+
"run" => Kind::Run,
435+
other => panic!("unknown kind: {}", other),
436+
}
437+
}
438+
439+
fn as_str(&self) -> &'static str {
440+
match self {
441+
Kind::Build => "build",
442+
Kind::Check => "check",
443+
Kind::Clippy => "clippy",
444+
Kind::Fix => "fix",
445+
Kind::Test => "test",
446+
Kind::Bench => "bench",
447+
Kind::Dist => "dist",
448+
Kind::Doc => "doc",
449+
Kind::Install => "install",
450+
Kind::Run => "run",
451+
}
452+
}
453+
}
454+
376455
impl<'a> Builder<'a> {
377456
fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> {
378457
macro_rules! describe {
379458
($($rule:ty),+ $(,)?) => {{
380-
vec![$(StepDescription::from::<$rule>()),+]
459+
vec![$(StepDescription::from::<$rule>(kind)),+]
381460
}};
382461
}
383462
match kind {
@@ -554,8 +633,11 @@ impl<'a> Builder<'a> {
554633

555634
let builder = Self::new_internal(build, kind, vec![]);
556635
let builder = &builder;
557-
let mut should_run = ShouldRun::new(builder);
636+
// The "build" kind here is just a placeholder, it will be replaced with something else in
637+
// the following statement.
638+
let mut should_run = ShouldRun::new(builder, Kind::Build);
558639
for desc in Builder::get_step_descriptions(builder.kind) {
640+
should_run.kind = desc.kind;
559641
should_run = (desc.should_run)(should_run);
560642
}
561643
let mut help = String::from("Available paths:\n");
@@ -1640,9 +1722,10 @@ impl<'a> Builder<'a> {
16401722
pub(crate) fn ensure_if_default<T, S: Step<Output = Option<T>>>(
16411723
&'a self,
16421724
step: S,
1725+
kind: Kind,
16431726
) -> S::Output {
1644-
let desc = StepDescription::from::<S>();
1645-
let should_run = (desc.should_run)(ShouldRun::new(self));
1727+
let desc = StepDescription::from::<S>(kind);
1728+
let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind));
16461729

16471730
// Avoid running steps contained in --exclude
16481731
for pathset in &should_run.paths {
@@ -1656,13 +1739,16 @@ impl<'a> Builder<'a> {
16561739
}
16571740

16581741
/// Checks if any of the "should_run" paths is in the `Builder` paths.
1659-
pub(crate) fn was_invoked_explicitly<S: Step>(&'a self) -> bool {
1660-
let desc = StepDescription::from::<S>();
1661-
let should_run = (desc.should_run)(ShouldRun::new(self));
1742+
pub(crate) fn was_invoked_explicitly<S: Step>(&'a self, kind: Kind) -> bool {
1743+
let desc = StepDescription::from::<S>(kind);
1744+
let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind));
16621745

16631746
for path in &self.paths {
1664-
if should_run.paths.iter().any(|s| s.has(path))
1665-
&& !desc.is_excluded(self, &PathSet::Suite(TaskPath::parse(path)))
1747+
if should_run.paths.iter().any(|s| s.has(path, Some(desc.kind)))
1748+
&& !desc.is_excluded(
1749+
self,
1750+
&PathSet::Suite(TaskPath { path: path.clone(), kind: Some(desc.kind.into()) }),
1751+
)
16661752
{
16671753
return true;
16681754
}

src/bootstrap/builder/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ mod dist {
499499
let host = TargetSelection::from_user("A");
500500

501501
builder.run_step_descriptions(
502-
&[StepDescription::from::<test::Crate>()],
502+
&[StepDescription::from::<test::Crate>(Kind::Test)],
503503
&["library/std".into()],
504504
);
505505

@@ -520,7 +520,7 @@ mod dist {
520520
#[test]
521521
fn test_exclude() {
522522
let mut config = configure(&["A"], &["A"]);
523-
config.exclude = vec!["src/tools/tidy".into()];
523+
config.exclude = vec![TaskPath::parse("src/tools/tidy")];
524524
config.cmd = Subcommand::Test {
525525
paths: Vec::new(),
526526
test_args: Vec::new(),

src/bootstrap/config.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::fs;
1212
use std::path::{Path, PathBuf};
1313
use std::str::FromStr;
1414

15+
use crate::builder::TaskPath;
1516
use crate::cache::{Interned, INTERNER};
1617
use crate::channel::GitInfo;
1718
pub use crate::flags::Subcommand;
@@ -62,7 +63,7 @@ pub struct Config {
6263
pub sanitizers: bool,
6364
pub profiler: bool,
6465
pub ignore_git: bool,
65-
pub exclude: Vec<PathBuf>,
66+
pub exclude: Vec<TaskPath>,
6667
pub include_default_paths: bool,
6768
pub rustc_error_format: Option<String>,
6869
pub json_output: bool,
@@ -635,7 +636,7 @@ impl Config {
635636
let flags = Flags::parse(&args);
636637

637638
let mut config = Config::default_opts();
638-
config.exclude = flags.exclude;
639+
config.exclude = flags.exclude.into_iter().map(|path| TaskPath::parse(path)).collect();
639640
config.include_default_paths = flags.include_default_paths;
640641
config.rustc_error_format = flags.rustc_error_format;
641642
config.json_output = flags.json_output;

src/bootstrap/dist.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::process::Command;
1616

1717
use build_helper::{output, t};
1818

19-
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
19+
use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
2020
use crate::cache::{Interned, INTERNER};
2121
use crate::compile;
2222
use crate::config::TargetSelection;
@@ -1368,7 +1368,7 @@ impl Step for Extended {
13681368
let mut built_tools = HashSet::new();
13691369
macro_rules! add_component {
13701370
($name:expr => $step:expr) => {
1371-
if let Some(tarball) = builder.ensure_if_default($step) {
1371+
if let Some(tarball) = builder.ensure_if_default($step, Kind::Dist) {
13721372
tarballs.push(tarball);
13731373
built_tools.insert($name);
13741374
}

0 commit comments

Comments
 (0)