diff --git a/Cargo.lock b/Cargo.lock index d42db12..bada16b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,7 +108,6 @@ dependencies = [ "crossterm", "directories", "dunce", - "env_logger", "log", "relative-path", "rustc_codegen_spirv-target-specs", @@ -119,6 +118,17 @@ dependencies = [ "test-log", ] +[[package]] +name = "cargo-gpu-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "cargo-gpu", + "clap", + "env_logger", + "log", +] + [[package]] name = "cargo-platform" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 62ca2ec..55af6bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,25 @@ [workspace] members = [ "crates/cargo-gpu", + "crates/cargo-gpu-cli", "crates/xtask", ] exclude = [ # This currently needs to be excluded because it depends on a version of `rust-gpu` that - # uses a toolchain whose Cargo version doesn't recognise version 4 of `Cargo.lock`. - "crates/shader-crate-template" + # uses a toolchain whose Cargo version doesn't recognize version 4 of `Cargo.lock`. + "crates/shader-crate-template", ] resolver = "2" +[workspace.package] +version = "0.1.0" +edition = "2021" +repository = "https://github.com/Rust-GPU/cargo-gpu" +keywords = ["gpu", "compiler", "rust-gpu"] +license = "MIT OR Apache-2.0" + [workspace.dependencies] spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu", rev = "3df836eb9d7b01344f52737bf9a310d1fb5a0c26", default-features = false } anyhow = "1.0.98" diff --git a/README.md b/README.md index 9065311..45bd5ec 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Command line tool for building Rust shaders using rust-gpu. To install the tool ensure you have `rustup`. Then run: ``` -cargo install --git https://github.com/rust-gpu/cargo-gpu cargo-gpu +cargo install --git https://github.com/rust-gpu/cargo-gpu cargo-gpu-cli ``` After that you can use `cargo gpu` to compile your shader crates with: diff --git a/crates/cargo-gpu-cli/Cargo.toml b/crates/cargo-gpu-cli/Cargo.toml new file mode 100644 index 0000000..e825066 --- /dev/null +++ b/crates/cargo-gpu-cli/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cargo-gpu-cli" +description = "Command line tool for building Rust shaders using `rust-gpu`" +version.workspace = true +edition.workspace = true +repository.workspace = true +keywords.workspace = true +license.workspace = true + +default-run = "cargo-gpu" + +[[bin]] +name = "cargo-gpu" +path = "src/main.rs" + +[dependencies] +cargo-gpu = { path = "../cargo-gpu", features = ["watch", "clap"] } +clap.workspace = true +log.workspace = true +env_logger.workspace = true +anyhow.workspace = true + +[lints] +workspace = true diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu-cli/src/main.rs similarity index 100% rename from crates/cargo-gpu/src/main.rs rename to crates/cargo-gpu-cli/src/main.rs diff --git a/crates/cargo-gpu/Cargo.toml b/crates/cargo-gpu/Cargo.toml index 8de68b2..f5c85a8 100644 --- a/crates/cargo-gpu/Cargo.toml +++ b/crates/cargo-gpu/Cargo.toml @@ -1,23 +1,20 @@ [package] name = "cargo-gpu" -version = "0.1.0" -edition = "2021" -description = "Generates shader .spv files from rust-gpu shader crates" -repository = "https://github.com/Rust-GPU/cargo-gpu" +description = "Generates shader .spv files from `rust-gpu` shader crates" readme = "../../README.md" -keywords = ["gpu", "compiler", "rust-gpu"] -license = "MIT OR Apache-2.0" -default-run = "cargo-gpu" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +version.workspace = true +edition.workspace = true +repository.workspace = true +keywords.workspace = true +license.workspace = true [dependencies] cargo_metadata.workspace = true anyhow.workspace = true -spirv-builder = { workspace = true, features = ["clap", "watch"] } +spirv-builder.workspace = true legacy_target_specs.workspace = true -clap.workspace = true +clap = { workspace = true, optional = true } directories.workspace = true -env_logger.workspace = true log.workspace = true relative-path.workspace = true serde.workspace = true @@ -29,7 +26,13 @@ dunce.workspace = true [dev-dependencies] test-log.workspace = true cargo_metadata = { workspace = true, features = ["builder"] } -cargo-util-schemas = "0.8.2" +cargo-util-schemas.workspace = true + +[features] +# Rebuilds target shader crate upon changes +watch = ["spirv-builder/watch"] +# Enables `clap` support for public structs +clap = ["dep:clap", "spirv-builder/clap"] [lints] workspace = true diff --git a/crates/cargo-gpu/src/build.rs b/crates/cargo-gpu/src/build.rs index 2a7f158..79355de 100644 --- a/crates/cargo-gpu/src/build.rs +++ b/crates/cargo-gpu/src/build.rs @@ -2,32 +2,35 @@ #![allow(clippy::unwrap_used, reason = "this is basically a test")] //! `cargo gpu build`, analogous to `cargo build` -use crate::install::Install; -use crate::linkage::Linkage; -use crate::lockfile::LockfileMismatchHandler; use anyhow::Context as _; use spirv_builder::{CompileResult, ModuleResult, SpirvBuilder}; use std::io::Write as _; use std::path::PathBuf; +use crate::install::Install; +use crate::linkage::Linkage; +use crate::lockfile::LockfileMismatchHandler; + /// Args for just a build -#[derive(clap::Parser, Debug, Clone, serde::Deserialize, serde::Serialize)] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +#[cfg_attr(feature = "clap", derive(clap::Parser))] pub struct BuildArgs { /// Path to the output directory for the compiled shaders. - #[clap(long, short, default_value = "./")] + #[cfg_attr(feature = "clap", clap(long, short, default_value = "./"))] pub output_dir: PathBuf, /// Watch the shader crate directory and automatically recompile on changes. - #[clap(long, short, action)] + #[cfg(feature = "watch")] + #[cfg_attr(feature = "clap", clap(long, short, action))] pub watch: bool, /// the flattened [`SpirvBuilder`] - #[clap(flatten)] + #[cfg_attr(feature = "clap", clap(flatten))] #[serde(flatten)] pub spirv_builder: SpirvBuilder, ///Renames the manifest.json file to the given name - #[clap(long, short, default_value = "manifest.json")] + #[cfg_attr(feature = "clap", clap(long, short, default_value = "manifest.json"))] pub manifest_file: String, } @@ -36,6 +39,7 @@ impl Default for BuildArgs { fn default() -> Self { Self { output_dir: PathBuf::from("./"), + #[cfg(feature = "watch")] watch: false, spirv_builder: SpirvBuilder::default(), manifest_file: String::from("manifest.json"), @@ -44,14 +48,15 @@ impl Default for BuildArgs { } /// `cargo build` subcommands -#[derive(Clone, clap::Parser, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[cfg_attr(feature = "clap", derive(clap::Parser))] pub struct Build { /// CLI args for install the `rust-gpu` compiler and components - #[clap(flatten)] + #[cfg_attr(feature = "clap", clap(flatten))] pub install: Install, /// CLI args for configuring the build of the shader - #[clap(flatten)] + #[cfg_attr(feature = "clap", clap(flatten))] pub build: BuildArgs, } @@ -89,30 +94,52 @@ impl Build { std::env::current_dir()?.display() ); - if self.build.watch { + #[cfg(feature = "watch")] + let watching = self.build.watch; + #[cfg(not(feature = "watch"))] + let watching = false; + if watching { + return self.watch(); + } + + self.build() + } + + /// Builds shader crate using [`SpirvBuilder`]. + fn build(&self) -> anyhow::Result<()> { + crate::user_output!( + "Compiling shaders at {}...\n", + self.install.shader_crate.display() + ); + let result = self.build.spirv_builder.build()?; + self.parse_compilation_result(&result)?; + Ok(()) + } + + /// Watches shader crate for changes using [`SpirvBuilder`] + /// or returns an error depending on presence of `watch` feature. + fn watch(&self) -> anyhow::Result<()> { + #[cfg(feature = "watch")] + { let this = self.clone(); self.build .spirv_builder .watch(move |result, accept| { - let result1 = this.parse_compilation_result(&result); + let parse_result = this.parse_compilation_result(&result); if let Some(accept) = accept { - accept.submit(result1); + accept.submit(parse_result); } })? - .context("unreachable")??; - std::thread::park(); - } else { - crate::user_output!( - "Compiling shaders at {}...\n", - self.install.shader_crate.display() - ); - let result = self.build.spirv_builder.build()?; - self.parse_compilation_result(&result)?; + .context("should always return the first compile result") + .flatten()?; + anyhow::bail!("unexpected end of watch") } - Ok(()) + + #[cfg(not(feature = "watch"))] + anyhow::bail!("cannot watch for changes without the `watch` feature") } - /// Parses compilation result from `SpirvBuilder` and writes it out to a file + /// Parses compilation result from [`SpirvBuilder`] and writes it out to a file fn parse_compilation_result(&self, result: &CompileResult) -> anyhow::Result<()> { let shaders = match &result.module { ModuleResult::MultiModule(modules) => { @@ -175,6 +202,8 @@ impl Build { #[cfg(test)] mod test { + #![cfg(feature = "clap")] + use clap::Parser as _; use crate::{Cli, Command}; diff --git a/crates/cargo-gpu/src/config.rs b/crates/cargo-gpu/src/config.rs index f1a67fa..0e08612 100644 --- a/crates/cargo-gpu/src/config.rs +++ b/crates/cargo-gpu/src/config.rs @@ -1,4 +1,7 @@ //! Manage and merge the various sources of config: shader crate's `Cargo.toml`(s) and CLI args. + +#![cfg(feature = "clap")] + use anyhow::Context as _; use clap::Parser as _; diff --git a/crates/cargo-gpu/src/dump_usage.rs b/crates/cargo-gpu/src/dump_usage.rs index d539414..e17351b 100644 --- a/crates/cargo-gpu/src/dump_usage.rs +++ b/crates/cargo-gpu/src/dump_usage.rs @@ -1,6 +1,8 @@ //! Convenience function for internal use. Dumps all the CLI usage instructions. Useful for //! updating the README. +#![cfg(feature = "clap")] + use crate::{user_output, Cli}; /// main dump usage function diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index aa3dfa1..fcfa1e8 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -61,11 +61,15 @@ impl InstalledBackend { clippy::struct_excessive_bools, reason = "cmdline args have many bools" )] -#[derive(clap::Parser, Debug, Clone, serde::Deserialize, serde::Serialize)] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +#[cfg_attr(feature = "clap", derive(clap::Parser))] #[non_exhaustive] pub struct Install { /// Directory containing the shader crate to compile. - #[clap(long, alias("package"), short_alias('p'), default_value = "./")] + #[cfg_attr( + feature = "clap", + clap(long, alias("package"), short_alias('p'), default_value = "./") + )] #[serde(alias = "package")] pub shader_crate: PathBuf, @@ -75,7 +79,7 @@ pub struct Install { )] /// Source of `spirv-builder` dependency /// Eg: "https://github.com/Rust-GPU/rust-gpu" - #[clap(long)] + #[cfg_attr(feature = "clap", clap(long))] pub spirv_builder_source: Option, /// Version of `spirv-builder` dependency. @@ -83,22 +87,22 @@ pub struct Install { /// version such as "0.9.0". /// * If `--spirv-builder-source` is set, then this is assumed to be a Git "commitsh", such /// as a Git commit hash or a Git tag, therefore anything that `git checkout` can resolve. - #[clap(long, verbatim_doc_comment)] + #[cfg_attr(feature = "clap", clap(long, verbatim_doc_comment))] pub spirv_builder_version: Option, /// Force `rustc_codegen_spirv` to be rebuilt. - #[clap(long)] + #[cfg_attr(feature = "clap", clap(long))] pub rebuild_codegen: bool, /// Assume "yes" to "Install Rust toolchain: [y/n]" prompt. /// /// Defaults to `false` in cli, `true` in [`Default`] - #[clap(long, action)] + #[cfg_attr(feature = "clap", clap(long, action))] pub auto_install_rust_toolchain: bool, /// Clear target dir of `rustc_codegen_spirv` build after a successful build, saves about /// 200MiB of disk space. - #[clap(long = "no-clear-target", default_value = "true", action = clap::ArgAction::SetFalse)] + #[cfg_attr(feature = "clap", clap(long = "no-clear-target", default_value = "true", action = clap::ArgAction::SetFalse))] pub clear_target: bool, /// There is a tricky situation where a shader crate that depends on workspace config can have @@ -122,7 +126,7 @@ pub struct Install { /// way source URLs are encoded. See these PRs for more details: /// * /// * - #[clap(long, action, verbatim_doc_comment)] + #[cfg_attr(feature = "clap", clap(long, action, verbatim_doc_comment))] pub force_overwrite_lockfiles_v4_to_v3: bool, } diff --git a/crates/cargo-gpu/src/lib.rs b/crates/cargo-gpu/src/lib.rs index 1be7abb..18589c6 100644 --- a/crates/cargo-gpu/src/lib.rs +++ b/crates/cargo-gpu/src/lib.rs @@ -52,10 +52,12 @@ use anyhow::Context as _; -use crate::dump_usage::dump_full_usage_for_readme; use build::Build; use show::Show; +#[cfg(feature = "clap")] +use crate::dump_usage::dump_full_usage_for_readme; + mod build; mod config; mod dump_usage; @@ -97,7 +99,7 @@ macro_rules! user_output { } /// All of the available subcommands for `cargo gpu` -#[derive(clap::Subcommand)] +#[cfg_attr(feature = "clap", derive(clap::Subcommand))] #[non_exhaustive] pub enum Command { /// Install rust-gpu compiler artifacts. @@ -112,6 +114,7 @@ pub enum Command { /// A hidden command that can be used to recursively print out all the subcommand help messages: /// `cargo gpu dump-usage` /// Useful for updating the README. + #[cfg(feature = "clap")] #[clap(hide(true))] DumpUsage, } @@ -122,6 +125,7 @@ impl Command { /// # Errors /// Any errors during execution, usually printed to the user #[inline] + #[cfg(feature = "clap")] pub fn run(&self, env_args: Vec) -> anyhow::Result<()> { match &self { Self::Install(install) => { @@ -140,15 +144,18 @@ impl Command { config::Config::clap_command_with_cargo_config(shader_crate_path, env_args)?; log::debug!("building with final merged arguments: {command:#?}"); + // When watching, do one normal run to setup the `manifest.json` file. + #[cfg(feature = "watch")] 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()?; } Self::Show(show) => show.run()?, + #[cfg(feature = "clap")] Self::DumpUsage => dump_full_usage_for_readme()?, } @@ -157,12 +164,15 @@ impl Command { } /// the Cli struct representing the main cli -#[derive(clap::Parser)] -#[clap(author, version, about, subcommand_required = true)] +#[cfg_attr(feature = "clap", derive(clap::Parser))] +#[cfg_attr( + feature = "clap", + clap(author, version, about, subcommand_required = true) +)] #[non_exhaustive] pub struct Cli { /// The command to run. - #[clap(subcommand)] + #[cfg_attr(feature = "clap", clap(subcommand))] pub command: Command, } diff --git a/crates/cargo-gpu/src/metadata.rs b/crates/cargo-gpu/src/metadata.rs index 0b330fb..f296595 100644 --- a/crates/cargo-gpu/src/metadata.rs +++ b/crates/cargo-gpu/src/metadata.rs @@ -1,5 +1,7 @@ //! Get config from the shader crate's `Cargo.toml` `[*.metadata.rust-gpu.*]` +#![cfg(feature = "clap")] + use cargo_metadata::MetadataCommand; use serde_json::Value; diff --git a/crates/cargo-gpu/src/show.rs b/crates/cargo-gpu/src/show.rs index b7eb19b..7630701 100644 --- a/crates/cargo-gpu/src/show.rs +++ b/crates/cargo-gpu/src/show.rs @@ -8,15 +8,18 @@ use std::fs; use std::path::Path; /// Show the computed source of the spirv-std dependency. -#[derive(Clone, Debug, clap::Parser)] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "clap", derive(clap::Parser))] pub struct SpirvSourceDep { /// The location of the shader-crate to inspect to determine its spirv-std dependency. - #[clap(long, default_value = "./")] + #[cfg_attr(feature = "clap", clap(long, default_value = "./"))] pub shader_crate: std::path::PathBuf, } /// Different tidbits of information that can be queried at the command line. -#[derive(Clone, Debug, clap::Subcommand)] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "clap", derive(clap::Subcommand))] +#[cfg_attr(not(feature = "clap"), allow(dead_code))] pub enum Info { /// Displays the location of the cache directory CacheDirectory, @@ -32,10 +35,10 @@ pub enum Info { } /// `cargo gpu show` -#[derive(clap::Parser)] +#[cfg_attr(feature = "clap", derive(clap::Parser))] pub struct Show { /// Display information about rust-gpu - #[clap(subcommand)] + #[cfg_attr(feature = "clap", clap(subcommand))] command: Info, } diff --git a/crates/cargo-gpu/src/test.rs b/crates/cargo-gpu/src/test.rs index c9ee93a..05e90eb 100644 --- a/crates/cargo-gpu/src/test.rs +++ b/crates/cargo-gpu/src/test.rs @@ -4,6 +4,7 @@ use crate::cache_dir; use std::io::Write as _; +#[cfg_attr(not(feature = "clap"), allow(dead_code))] fn copy_dir_all( src: impl AsRef, dst: impl AsRef, @@ -26,12 +27,14 @@ pub fn shader_crate_template_path() -> std::path::PathBuf { project_base.join("../shader-crate-template") } +#[cfg_attr(not(feature = "clap"), allow(dead_code))] 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 } +#[cfg_attr(not(feature = "clap"), allow(dead_code))] 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() @@ -44,6 +47,7 @@ pub fn overwrite_shader_cargo_toml(shader_crate_path: &std::path::Path) -> std:: file } +#[cfg_attr(not(feature = "clap"), allow(dead_code))] pub fn tests_teardown() { let cache_dir = cache_dir().unwrap(); if !cache_dir.exists() { diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index 8eec0be..bd4eb46 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -149,7 +149,7 @@ fn main() { rust_gpu_version: maybe_rust_gpu_version, } => { log::info!("installing cargo gpu"); - cmd(["cargo", "install", "--path", "crates/cargo-gpu"]).unwrap(); + cmd(["cargo", "install", "--path", "crates/cargo-gpu-cli"]).unwrap(); log::info!("setup project"); let dir = tempfile::TempDir::with_prefix("test-shader-output").unwrap();