Skip to content

Commit 76282f1

Browse files
committed
Add methods for shader crate builder, add build script entry
1 parent d5630e1 commit 76282f1

File tree

12 files changed

+170
-51
lines changed

12 files changed

+170
-51
lines changed

Cargo.lock

Lines changed: 3 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
@@ -26,7 +26,7 @@ license = "MIT OR Apache-2.0"
2626
cargo-gpu-build = { path = "crates/cargo-gpu-build", default-features = false }
2727
cargo-gpu-test-utils = { path = "crates/cargo-gpu-test-utils", default-features = false }
2828
rustc_codegen_spirv-cache = { path = "crates/rustc_codegen_spirv-cache", default-features = false }
29-
spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu", rev = "ad2a34abb0576e1cf032237d191e8e63de1c3cea", default-features = false }
29+
spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu", rev = "c5fbc8b712e96ad689643293b7d80ebe8af480d2", default-features = false }
3030
anyhow = "1.0.98"
3131
thiserror = "2.0.12"
3232
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,10 +9,17 @@ license.workspace = true
99

1010
[dependencies]
1111
rustc_codegen_spirv-cache.workspace = true
12+
spirv-builder.workspace = true
1213
thiserror.workspace = true
1314
semver.workspace = true
1415
dunce.workspace = true
1516
log.workspace = true
1617

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

crates/cargo-gpu-build/src/build.rs

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@ use std::{io, path::Path};
55

66
use crate::{
77
lockfile::{LockfileMismatchError, LockfileMismatchHandler},
8-
spirv_builder::SpirvBuilder,
8+
spirv_builder::{CompileResult, SpirvBuilder, SpirvBuilderError},
99
spirv_cache::{
1010
command::CommandExecError,
1111
install::{Install, InstallError, InstallParams, InstalledBackend},
1212
toolchain::HaltToolchainInstallation,
13+
user_output,
1314
},
1415
};
1516

17+
#[cfg(feature = "watch")]
18+
use crate::spirv_builder::{AcceptFirstCompile, Watch};
19+
1620
/// Creates shader crate builder, allowing to modify install and build parameters separately.
1721
///
1822
/// # Errors
@@ -24,14 +28,14 @@ use crate::{
2428
/// * there is a lockfile version mismatch that cannot be resolved automatically.
2529
#[inline]
2630
#[expect(clippy::unreachable, reason = "target was already set")]
27-
pub fn shader_crate_builder<P, S, E, T, C, W>(
31+
pub fn make_shader_crate_builder<P, S, E, T, C, W>(
2832
shader_crate: P,
2933
target: S,
3034
mut build: SpirvBuilder,
3135
install: InstallParams,
3236
writer: W,
3337
halt_toolchain_installation: HaltToolchainInstallation<T, C>,
34-
) -> Result<ShaderCrateBuilder, MakeShaderBuilderError<E>>
38+
) -> Result<ShaderCrateBuilder, MakeShaderCrateBuilderError<E>>
3539
where
3640
P: AsRef<Path>,
3741
S: Into<String>,
@@ -78,10 +82,57 @@ pub struct ShaderCrateBuilder {
7882
pub lockfile_mismatch_handler: LockfileMismatchHandler,
7983
}
8084

85+
impl ShaderCrateBuilder {
86+
/// Builds the shader crate using the configured [`SpirvBuilder`].
87+
///
88+
/// # Errors
89+
///
90+
/// Returns an error if building the shader crate failed.
91+
#[inline]
92+
pub fn build<W>(&self, writer: W) -> Result<CompileResult, ShaderCrateBuildError>
93+
where
94+
W: io::Write,
95+
{
96+
let shader_crate = self.installed_backend_args.shader_crate.display();
97+
user_output!(writer, "Compiling shaders at {shader_crate}...\n")?;
98+
99+
let result = self.builder.build()?;
100+
Ok(result)
101+
}
102+
103+
/// Watches the shader crate for changes using the configured [`SpirvBuilder`].
104+
///
105+
/// Calls `on_compilation_finishes` after each successful compilation.
106+
///
107+
/// # Errors
108+
///
109+
/// Returns an error if watching shader crate for changes failed.
110+
#[cfg(feature = "watch")]
111+
#[inline]
112+
pub fn watch<W, T, F>(
113+
&self,
114+
writer: W,
115+
on_compilation_finishes: F,
116+
) -> Result<Watch<T>, ShaderCrateBuildError>
117+
where
118+
W: io::Write,
119+
F: FnMut(CompileResult, Option<AcceptFirstCompile<'_, T>>) + Send + 'static,
120+
{
121+
let shader_crate = self.installed_backend_args.shader_crate.display();
122+
user_output!(
123+
writer,
124+
"Watching shaders for changes at {shader_crate}...\n"
125+
)?;
126+
127+
let watch = self.builder.watch(on_compilation_finishes)?;
128+
Ok(watch)
129+
}
130+
}
131+
81132
/// An error indicating what went wrong when creating a [`ShaderCrateBuilder`].
82133
#[derive(Debug, thiserror::Error)]
83134
#[non_exhaustive]
84-
pub enum MakeShaderBuilderError<E> {
135+
pub enum MakeShaderCrateBuilderError<E = CommandExecError> {
85136
/// The given shader crate path is not valid.
86137
#[error("shader crate path is not valid: {0}")]
87138
InvalidCratePath(#[from] io::Error),
@@ -92,3 +143,15 @@ pub enum MakeShaderBuilderError<E> {
92143
#[error(transparent)]
93144
LockfileMismatch(#[from] LockfileMismatchError),
94145
}
146+
147+
/// An error indicating what went wrong when building the shader crate.
148+
#[derive(Debug, thiserror::Error)]
149+
#[non_exhaustive]
150+
pub enum ShaderCrateBuildError {
151+
/// Failed to write user output.
152+
#[error("failed to write user output: {0}")]
153+
IoWrite(#[from] io::Error),
154+
/// Failed to build shader crate.
155+
#[error("failed to build shader crate: {0}")]
156+
Build(#[from] SpirvBuilderError),
157+
}

crates/cargo-gpu-build/src/lib.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,60 @@
1414
1515
#![expect(clippy::pub_use, reason = "pub use for build scripts")]
1616

17+
use std::{io, path::Path};
18+
19+
use self::{
20+
build::{make_shader_crate_builder, MakeShaderCrateBuilderError, ShaderCrateBuildError},
21+
spirv_builder::{CompileResult, SpirvBuilder},
22+
spirv_cache::{install::InstallParams, toolchain::HaltToolchainInstallation},
23+
};
24+
1725
pub use rustc_codegen_spirv_cache as spirv_cache;
1826
pub use rustc_codegen_spirv_cache::spirv_builder;
1927

2028
pub mod build;
2129
pub mod lockfile;
30+
31+
/// Builds a `rust-gpu` shader crate, allowing to modify install and build parameters separately.
32+
///
33+
/// This function is intended to be used from a build script.
34+
///
35+
/// # Errors
36+
///
37+
/// Returns an error if the codegen backend for the shader crate could not be installed
38+
/// or this shader crate could not be built.
39+
#[inline]
40+
pub fn build_shader_crate<P, S>(
41+
shader_crate: P,
42+
target: S,
43+
build: SpirvBuilder,
44+
install: InstallParams,
45+
) -> Result<CompileResult, BuildScriptError>
46+
where
47+
P: AsRef<Path>,
48+
S: Into<String>,
49+
{
50+
let mut writer = io::stdout();
51+
let shader_crate_builder = make_shader_crate_builder(
52+
shader_crate,
53+
target,
54+
build,
55+
install,
56+
&mut writer,
57+
HaltToolchainInstallation::fallthrough(),
58+
)?;
59+
let result = shader_crate_builder.build(&mut writer)?;
60+
Ok(result)
61+
}
62+
63+
/// An error indicating that building a shader crate from a build script failed.
64+
#[derive(Debug, thiserror::Error)]
65+
#[non_exhaustive]
66+
pub enum BuildScriptError {
67+
/// Failed to create a shader crate builder.
68+
#[error(transparent)]
69+
MakeBuilder(#[from] MakeShaderCrateBuilderError),
70+
/// Failed to build the shader crate.
71+
#[error(transparent)]
72+
Build(#[from] ShaderCrateBuildError),
73+
}

crates/cargo-gpu/Cargo.toml

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

1212
[dependencies]
13-
cargo-gpu-build.workspace = true
14-
rustc_codegen_spirv-cache = { workspace = true, features = ["clap"] }
15-
spirv-builder = { workspace = true, features = ["clap", "watch"] }
13+
cargo-gpu-build = { workspace = true, features = ["clap", "watch"] }
1614
cargo_metadata.workspace = true
1715
anyhow.workspace = true
1816
thiserror.workspace = true

crates/cargo-gpu/src/build.rs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99

1010
use anyhow::Context as _;
1111
use cargo_gpu_build::{
12-
build::shader_crate_builder,
12+
build::{make_shader_crate_builder, ShaderCrateBuilder},
1313
spirv_builder::{CompileResult, ModuleResult, SpirvBuilder, SpirvBuilderError, Watch},
1414
spirv_cache::install::Install,
1515
};
@@ -72,7 +72,7 @@ impl Build {
7272
/// Returns an error if the build process fails somehow.
7373
#[inline]
7474
pub fn run(&mut self) -> anyhow::Result<()> {
75-
let crate_builder = shader_crate_builder(
75+
let crate_builder = make_shader_crate_builder(
7676
self.install.shader_crate.clone(),
7777
self.build
7878
.spirv_builder
@@ -84,8 +84,8 @@ impl Build {
8484
io::stdout(),
8585
ask_for_user_consent(self.install.params.auto_install_rust_toolchain),
8686
)?;
87-
self.install = crate_builder.installed_backend_args;
88-
self.build.spirv_builder = crate_builder.builder;
87+
self.install = crate_builder.installed_backend_args.clone();
88+
self.build.spirv_builder = crate_builder.builder.clone();
8989

9090
// Ensure the shader output dir exists
9191
log::debug!(
@@ -98,35 +98,30 @@ impl Build {
9898
self.build.output_dir = canonicalized;
9999

100100
if self.build.watch {
101-
let never = self.watch()?;
101+
let never = self.watch(crate_builder)?;
102102
match never {}
103103
}
104-
self.build()
104+
self.build(crate_builder)
105105
}
106106

107-
/// Builds shader crate using [`SpirvBuilder`].
108-
fn build(&self) -> anyhow::Result<()> {
109-
crate::user_output!(
110-
"Compiling shaders at {}...\n",
111-
self.install.shader_crate.display()
112-
)?;
113-
let result = self.build.spirv_builder.build()?;
107+
/// Builds shader crate using [`ShaderCrateBuilder`].
108+
#[expect(clippy::needless_pass_by_value, reason = "move of responsibility")]
109+
fn build(&self, crate_builder: ShaderCrateBuilder) -> anyhow::Result<()> {
110+
let result = crate_builder.build(io::stdout())?;
114111
self.parse_compilation_result(&result)?;
115112
Ok(())
116113
}
117114

118-
/// Watches shader crate for changes using [`SpirvBuilder`].
119-
fn watch(&self) -> anyhow::Result<Infallible> {
115+
/// Watches shader crate for changes using [`ShaderCrateBuilder`].
116+
#[expect(clippy::needless_pass_by_value, reason = "move of responsibility")]
117+
fn watch(&self, crate_builder: ShaderCrateBuilder) -> anyhow::Result<Infallible> {
120118
let this = self.clone();
121-
let watch = self
122-
.build
123-
.spirv_builder
124-
.watch(move |result, maybe_accept| {
125-
let parse_result = this.parse_compilation_result(&result);
126-
if let Some(accept) = maybe_accept {
127-
accept.submit(parse_result);
128-
}
129-
})?;
119+
let watch = crate_builder.watch(io::stdout(), move |result, maybe_accept| {
120+
let parse_result = this.parse_compilation_result(&result);
121+
if let Some(accept) = maybe_accept {
122+
accept.submit(parse_result);
123+
}
124+
})?;
130125

131126
let Watch {
132127
first_compile,

crates/cargo-gpu/src/config.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ impl Config {
8787
mod test {
8888
use std::io::Write as _;
8989

90+
use cargo_gpu_build::spirv_builder::Capability;
9091
use cargo_gpu_test_utils::{overwrite_shader_cargo_toml, shader_crate_test_path};
9192

9293
use super::*;
@@ -191,10 +192,7 @@ mod test {
191192
let args = Config::clap_command_with_cargo_config(&shader_crate_path, vec![]).unwrap();
192193
assert_eq!(
193194
args.build.spirv_builder.capabilities,
194-
vec![
195-
spirv_builder::Capability::AtomicStorage,
196-
spirv_builder::Capability::Matrix
197-
]
195+
vec![Capability::AtomicStorage, Capability::Matrix]
198196
);
199197
}
200198

crates/cargo-gpu/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
//!
1616
//! ## Where the binaries are
1717
//!
18-
//! Prebuilt binaries are stored in the [cache directory](rustc_codegen_spirv_cache::cache::cache_dir()),
18+
//! Prebuilt binaries are stored in the [cache directory](cargo_gpu_build::spirv_cache::cache::cache_dir()),
1919
//! which path differs by OS you are using.
2020
2121
#![expect(clippy::pub_use, reason = "part of public interface")]
2222

23-
pub use rustc_codegen_spirv_cache::install::Install;
23+
pub use cargo_gpu_build::spirv_cache::install::Install;
2424

2525
use self::{
2626
build::Build, dump_usage::dump_full_usage_for_readme, show::Show,
@@ -39,7 +39,7 @@ mod user_consent;
3939
/// Central function to write to the user.
4040
#[macro_export]
4141
macro_rules! user_output {
42-
($($args: tt)*) => { ::rustc_codegen_spirv_cache::user_output!(::std::io::stdout(), $($args)*) };
42+
($($args: tt)*) => { cargo_gpu_build::spirv_cache::user_output!(::std::io::stdout(), $($args)*) };
4343
}
4444

4545
/// All of the available subcommands for `cargo gpu`

0 commit comments

Comments
 (0)