Skip to content

Commit 56a46c3

Browse files
committed
build_script: replace enum MetadataPrintout with struct BuildScriptConfig for more flexibility
1 parent 5719b27 commit 56a46c3

File tree

8 files changed

+97
-81
lines changed

8 files changed

+97
-81
lines changed

crates/spirv-builder/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ It takes care of pulling in the `SPIR-V` backend for Rust, `rustc_codegen_spirv`
1111
## Example
1212

1313
```rust,no_run
14-
use spirv_builder::{MetadataPrintout, SpirvBuilder};
15-
14+
# use spirv_builder::SpirvBuilder;
15+
#
1616
fn main() -> Result<(), Box<dyn std::error::Error>> {
17-
SpirvBuilder::new("my_shaders", "spirv-unknown-vulkan1.1")
18-
.print_metadata(MetadataPrintout::Full)
19-
.build()?;
17+
let mut builder = SpirvBuilder::new("my_shaders", "spirv-unknown-vulkan1.3");
18+
builder.build_script.defaults = true;
19+
builder.build_script.env_shader_spv_path = Some(true);
20+
builder.build()?;
2021
Ok(())
2122
}
2223
```

crates/spirv-builder/src/lib.rs

Lines changed: 73 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,10 @@ pub enum SpirvBuilderError {
123123
MissingTargetSpec,
124124
#[error("build failed")]
125125
BuildFailed,
126-
#[error("multi-module build cannot be used with print_metadata = MetadataPrintout::Full")]
127-
MultiModuleWithPrintMetadata,
126+
#[error(
127+
"`multimodule: true` build cannot be used together with `build_script.env_shader_spv_path: true`"
128+
)]
129+
MultiModuleWithEnvShaderSpvPath,
128130
#[error("multi-module metadata file missing")]
129131
MetadataFileMissing(#[from] std::io::Error),
130132
#[error("unable to parse multi-module metadata file")]
@@ -138,21 +140,6 @@ pub enum SpirvBuilderError {
138140

139141
const SPIRV_TARGET_PREFIX: &str = "spirv-unknown-";
140142

141-
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, serde::Deserialize, serde::Serialize)]
142-
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
143-
#[non_exhaustive]
144-
pub enum MetadataPrintout {
145-
/// Print no cargo metadata.
146-
#[default]
147-
None,
148-
/// Print only dependency information (eg for multiple modules).
149-
DependencyOnly,
150-
/// Print all cargo metadata.
151-
///
152-
/// Includes dependency information and spirv environment variable.
153-
Full,
154-
}
155-
156143
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, serde::Deserialize, serde::Serialize)]
157144
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
158145
#[non_exhaustive]
@@ -396,6 +383,50 @@ impl Default for ShaderCrateFeatures {
396383
}
397384
}
398385

386+
/// Configuration for build scripts
387+
#[derive(Clone, Debug, Default)]
388+
#[non_exhaustive]
389+
pub struct BuildScriptConfig {
390+
/// Enable this if you are using `spirv-builder` from a build script to apply some recommended default options, such
391+
/// as [`Self::dependency_info`].
392+
pub defaults: bool,
393+
394+
/// Print dependency information for cargo build scripts (with `cargo::rerun-if-changed={}` and such).
395+
/// Dependency information makes cargo rerun the build script is rerun when shader source files change, thus
396+
/// rebuilding the shader.
397+
///
398+
/// Default: [`Self::defaults`]
399+
pub dependency_info: Option<bool>,
400+
401+
/// Whether to emit an env var pointing to the shader module file (via `cargo::rustc-env={}`). The name of the env
402+
/// var is the crate name with `.spv` appended, e.g. `sky_shader.spv`.
403+
/// Not supported together with `multimodule=true` or `.watch()`.
404+
///
405+
/// Some examples on how to include the shader module in the source code:
406+
/// * wgpu:
407+
/// ```rust,ignore
408+
/// let shader: ShaderModuleDescriptorPassthrough = include_spirv_raw!(env!("my_shader.spv"));
409+
/// ```
410+
/// * ash
411+
/// ```rust,ignore
412+
/// let bytes: &[u8] = include_bytes!(env!("my_shader.spv"))
413+
/// let words = ash::util::read_spv(&mut std::io::Cursor::new(bytes)).unwrap();
414+
/// ```
415+
///
416+
/// Default: `false`
417+
pub env_shader_spv_path: Option<bool>,
418+
}
419+
420+
/// these all have the prefix `get` so the doc items link to the members, not these private fns
421+
impl BuildScriptConfig {
422+
fn get_dependency_info(&self) -> bool {
423+
self.dependency_info.unwrap_or(self.defaults)
424+
}
425+
fn get_env_shader_spv_path(&self) -> bool {
426+
self.env_shader_spv_path.unwrap_or(false)
427+
}
428+
}
429+
399430
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
400431
#[cfg_attr(feature = "clap", derive(clap::Parser))]
401432
#[non_exhaustive]
@@ -410,10 +441,10 @@ pub struct SpirvBuilder {
410441
/// `--crate-type dylib`. Defaults to true if `cargo_cmd` is `None` or `Some("rustc")`.
411442
#[cfg_attr(feature = "clap", clap(skip))]
412443
pub cargo_cmd_like_rustc: Option<bool>,
413-
/// Whether to print build.rs cargo metadata (e.g. cargo:rustc-env=var=val). Defaults to [`MetadataPrintout::None`].
414-
/// Within build scripts, set it to [`MetadataPrintout::DependencyOnly`] or [`MetadataPrintout::Full`] to ensure the build script is rerun on code changes.
444+
/// Configuration for build scripts
415445
#[cfg_attr(feature = "clap", clap(skip))]
416-
pub print_metadata: MetadataPrintout,
446+
#[serde(skip)]
447+
pub build_script: BuildScriptConfig,
417448
/// Build in release. Defaults to true.
418449
#[cfg_attr(feature = "clap", clap(long = "debug", default_value = "true", action = clap::ArgAction::SetFalse))]
419450
pub release: bool,
@@ -507,7 +538,7 @@ impl Default for SpirvBuilder {
507538
path_to_crate: None,
508539
cargo_cmd: None,
509540
cargo_cmd_like_rustc: None,
510-
print_metadata: MetadataPrintout::default(),
541+
build_script: BuildScriptConfig::default(),
511542
release: true,
512543
target: None,
513544
deny_warnings: false,
@@ -548,13 +579,6 @@ impl SpirvBuilder {
548579
self
549580
}
550581

551-
/// Whether to print build.rs cargo metadata (e.g. cargo:rustc-env=var=val). Defaults to [`MetadataPrintout::Full`].
552-
#[must_use]
553-
pub fn print_metadata(mut self, v: MetadataPrintout) -> Self {
554-
self.print_metadata = v;
555-
self
556-
}
557-
558582
#[must_use]
559583
pub fn deny_warnings(mut self, v: bool) -> Self {
560584
self.deny_warnings = v;
@@ -700,19 +724,15 @@ impl SpirvBuilder {
700724
self
701725
}
702726

703-
/// Builds the module. If `print_metadata` is [`MetadataPrintout::Full`], you usually don't have to inspect the path
704-
/// in the result, as the environment variable for the path to the module will already be set.
727+
/// Builds the module
705728
pub fn build(&self) -> Result<CompileResult, SpirvBuilderError> {
706729
let metadata_file = invoke_rustc(self)?;
707-
match self.print_metadata {
708-
MetadataPrintout::Full | MetadataPrintout::DependencyOnly => {
709-
leaf_deps(&metadata_file, |artifact| {
710-
println!("cargo:rerun-if-changed={artifact}");
711-
})
712-
// Close enough
713-
.map_err(SpirvBuilderError::MetadataFileMissing)?;
714-
}
715-
MetadataPrintout::None => (),
730+
if self.build_script.get_dependency_info() {
731+
leaf_deps(&metadata_file, |artifact| {
732+
println!("cargo:rerun-if-changed={artifact}");
733+
})
734+
// Close enough
735+
.map_err(SpirvBuilderError::MetadataFileMissing)?;
716736
}
717737
let metadata = self.parse_metadata_file(&metadata_file)?;
718738

@@ -731,17 +751,17 @@ impl SpirvBuilder {
731751
match &metadata.module {
732752
ModuleResult::SingleModule(spirv_module) => {
733753
assert!(!self.multimodule);
734-
let env_var = format!(
735-
"{}.spv",
736-
at.file_name()
737-
.unwrap()
738-
.to_str()
739-
.unwrap()
740-
.strip_suffix(ARTIFACT_SUFFIX)
741-
.unwrap()
742-
);
743-
if self.print_metadata == MetadataPrintout::Full {
744-
println!("cargo:rustc-env={}={}", env_var, spirv_module.display());
754+
if self.build_script.get_env_shader_spv_path() {
755+
let env_var = format!(
756+
"{}.spv",
757+
at.file_name()
758+
.unwrap()
759+
.to_str()
760+
.unwrap()
761+
.strip_suffix(ARTIFACT_SUFFIX)
762+
.unwrap()
763+
);
764+
println!("cargo::rustc-env={}={}", env_var, spirv_module.display());
745765
}
746766
}
747767
ModuleResult::MultiModule(_) => {
@@ -839,8 +859,8 @@ fn invoke_rustc(builder: &SpirvBuilder) -> Result<PathBuf, SpirvBuilderError> {
839859
}
840860
}
841861

842-
if (builder.print_metadata == MetadataPrintout::Full) && builder.multimodule {
843-
return Err(SpirvBuilderError::MultiModuleWithPrintMetadata);
862+
if builder.build_script.get_env_shader_spv_path() && builder.multimodule {
863+
return Err(SpirvBuilderError::MultiModuleWithEnvShaderSpvPath);
844864
}
845865
if !path_to_crate.is_dir() {
846866
return Err(SpirvBuilderError::CratePathDoesntExist(
@@ -905,7 +925,7 @@ fn invoke_rustc(builder: &SpirvBuilder) -> Result<PathBuf, SpirvBuilderError> {
905925

906926
// Wrapper for `env::var` that appropriately informs Cargo of the dependency.
907927
let tracked_env_var_get = |name| {
908-
if let MetadataPrintout::Full | MetadataPrintout::DependencyOnly = builder.print_metadata {
928+
if builder.build_script.get_dependency_info() {
909929
println!("cargo:rerun-if-env-changed={name}");
910930
}
911931
env::var(name)

crates/spirv-builder/src/watch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl SpirvWatcher {
5555
.as_ref()
5656
.ok_or(SpirvBuilderError::MissingCratePath)?
5757
.clone();
58-
if !matches!(builder.print_metadata, crate::MetadataPrintout::None) {
58+
if builder.build_script.get_env_shader_spv_path() {
5959
return Err(SpirvWatcherError::WatchWithPrintMetadata.into());
6060
}
6161

examples/multibuilder/src/main.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
use spirv_builder::{MetadataPrintout, SpirvBuilder};
1+
use spirv_builder::SpirvBuilder;
22

33
fn main() {
4-
let result = SpirvBuilder::new(
4+
let mut builder = SpirvBuilder::new(
55
concat!(env!("CARGO_MANIFEST_DIR"), "/../shaders/sky-shader"),
66
"spirv-unknown-spv1.3",
7-
)
8-
.print_metadata(MetadataPrintout::DependencyOnly)
9-
.multimodule(true)
10-
.build()
11-
.unwrap();
7+
);
8+
builder.multimodule = true;
9+
let result = builder.build().unwrap();
1210
println!("{result:#?}");
1311
}

examples/runners/ash/src/main.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ use ash::util::read_spv;
7878
use clap::{Parser, ValueEnum};
7979
use raw_window_handle::HasDisplayHandle as _;
8080
use shared::ShaderConstants;
81-
use spirv_builder::{MetadataPrintout, SpirvBuilder};
81+
use spirv_builder::SpirvBuilder;
8282
use std::{
8383
fs::File,
8484
path::PathBuf,
@@ -259,7 +259,6 @@ pub fn compile_shaders(shader: &RustGPUShader) -> anyhow::Result<Vec<u32>> {
259259
.collect::<PathBuf>();
260260

261261
let compile_result = SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.3")
262-
.print_metadata(MetadataPrintout::None)
263262
.shader_panic_strategy(spirv_builder::ShaderPanicStrategy::DebugPrintfThenExit {
264263
print_inputs: true,
265264
print_backtrace: true,

examples/runners/wgpu/builder/src/main.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
use spirv_builder::{MetadataPrintout, SpirvBuilder};
1+
use spirv_builder::SpirvBuilder;
22
use std::env;
33
use std::error::Error;
44
use std::fs;
5-
use std::path::Path;
5+
use std::path::{Path, PathBuf};
66

77
fn build_shader(path_to_crate: &str, codegen_names: bool) -> Result<(), Box<dyn Error>> {
8-
let builder_dir = &Path::new(env!("CARGO_MANIFEST_DIR"));
9-
let path_to_crate = builder_dir.join(path_to_crate);
10-
let result = SpirvBuilder::new(path_to_crate, "spirv-unknown-vulkan1.1")
11-
.print_metadata(MetadataPrintout::Full)
12-
// Give this spirv-builder a unique target dir, so that rebuilding android and the main wgpu app's target dir
13-
// don't clash and break each other's incremental
14-
.target_dir_path("example-runner-wgpu-builder")
15-
.build()?;
8+
let path_to_crate = Path::new(env!("CARGO_MANIFEST_DIR")).join(path_to_crate);
9+
let mut builder = SpirvBuilder::new(path_to_crate, "spirv-unknown-vulkan1.1");
10+
builder.build_script.defaults = true;
11+
builder.build_script.env_shader_spv_path = Some(true);
12+
// Give this spirv-builder a unique target dir, so that rebuilding android and the main wgpu app's target dir
13+
// don't clash and break each other's incremental
14+
builder.target_dir_path = Some(PathBuf::from("example-runner-wgpu-builder"));
15+
let result = builder.build()?;
1616
if codegen_names {
1717
let out_dir = env::var_os("OUT_DIR").unwrap();
1818
let dest_path = Path::new(&out_dir).join("entry_points.rs");

examples/runners/wgpu/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ fn maybe_watch(
124124
) -> CompiledShaderModules {
125125
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
126126
{
127-
use spirv_builder::{CompileResult, MetadataPrintout, SpirvBuilder};
127+
use spirv_builder::{CompileResult, SpirvBuilder};
128128
use std::path::PathBuf;
129129

130130
let crate_name = match options.shader {
@@ -142,7 +142,6 @@ fn maybe_watch(
142142
let has_debug_printf = options.force_spirv_passthru;
143143

144144
let builder = SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.1")
145-
.print_metadata(MetadataPrintout::None)
146145
.shader_panic_strategy(if has_debug_printf {
147146
spirv_builder::ShaderPanicStrategy::DebugPrintfThenExit {
148147
print_inputs: true,

tests/difftests/lib/src/scaffold/shader/rust_gpu_shader.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ impl RustComputeShader {
3838
impl SpirvShader for RustComputeShader {
3939
fn spirv_bytes(&self) -> anyhow::Result<(Vec<u8>, String)> {
4040
let mut builder = SpirvBuilder::new(&self.path, &self.target)
41-
.print_metadata(spirv_builder::MetadataPrintout::None)
4241
.release(true)
4342
.multimodule(false)
4443
.shader_panic_strategy(spirv_builder::ShaderPanicStrategy::SilentExit)

0 commit comments

Comments
 (0)