Skip to content

Commit ad194a8

Browse files
authored
Simple Check Feature Task xtask (#7041)
1 parent 7cde470 commit ad194a8

File tree

4 files changed

+155
-21
lines changed

4 files changed

+155
-21
lines changed

.github/workflows/ci.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,3 +780,22 @@ jobs:
780780
command: check bans licenses sources
781781
arguments: --all-features --workspace
782782
rust-version: ${{ env.REPO_MSRV }}
783+
784+
check-feature-dependencies:
785+
# runtime is normally 1 minute
786+
timeout-minutes: 5
787+
788+
name: "Feature Dependencies"
789+
runs-on: ubuntu-latest
790+
steps:
791+
- name: checkout repo
792+
uses: actions/checkout@v4
793+
794+
- name: Install Repo MSRV toolchain
795+
run: |
796+
rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal
797+
rustup override set ${{ env.REPO_MSRV }}
798+
cargo -V
799+
800+
- name: Run `cargo feature-dependencies`
801+
run: cargo xtask check-feature-dependencies
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
use pico_args::Arguments;
2+
use xshell::Shell;
3+
4+
#[derive(Debug)]
5+
enum Search<'a> {
6+
#[expect(dead_code)]
7+
Positive(&'a str),
8+
Negative(&'a str),
9+
}
10+
11+
#[derive(Debug)]
12+
struct Requirement<'a> {
13+
human_readable_name: &'a str,
14+
target: &'a str,
15+
packages: &'a [&'a str],
16+
features: &'a [&'a str],
17+
default_features: bool,
18+
search_terms: &'a [Search<'a>],
19+
}
20+
21+
const ALL_WGPU_FEATURES: &[&str] = &[
22+
"dx12",
23+
"metal",
24+
"webgpu",
25+
"angle",
26+
"vulkan-portability",
27+
"webgl",
28+
"spirv",
29+
"glsl",
30+
"wgsl",
31+
"naga-ir",
32+
"serde",
33+
"replay",
34+
"counters",
35+
"fragile-send-sync-non-atomic-wasm",
36+
"static-dxc",
37+
];
38+
39+
pub fn check_feature_dependencies(shell: Shell, arguments: Arguments) -> anyhow::Result<()> {
40+
let mut _args = arguments.finish();
41+
42+
let features_no_webgl: Vec<&str> = ALL_WGPU_FEATURES
43+
.iter()
44+
.copied()
45+
.filter(|feature| *feature != "webgl")
46+
.collect();
47+
48+
let requirements = [
49+
Requirement {
50+
human_readable_name: "wasm32 without `webgl` feature does not depend on `wgpu-core`",
51+
target: "wasm32-unknown-unknown",
52+
packages: &["wgpu"],
53+
features: &features_no_webgl,
54+
default_features: false,
55+
search_terms: &[Search::Negative("wgpu-core")],
56+
},
57+
Requirement {
58+
human_readable_name:
59+
"wasm32 with `webgpu` and `wgsl` feature does not depend on `naga`",
60+
target: "wasm32-unknown-unknown",
61+
packages: &["wgpu"],
62+
features: &["webgpu", "wgsl"],
63+
default_features: false,
64+
search_terms: &[Search::Negative("naga")],
65+
},
66+
];
67+
68+
let mut any_failures = false;
69+
for requirement in requirements {
70+
let mut cmd = shell
71+
.cmd("cargo")
72+
.args(["tree", "--target", requirement.target]);
73+
74+
for package in requirement.packages {
75+
cmd = cmd.arg("--package").arg(package);
76+
}
77+
78+
if !requirement.default_features {
79+
cmd = cmd.arg("--no-default-features");
80+
}
81+
82+
if !requirement.features.is_empty() {
83+
cmd = cmd.arg("--features").arg(requirement.features.join(","));
84+
}
85+
86+
log::info!("Checking Requirement: {}", requirement.human_readable_name);
87+
log::debug!("{:#?}", requirement);
88+
log::debug!("$ {cmd}");
89+
90+
let output = cmd.read()?;
91+
92+
log::debug!("{output}");
93+
94+
for search_term in requirement.search_terms {
95+
let found = match search_term {
96+
Search::Positive(search_term) => output.contains(search_term),
97+
Search::Negative(search_term) => !output.contains(search_term),
98+
};
99+
100+
if found {
101+
log::info!("✅ Passed!");
102+
} else {
103+
log::info!("❌ Failed");
104+
any_failures = true;
105+
}
106+
}
107+
}
108+
109+
if any_failures {
110+
anyhow::bail!("Some feature dependencies are not met");
111+
}
112+
113+
Ok(())
114+
}

xtask/src/main.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::process::ExitCode;
33
use anyhow::Context;
44
use pico_args::Arguments;
55

6+
mod check_feature_dependencies;
67
mod run_wasm;
78
mod test;
89
mod util;
@@ -12,6 +13,9 @@ const HELP: &str = "\
1213
Usage: xtask <COMMAND>
1314
1415
Commands:
16+
check-feature-dependencies
17+
Check certain dependency invariants are upheld.
18+
1519
run-wasm
1620
Build and run web examples
1721
@@ -71,6 +75,9 @@ fn main() -> anyhow::Result<ExitCode> {
7175
shell.change_dir(String::from(env!("CARGO_MANIFEST_DIR")) + "/..");
7276

7377
match subcommand.as_deref() {
78+
Some("check-feature-dependencies") => {
79+
check_feature_dependencies::check_feature_dependencies(shell, args)?
80+
}
7481
Some("run-wasm") => run_wasm::run_wasm(shell, args)?,
7582
Some("test") => test::run_tests(shell, args)?,
7683
Some("vendor-web-sys") => vendor_web_sys::run_vendor_web_sys(shell, args)?,

xtask/src/run_wasm.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,22 @@ use xshell::Shell;
66
use crate::util::{check_all_programs, Program};
77

88
pub(crate) fn run_wasm(shell: Shell, mut args: Arguments) -> anyhow::Result<()> {
9-
let no_serve = args.contains("--no-serve");
9+
let should_serve = !args.contains("--no-serve");
1010
let release = args.contains("--release");
1111

12-
let programs_needed: &[_] = if no_serve {
13-
&[Program {
14-
crate_name: "wasm-bindgen-cli",
15-
binary_name: "wasm-bindgen",
16-
}]
17-
} else {
18-
&[
19-
Program {
20-
crate_name: "wasm-bindgen-cli",
21-
binary_name: "wasm-bindgen",
22-
},
23-
Program {
24-
crate_name: "simple-http-server",
25-
binary_name: "simple-http-server",
26-
},
27-
]
28-
};
29-
30-
check_all_programs(programs_needed)?;
12+
let mut programs_needed = vec![Program {
13+
crate_name: "wasm-bindgen-cli",
14+
binary_name: "wasm-bindgen",
15+
}];
16+
17+
if should_serve {
18+
programs_needed.push(Program {
19+
crate_name: "simple-http-server",
20+
binary_name: "simple-http-server",
21+
});
22+
}
23+
24+
check_all_programs(&programs_needed)?;
3125

3226
let release_flag: &[_] = if release { &["--release"] } else { &[] };
3327
let output_dir = if release { "release" } else { "debug" };
@@ -91,7 +85,7 @@ pub(crate) fn run_wasm(shell: Shell, mut args: Arguments) -> anyhow::Result<()>
9185
.with_context(|| format!("Failed to copy static file \"{}\"", file.display()))?;
9286
}
9387

94-
if !no_serve {
88+
if should_serve {
9589
log::info!("serving on port 8000");
9690

9791
// Explicitly specify the IP address to 127.0.0.1 since otherwise simple-http-server will

0 commit comments

Comments
 (0)