Skip to content

Commit 58b97d9

Browse files
committed
Add error handling for rustc_codegen_spirv-cache
1 parent bf32a23 commit 58b97d9

File tree

18 files changed

+1048
-446
lines changed

18 files changed

+1048
-446
lines changed

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ keywords = ["gpu", "compiler", "rust-gpu"]
2222
license = "MIT OR Apache-2.0"
2323

2424
[workspace.dependencies]
25-
spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu", rev = "3df836eb9d7b01344f52737bf9a310d1fb5a0c26", default-features = false }
25+
spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu", rev = "929112a325335c3acd520ceca10e54596c3be93e", default-features = false }
2626
anyhow = "1.0.98"
2727
thiserror = "2.0.12"
2828
clap = { version = "4.5.41", features = ["derive"] }

crates/cargo-gpu-build/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,16 @@ license.workspace = true
99

1010
[dependencies]
1111
rustc_codegen_spirv-cache = { path = "../rustc_codegen_spirv-cache" }
12+
spirv-builder.workspace = true
1213
anyhow.workspace = true
1314
semver.workspace = true
1415
log.workspace = true
1516

17+
[features]
18+
# Rebuilds target shader crate upon changes
19+
watch = ["spirv-builder/watch"]
20+
# Enables `clap` support for public structs
21+
clap = ["spirv-builder/clap", "rustc_codegen_spirv-cache/clap"]
22+
1623
[lints]
1724
workspace = true

crates/cargo-gpu/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ license.workspace = true
1010
default-run = "cargo-gpu"
1111

1212
[dependencies]
13-
cargo-gpu-build = { path = "../cargo-gpu-build", default-features = false }
13+
cargo-gpu-build = { path = "../cargo-gpu-build", features = ["clap", "watch"] }
1414
cargo_metadata.workspace = true
1515
anyhow.workspace = true
16+
thiserror.workspace = true
1617
spirv-builder = { workspace = true, features = ["clap", "watch"] }
1718
clap.workspace = true
1819
env_logger.workspace = true

crates/cargo-gpu/src/build.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use anyhow::Context as _;
99
use cargo_gpu_build::{lockfile::LockfileMismatchHandler, spirv_cache::user_output};
1010
use spirv_builder::{CompileResult, ModuleResult, SpirvBuilder};
1111

12-
use crate::{linkage::Linkage, Install};
12+
use crate::{linkage::Linkage, user_consent::ask_for_user_consent, Install};
1313

1414
/// Args for just a build
1515
#[derive(clap::Parser, Debug, Clone, serde::Deserialize, serde::Serialize)]
@@ -59,12 +59,14 @@ pub struct Build {
5959
impl Build {
6060
/// Entrypoint
6161
pub fn run(&mut self) -> anyhow::Result<()> {
62-
let installed_backend = self.install.run()?;
62+
let skip_consent = self.install.params.auto_install_rust_toolchain;
63+
let halt_installation = ask_for_user_consent(skip_consent);
64+
let installed_backend = self.install.run(std::io::stdout(), halt_installation)?;
6365

6466
let _lockfile_mismatch_handler = LockfileMismatchHandler::new(
6567
&self.install.shader_crate,
6668
&installed_backend.toolchain_channel,
67-
self.install.force_overwrite_lockfiles_v4_to_v3,
69+
self.install.params.force_overwrite_lockfiles_v4_to_v3,
6870
)?;
6971

7072
let builder = &mut self.build.spirv_builder;

crates/cargo-gpu/src/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ mod test {
103103
)
104104
.unwrap();
105105
assert!(!args.build.spirv_builder.release);
106-
assert!(args.install.auto_install_rust_toolchain);
106+
assert!(args.install.params.auto_install_rust_toolchain);
107107
}
108108

109109
#[test_log::test]
@@ -124,7 +124,7 @@ mod test {
124124

125125
let args = Config::clap_command_with_cargo_config(&shader_crate_path, vec![]).unwrap();
126126
assert!(!args.build.spirv_builder.release);
127-
assert!(args.install.auto_install_rust_toolchain);
127+
assert!(args.install.params.auto_install_rust_toolchain);
128128
}
129129

130130
fn update_cargo_output_dir() -> std::path::PathBuf {

crates/cargo-gpu/src/lib.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@
5050
//! conduct other post-processing, like converting the `spv` files into `wgsl` files,
5151
//! for example.
5252
53-
use self::{build::Build, dump_usage::dump_full_usage_for_readme, show::Show};
53+
use self::{
54+
build::Build, dump_usage::dump_full_usage_for_readme, show::Show,
55+
user_consent::ask_for_user_consent,
56+
};
5457

5558
mod build;
5659
mod config;
@@ -59,6 +62,7 @@ mod linkage;
5962
mod metadata;
6063
mod show;
6164
mod test;
65+
mod user_consent;
6266

6367
pub use cargo_gpu_build::spirv_cache::backend::*;
6468
pub use spirv_builder;
@@ -99,7 +103,10 @@ impl Command {
99103
"installing with final merged arguments: {:#?}",
100104
command.install
101105
);
102-
command.install.run()?;
106+
107+
let skip_consent = command.install.params.auto_install_rust_toolchain;
108+
let halt_installation = ask_for_user_consent(skip_consent);
109+
command.install.run(std::io::stdout(), halt_installation)?;
103110
}
104111
Self::Build(build) => {
105112
let shader_crate_path = &build.install.shader_crate;

crates/cargo-gpu/src/show.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ use std::{fs, path::Path};
44

55
use anyhow::bail;
66
use cargo_gpu_build::spirv_cache::{
7-
cache::cache_dir,
8-
spirv_source::{query_metadata, SpirvSource},
7+
cache::cache_dir, metadata::query_metadata, spirv_source::SpirvSource,
98
target_specs::update_target_specs_files,
109
};
1110

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//! User consent acquiring logic.
2+
3+
use std::io;
4+
5+
use cargo_gpu_build::spirv_cache::{
6+
command::CommandExecError,
7+
toolchain::{HaltToolchainInstallation, REQUIRED_TOOLCHAIN_COMPONENTS},
8+
user_output,
9+
};
10+
use crossterm::tty::IsTty as _;
11+
12+
/// Halts the installation process of toolchain or its required components
13+
/// if the user does not consent to install either of them.
14+
#[expect(
15+
clippy::type_complexity,
16+
reason = "it is impossible to create an alias for now"
17+
)]
18+
pub fn ask_for_user_consent(
19+
skip: bool,
20+
) -> HaltToolchainInstallation<
21+
impl FnOnce(&str) -> Result<(), UserConsentError>,
22+
impl FnOnce(&str) -> Result<(), UserConsentError>,
23+
> {
24+
let on_toolchain_install = move |channel: &str| {
25+
let message = format!("Rust {channel} with `rustup`");
26+
get_consent_for_toolchain_install(format!("Install {message}").as_ref(), skip)?;
27+
log::debug!("installing toolchain {channel}");
28+
user_output!(io::stdout(), "Installing {message}\n").map_err(UserConsentError::IoWrite)?;
29+
Ok(())
30+
};
31+
let on_components_install = move |channel: &str| {
32+
let message = format!(
33+
"components {REQUIRED_TOOLCHAIN_COMPONENTS:?} for toolchain {channel} with `rustup`"
34+
);
35+
get_consent_for_toolchain_install(format!("Install {message}").as_ref(), skip)?;
36+
log::debug!("installing required components of toolchain {channel}");
37+
user_output!(io::stdout(), "Installing {message}\n").map_err(UserConsentError::IoWrite)?;
38+
Ok(())
39+
};
40+
41+
HaltToolchainInstallation {
42+
on_toolchain_install,
43+
on_components_install,
44+
}
45+
}
46+
47+
/// Prompt user if they want to install a new Rust toolchain.
48+
fn get_consent_for_toolchain_install(prompt: &str, skip: bool) -> Result<(), UserConsentError> {
49+
if skip {
50+
return Ok(());
51+
}
52+
53+
if !io::stdout().is_tty() {
54+
log::error!("attempted to ask for consent when there's no TTY");
55+
return Err(UserConsentError::NoTTY);
56+
}
57+
58+
log::debug!("asking for consent to install the required toolchain");
59+
crossterm::terminal::enable_raw_mode().map_err(UserConsentError::IoRead)?;
60+
user_output!(io::stdout(), "{prompt} [y/n]: ").map_err(UserConsentError::IoWrite)?;
61+
let mut input = crossterm::event::read().map_err(UserConsentError::IoRead)?;
62+
63+
if let crossterm::event::Event::Key(crossterm::event::KeyEvent {
64+
code: crossterm::event::KeyCode::Enter,
65+
kind: crossterm::event::KeyEventKind::Release,
66+
..
67+
}) = input
68+
{
69+
// In Powershell, programs will potentially observe the Enter key release after they started
70+
// (see crossterm#124). If that happens, re-read the input.
71+
input = crossterm::event::read().map_err(UserConsentError::IoRead)?;
72+
}
73+
crossterm::terminal::disable_raw_mode().map_err(UserConsentError::IoRead)?;
74+
75+
if let crossterm::event::Event::Key(crossterm::event::KeyEvent {
76+
code: crossterm::event::KeyCode::Char('y'),
77+
..
78+
}) = input
79+
{
80+
Ok(())
81+
} else {
82+
Err(UserConsentError::UserDenied)
83+
}
84+
}
85+
86+
/// An error indicating that user consent were not acquired.
87+
#[derive(Debug, thiserror::Error)]
88+
pub enum UserConsentError {
89+
/// An error occurred while executing a command.
90+
#[error(transparent)]
91+
CommandExec(#[from] CommandExecError),
92+
/// No TTY detected, so can't ask for consent to install Rust toolchain.
93+
#[error("no TTY detected, so can't ask for consent to install Rust toolchain")]
94+
NoTTY,
95+
/// An I/O error occurred while reading user input.
96+
#[error("failed to read user input: {0}")]
97+
IoRead(#[source] io::Error),
98+
/// An I/O error occurred while writing user output.
99+
#[error("failed to write user output: {0}")]
100+
IoWrite(#[source] io::Error),
101+
/// User denied to install the required toolchain.
102+
#[error("user denied to install the required toolchain")]
103+
UserDenied,
104+
}

crates/rustc_codegen_spirv-cache/Cargo.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@ license.workspace = true
1111
spirv-builder.workspace = true
1212
legacy_target_specs.workspace = true
1313
thiserror.workspace = true
14-
anyhow.workspace = true
15-
log.workspace = true
1614
directories.workspace = true
1715
cargo_metadata.workspace = true
18-
crossterm.workspace = true
19-
clap.workspace = true
16+
log.workspace = true
2017
serde.workspace = true
18+
clap = { workspace = true, optional = true }
2119

2220
[dev-dependencies]
2321
test-log.workspace = true
2422
cargo-util-schemas.workspace = true
2523

24+
[features]
25+
# Enables `clap` support for public structs
26+
clap = ["dep:clap", "spirv-builder/clap"]
27+
2628
[lints]
2729
workspace = true

0 commit comments

Comments
 (0)