Skip to content

Commit 0fc302e

Browse files
committed
Make build script API
1 parent 09b6857 commit 0fc302e

File tree

9 files changed

+337
-122
lines changed

9 files changed

+337
-122
lines changed

Cargo.lock

Lines changed: 3 additions & 3 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 = "929112a325335c3acd520ceca10e54596c3be93e", default-features = false }
25+
spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu", rev = "e5e56df456ee44f9864076fea19529206ea7b29b", 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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ license.workspace = true
1010
[dependencies]
1111
rustc_codegen_spirv-cache = { path = "../rustc_codegen_spirv-cache" }
1212
spirv-builder.workspace = true
13+
dunce.workspace = true
1314
thiserror.workspace = true
1415
semver.workspace = true
1516
log.workspace = true
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//! This module provides a `rust-gpu` shader crate builder
2+
//! usable inside of build scripts or as a part of CLI.
3+
4+
use std::{io, path::Path};
5+
6+
use crate::{
7+
lockfile::{LockfileMismatchError, LockfileMismatchHandler},
8+
spirv_builder::{CompileResult, SpirvBuilder, SpirvBuilderError},
9+
spirv_cache::{
10+
backend::{Install, InstallError, InstallParams, InstalledBackend},
11+
command::CommandExecError,
12+
toolchain::HaltToolchainInstallation,
13+
user_output,
14+
},
15+
};
16+
17+
#[cfg(feature = "watch")]
18+
use crate::spirv_builder::SpirvWatcher;
19+
20+
/// Creates shader crate builder, allowing to modify install and build parameters separately.
21+
///
22+
/// # Errors
23+
///
24+
/// Returns an error if:
25+
///
26+
/// * the shader crate path is not valid,
27+
/// * the backend installation fails,
28+
/// * there is a lockfile version mismatch that cannot be resolved automatically.
29+
#[inline]
30+
#[expect(clippy::unreachable, reason = "target was already set")]
31+
pub fn make_shader_crate_builder<P, S, E, T, C, W>(
32+
shader_crate: P,
33+
target: S,
34+
mut build: SpirvBuilder,
35+
install: InstallParams,
36+
writer: W,
37+
halt_toolchain_installation: HaltToolchainInstallation<T, C>,
38+
) -> Result<ShaderCrateBuilder, MakeShaderCrateBuilderError<E>>
39+
where
40+
P: AsRef<Path>,
41+
S: Into<String>,
42+
W: io::Write,
43+
E: From<CommandExecError>,
44+
T: FnOnce(&str) -> Result<(), E>,
45+
C: FnOnce(&str) -> Result<(), E>,
46+
{
47+
let shader_crate_path = dunce::canonicalize(shader_crate)?;
48+
let backend_args = Install::new(shader_crate_path, install);
49+
let backend = backend_args.run(writer, halt_toolchain_installation)?;
50+
51+
let lockfile_mismatch_handler = LockfileMismatchHandler::new(
52+
&backend_args.shader_crate,
53+
&backend.toolchain_channel,
54+
backend_args.params.force_overwrite_lockfiles_v4_to_v3,
55+
)?;
56+
57+
build.path_to_crate = Some(backend_args.shader_crate.clone());
58+
build.target = Some(target.into());
59+
backend
60+
.configure_spirv_builder(&mut build)
61+
.unwrap_or_else(|_| unreachable!("we set target before calling this function"));
62+
63+
Ok(ShaderCrateBuilder {
64+
builder: build,
65+
installed_backend_args: backend_args,
66+
installed_backend: backend,
67+
lockfile_mismatch_handler,
68+
})
69+
}
70+
71+
/// A builder for compiling a `rust-gpu` shader crate.
72+
#[derive(Debug, Clone)]
73+
#[non_exhaustive]
74+
pub struct ShaderCrateBuilder {
75+
/// The underlying builder for compiling the shader crate.
76+
pub builder: SpirvBuilder,
77+
/// The arguments used to install the backend.
78+
pub installed_backend_args: Install,
79+
/// The installed backend.
80+
pub installed_backend: InstalledBackend,
81+
/// The lockfile mismatch handler.
82+
pub lockfile_mismatch_handler: LockfileMismatchHandler,
83+
}
84+
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>(&self, writer: W) -> Result<SpirvWatcher<&SpirvBuilder>, ShaderCrateBuildError>
113+
where
114+
W: io::Write,
115+
{
116+
let shader_crate = self.installed_backend_args.shader_crate.display();
117+
user_output!(
118+
writer,
119+
"Watching shaders for changes at {shader_crate}...\n"
120+
)?;
121+
122+
let watcher = self.builder.watch()?;
123+
Ok(watcher)
124+
}
125+
}
126+
127+
/// An error indicating what went wrong when creating a [`ShaderCrateBuilder`].
128+
#[derive(Debug, thiserror::Error)]
129+
#[non_exhaustive]
130+
pub enum MakeShaderCrateBuilderError<E = CommandExecError> {
131+
/// The given shader crate path is not valid.
132+
#[error("shader crate path is not valid: {0}")]
133+
InvalidCratePath(#[from] io::Error),
134+
/// The backend installation failed.
135+
#[error("could not install backend: {0}")]
136+
Install(#[from] InstallError<E>),
137+
/// There is a lockfile version mismatch that cannot be resolved automatically.
138+
#[error(transparent)]
139+
LockfileMismatch(#[from] LockfileMismatchError),
140+
}
141+
142+
/// An error indicating what went wrong when building the shader crate.
143+
#[derive(Debug, thiserror::Error)]
144+
#[non_exhaustive]
145+
pub enum ShaderCrateBuildError {
146+
/// Failed to write user output.
147+
#[error("failed to write user output: {0}")]
148+
IoWrite(#[from] io::Error),
149+
/// Failed to build shader crate.
150+
#[error("failed to build shader crate: {0}")]
151+
Build(#[from] SpirvBuilderError),
152+
}

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

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,62 @@
1212
//! to pass the many additional parameters required to configure rustc and our codegen backend,
1313
//! but provide you with a toolchain-agnostic version that you may use from stable rustc.
1414
15-
#![expect(clippy::pub_use, reason = "part of public API")]
15+
#![expect(clippy::pub_use, reason = "pub use for build scripts")]
16+
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::{backend::InstallParams, toolchain::HaltToolchainInstallation},
23+
};
1624

1725
pub use rustc_codegen_spirv_cache as spirv_cache;
1826
pub use rustc_codegen_spirv_cache::spirv_builder;
1927

28+
pub mod build;
2029
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::noop(),
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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,17 @@ default-run = "cargo-gpu"
1111

1212
[dependencies]
1313
cargo-gpu-build = { path = "../cargo-gpu-build", features = ["clap", "watch"] }
14+
spirv-builder = { workspace = true, features = ["clap", "watch"] }
1415
cargo_metadata.workspace = true
1516
anyhow.workspace = true
1617
thiserror.workspace = true
17-
spirv-builder = { workspace = true, features = ["clap", "watch"] }
1818
clap.workspace = true
1919
env_logger.workspace = true
2020
log.workspace = true
2121
relative-path.workspace = true
2222
serde.workspace = true
2323
serde_json.workspace = true
2424
crossterm.workspace = true
25-
semver.workspace = true
2625
dunce.workspace = true
2726

2827
[dev-dependencies]

0 commit comments

Comments
 (0)