diff --git a/crates/spirv-builder/src/lib.rs b/crates/spirv-builder/src/lib.rs index cd19f00862e..345539c039d 100644 --- a/crates/spirv-builder/src/lib.rs +++ b/crates/spirv-builder/src/lib.rs @@ -883,6 +883,29 @@ fn join_checking_for_separators(strings: Vec>, sep: &str) -> St strings.join(sep) } +impl SpirvBuilder { + /// The absolute target dir: [`Self::target_dir_path`] (default `spirv-builder`), relative to + /// the shader crate's cargo target dir unless already absolute. + /// + /// Relative paths need `cargo metadata`, which is slow, so the watcher resolves this only once. + fn resolve_target_dir(&self, path_to_crate: &Path) -> Result { + let target_dir_path = self + .target_dir_path + .clone() + .unwrap_or_else(|| PathBuf::from("spirv-builder")); + if target_dir_path.is_absolute() { + return Ok(target_dir_path); + } + let metadata = cargo_metadata::MetadataCommand::new() + .current_dir(path_to_crate) + .exec()?; + Ok(metadata + .target_directory + .into_std_path_buf() + .join(target_dir_path)) + } +} + pub struct RustcOutput { pub compile_result: CompileResult, pub deps: Vec, @@ -1048,21 +1071,7 @@ impl SpirvBuilder { rustflags.extend(extra_rustflags.split_whitespace().map(|s| s.to_string())); } - let target_dir_path = self - .target_dir_path - .clone() - .unwrap_or_else(|| PathBuf::from("spirv-builder")); - let target_dir = if target_dir_path.is_absolute() { - target_dir_path - } else { - let metadata = cargo_metadata::MetadataCommand::new() - .current_dir(path_to_crate) - .exec()?; - metadata - .target_directory - .into_std_path_buf() - .join(target_dir_path) - }; + let target_dir = self.resolve_target_dir(path_to_crate)?; let mut cargo = cargo_cmd::CargoCmd::new(); if let Some(toolchain) = &self.toolchain_overwrite { diff --git a/crates/spirv-builder/src/tests.rs b/crates/spirv-builder/src/tests.rs index 6b37072be07..9a523da7534 100644 --- a/crates/spirv-builder/src/tests.rs +++ b/crates/spirv-builder/src/tests.rs @@ -102,3 +102,33 @@ mod dylib_lookup { ); } } + +mod target_dir { + use crate::SpirvBuilder; + use std::path::{Path, PathBuf}; + + fn sky_shader() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("../../examples/shaders/sky-shader") + } + + #[test] + fn absolute_target_dir_is_kept() { + let abs = std::env::temp_dir().join("my-spirv-target"); + let mut builder = SpirvBuilder::new(sky_shader(), "spirv-unknown-vulkan1.2"); + builder.target_dir_path = Some(abs.clone()); + // a crate path without a `Cargo.toml`: `cargo metadata` would fail if it ran + let not_a_crate = std::env::temp_dir(); + assert_eq!(builder.resolve_target_dir(¬_a_crate).unwrap(), abs); + } + + #[test] + fn relative_target_dir_is_inside_cargo_target_dir() { + let builder = SpirvBuilder::new(sky_shader(), "spirv-unknown-vulkan1.2"); + let resolved = builder.resolve_target_dir(&sky_shader()).unwrap(); + assert!(resolved.is_absolute(), "{resolved:?}"); + assert!( + resolved.ends_with("spirv-builder") && resolved != Path::new("spirv-builder"), + "{resolved:?}" + ); + } +} diff --git a/crates/spirv-builder/src/watch.rs b/crates/spirv-builder/src/watch.rs index 47914828fd4..03a8555d8ac 100644 --- a/crates/spirv-builder/src/watch.rs +++ b/crates/spirv-builder/src/watch.rs @@ -120,6 +120,7 @@ impl SpirvWatcher { Err(TryRecvError::Disconnected) => return Err(SpirvWatcherError::WatcherDied.into()), } + self.pin_target_dir(); let result = (|| { let out = self.builder.invoke_rustc()?; self.watch_leaf_deps(out.deps.iter().map(|d| d.as_ref())); @@ -151,6 +152,20 @@ impl SpirvWatcher { } } + /// Resolves a relative target dir to an absolute one once, so rebuilds don't each have to + /// run `cargo metadata`. Failures are left for [`SpirvBuilder::invoke_rustc`] to report, and + /// resolving is retried on the next build. + fn pin_target_dir(&mut self) { + let is_absolute = (self.builder.target_dir_path.as_ref()).is_some_and(|p| p.is_absolute()); + if is_absolute || !self.path_to_crate.is_dir() { + return; + } + match self.builder.resolve_target_dir(&self.path_to_crate) { + Ok(target_dir) => self.builder.target_dir_path = Some(target_dir), + Err(err) => log::debug!("couldn't resolve target dir yet: {err}"), + } + } + fn watch_leaf_deps<'a>(&mut self, deps: impl Iterator) { for dep in deps { let path = dep.to_path().unwrap();