Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
5 changes: 5 additions & 0 deletions crates/cargo-gpu/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ 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 = metadata.resolve_package_to_shader_crate(package)?;
}

let _lockfile_mismatch_handler = LockfileMismatchHandler::new(
&self.install.shader_crate,
Expand Down
31 changes: 26 additions & 5 deletions crates/cargo-gpu/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
use anyhow::Context as _;
use clap::Parser as _;

use crate::metadata::MetadataCache;

/// Config
pub struct Config;

Expand All @@ -23,10 +25,11 @@ 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<String>,
metadata: &mut MetadataCache,
) -> anyhow::Result<crate::build::Build> {
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)?;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand All @@ -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 {
Expand All @@ -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"));
Expand All @@ -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![
Expand All @@ -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());
Expand Down
12 changes: 10 additions & 2 deletions crates/cargo-gpu/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,

/// Directory containing the shader crate to compile.
#[clap(long, alias("package"), short_alias('p'), default_value = "./")]
#[serde(alias = "package")]
///
/// Conflicts with `--package` or `-p`.
#[clap(long, default_value = "./", conflicts_with("package"))]
pub shader_crate: PathBuf,

#[expect(
Expand Down Expand Up @@ -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,
Expand Down
21 changes: 16 additions & 5 deletions crates/cargo-gpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -122,12 +123,19 @@ impl Command {
/// # Errors
/// Any errors during execution, usually printed to the user
#[inline]
pub fn run(&self, env_args: Vec<String>) -> anyhow::Result<()> {
pub fn run(
&self,
env_args: Vec<String>,
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
Expand All @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion crates/cargo-gpu/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ fn run() -> anyhow::Result<()> {
.collect::<Vec<_>>();
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)
}
87 changes: 76 additions & 11 deletions crates/cargo-gpu/src/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,86 @@
//! 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<std::path::PathBuf, cargo_metadata::Metadata>,
}

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>,
) -> 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);
}

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,
) -> anyhow::Result<std::path::PathBuf> {
log::debug!("resolving package '{package}' to shader crate");
let metadata = self.get_metadata(None)?;

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)
}

impl Metadata {
/// 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.
///
/// 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<Value> {
let cargo_json = Self::get_cargo_toml_as_json(path)?;
///
/// ## 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<Value> {
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)
}
Expand All @@ -27,6 +90,7 @@ impl Metadata {
cargo_json: &cargo_metadata::Metadata,
path: &std::path::Path,
) -> anyhow::Result<Value> {
log::debug!("merging cargo metadata from {}", path.display());
let mut metadata = crate::config::Config::defaults_as_json()?;
crate::config::Config::json_merge(
&mut metadata,
Expand Down Expand Up @@ -65,9 +129,10 @@ impl Metadata {

/// Convert a `Cargo.toml` to JSON
fn get_cargo_toml_as_json(
path: &std::path::PathBuf,
&mut self,
path: &std::path::Path,
) -> anyhow::Result<cargo_metadata::Metadata> {
Ok(MetadataCommand::new().current_dir(path).exec()?)
self.get_metadata(Some(path)).cloned()
}

/// Get any `rust-gpu` metadata set in the crate's `Cargo.toml`
Expand Down Expand Up @@ -136,7 +201,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"],
Expand All @@ -160,7 +225,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"],
Expand Down Expand Up @@ -189,7 +254,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"],
Expand Down
Loading