From a4f2502c3eeed3c5f3edaf0a1d12c0d1d7589299 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 26 May 2025 13:43:03 +0200 Subject: [PATCH 01/13] build script: move Command run logic to `.run()` --- crates/cargo-gpu/src/main.rs | 75 ++++++++++++++++++++---------------- crates/cargo-gpu/src/show.rs | 8 ++-- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu/src/main.rs index 9aae856..cd5089d 100644 --- a/crates/cargo-gpu/src/main.rs +++ b/crates/cargo-gpu/src/main.rs @@ -124,40 +124,8 @@ fn run() -> anyhow::Result<()> { }) .collect::>(); log::trace!("CLI args: {env_args:#?}"); - let cli = Cli::parse_from(env_args.clone()); - - match cli.command { - Command::Install(install) => { - let shader_crate_path = install.shader_crate; - let command = - config::Config::clap_command_with_cargo_config(&shader_crate_path, env_args)?; - log::debug!( - "installing with final merged arguments: {:#?}", - command.install - ); - command.install.run()?; - } - Command::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)?; - log::debug!("building with final merged arguments: {command:#?}"); - - if command.build.watch { - // When watching, do one normal run to setup the `manifest.json` file. - command.build.watch = false; - command.run()?; - command.build.watch = true; - command.run()?; - } else { - command.run()?; - } - } - Command::Show(show) => show.run()?, - Command::DumpUsage => dump_full_usage_for_readme()?, - } - - Ok(()) + let cli = Cli::parse_from(&env_args); + cli.command.run(env_args) } /// All of the available subcommands for `cargo gpu` @@ -179,6 +147,45 @@ enum Command { DumpUsage, } +impl Command { + /// run the command + pub fn run(&self, env_args: Vec) -> 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)?; + log::debug!( + "installing with final merged arguments: {:#?}", + command.install + ); + command.install.run()?; + } + 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)?; + log::debug!("building with final merged arguments: {command:#?}"); + + if command.build.watch { + // When watching, do one normal run to setup the `manifest.json` file. + command.build.watch = false; + command.run()?; + command.build.watch = true; + command.run()?; + } else { + command.run()?; + } + } + Self::Show(show) => show.run()?, + Self::DumpUsage => dump_full_usage_for_readme()?, + } + + Ok(()) + } +} + +/// the Cli struct representing the main cli #[derive(clap::Parser)] #[clap(author, version, about, subcommand_required = true)] pub(crate) struct Cli { diff --git a/crates/cargo-gpu/src/show.rs b/crates/cargo-gpu/src/show.rs index 3b0e2f8..265b126 100644 --- a/crates/cargo-gpu/src/show.rs +++ b/crates/cargo-gpu/src/show.rs @@ -33,7 +33,7 @@ pub struct Show { impl Show { /// Entrypoint - pub fn run(self) -> anyhow::Result<()> { + pub fn run(&self) -> anyhow::Result<()> { log::info!("{:?}: ", self.command); #[expect( @@ -41,17 +41,17 @@ impl Show { reason = "The output of this command could potentially be used in a script, \ so we _don't_ want to use `crate::user_output`, as that prefixes a crab." )] - match self.command { + match &self.command { Info::CacheDirectory => { println!("{}\n", cache_dir()?.display()); } Info::SpirvSource(SpirvSourceDep { shader_crate }) => { let rust_gpu_source = - crate::spirv_source::SpirvSource::get_rust_gpu_deps_from_shader(&shader_crate)?; + crate::spirv_source::SpirvSource::get_rust_gpu_deps_from_shader(shader_crate)?; println!("{rust_gpu_source}\n"); } Info::Commitsh => { - println!("{}", std::env!("GIT_HASH")); + println!("{}", env!("GIT_HASH")); } Info::Capabilities => { println!("All available options to the `cargo gpu build --capabilities` argument:"); From f7aec7a388c4dc3f932b095c79b2a87c085e0355 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 26 May 2025 14:50:26 +0200 Subject: [PATCH 02/13] move usage dumping for readme to `mod dump_usage` --- crates/cargo-gpu/src/dump_usage.rs | 51 ++++++++++++++++++++++++++++++ crates/cargo-gpu/src/main.rs | 50 ++--------------------------- 2 files changed, 53 insertions(+), 48 deletions(-) create mode 100644 crates/cargo-gpu/src/dump_usage.rs diff --git a/crates/cargo-gpu/src/dump_usage.rs b/crates/cargo-gpu/src/dump_usage.rs new file mode 100644 index 0000000..d539414 --- /dev/null +++ b/crates/cargo-gpu/src/dump_usage.rs @@ -0,0 +1,51 @@ +//! Convenience function for internal use. Dumps all the CLI usage instructions. Useful for +//! updating the README. + +use crate::{user_output, Cli}; + +/// main dump usage function +pub fn dump_full_usage_for_readme() -> anyhow::Result<()> { + use clap::CommandFactory as _; + let mut command = Cli::command(); + + let mut buffer: Vec = Vec::default(); + command.build(); + + write_help(&mut buffer, &mut command, 0)?; + user_output!("{}", String::from_utf8(buffer)?); + + Ok(()) +} + +/// Recursive function to print the usage instructions for each subcommand. +fn write_help( + buffer: &mut impl std::io::Write, + cmd: &mut clap::Command, + depth: usize, +) -> anyhow::Result<()> { + if cmd.get_name() == "help" { + return Ok(()); + } + + let mut command = cmd.get_name().to_owned(); + let indent_depth = if depth == 0 || depth == 1 { 0 } else { depth }; + let indent = " ".repeat(indent_depth * 4); + writeln!( + buffer, + "\n{}* {}{}", + indent, + command.remove(0).to_uppercase(), + command + )?; + + for line in cmd.render_long_help().to_string().lines() { + writeln!(buffer, "{indent} {line}")?; + } + + for sub in cmd.get_subcommands_mut() { + writeln!(buffer)?; + write_help(buffer, sub, depth + 1)?; + } + + Ok(()) +} diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu/src/main.rs index cd5089d..9a4a264 100644 --- a/crates/cargo-gpu/src/main.rs +++ b/crates/cargo-gpu/src/main.rs @@ -50,6 +50,7 @@ use anyhow::Context as _; +use crate::dump_usage::dump_full_usage_for_readme; use build::Build; use clap::Parser as _; use install::Install; @@ -57,6 +58,7 @@ use show::Show; mod build; mod config; +mod dump_usage; mod install; mod install_toolchain; mod legacy_target_specs; @@ -209,54 +211,6 @@ fn cache_dir() -> anyhow::Result { }) } -/// Convenience function for internal use. Dumps all the CLI usage instructions. Useful for -/// updating the README. -fn dump_full_usage_for_readme() -> anyhow::Result<()> { - use clap::CommandFactory as _; - let mut command = Cli::command(); - - let mut buffer: Vec = Vec::default(); - command.build(); - - write_help(&mut buffer, &mut command, 0)?; - user_output!("{}", String::from_utf8(buffer)?); - - Ok(()) -} - -/// Recursive function to print the usage instructions for each subcommand. -fn write_help( - buffer: &mut impl std::io::Write, - cmd: &mut clap::Command, - depth: usize, -) -> anyhow::Result<()> { - if cmd.get_name() == "help" { - return Ok(()); - } - - let mut command = cmd.get_name().to_owned(); - let indent_depth = if depth == 0 || depth == 1 { 0 } else { depth }; - let indent = " ".repeat(indent_depth * 4); - writeln!( - buffer, - "\n{}* {}{}", - indent, - command.remove(0).to_uppercase(), - command - )?; - - for line in cmd.render_long_help().to_string().lines() { - writeln!(buffer, "{indent} {line}")?; - } - - for sub in cmd.get_subcommands_mut() { - writeln!(buffer)?; - write_help(buffer, sub, depth + 1)?; - } - - Ok(()) -} - /// Returns a string suitable to use as a directory. /// /// Created from the spirv-builder source dep and the rustc channel. From fb4795a95a911ce9cef988a635b31b8df7a90d05 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 26 May 2025 14:52:17 +0200 Subject: [PATCH 03/13] move testing utilities to `mod test` --- crates/cargo-gpu/src/main.rs | 55 +----------------------------------- crates/cargo-gpu/src/test.rs | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 54 deletions(-) create mode 100644 crates/cargo-gpu/src/test.rs diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu/src/main.rs index 9a4a264..a7a3f32 100644 --- a/crates/cargo-gpu/src/main.rs +++ b/crates/cargo-gpu/src/main.rs @@ -67,6 +67,7 @@ mod lockfile; mod metadata; mod show; mod spirv_source; +mod test; /// Central function to write to the user. #[macro_export] @@ -223,57 +224,3 @@ fn to_dirname(text: &str) -> String { .collect::>() .concat() } - -#[cfg(test)] -mod test { - use crate::cache_dir; - use std::io::Write as _; - - fn copy_dir_all( - src: impl AsRef, - dst: impl AsRef, - ) -> anyhow::Result<()> { - std::fs::create_dir_all(&dst)?; - for maybe_entry in std::fs::read_dir(src)? { - let entry = maybe_entry?; - let ty = entry.file_type()?; - if ty.is_dir() { - copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; - } else { - std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; - } - } - Ok(()) - } - - pub fn shader_crate_template_path() -> std::path::PathBuf { - let project_base = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); - project_base.join("../shader-crate-template") - } - - pub fn shader_crate_test_path() -> std::path::PathBuf { - let shader_crate_path = crate::cache_dir().unwrap().join("shader_crate"); - copy_dir_all(shader_crate_template_path(), shader_crate_path.clone()).unwrap(); - shader_crate_path - } - - pub fn overwrite_shader_cargo_toml(shader_crate_path: &std::path::Path) -> std::fs::File { - let cargo_toml = shader_crate_path.join("Cargo.toml"); - let mut file = std::fs::OpenOptions::new() - .write(true) - .truncate(true) - .open(cargo_toml) - .unwrap(); - writeln!(file, "[package]").unwrap(); - writeln!(file, "name = \"test\"").unwrap(); - file - } - - pub fn tests_teardown() { - let cache_dir = cache_dir().unwrap(); - if !cache_dir.exists() { - return; - } - std::fs::remove_dir_all(cache_dir).unwrap(); - } -} diff --git a/crates/cargo-gpu/src/test.rs b/crates/cargo-gpu/src/test.rs new file mode 100644 index 0000000..c9ee93a --- /dev/null +++ b/crates/cargo-gpu/src/test.rs @@ -0,0 +1,53 @@ +//! utilities for tests +#![cfg(test)] + +use crate::cache_dir; +use std::io::Write as _; + +fn copy_dir_all( + src: impl AsRef, + dst: impl AsRef, +) -> anyhow::Result<()> { + std::fs::create_dir_all(&dst)?; + for maybe_entry in std::fs::read_dir(src)? { + let entry = maybe_entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + } else { + std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + +pub fn shader_crate_template_path() -> std::path::PathBuf { + let project_base = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + project_base.join("../shader-crate-template") +} + +pub fn shader_crate_test_path() -> std::path::PathBuf { + let shader_crate_path = crate::cache_dir().unwrap().join("shader_crate"); + copy_dir_all(shader_crate_template_path(), shader_crate_path.clone()).unwrap(); + shader_crate_path +} + +pub fn overwrite_shader_cargo_toml(shader_crate_path: &std::path::Path) -> std::fs::File { + let cargo_toml = shader_crate_path.join("Cargo.toml"); + let mut file = std::fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(cargo_toml) + .unwrap(); + writeln!(file, "[package]").unwrap(); + writeln!(file, "name = \"test\"").unwrap(); + file +} + +pub fn tests_teardown() { + let cache_dir = cache_dir().unwrap(); + if !cache_dir.exists() { + return; + } + std::fs::remove_dir_all(cache_dir).unwrap(); +} From ef6ad5c10808a4cbcbbffa5f83e5eed9fc592418 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 12 May 2025 12:12:18 +0200 Subject: [PATCH 04/13] turn cargo-gpu into a pure library --- crates/cargo-gpu/src/{main.rs => lib.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename crates/cargo-gpu/src/{main.rs => lib.rs} (100%) diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu/src/lib.rs similarity index 100% rename from crates/cargo-gpu/src/main.rs rename to crates/cargo-gpu/src/lib.rs From fd2921785940a8d6d47faf75a7ad5a1e7becb61f Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 26 May 2025 13:18:36 +0200 Subject: [PATCH 05/13] recreate the binary and main function --- crates/cargo-gpu/src/lib.rs | 48 ++++-------------------------------- crates/cargo-gpu/src/main.rs | 41 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 43 deletions(-) create mode 100644 crates/cargo-gpu/src/main.rs diff --git a/crates/cargo-gpu/src/lib.rs b/crates/cargo-gpu/src/lib.rs index a7a3f32..7bb9f37 100644 --- a/crates/cargo-gpu/src/lib.rs +++ b/crates/cargo-gpu/src/lib.rs @@ -52,7 +52,6 @@ use anyhow::Context as _; use crate::dump_usage::dump_full_usage_for_readme; use build::Build; -use clap::Parser as _; use install::Install; use show::Show; @@ -93,47 +92,9 @@ macro_rules! user_output { } } -fn main() { - #[cfg(debug_assertions)] - std::env::set_var("RUST_BACKTRACE", "1"); - - env_logger::builder().init(); - - if let Err(error) = run() { - log::error!("{error:?}"); - - #[expect( - clippy::print_stderr, - reason = "Our central place for outputting error messages" - )] - { - eprintln!("Error: {error}"); - - // `clippy::exit` seems to be a false positive in `main()`. - // See: https://github.com/rust-lang/rust-clippy/issues/13518 - #[expect(clippy::restriction, reason = "Our central place for safely exiting")] - std::process::exit(1); - }; - } -} - -/// Wrappable "main" to catch errors. -fn run() -> anyhow::Result<()> { - let env_args = std::env::args() - .filter(|arg| { - // Calling our `main()` with the cargo subcommand `cargo gpu` passes "gpu" - // as the first parameter, so we want to ignore it. - arg != "gpu" - }) - .collect::>(); - log::trace!("CLI args: {env_args:#?}"); - let cli = Cli::parse_from(&env_args); - cli.command.run(env_args) -} - /// All of the available subcommands for `cargo gpu` #[derive(clap::Subcommand)] -enum Command { +pub enum Command { /// Install rust-gpu compiler artifacts. Install(Box), @@ -191,13 +152,14 @@ impl Command { /// the Cli struct representing the main cli #[derive(clap::Parser)] #[clap(author, version, about, subcommand_required = true)] -pub(crate) struct Cli { +pub struct Cli { /// The command to run. #[clap(subcommand)] - command: Command, + pub command: Command, } -fn cache_dir() -> anyhow::Result { +/// The central cache directory of cargo gpu +pub fn cache_dir() -> anyhow::Result { let dir = directories::BaseDirs::new() .with_context(|| "could not find the user home directory")? .cache_dir() diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu/src/main.rs new file mode 100644 index 0000000..e036e9c --- /dev/null +++ b/crates/cargo-gpu/src/main.rs @@ -0,0 +1,41 @@ +//! main executable of cargo gpu +use cargo_gpu::Cli; +use clap::Parser; + +fn main() { + #[cfg(debug_assertions)] + std::env::set_var("RUST_BACKTRACE", "1"); + + env_logger::builder().init(); + + if let Err(error) = run() { + log::error!("{error:?}"); + + #[expect( + clippy::print_stderr, + reason = "Our central place for outputting error messages" + )] + { + eprintln!("Error: {error}"); + + // `clippy::exit` seems to be a false positive in `main()`. + // See: https://github.com/rust-lang/rust-clippy/issues/13518 + #[expect(clippy::restriction, reason = "Our central place for safely exiting")] + std::process::exit(1); + }; + } +} + +/// Wrappable "main" to catch errors. +pub fn run() -> anyhow::Result<()> { + let env_args = std::env::args() + .filter(|arg| { + // Calling our `main()` with the cargo subcommand `cargo gpu` passes "gpu" + // as the first parameter, so we want to ignore it. + arg != "gpu" + }) + .collect::>(); + log::trace!("CLI args: {env_args:#?}"); + let cli = Cli::parse_from(&env_args); + cli.command.run(env_args) +} From a508ea74ae0deb800f89d8c48a55510577c276eb Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 26 May 2025 17:15:15 +0200 Subject: [PATCH 06/13] fix clippy warnings --- crates/cargo-gpu/src/lib.rs | 12 +++++++++++- crates/cargo-gpu/src/main.rs | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/cargo-gpu/src/lib.rs b/crates/cargo-gpu/src/lib.rs index 7bb9f37..5884a15 100644 --- a/crates/cargo-gpu/src/lib.rs +++ b/crates/cargo-gpu/src/lib.rs @@ -94,6 +94,7 @@ macro_rules! user_output { /// All of the available subcommands for `cargo gpu` #[derive(clap::Subcommand)] +#[non_exhaustive] pub enum Command { /// Install rust-gpu compiler artifacts. Install(Box), @@ -112,7 +113,11 @@ pub enum Command { } impl Command { - /// run the command + /// Runs the command + /// + /// # Errors + /// Any errors during execution, usually printed to the user + #[inline] pub fn run(&self, env_args: Vec) -> anyhow::Result<()> { match &self { Self::Install(install) => { @@ -152,6 +157,7 @@ impl Command { /// the Cli struct representing the main cli #[derive(clap::Parser)] #[clap(author, version, about, subcommand_required = true)] +#[non_exhaustive] pub struct Cli { /// The command to run. #[clap(subcommand)] @@ -159,6 +165,10 @@ pub struct Cli { } /// The central cache directory of cargo gpu +/// +/// # Errors +/// may fail if we can't find the user home directory +#[inline] pub fn cache_dir() -> anyhow::Result { let dir = directories::BaseDirs::new() .with_context(|| "could not find the user home directory")? diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu/src/main.rs index e036e9c..f40e3c6 100644 --- a/crates/cargo-gpu/src/main.rs +++ b/crates/cargo-gpu/src/main.rs @@ -1,6 +1,6 @@ //! main executable of cargo gpu use cargo_gpu::Cli; -use clap::Parser; +use clap::Parser as _; fn main() { #[cfg(debug_assertions)] @@ -27,7 +27,7 @@ fn main() { } /// Wrappable "main" to catch errors. -pub fn run() -> anyhow::Result<()> { +fn run() -> anyhow::Result<()> { let env_args = std::env::args() .filter(|arg| { // Calling our `main()` with the cargo subcommand `cargo gpu` passes "gpu" From 0893f0651f92be8f97d94699e87b8ae90184ba19 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 26 May 2025 17:40:13 +0200 Subject: [PATCH 07/13] make Install usable from a lib --- crates/cargo-gpu/src/install.rs | 44 ++++++++++++++++++++++----------- crates/cargo-gpu/src/lib.rs | 4 ++- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index bd08e6d..74236c6 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -44,6 +44,8 @@ pub struct Install { pub rebuild_codegen: bool, /// Assume "yes" to "Install Rust toolchain: [y/n]" prompt. + /// + /// Defaults to `false` in cli, `true` in [`Default`] #[clap(long, action)] pub auto_install_rust_toolchain: bool, @@ -77,6 +79,21 @@ pub struct Install { pub force_overwrite_lockfiles_v4_to_v3: bool, } +impl Install { + /// Create a default install for a shader crate of some path + pub fn from_shader_crate(shader_crate: PathBuf) -> Self { + Self { + shader_crate, + spirv_builder_source: None, + spirv_builder_version: None, + rebuild_codegen: false, + auto_install_rust_toolchain: true, + clear_target: true, + force_overwrite_lockfiles_v4_to_v3: false, + } + } +} + /// Represents a functional backend installation, whether it was cached or just installed. #[derive(Clone, Debug)] pub struct InstalledBackend { @@ -89,6 +106,18 @@ pub struct InstalledBackend { } impl InstalledBackend { + /// Creates a new `SpirvBuilder` configured to use this installed backend. + pub fn to_spirv_builder( + &self, + path_to_crate: impl AsRef, + target: impl Into, + ) -> SpirvBuilder { + let mut builder = SpirvBuilder::new(path_to_crate, target); + self.configure_spirv_builder(&mut builder) + .expect("unreachable"); + builder + } + /// Configures the supplied [`SpirvBuilder`]. `SpirvBuilder.target` must be set and must not change after calling this function. pub fn configure_spirv_builder(&self, builder: &mut SpirvBuilder) -> anyhow::Result<()> { builder.rustc_codegen_spirv_location = Some(self.rustc_codegen_spirv_location.clone()); @@ -101,21 +130,6 @@ impl InstalledBackend { } } -impl Default for Install { - #[inline] - fn default() -> Self { - Self { - shader_crate: PathBuf::from("./"), - spirv_builder_source: None, - spirv_builder_version: None, - rebuild_codegen: false, - auto_install_rust_toolchain: false, - clear_target: true, - force_overwrite_lockfiles_v4_to_v3: false, - } - } -} - impl Install { /// Create the `rustc_codegen_spirv_dummy` crate that depends on `rustc_codegen_spirv` fn write_source_files(source: &SpirvSource, checkout: &Path) -> anyhow::Result<()> { diff --git a/crates/cargo-gpu/src/lib.rs b/crates/cargo-gpu/src/lib.rs index 5884a15..9a1c661 100644 --- a/crates/cargo-gpu/src/lib.rs +++ b/crates/cargo-gpu/src/lib.rs @@ -52,7 +52,6 @@ use anyhow::Context as _; use crate::dump_usage::dump_full_usage_for_readme; use build::Build; -use install::Install; use show::Show; mod build; @@ -68,6 +67,9 @@ mod show; mod spirv_source; mod test; +pub use install::*; +pub use spirv_builder; + /// Central function to write to the user. #[macro_export] macro_rules! user_output { From 16eb63598996beb2c1ee932ffa1781fd81b22218 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 26 May 2025 18:45:15 +0200 Subject: [PATCH 08/13] fix clippy --- crates/cargo-gpu/src/install.rs | 103 +++++++++++++++++++------------- crates/cargo-gpu/src/lib.rs | 2 + 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index 74236c6..1b1f952 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -11,12 +11,67 @@ use log::{info, trace}; use spirv_builder::SpirvBuilder; use std::path::{Path, PathBuf}; +/// Represents a functional backend installation, whether it was cached or just installed. +#[derive(Clone, Debug)] +#[expect( + clippy::exhaustive_structs, + reason = "never adding private members to this struct" +)] +pub struct InstalledBackend { + /// path to the `rustc_codegen_spirv` dylib + pub rustc_codegen_spirv_location: PathBuf, + /// toolchain channel name + pub toolchain_channel: String, + /// directory with target-specs json files + pub target_spec_dir: PathBuf, +} + +impl InstalledBackend { + /// Creates a new `SpirvBuilder` configured to use this installed backend. + #[expect( + clippy::missing_panics_doc, + clippy::expect_used, + reason = "unreachable" + )] + #[expect(clippy::impl_trait_in_params, reason = "forwarding spirv-builder API")] + #[inline] + pub fn to_spirv_builder( + &self, + path_to_crate: impl AsRef, + target: impl Into, + ) -> SpirvBuilder { + let mut builder = SpirvBuilder::new(path_to_crate, target); + self.configure_spirv_builder(&mut builder) + .expect("unreachable"); + builder + } + + /// Configures the supplied [`SpirvBuilder`]. `SpirvBuilder.target` must be set and must not change after calling this function. + /// + /// # Errors + /// if `SpirvBuilder.target` is not set + #[inline] + pub fn configure_spirv_builder(&self, builder: &mut SpirvBuilder) -> anyhow::Result<()> { + builder.rustc_codegen_spirv_location = Some(self.rustc_codegen_spirv_location.clone()); + builder.toolchain_overwrite = Some(self.toolchain_channel.clone()); + builder.path_to_target_spec = Some(self.target_spec_dir.join(format!( + "{}.json", + builder.target.as_ref().context("expect target to be set")? + ))); + Ok(()) + } +} + /// Args for an install #[expect( clippy::struct_excessive_bools, reason = "cmdline args have many bools" )] #[derive(clap::Parser, Debug, Clone, serde::Deserialize, serde::Serialize)] +#[expect( + clippy::exhaustive_structs, + reason = "never adding private members to this struct" +)] pub struct Install { /// Directory containing the shader crate to compile. #[clap(long, default_value = "./")] @@ -81,7 +136,9 @@ pub struct Install { impl Install { /// Create a default install for a shader crate of some path - pub fn from_shader_crate(shader_crate: PathBuf) -> Self { + #[inline] + #[must_use] + pub const fn from_shader_crate(shader_crate: PathBuf) -> Self { Self { shader_crate, spirv_builder_source: None, @@ -92,45 +149,7 @@ impl Install { force_overwrite_lockfiles_v4_to_v3: false, } } -} -/// Represents a functional backend installation, whether it was cached or just installed. -#[derive(Clone, Debug)] -pub struct InstalledBackend { - /// path to the `rustc_codegen_spirv` dylib - pub rustc_codegen_spirv_location: PathBuf, - /// toolchain channel name - pub toolchain_channel: String, - /// directory with target-specs json files - pub target_spec_dir: PathBuf, -} - -impl InstalledBackend { - /// Creates a new `SpirvBuilder` configured to use this installed backend. - pub fn to_spirv_builder( - &self, - path_to_crate: impl AsRef, - target: impl Into, - ) -> SpirvBuilder { - let mut builder = SpirvBuilder::new(path_to_crate, target); - self.configure_spirv_builder(&mut builder) - .expect("unreachable"); - builder - } - - /// Configures the supplied [`SpirvBuilder`]. `SpirvBuilder.target` must be set and must not change after calling this function. - pub fn configure_spirv_builder(&self, builder: &mut SpirvBuilder) -> anyhow::Result<()> { - builder.rustc_codegen_spirv_location = Some(self.rustc_codegen_spirv_location.clone()); - builder.toolchain_overwrite = Some(self.toolchain_channel.clone()); - builder.path_to_target_spec = Some(self.target_spec_dir.join(format!( - "{}.json", - builder.target.as_ref().context("expect target to be set")? - ))); - Ok(()) - } -} - -impl Install { /// Create the `rustc_codegen_spirv_dummy` crate that depends on `rustc_codegen_spirv` fn write_source_files(source: &SpirvSource, checkout: &Path) -> anyhow::Result<()> { // skip writing a dummy project if we use a local rust-gpu checkout @@ -252,7 +271,11 @@ package = "rustc_codegen_spirv" Ok(target_specs_dst) } - /// Install the binary pair and return the `(dylib_path, toolchain_channel)`. + /// Install the binary pair and return the [`InstalledBackend`], from which you can create [`SpirvBuilder`] instances. + /// + /// # Errors + /// If the installation somehow fails. + #[inline] #[expect(clippy::too_many_lines, reason = "it's fine")] pub fn run(&self) -> anyhow::Result { // Ensure the cache dir exists diff --git a/crates/cargo-gpu/src/lib.rs b/crates/cargo-gpu/src/lib.rs index 9a1c661..b3adabf 100644 --- a/crates/cargo-gpu/src/lib.rs +++ b/crates/cargo-gpu/src/lib.rs @@ -1,3 +1,5 @@ +#![expect(clippy::pub_use, reason = "pub use for build scripts")] + //! Rust GPU shader crate builder. //! //! This program and library allows you to easily compile your rust-gpu shaders, From de3f89ad05c2c5fa08c82d1a59f4bb9158e25d18 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Fri, 30 May 2025 15:47:35 +0200 Subject: [PATCH 09/13] make dummy a library, skip linking an executable binary --- crates/cargo-gpu/src/install.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index 1b1f952..f6190a2 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -162,11 +162,10 @@ impl Install { ); { - trace!("writing dummy main.rs"); - let main = "fn main() {}"; + trace!("writing dummy lib.rs"); let src = checkout.join("src"); - std::fs::create_dir_all(&src).context("creating directory for 'src'")?; - std::fs::write(src.join("main.rs"), main).context("writing 'main.rs'")?; + std::fs::create_dir_all(&src).context("creating 'src' directory")?; + std::fs::File::create(src.join("lib.rs")).context("creating 'src/lib.rs'")?; }; { From 30aac852bd7304718882fa3ff6ecdf97ecc5cdab Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Fri, 30 May 2025 16:13:21 +0200 Subject: [PATCH 10/13] add logging on target-specs writing --- crates/cargo-gpu/src/install.rs | 36 +++++++++++++-------- crates/cargo-gpu/src/legacy_target_specs.rs | 14 ++------ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index f6190a2..ccad6c0 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -7,7 +7,6 @@ use crate::spirv_source::{ use crate::{cache_dir, spirv_source::SpirvSource}; use anyhow::Context as _; use cargo_metadata::Metadata; -use log::{info, trace}; use spirv_builder::SpirvBuilder; use std::path::{Path, PathBuf}; @@ -162,14 +161,14 @@ impl Install { ); { - trace!("writing dummy lib.rs"); + log::trace!("writing dummy lib.rs"); let src = checkout.join("src"); std::fs::create_dir_all(&src).context("creating 'src' directory")?; std::fs::File::create(src.join("lib.rs")).context("creating 'src/lib.rs'")?; }; { - trace!("writing dummy Cargo.toml"); + log::trace!("writing dummy Cargo.toml"); let version_spec = match &source { SpirvSource::CratesIO(version) => { format!("version = \"{version}\"") @@ -206,11 +205,6 @@ package = "rustc_codegen_spirv" /// Copy spec files from one dir to another, assuming no subdirectories fn copy_spec_files(src: &Path, dst: &Path) -> anyhow::Result<()> { - info!( - "Copy target specs from {:?} to {:?}", - src.display(), - dst.display() - ); std::fs::create_dir_all(dst)?; let dir = std::fs::read_dir(src)?; for dir_entry in dir { @@ -225,7 +219,6 @@ package = "rustc_codegen_spirv" /// Add the target spec files to the crate. fn update_spec_files( - &self, source: &SpirvSource, install_dir: &Path, dummy_metadata: &Metadata, @@ -236,6 +229,11 @@ package = "rustc_codegen_spirv" if let Ok(target_specs) = dummy_metadata.find_package("rustc_codegen_spirv-target-specs") { + log::info!( + "target-specs: found crate `rustc_codegen_spirv-target-specs` with manifest at `{}`", + target_specs.manifest_path + ); + let target_specs_src = target_specs .manifest_path .as_std_path() @@ -247,9 +245,17 @@ package = "rustc_codegen_spirv" .context("Could not find `target-specs` directory within `rustc_codegen_spirv-target-specs` dependency")?; if source.is_path() { // skip copy + log::info!( + "target-specs: source is local path, use target-specs from `{}`", + target_specs_src.display() + ); target_specs_dst = target_specs_src; } else { // copy over the target-specs + log::info!( + "target-specs: Copy target specs from `{}`", + target_specs_src.display() + ); Self::copy_spec_files(&target_specs_src, &target_specs_dst) .context("copying target-specs json files")?; } @@ -263,7 +269,11 @@ package = "rustc_codegen_spirv" // and hope parallel runs don't shred each other target_specs_dst = cache_dir()?.join("legacy-target-specs-for-local-checkout"); } - write_legacy_target_specs(&target_specs_dst, self.rebuild_codegen)?; + log::info!( + "target-specs: Writing legacy target specs to `{}`", + target_specs_dst.display() + ); + write_legacy_target_specs(&target_specs_dst)?; } } @@ -335,9 +345,9 @@ package = "rustc_codegen_spirv" log::info!("selected toolchain channel `{toolchain_channel:?}`"); log::debug!("update_spec_files"); - let target_spec_dir = self - .update_spec_files(&source, &install_dir, &dummy_metadata, skip_rebuild) - .context("writing target spec files")?; + let target_spec_dir = + Self::update_spec_files(&source, &install_dir, &dummy_metadata, skip_rebuild) + .context("writing target spec files")?; if !skip_rebuild { log::debug!("ensure_toolchain_and_components_exist"); diff --git a/crates/cargo-gpu/src/legacy_target_specs.rs b/crates/cargo-gpu/src/legacy_target_specs.rs index cabcdec..796b0e3 100644 --- a/crates/cargo-gpu/src/legacy_target_specs.rs +++ b/crates/cargo-gpu/src/legacy_target_specs.rs @@ -4,23 +4,15 @@ //! introduced before the first target spec update. use anyhow::Context as _; -use log::info; use std::path::Path; /// Extract legacy target specs from our executable into some directory -pub fn write_legacy_target_specs(target_spec_dir: &Path, rebuild: bool) -> anyhow::Result<()> { - info!( - "Writing legacy target specs to {}", - target_spec_dir.display() - ); +pub fn write_legacy_target_specs(target_spec_dir: &Path) -> anyhow::Result<()> { std::fs::create_dir_all(target_spec_dir)?; for (filename, contents) in legacy_target_specs::TARGET_SPECS { let path = target_spec_dir.join(filename); - if !path.is_file() || rebuild { - std::fs::write(&path, contents.as_bytes()).with_context(|| { - format!("writing legacy target spec file at [{}]", path.display()) - })?; - } + std::fs::write(&path, contents.as_bytes()) + .with_context(|| format!("writing legacy target spec file at [{}]", path.display()))?; } Ok(()) } From 5bb4db7d2932d2d61e5f15a5ed24f7ab4d3607e4 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Fri, 30 May 2025 16:21:45 +0200 Subject: [PATCH 11/13] deduplicate cargo cloning rust-gpu due to slightly different url --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1714216..fbc2f16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -754,7 +754,7 @@ checksum = "6c89eaf493b3dfc730cda42a77014aad65e03213992c7afe0dff60a9f7d3dd94" [[package]] name = "rustc_codegen_spirv-types" version = "0.9.0" -source = "git+https://github.com/Rust-GPU/rust-gpu.git?rev=86fc48032c4cd4afb74f1d81ae859711d20386a1#86fc48032c4cd4afb74f1d81ae859711d20386a1" +source = "git+https://github.com/Rust-GPU/rust-gpu?rev=86fc48032c4cd4afb74f1d81ae859711d20386a1#86fc48032c4cd4afb74f1d81ae859711d20386a1" dependencies = [ "rspirv", "serde", @@ -904,7 +904,7 @@ dependencies = [ [[package]] name = "spirv-builder" version = "0.9.0" -source = "git+https://github.com/Rust-GPU/rust-gpu.git?rev=86fc48032c4cd4afb74f1d81ae859711d20386a1#86fc48032c4cd4afb74f1d81ae859711d20386a1" +source = "git+https://github.com/Rust-GPU/rust-gpu?rev=86fc48032c4cd4afb74f1d81ae859711d20386a1#86fc48032c4cd4afb74f1d81ae859711d20386a1" dependencies = [ "clap", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 036c971..78a6b52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ exclude = [ resolver = "2" [workspace.dependencies] -spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu.git", rev = "86fc48032c4cd4afb74f1d81ae859711d20386a1", default-features = false } +spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu", rev = "86fc48032c4cd4afb74f1d81ae859711d20386a1", default-features = false } anyhow = "1.0.94" clap = { version = "4.5.37", features = ["derive"] } crossterm = "0.28.1" From 409ecfd5b9d0253b29c2e5d485341d2d8b156333 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 2 Jun 2025 17:27:53 +0200 Subject: [PATCH 12/13] change unreachable Err handling --- crates/cargo-gpu/src/install.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index ccad6c0..e9c523b 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -28,9 +28,8 @@ pub struct InstalledBackend { impl InstalledBackend { /// Creates a new `SpirvBuilder` configured to use this installed backend. #[expect( - clippy::missing_panics_doc, - clippy::expect_used, - reason = "unreachable" + clippy::unreachable, + reason = "it's unreachable, no need to return a Result" )] #[expect(clippy::impl_trait_in_params, reason = "forwarding spirv-builder API")] #[inline] @@ -41,7 +40,7 @@ impl InstalledBackend { ) -> SpirvBuilder { let mut builder = SpirvBuilder::new(path_to_crate, target); self.configure_spirv_builder(&mut builder) - .expect("unreachable"); + .unwrap_or_else(|_| unreachable!("we set target before calling this function")); builder } From c2d886d8864423a1f91383b294a809868ceedb44 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 2 Jun 2025 17:28:47 +0200 Subject: [PATCH 13/13] make `Install` and `InstalledBackend` `#[non_exhaustive]` --- crates/cargo-gpu/src/install.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index e9c523b..50a34e0 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -12,10 +12,7 @@ use std::path::{Path, PathBuf}; /// Represents a functional backend installation, whether it was cached or just installed. #[derive(Clone, Debug)] -#[expect( - clippy::exhaustive_structs, - reason = "never adding private members to this struct" -)] +#[non_exhaustive] pub struct InstalledBackend { /// path to the `rustc_codegen_spirv` dylib pub rustc_codegen_spirv_location: PathBuf, @@ -66,10 +63,7 @@ impl InstalledBackend { reason = "cmdline args have many bools" )] #[derive(clap::Parser, Debug, Clone, serde::Deserialize, serde::Serialize)] -#[expect( - clippy::exhaustive_structs, - reason = "never adding private members to this struct" -)] +#[non_exhaustive] pub struct Install { /// Directory containing the shader crate to compile. #[clap(long, default_value = "./")]