From cdd82bab95b3eb6a8b4e0d0d543b4f64aae986bf Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 30 Oct 2025 08:48:12 +1300 Subject: [PATCH 1/6] added logging --- crates/cargo-gpu/src/metadata.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/cargo-gpu/src/metadata.rs b/crates/cargo-gpu/src/metadata.rs index 0b330fb..6daf190 100644 --- a/crates/cargo-gpu/src/metadata.rs +++ b/crates/cargo-gpu/src/metadata.rs @@ -17,6 +17,7 @@ impl Metadata { /// from the workspace `Cargo.toml`, then on top of those we merge any config from the shader /// crate's `Cargo.toml`. pub fn as_json(path: &std::path::PathBuf) -> anyhow::Result { + log::debug!("reading package metadata from {path:?}"); let cargo_json = Self::get_cargo_toml_as_json(path)?; let config = Self::merge_configs(&cargo_json, path)?; Ok(config) @@ -27,6 +28,7 @@ impl Metadata { cargo_json: &cargo_metadata::Metadata, path: &std::path::Path, ) -> anyhow::Result { + log::debug!("merging cargo metadata from {path:?}"); let mut metadata = crate::config::Config::defaults_as_json()?; crate::config::Config::json_merge( &mut metadata, From 0c749878dd8bdd1a6258abeda4c886f2295e385f Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 30 Oct 2025 08:50:38 +1300 Subject: [PATCH 2/6] remove -p as alias for --shader-crate --- crates/cargo-gpu/src/install.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index 2aa7bde..3b00318 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -65,7 +65,7 @@ impl InstalledBackend { #[non_exhaustive] pub struct Install { /// Directory containing the shader crate to compile. - #[clap(long, alias("package"), short_alias('p'), default_value = "./")] + #[clap(long, alias("package"), default_value = "./")] #[serde(alias = "package")] pub shader_crate: PathBuf, From 857b22a8a6b119e6ecb9a6154d04c5479592e059 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 30 Oct 2025 08:57:42 +1300 Subject: [PATCH 3/6] add --package and -p as its own clap option --- crates/cargo-gpu/src/install.rs | 12 ++++++++++-- crates/cargo-gpu/src/metadata.rs | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index 3b00318..2cf3620 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -64,9 +64,16 @@ impl InstalledBackend { #[derive(clap::Parser, Debug, Clone, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct Install { + /// Cargo target to compile. + /// + /// Conflicts with `--shader-crate`. + #[clap(short, long, conflicts_with("shader_crate"))] + pub package: Option, + /// Directory containing the shader crate to compile. - #[clap(long, alias("package"), default_value = "./")] - #[serde(alias = "package")] + /// + /// Conflicts with `--package` or `-p`. + #[clap(long, default_value = "./", conflicts_with("package"))] pub shader_crate: PathBuf, #[expect( @@ -132,6 +139,7 @@ impl Install { #[must_use] pub const fn from_shader_crate(shader_crate: PathBuf) -> Self { Self { + package: None, shader_crate, spirv_builder_source: None, spirv_builder_version: None, diff --git a/crates/cargo-gpu/src/metadata.rs b/crates/cargo-gpu/src/metadata.rs index 6daf190..cc7cf54 100644 --- a/crates/cargo-gpu/src/metadata.rs +++ b/crates/cargo-gpu/src/metadata.rs @@ -17,7 +17,7 @@ impl Metadata { /// from the workspace `Cargo.toml`, then on top of those we merge any config from the shader /// crate's `Cargo.toml`. pub fn as_json(path: &std::path::PathBuf) -> anyhow::Result { - log::debug!("reading package metadata from {path:?}"); + log::debug!("reading package metadata from {}", path.display()); let cargo_json = Self::get_cargo_toml_as_json(path)?; let config = Self::merge_configs(&cargo_json, path)?; Ok(config) @@ -28,7 +28,7 @@ impl Metadata { cargo_json: &cargo_metadata::Metadata, path: &std::path::Path, ) -> anyhow::Result { - log::debug!("merging cargo metadata from {path:?}"); + log::debug!("merging cargo metadata from {}", path.display()); let mut metadata = crate::config::Config::defaults_as_json()?; crate::config::Config::json_merge( &mut metadata, From 4a5e5eca858c9a39fd0e5c58826c8621dae64444 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 30 Oct 2025 09:19:18 +1300 Subject: [PATCH 4/6] resolve package to shader crate --- crates/cargo-gpu/src/build.rs | 5 +++++ crates/cargo-gpu/src/metadata.rs | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/crates/cargo-gpu/src/build.rs b/crates/cargo-gpu/src/build.rs index 2a7f158..20c5449 100644 --- a/crates/cargo-gpu/src/build.rs +++ b/crates/cargo-gpu/src/build.rs @@ -60,6 +60,11 @@ impl Build { pub fn run(&mut self) -> anyhow::Result<()> { let installed_backend = self.install.run()?; + if let Some(package) = self.install.package.as_ref() { + self.install.shader_crate = + crate::metadata::Metadata::resolve_package_to_shader_crate(package)?; + } + let _lockfile_mismatch_handler = LockfileMismatchHandler::new( &self.install.shader_crate, &installed_backend.toolchain_channel, diff --git a/crates/cargo-gpu/src/metadata.rs b/crates/cargo-gpu/src/metadata.rs index cc7cf54..224df72 100644 --- a/crates/cargo-gpu/src/metadata.rs +++ b/crates/cargo-gpu/src/metadata.rs @@ -1,5 +1,6 @@ //! Get config from the shader crate's `Cargo.toml` `[*.metadata.rust-gpu.*]` +use anyhow::Context as _; use cargo_metadata::MetadataCommand; use serde_json::Value; @@ -9,6 +10,28 @@ use serde_json::Value; pub struct Metadata; impl Metadata { + /// Resolve a package name to a crate directory. + pub fn resolve_package_to_shader_crate(package: &str) -> anyhow::Result { + log::debug!("resolving package '{package}' to shader crate"); + let metadata = MetadataCommand::new().exec()?; + let meta_package = metadata + .packages + .iter() + .find(|pkg| pkg.name.as_str() == package) + .context("Package not found in metadata")?; + let shader_crate_path: std::path::PathBuf = meta_package + .manifest_path + .parent() + .context("manifest is missing a parent directory")? + .to_path_buf() + .into(); + log::debug!( + " determined shader crate path to be '{}'", + shader_crate_path.display() + ); + Ok(shader_crate_path) + } + /// Convert `rust-gpu`-specific sections in `Cargo.toml` to `clap`-compatible arguments. /// The section in question is: `[package.metadata.rust-gpu.*]`. See the `shader-crate-template` /// for an example. From 927ba3ad5fb4acf3697bb310ee5c1c164434d5a8 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 6 Nov 2025 15:31:50 +1300 Subject: [PATCH 5/6] cache cargo metadata --- crates/cargo-gpu/src/build.rs | 4 +-- crates/cargo-gpu/src/config.rs | 29 ++++++++++++++--- crates/cargo-gpu/src/lib.rs | 21 +++++++++--- crates/cargo-gpu/src/main.rs | 3 +- crates/cargo-gpu/src/metadata.rs | 55 +++++++++++++++++++++++++------- 5 files changed, 88 insertions(+), 24 deletions(-) diff --git a/crates/cargo-gpu/src/build.rs b/crates/cargo-gpu/src/build.rs index 20c5449..5baf487 100644 --- a/crates/cargo-gpu/src/build.rs +++ b/crates/cargo-gpu/src/build.rs @@ -59,10 +59,10 @@ impl Build { /// Entrypoint pub fn run(&mut self) -> anyhow::Result<()> { let installed_backend = self.install.run()?; + let mut metadata = crate::metadata::MetadataCache::default(); if let Some(package) = self.install.package.as_ref() { - self.install.shader_crate = - crate::metadata::Metadata::resolve_package_to_shader_crate(package)?; + self.install.shader_crate = metadata.resolve_package_to_shader_crate(package)?; } let _lockfile_mismatch_handler = LockfileMismatchHandler::new( diff --git a/crates/cargo-gpu/src/config.rs b/crates/cargo-gpu/src/config.rs index f1a67fa..c514ba2 100644 --- a/crates/cargo-gpu/src/config.rs +++ b/crates/cargo-gpu/src/config.rs @@ -2,6 +2,8 @@ use anyhow::Context as _; use clap::Parser as _; +use crate::metadata::MetadataCache; + /// Config pub struct Config; @@ -25,8 +27,9 @@ impl Config { pub fn clap_command_with_cargo_config( shader_crate_path: &std::path::PathBuf, mut env_args: Vec, + metadata: &mut MetadataCache, ) -> anyhow::Result { - let mut config = crate::metadata::Metadata::as_json(shader_crate_path)?; + let mut config = metadata.as_json(shader_crate_path)?; env_args.retain(|arg| !(arg == "build" || arg == "install")); let cli_args_json = Self::cli_args_to_json(env_args)?; @@ -100,6 +103,7 @@ mod test { "--debug".to_owned(), "--auto-install-rust-toolchain".to_owned(), ], + &mut MetadataCache::default(), ) .unwrap(); assert!(!args.build.spirv_builder.release); @@ -122,7 +126,12 @@ mod test { ) .unwrap(); - let args = Config::clap_command_with_cargo_config(&shader_crate_path, vec![]).unwrap(); + let args = Config::clap_command_with_cargo_config( + &shader_crate_path, + vec![], + &mut MetadataCache::default(), + ) + .unwrap(); assert!(!args.build.spirv_builder.release); assert!(args.install.auto_install_rust_toolchain); } @@ -146,7 +155,12 @@ mod test { fn string_from_cargo() { let shader_crate_path = update_cargo_output_dir(); - let args = Config::clap_command_with_cargo_config(&shader_crate_path, vec![]).unwrap(); + let args = Config::clap_command_with_cargo_config( + &shader_crate_path, + vec![], + &mut MetadataCache::default(), + ) + .unwrap(); if cfg!(target_os = "windows") { assert_eq!(args.build.output_dir, std::path::Path::new("C:/the/moon")); } else { @@ -166,6 +180,7 @@ mod test { "--output-dir".to_owned(), "/the/river".to_owned(), ], + &mut MetadataCache::default(), ) .unwrap(); assert_eq!(args.build.output_dir, std::path::Path::new("/the/river")); @@ -185,7 +200,12 @@ mod test { ) .unwrap(); - let args = Config::clap_command_with_cargo_config(&shader_crate_path, vec![]).unwrap(); + let args = Config::clap_command_with_cargo_config( + &shader_crate_path, + vec![], + &mut MetadataCache::default(), + ) + .unwrap(); assert_eq!( args.build.spirv_builder.capabilities, vec![ @@ -207,6 +227,7 @@ mod test { "--manifest-file".to_owned(), "mymanifest".to_owned(), ], + &mut MetadataCache::default(), ) .unwrap(); assert_eq!(args.build.manifest_file, "mymanifest".to_owned()); diff --git a/crates/cargo-gpu/src/lib.rs b/crates/cargo-gpu/src/lib.rs index 1be7abb..4c0cb33 100644 --- a/crates/cargo-gpu/src/lib.rs +++ b/crates/cargo-gpu/src/lib.rs @@ -70,6 +70,7 @@ mod target_specs; mod test; pub use install::*; +pub use metadata::MetadataCache; pub use spirv_builder; /// Central function to write to the user. @@ -122,12 +123,19 @@ impl Command { /// # Errors /// Any errors during execution, usually printed to the user #[inline] - pub fn run(&self, env_args: Vec) -> anyhow::Result<()> { + pub fn run( + &self, + env_args: Vec, + metadata_cache: &mut metadata::MetadataCache, + ) -> anyhow::Result<()> { match &self { Self::Install(install) => { let shader_crate_path = &install.shader_crate; - let command = - config::Config::clap_command_with_cargo_config(shader_crate_path, env_args)?; + let command = config::Config::clap_command_with_cargo_config( + shader_crate_path, + env_args, + metadata_cache, + )?; log::debug!( "installing with final merged arguments: {:#?}", command.install @@ -136,8 +144,11 @@ impl Command { } Self::Build(build) => { let shader_crate_path = &build.install.shader_crate; - let mut command = - config::Config::clap_command_with_cargo_config(shader_crate_path, env_args)?; + let mut command = config::Config::clap_command_with_cargo_config( + shader_crate_path, + env_args, + metadata_cache, + )?; log::debug!("building with final merged arguments: {command:#?}"); if command.build.watch { diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu/src/main.rs index f40e3c6..449aa70 100644 --- a/crates/cargo-gpu/src/main.rs +++ b/crates/cargo-gpu/src/main.rs @@ -37,5 +37,6 @@ fn run() -> anyhow::Result<()> { .collect::>(); log::trace!("CLI args: {env_args:#?}"); let cli = Cli::parse_from(&env_args); - cli.command.run(env_args) + let mut metadata_cache = cargo_gpu::MetadataCache::default(); + cli.command.run(env_args, &mut metadata_cache) } diff --git a/crates/cargo-gpu/src/metadata.rs b/crates/cargo-gpu/src/metadata.rs index 224df72..fe67d78 100644 --- a/crates/cargo-gpu/src/metadata.rs +++ b/crates/cargo-gpu/src/metadata.rs @@ -1,19 +1,49 @@ //! Get config from the shader crate's `Cargo.toml` `[*.metadata.rust-gpu.*]` +use std::collections::HashMap; + use anyhow::Context as _; use cargo_metadata::MetadataCommand; use serde_json::Value; -/// `Metadata` refers to the `[metadata.*]` section of `Cargo.toml` that `cargo` formally +/// A cache of metadata from various `Cargo.toml` files. +/// +/// "Metadata" refers to the `[metadata.*]` section of `Cargo.toml` that `cargo` formally /// ignores so that packages can implement their own behaviour with it. -#[derive(Debug)] -pub struct Metadata; +#[derive(Debug, Default)] +pub struct MetadataCache { + /// Cached result of `MetadataCommand::new().exec()`. + inner: HashMap, +} + +impl MetadataCache { + fn get_metadata( + &mut self, + maybe_path_to_manifest_dir: Option<&std::path::Path>, + ) -> anyhow::Result<&cargo_metadata::Metadata> { + let path = if let Some(path) = maybe_path_to_manifest_dir { + path.to_path_buf() + } else { + std::env::current_dir().context("cannot determine the current working directory")? + }; + + if !self.inner.contains_key(&path) { + let metadata = MetadataCommand::new().current_dir(&path).exec()?; + self.inner.insert(path.clone(), metadata); + } + + // UNWRAP: safe because we just inserted it + Ok(self.inner.get(&path).unwrap()) + } -impl Metadata { /// Resolve a package name to a crate directory. - pub fn resolve_package_to_shader_crate(package: &str) -> anyhow::Result { + pub fn resolve_package_to_shader_crate( + &mut self, + package: &str, + ) -> anyhow::Result { log::debug!("resolving package '{package}' to shader crate"); - let metadata = MetadataCommand::new().exec()?; + let metadata = self.get_metadata(None)?; + let meta_package = metadata .packages .iter() @@ -39,9 +69,9 @@ impl Metadata { /// First we generate the CLI arg defaults as JSON. Then on top of those we merge any config /// from the workspace `Cargo.toml`, then on top of those we merge any config from the shader /// crate's `Cargo.toml`. - pub fn as_json(path: &std::path::PathBuf) -> anyhow::Result { + pub fn as_json(&mut self, path: &std::path::PathBuf) -> anyhow::Result { log::debug!("reading package metadata from {}", path.display()); - let cargo_json = Self::get_cargo_toml_as_json(path)?; + let cargo_json = self.get_cargo_toml_as_json(path)?; let config = Self::merge_configs(&cargo_json, path)?; Ok(config) } @@ -90,9 +120,10 @@ impl Metadata { /// Convert a `Cargo.toml` to JSON fn get_cargo_toml_as_json( + &mut self, path: &std::path::PathBuf, ) -> anyhow::Result { - Ok(MetadataCommand::new().current_dir(path).exec()?) + self.get_metadata(Some(path.as_path())).cloned() } /// Get any `rust-gpu` metadata set in the crate's `Cargo.toml` @@ -161,7 +192,7 @@ mod test { .exec() .unwrap(); metadata.packages.first_mut().unwrap().metadata = serde_json::json!({}); - let configs = Metadata::merge_configs(&metadata, Path::new("./")).unwrap(); + let configs = MetadataCache::merge_configs(&metadata, Path::new("./")).unwrap(); assert_eq!(configs["build"]["release"], Value::Bool(true)); assert_eq!( configs["install"]["auto_install_rust_toolchain"], @@ -185,7 +216,7 @@ mod test { } } }); - let configs = Metadata::merge_configs(&metadata, Path::new("./")).unwrap(); + let configs = MetadataCache::merge_configs(&metadata, Path::new("./")).unwrap(); assert_eq!(configs["build"]["release"], Value::Bool(false)); assert_eq!( configs["install"]["auto_install_rust_toolchain"], @@ -214,7 +245,7 @@ mod test { } } }); - let configs = Metadata::merge_configs(&metadata, Path::new(".")).unwrap(); + let configs = MetadataCache::merge_configs(&metadata, Path::new(".")).unwrap(); assert_eq!(configs["build"]["release"], Value::Bool(false)); assert_eq!( configs["install"]["auto_install_rust_toolchain"], From fe9047f263f4a7ea32649f1a31d9b5f740cee927 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Thu, 6 Nov 2025 15:49:34 +1300 Subject: [PATCH 6/6] fix or allow clippy lints --- Cargo.toml | 1 + crates/cargo-gpu/src/config.rs | 2 +- crates/cargo-gpu/src/metadata.rs | 19 ++++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 62ca2ec..a5c45be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,3 +57,4 @@ partial_pub_fields = { level = "allow", priority = 1 } pattern_type_mismatch = { level = "allow", priority = 1 } std_instead_of_alloc = { level = "allow", priority = 1 } arbitrary_source_item_ordering = { level = "allow", priority = 1 } +missing_inline_in_public_items = { level = "allow", priority = 1 } diff --git a/crates/cargo-gpu/src/config.rs b/crates/cargo-gpu/src/config.rs index c514ba2..8c8257b 100644 --- a/crates/cargo-gpu/src/config.rs +++ b/crates/cargo-gpu/src/config.rs @@ -25,7 +25,7 @@ impl Config { /// `Cargo.toml`, so here we load that config first as the base config, and the CLI arguments can /// then later override it. pub fn clap_command_with_cargo_config( - shader_crate_path: &std::path::PathBuf, + shader_crate_path: &std::path::Path, mut env_args: Vec, metadata: &mut MetadataCache, ) -> anyhow::Result { diff --git a/crates/cargo-gpu/src/metadata.rs b/crates/cargo-gpu/src/metadata.rs index fe67d78..00aeea1 100644 --- a/crates/cargo-gpu/src/metadata.rs +++ b/crates/cargo-gpu/src/metadata.rs @@ -17,6 +17,8 @@ pub struct MetadataCache { } impl MetadataCache { + /// Return the cached cargo metadata for the Cargo.toml at the given path, + /// or find it, populate the cache with it and return it. fn get_metadata( &mut self, maybe_path_to_manifest_dir: Option<&std::path::Path>, @@ -32,11 +34,15 @@ impl MetadataCache { self.inner.insert(path.clone(), metadata); } - // UNWRAP: safe because we just inserted it - Ok(self.inner.get(&path).unwrap()) + self.inner.get(&path).context("unreachable") } /// Resolve a package name to a crate directory. + /// + /// ## Errors + /// * if fetching cargo metadata fails. + /// * if no packages are listed in the cargo metadata. + /// * if the manifest path has no parent. pub fn resolve_package_to_shader_crate( &mut self, package: &str, @@ -69,7 +75,10 @@ impl MetadataCache { /// First we generate the CLI arg defaults as JSON. Then on top of those we merge any config /// from the workspace `Cargo.toml`, then on top of those we merge any config from the shader /// crate's `Cargo.toml`. - pub fn as_json(&mut self, path: &std::path::PathBuf) -> anyhow::Result { + /// + /// ## Errors + /// Errors if cargo metadata cannot be found or if it cannot be operated on. + pub fn as_json(&mut self, path: &std::path::Path) -> anyhow::Result { log::debug!("reading package metadata from {}", path.display()); let cargo_json = self.get_cargo_toml_as_json(path)?; let config = Self::merge_configs(&cargo_json, path)?; @@ -121,9 +130,9 @@ impl MetadataCache { /// Convert a `Cargo.toml` to JSON fn get_cargo_toml_as_json( &mut self, - path: &std::path::PathBuf, + path: &std::path::Path, ) -> anyhow::Result { - self.get_metadata(Some(path.as_path())).cloned() + self.get_metadata(Some(path)).cloned() } /// Get any `rust-gpu` metadata set in the crate's `Cargo.toml`