diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 6d561da..92d9eb2 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -13,8 +13,12 @@ env: jobs: install-and-build-shaders: strategy: + fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: + - ubuntu-latest + - macos-latest + - windows-latest runs-on: ${{ matrix.os }} defaults: run: @@ -27,15 +31,15 @@ jobs: - run: rustup default stable - run: rustup update - run: cargo test - - run: cargo install --path crates/cargo-gpu - - run: cargo gpu install - - run: cargo gpu build --shader-crate crates/shader-crate-template --output-dir test-shaders - - run: ls -lah test-shaders - - run: cat test-shaders/manifest.json + # - run: cargo install --path crates/cargo-gpu + # - run: cargo gpu install + # - run: cargo gpu build --shader-crate crates/shader-crate-template --output-dir test-shaders + # - run: ls -lah test-shaders + # - run: cat test-shaders/manifest.json clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: moonrepo/setup-rust@v1 - - run: cargo clippy + - run: cargo clippy -- --deny warnings diff --git a/Cargo.lock b/Cargo.lock index e27864f..37d3787 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -85,12 +85,13 @@ dependencies = [ "chrono", "clap", "directories", - "env_logger", + "env_logger 0.10.2", "log", "relative-path", "serde", "serde_json", "spirv-builder-cli", + "test-log", "toml", ] @@ -176,6 +177,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", +] + [[package]] name = "env_home" version = "0.1.0" @@ -195,6 +205,18 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -287,6 +309,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.161" @@ -315,12 +343,31 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -331,12 +378,30 @@ dependencies = [ "libm", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + [[package]] name = "proc-macro2" version = "1.0.89" @@ -374,8 +439,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -386,9 +460,15 @@ checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -456,12 +536,21 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "spirv-builder-cli" version = "0.1.0" dependencies = [ "env_home", - "env_logger", + "env_logger 0.10.2", "log", "serde", "serde_json", @@ -536,6 +625,28 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-log" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93" +dependencies = [ + "env_logger 0.11.5", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "thiserror" version = "1.0.68" @@ -556,6 +667,16 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "toml" version = "0.8.19" @@ -590,6 +711,54 @@ dependencies = [ "winnow", ] +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.13" @@ -602,12 +771,34 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" @@ -617,6 +808,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 9dd7fcb..9dd7f6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ relative-path = "1.9.3" serde = { version = "1.0.214", features = ["derive"] } serde_json = "1.0.132" toml = "0.8.19" +test-log = "0.2.16" [workspace.lints.rust] missing_docs = "warn" @@ -46,7 +47,9 @@ pattern_type_mismatch = { level = "allow", priority = 1 } print_stdout = { level = "allow", priority = 1 } std_instead_of_alloc = { level = "allow", priority = 1 } -# TODO: Try to not depend on these lints +# TODO: Try to not depend on allowing these lints unwrap_used = { level = "allow", priority = 1 } +get_unwrap = { level = "allow", priority = 1 } +expect_used = { level = "allow", priority = 1 } panic = { level = "allow", priority = 1 } diff --git a/README.md b/README.md index 382fdfd..9e6bc3a 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,24 @@ # cargo-gpu + Command line tool for building Rust shaders using rust-gpu. ## Getting Started ### Installation -To install the tool ensure you have `rustup`. Then run: + +To install the tool ensure you have `rustup`. Then run: ``` cargo install --git https://github.com/rust-gpu/cargo-gpu ``` -After that you can use `cargo gpu` to compile your shader crates with: +After that you can use `cargo gpu` to compile your shader crates with: ``` cargo gpu build ``` -This plain invocation will compile the crate in the current directory and +This plain invocation will compile the crate in the current directory and place the compiled shaders in the current directory. Use `cargo gpu help` to see other options :) @@ -35,11 +37,12 @@ cargo gpu build ## Usage -``` +```` Commands: install Install rust-gpu compiler artifacts build Compile a shader crate to SPIR-V toml Compile a shader crate according to the `cargo gpu build` parameters found in the given toml file + show Show some useful values help Print this message or the help of the given subcommand(s) Options: @@ -57,17 +60,25 @@ Install rust-gpu compiler artifacts Usage: cargo-gpu install [OPTIONS] Options: - --spirv-builder - spirv-builder dependency, written just like in a Cargo.toml file + --shader-crate + Directory containing the shader crate to compile + + [default: ./] + + --spirv-builder-source + Source of `spirv-builder` dependency Eg: "https://github.com/Rust-GPU/rust-gpu" - [default: "{ git = \"https://github.com/Rust-GPU/rust-gpu.git\" }"] + --spirv-builder-version + Version of `spirv-builder` dependency. + * If `--spirv_builder_source` is not set, then this is assumed to be a crates.io semantic + 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. --rust-toolchain Rust toolchain channel to use to build `spirv-builder`. - This must match the `spirv_builder` argument. - - [default: nightly-2024-04-24] + This must be compatible with the `spirv_builder` argument as defined in the `rust-gpu` repo. --force-spirv-cli-rebuild Force `spirv-builder-cli` and `rustc_codegen_spirv` to be rebuilt @@ -83,26 +94,29 @@ Compile a shader crate to SPIR-V Usage: cargo-gpu build [OPTIONS] Options: - --spirv-builder - spirv-builder dependency, written just like in a Cargo.toml file + --shader-crate + Directory containing the shader crate to compile - [default: "{ git = \"https://github.com/Rust-GPU/rust-gpu.git\" }"] + [default: ./] + + --spirv-builder-source + Source of `spirv-builder` dependency Eg: "https://github.com/Rust-GPU/rust-gpu" + + --spirv-builder-version + Version of `spirv-builder` dependency. + * If `--spirv_builder_source` is not set, then this is assumed to be a crates.io semantic + 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. --rust-toolchain Rust toolchain channel to use to build `spirv-builder`. - This must match the `spirv_builder` argument. - - [default: nightly-2024-04-24] + This must be compatible with the `spirv_builder` argument as defined in the `rust-gpu` repo. --force-spirv-cli-rebuild Force `spirv-builder-cli` and `rustc_codegen_spirv` to be rebuilt - --shader-crate - Directory containing the shader crate to compile - - [default: ./] - --shader-target Shader target @@ -161,4 +175,18 @@ Options: -h, --help Print help (see a summary with '-h') -``` + +* Show + +Show some useful values + +Usage: cargo-gpu show [OPTIONS] + +Options: + --cache-directory + Displays the location of the cache directory + + -h, --help + Print help + +```` diff --git a/crates/cargo-gpu/Cargo.toml b/crates/cargo-gpu/Cargo.toml index 0935ad9..c567e2e 100644 --- a/crates/cargo-gpu/Cargo.toml +++ b/crates/cargo-gpu/Cargo.toml @@ -20,6 +20,9 @@ serde_json.workspace = true toml.workspace = true chrono.workspace = true +[dev-dependencies] +test-log.workspace = true + # Enable incremental by default in release mode. [profile.release] incremental = true diff --git a/crates/cargo-gpu/src/build.rs b/crates/cargo-gpu/src/build.rs index 665dd20..a562e56 100644 --- a/crates/cargo-gpu/src/build.rs +++ b/crates/cargo-gpu/src/build.rs @@ -12,11 +12,7 @@ use crate::{install::Install, target_spec_dir}; pub struct Build { /// Install the `rust-gpu` compiler and components #[clap(flatten)] - install: Install, - - /// Directory containing the shader crate to compile. - #[clap(long, default_value = "./")] - pub shader_crate: std::path::PathBuf, + pub install: Install, /// Shader target. #[clap(long, default_value = "spirv-unknown-vulkan1.2")] @@ -46,17 +42,17 @@ impl Build { self.output_dir = self.output_dir.canonicalize().unwrap(); // Ensure the shader crate exists - self.shader_crate = self.shader_crate.canonicalize().unwrap(); + self.install.shader_crate = self.install.shader_crate.canonicalize().unwrap(); assert!( - self.shader_crate.exists(), + self.install.shader_crate.exists(), "shader crate '{}' does not exist. (Current dir is '{}')", - self.shader_crate.display(), + self.install.shader_crate.display(), std::env::current_dir().unwrap().display() ); let spirv_builder_args = spirv_builder_cli::Args { dylib_path, - shader_crate: self.shader_crate.clone(), + shader_crate: self.install.shader_crate.clone(), shader_target: self.shader_target.clone(), path_to_target_spec: target_spec_dir().join(format!("{}.json", self.shader_target)), no_default_features: self.no_default_features, @@ -101,8 +97,10 @@ impl Build { use relative_path::PathExt as _; let path = self.output_dir.join(filepath.file_name().unwrap()); std::fs::copy(&filepath, &path).unwrap(); - let path_relative_to_shader_crate = - path.relative_to(&self.shader_crate).unwrap().to_path(""); + let path_relative_to_shader_crate = path + .relative_to(&self.install.shader_crate) + .unwrap() + .to_path(""); Linkage::new(entry, path_relative_to_shader_crate) }, ) @@ -140,3 +138,41 @@ impl Build { } } } + +#[cfg(test)] +mod test { + use crate::{cache_dir, Cli, Command}; + + use super::*; + + #[test_log::test] + fn builder_from_params() { + crate::test::tests_teardown(); + + // We need to copy the shader crate template so that the workspace doesn't interfere with compilation. + let shader_crate_source = std::path::PathBuf::from("../shader-crate-template"); + let shader_crate_destination = cache_dir().join("shader-crate-template"); + let output_dir = shader_crate_destination.join("shaders"); + crate::test::copy_dir_all(shader_crate_source, shader_crate_destination.clone()).unwrap(); + + let args = [ + "target/debug/cargo-gpu", + "build", + "--shader-crate", + &format!("{}", shader_crate_destination.display()), + "--output-dir", + &format!("{}", output_dir.display()), + ]; + if let Cli { + command: Command::Build(mut build), + } = Cli::parse_from(args) + { + assert_eq!(shader_crate_destination, build.install.shader_crate); + assert_eq!(output_dir, build.output_dir); + + build.run(); + } else { + panic!("was not a build command"); + } + } +} diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index 68d4f4c..ae3072d 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -1,7 +1,7 @@ //! Install a dedicated per-shader crate that has the `rust-gpu` compiler in it. use std::io::Write as _; -use crate::{cache_dir, spirv_cli::Spirv, target_spec_dir}; +use crate::{cache_dir, spirv_cli::SpirvCLI, spirv_source::SpirvSource, target_spec_dir}; /// These are the files needed to create the dedicated, per-shader `rust-gpu` builder create. const SPIRV_BUILDER_FILES: &[(&str, &str)] = &[ @@ -86,15 +86,32 @@ const TARGET_SPECS: &[(&str, &str)] = &[ /// `cargo gpu install` #[derive(clap::Parser, Debug)] pub struct Install { - /// spirv-builder dependency, written just like in a Cargo.toml file. - #[clap(long, default_value = Spirv::DEFAULT_DEP)] - spirv_builder: String, + /// Directory containing the shader crate to compile. + #[clap(long, default_value = "./")] + pub shader_crate: std::path::PathBuf, + + #[expect( + clippy::doc_markdown, + reason = "The URL should appear literally like this. But Clippy wants it to be a in markdown clickable link" + )] + /// Source of `spirv-builder` dependency + /// Eg: "https://github.com/Rust-GPU/rust-gpu" + #[clap(long)] + spirv_builder_source: Option, + + /// Version of `spirv-builder` dependency. + /// * If `--spirv_builder_source` is not set, then this is assumed to be a crates.io semantic + /// 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)] + spirv_builder_version: Option, /// Rust toolchain channel to use to build `spirv-builder`. /// /// This must be compatible with the `spirv_builder` argument as defined in the `rust-gpu` repo. - #[clap(long, default_value = Spirv::DEFAULT_CHANNEL)] - rust_toolchain: String, + #[clap(long)] + rust_toolchain: Option, /// Force `spirv-builder-cli` and `rustc_codegen_spirv` to be rebuilt. #[clap(long)] @@ -102,30 +119,60 @@ pub struct Install { } impl Install { - /// Returns a [`Spirv`] instance, responsible for ensuring the right version of the `spirv-builder-cli` crate. - fn spirv_cli(&self) -> Spirv { - Spirv { - dep: self.spirv_builder.clone(), - channel: self.rust_toolchain.clone(), - } + /// Returns a [`SpirvCLI`] instance, responsible for ensuring the right version of the `spirv-builder-cli` crate. + fn spirv_cli(&self, shader_crate_path: &std::path::PathBuf) -> SpirvCLI { + SpirvCLI::new( + shader_crate_path, + self.spirv_builder_source.clone(), + self.spirv_builder_version.clone(), + self.rust_toolchain.clone(), + ) } /// Create the `spirv-builder-cli` crate. fn write_source_files(&self) { - let cli = self.spirv_cli(); - let checkout = cli.cached_checkout_path(); + let spirv_cli = self.spirv_cli(&self.shader_crate); + let checkout = spirv_cli.cached_checkout_path(); std::fs::create_dir_all(checkout.join("src")).unwrap(); for (filename, contents) in SPIRV_BUILDER_FILES { log::debug!("writing {filename}"); let path = checkout.join(filename); let mut file = std::fs::File::create(&path).unwrap(); - let replaced_contents = contents - .replace("${SPIRV_BUILDER_SOURCE}", &cli.dep) - .replace("${CHANNEL}", &cli.channel); + let mut replaced_contents = contents.replace("${CHANNEL}", &spirv_cli.channel); + if filename == &"Cargo.toml" { + replaced_contents = Self::update_cargo_toml(&replaced_contents, &spirv_cli.source); + } file.write_all(replaced_contents.as_bytes()).unwrap(); } } + /// Create the `spirv-builder-cli` crate. + fn update_cargo_toml(contents: &str, spirv_source: &SpirvSource) -> String { + let updated = contents.lines().map(|line| { + if line.contains("${AUTO-REPLACE-SOURCE}") { + let replaced_line = match spirv_source { + SpirvSource::CratesIO(_) => String::new(), + SpirvSource::Git((repo, _)) => format!("git = \"{repo}\""), + }; + return format!("{replaced_line}\n"); + } + + if line.contains("${AUTO-REPLACE-VERSION}") { + let replaced_line = match spirv_source { + SpirvSource::CratesIO(version) => { + format!("version = \"{}\"", version.replace('v', "")) + } + SpirvSource::Git((_, revision)) => format!("rev = \"{revision}\""), + }; + return format!("{replaced_line}\n"); + } + + format!("{line}\n") + }); + + updated.collect() + } + /// Add the target spec files to the crate. fn write_target_spec_files(&self) { for (filename, contents) in TARGET_SPECS { @@ -150,8 +197,7 @@ impl Install { panic!("could not create cache dir"); }); - let spirv_version = self.spirv_cli(); - spirv_version.ensure_version_channel_compatibility(); + let spirv_version = self.spirv_cli(&self.shader_crate); spirv_version.ensure_toolchain_and_components_exist(); let checkout = spirv_version.cached_checkout_path(); @@ -191,7 +237,7 @@ impl Install { command.args([ "--features", - &Self::get_required_spirv_builder_version(&spirv_version.channel), + &Self::get_required_spirv_builder_version(spirv_version.date), ]); log::debug!("building artifacts with `{:?}`", command); @@ -237,16 +283,13 @@ impl Install { /// here we choose the right Cargo feature to enable/disable code in `spirv-builder-cli`. /// /// TODO: - /// * Download the actual `rust-gpu` repo as pinned in the shader's `Cargo.lock` and get the - /// `spirv-builder` version from there. /// * Warn the user that certain `cargo-gpu` features aren't available when building with /// older versions of `spirv-builder`, eg setting the target spec. - fn get_required_spirv_builder_version(toolchain_channel: &str) -> String { + fn get_required_spirv_builder_version(date: chrono::NaiveDate) -> String { let parse_date = chrono::NaiveDate::parse_from_str; - let datetime = parse_date(toolchain_channel, "nightly-%Y-%m-%d").unwrap(); let pre_cli_date = parse_date("2024-04-24", "%Y-%m-%d").unwrap(); - if datetime < pre_cli_date { + if date < pre_cli_date { "spirv-builder-pre-cli" } else { "spirv-builder-0_10" diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu/src/main.rs index 542b847..9c5d6a3 100644 --- a/crates/cargo-gpu/src/main.rs +++ b/crates/cargo-gpu/src/main.rs @@ -60,6 +60,7 @@ mod build; mod install; mod show; mod spirv_cli; +mod spirv_source; mod toml; fn main() { @@ -118,13 +119,20 @@ pub(crate) struct Cli { } fn cache_dir() -> std::path::PathBuf { - directories::BaseDirs::new() + let dir = directories::BaseDirs::new() .unwrap_or_else(|| { log::error!("could not find the user home directory"); panic!("cache_dir failed"); }) .cache_dir() - .join("rust-gpu") + .join("rust-gpu"); + + if cfg!(test) { + let id = std::thread::current().id(); + dir.join("tests").join(format!("{id:?}")) + } else { + dir + } } /// Location of the target spec metadata files @@ -171,55 +179,50 @@ fn write_help(buffer: &mut impl std::io::Write, cmd: &mut clap::Command, _depth: } } +/// Returns a string suitable to use as a directory. +/// +/// Created from the spirv-builder source dep and the rustc channel. +fn to_dirname(text: &str) -> String { + text.replace( + [std::path::MAIN_SEPARATOR, '\\', '/', '.', ':', '@', '='], + "_", + ) + .split(['{', '}', ' ', '\n', '"', '\'']) + .collect::>() + .concat() +} + #[cfg(test)] mod test { - use spirv_cli::Spirv as SpirvCLI; - - use super::*; - - #[test] - fn cached_checkout_dir_sanity() { - // Test that - let spirv = SpirvCLI::default(); - let dir = spirv.cached_checkout_path(); - let name = dir - .file_name() - .unwrap() - .to_str() - .map(std::string::ToString::to_string) - .unwrap(); - assert_eq!( - "git_https___github_com_Rust-GPU_rust-gpu_git+nightly-2024-04-24", - &name - ); + use crate::cache_dir; + + pub fn shader_crate_template_path() -> std::path::PathBuf { + std::path::PathBuf::from("../shader-crate-template") + } + + pub fn tests_teardown() { + let cache_dir = cache_dir(); + let test_dir_root = cache_dir.parent().unwrap(); + if !test_dir_root.exists() { + return; + } + std::fs::remove_dir_all(test_dir_root).unwrap(); } - #[test] - fn builder_from_params() { - let shader_crate = std::path::PathBuf::from("../shader-crate-template"); - let output_dir = std::path::PathBuf::from("../shader-crate-template/shaders"); - let args = [ - "target/debug/cargo-gpu", - "build", - "--shader-crate", - &format!("{}", shader_crate.display()), - "--output-dir", - &format!("{}", output_dir.display()), - ]; - if let Cli { - command: Command::Build(mut build), - } = Cli::parse_from(args) - { - assert_eq!(shader_crate, build.shader_crate); - assert_eq!(output_dir, build.output_dir); - - // TODO: - // What's the best way to reset caches for this? For example we could add a - // `--force-spirv-cli-rebuild`, but that would slow down each test. But without - // something like that we might not be getting actual idempotent tests. - build.run(); - } else { - panic!("was not a build command"); + pub fn copy_dir_all( + src: impl AsRef, + dst: impl AsRef, + ) -> std::io::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(()) } } diff --git a/crates/cargo-gpu/src/spirv_cli.rs b/crates/cargo-gpu/src/spirv_cli.rs index 4c114b0..7a66a8b 100644 --- a/crates/cargo-gpu/src/spirv_cli.rs +++ b/crates/cargo-gpu/src/spirv_cli.rs @@ -1,64 +1,70 @@ -//! Install the relevant Rust compiler and toolchain components required by the version of -//! `rust-gpu` defined in the shader. -use crate::cache_dir; +//! Query the shader crate to find what version of `rust-gpu` it depends on. +//! Then ensure that the relevant Rust toolchain and components are installed. -/// The canonical pairs of `rust-gpu` version to `rust-gpu` Rust toolchain channel. -/// -/// TODO: We may be able to automate this by checking out the locked version of `spirv-std` in the -/// shader's crate. -const SPIRV_STD_TOOLCHAIN_PAIRS: &[(&str, &str)] = &[("0.10", "nightly-2024-04-24")]; +use crate::spirv_source::SpirvSource; /// Cargo dependency for `spirv-builder` and the rust toolchain channel. #[derive(Debug, Clone)] -pub struct Spirv { - /// The version of `rust-gpu`, eg `"0.10"` or `"{ git = "https://github.com/Rust-GPU/rust-gpu.git" }` - pub dep: String, - //// The toolchain that `rust-gpu` uses, eg "nightly-2024-04-24" +pub struct SpirvCLI { + #[expect( + clippy::doc_markdown, + reason = "The URL should appear literally like this. But Clippy wants it to be a in markdown clickable link" + )] + /// The source and version of `rust-gpu`. + /// Eg: + /// * From crates.io with version "0.10.0" + /// * From Git with: + /// - a repo of "https://github.com/Rust-GPU/rust-gpu.git" + /// - a revision of "abc213" + pub source: SpirvSource, + /// The toolchain channel that `rust-gpu` uses, eg "nightly-2024-04-24" pub channel: String, + /// The date of the pinned version of `rust-gpu` + pub date: chrono::NaiveDate, } -impl Default for Spirv { - fn default() -> Self { - Self { - dep: Self::DEFAULT_DEP.into(), - channel: Self::DEFAULT_CHANNEL.into(), - } - } -} - -impl core::fmt::Display for Spirv { +impl core::fmt::Display for SpirvCLI { #[expect( clippy::min_ident_chars, reason = "It's a core library trait implementation" )] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - format!("{}+{}", self.dep, self.channel).fmt(f) + format!("{}+{}", self.source, self.channel).fmt(f) } } -impl Spirv { - /// The default `rust-gpu` dependency defintion - pub const DEFAULT_DEP: &str = r#"{ git = "https://github.com/Rust-GPU/rust-gpu.git" }"#; - /// The default `rust-gpu` toolchain - pub const DEFAULT_CHANNEL: &str = "nightly-2024-04-24"; +impl SpirvCLI { + /// Create instance + pub fn new( + shader_crate_path: &std::path::PathBuf, + maybe_rust_gpu_source: Option, + maybe_rust_gpu_version: Option, + maybe_rust_gpu_channel: Option, + ) -> Self { + let (default_rust_gpu_source, rust_gpu_date, default_rust_gpu_channel) = + SpirvSource::get_rust_gpu_deps_from_shader(shader_crate_path); - /// Returns a string suitable to use as a directory. - /// - /// Created from the spirv-builder source dep and the rustc channel. - fn to_dirname(&self) -> String { - self.to_string() - .replace( - [std::path::MAIN_SEPARATOR, '\\', '/', '.', ':', '@', '='], - "_", - ) - .split(['{', '}', ' ', '\n', '"', '\'']) - .collect::>() - .concat() + let mut maybe_spirv_source: Option = None; + if let Some(rust_gpu_version) = maybe_rust_gpu_version { + let mut source = SpirvSource::CratesIO(rust_gpu_version.clone()); + if let Some(rust_gpu_source) = maybe_rust_gpu_source { + source = SpirvSource::Git((rust_gpu_source, rust_gpu_version)); + } + maybe_spirv_source = Some(source); + } + + Self { + source: maybe_spirv_source.unwrap_or(default_rust_gpu_source), + channel: maybe_rust_gpu_channel.unwrap_or(default_rust_gpu_channel), + date: rust_gpu_date, + } } /// Create and/or return the cache directory pub fn cached_checkout_path(&self) -> std::path::PathBuf { - let checkout_dir = cache_dir().join(self.to_dirname()); + let checkout_dir = crate::cache_dir() + .join("spirv-builder-clis") + .join(crate::to_dirname(self.to_string().as_ref())); std::fs::create_dir_all(&checkout_dir).unwrap_or_else(|error| { log::error!( "could not create checkout dir '{}': {error}", @@ -70,20 +76,6 @@ impl Spirv { checkout_dir } - /// Ensure that the requested `rust-gpu` version/toolchain are known to be canonically - /// compatible. - /// - /// TODO: We may be able to automatically get the pair by downloading the `rust-gpu` repo as defined by the - /// `spriv-std` dependency in the shader. - pub fn ensure_version_channel_compatibility(&self) { - for (version, channel) in SPIRV_STD_TOOLCHAIN_PAIRS { - assert!( - !(version.starts_with(&self.dep) && channel != &self.channel), - "expected spirv-std version to be matched with rust toolchain channel {channel}" - ); - } - } - /// Use `rustup` to install the toolchain and components, if not already installed. /// /// Pretty much runs: @@ -158,3 +150,24 @@ impl Spirv { } } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn cached_checkout_dir_sanity() { + let spirv = SpirvCLI::new(&"./".into(), None, None, None); + let dir = spirv.cached_checkout_path(); + let name = dir + .file_name() + .unwrap() + .to_str() + .map(std::string::ToString::to_string) + .unwrap(); + assert_eq!( + "https___github_com_Rust-GPU_rust-gpu+v0_9_0+nightly-2023-05-27", + &name + ); + } +} diff --git a/crates/cargo-gpu/src/spirv_source.rs b/crates/cargo-gpu/src/spirv_source.rs new file mode 100644 index 0000000..23acd0e --- /dev/null +++ b/crates/cargo-gpu/src/spirv_source.rs @@ -0,0 +1,264 @@ +//! Use the shader that we're compiling as the default source for which version of `rust-gpu` to use. +//! +//! We do this by calling `cargo tree` inside the shader's crate to get the defined `spirv-std` +//! version. Then with that we `git checkout` the `rust-gpu` repo that corresponds to that version. +//! From there we can look at the source code to get the required Rust toolchain. + +/// The canonical `rust-gpu` URI +const RUST_GPU_REPO: &str = "https://github.com/Rust-GPU/rust-gpu"; + +/// The various sources that the `rust-gpu` repo can have. +/// Most commonly it will simply be the canonical version on crates.io. But it could also be the +/// Git version, or a fork. +#[derive(Eq, PartialEq, Clone, Debug)] +pub enum SpirvSource { + /// If the shader specifies a simple version like `spirv-std = "0.9.0"` then the source of + /// `rust-gpu` is the conventional crates.io version. + /// + /// `String` is the simple version like, "0.9.0" + CratesIO(String), + /// If the shader specifies a version like: + /// `spirv-std = { git = "https://github.com..." ... }` + /// then the source of `rust-gpu` is `Git`. + /// + /// `(String, String)` is the repo source and revision hash or tag. + Git((String, String)), +} + +impl core::fmt::Display for SpirvSource { + #[expect( + clippy::min_ident_chars, + reason = "It's a core library trait implementation" + )] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + format!("{}+{}", self.to_repo(), self.to_version()).fmt(f) + } +} + +impl SpirvSource { + /// Look into the shader crate to get the version of `rust-gpu` it's using. + pub fn get_rust_gpu_deps_from_shader( + shader_crate_path: &std::path::PathBuf, + ) -> (Self, chrono::NaiveDate, String) { + let rust_gpu_source = Self::get_spirv_std_dep_defintiion(shader_crate_path); + + rust_gpu_source.ensure_repo_is_installed(); + rust_gpu_source.checkout(); + + let date = rust_gpu_source.get_version_date(); + let channel = Self::get_channel_from_toolchain_toml(&rust_gpu_source.to_dirname()); + + log::debug!("Parsed version, date and toolchain channel from shader-defined `rust-gpu`: {rust_gpu_source:?}, {date}, {channel}"); + + (rust_gpu_source, date, channel) + } + + /// Convert the source to just its version. + pub fn to_version(&self) -> String { + match self { + Self::CratesIO(version) => version.to_string(), + Self::Git((_, revision)) => revision.to_string(), + } + } + + /// Convert the source to just its repo. + fn to_repo(&self) -> String { + match self { + Self::CratesIO(_) => RUST_GPU_REPO.to_owned(), + Self::Git((repo, _)) => repo.to_owned(), + } + } + + /// Convert the `rust-gpu` source into a string that can be used as a directory. + /// It needs to be dynamically created because an end-user might want to swap out the source, + /// maybe using their own fork for example. + fn to_dirname(&self) -> std::path::PathBuf { + let dir = crate::to_dirname(self.to_string().as_ref()); + crate::cache_dir().join("rust-gpu_repos").join(dir) + } + + /// Checkout the `rust-gpu` to the requested version. + fn checkout(&self) { + log::debug!( + "Checking out `rust-gpu` repo at {} to {}", + self.to_dirname().display(), + self.to_version() + ); + let output_checkout = std::process::Command::new("git") + .current_dir(self.to_dirname()) + .args(["checkout", self.to_version().as_ref()]) + .output() + .unwrap(); + assert!( + output_checkout.status.success(), + "couldn't checkout revision '{}' of `rust-gpu` at {}", + self.to_version(), + self.to_dirname().to_string_lossy() + ); + } + + /// Get the date of the version of `rust-gpu` used by the shader. This allows us to know what + /// features we can use in the `spirv-builder` crate. + fn get_version_date(&self) -> chrono::NaiveDate { + let date_format = "%Y-%m-%d"; + + log::debug!( + "Getting `rust-gpu` version date from {}", + self.to_dirname().display(), + ); + let output_date = std::process::Command::new("git") + .current_dir(self.to_dirname()) + .args([ + "show", + "--no-patch", + "--format=%cd", + format!("--date=format:'{date_format}'").as_ref(), + self.to_version().as_ref(), + ]) + .output() + .unwrap(); + assert!( + output_date.status.success(), + "couldn't get `rust-gpu` version date at for {} at {}", + self.to_version(), + self.to_dirname().to_string_lossy() + ); + let date_string = String::from_utf8_lossy(&output_date.stdout) + .to_string() + .trim() + .replace('\'', ""); + + log::debug!( + "Parsed date for version {}: {date_string}", + self.to_version() + ); + + chrono::NaiveDate::parse_from_str(&date_string, date_format).unwrap() + } + + /// Parse the `rust-toolchain.toml` in the working tree of the checked-out version of the `rust-gpu` repo. + fn get_channel_from_toolchain_toml(path: &std::path::PathBuf) -> String { + log::debug!("Parsing `rust-toolchain.toml` at {path:?} for the used toolchain"); + + let contents = std::fs::read_to_string(path.join("rust-toolchain.toml")).unwrap(); + let toml: toml::Table = toml::from_str(&contents).unwrap(); + let Some(toolchain) = toml.get("toolchain") else { + panic!("Couldn't find `[toolchain]` section in `rust-toolchain.toml` at {path:?}"); + }; + let Some(channel) = toolchain.get("channel") else { + panic!("Couldn't find `channel` field in `rust-toolchain.toml` at {path:?}"); + }; + + channel.to_string().replace('"', "") + } + + /// Get the shader crate's `spirv_std = ...` definition in its `Cargo.toml` + fn get_spirv_std_dep_defintiion(shader_crate_path: &std::path::PathBuf) -> Self { + log::debug!("Running `cargo tree` on {}", shader_crate_path.display()); + let output_cargo_tree = std::process::Command::new("cargo") + .current_dir(shader_crate_path) + .args(["tree", "--workspace", "--depth", "1", "--prefix", "none"]) + .output() + .unwrap(); + assert!( + output_cargo_tree.status.success(), + "could not query shader's `Cargo.toml` for `spirv-std` dependency" + ); + let cargo_tree_string = String::from_utf8_lossy(&output_cargo_tree.stdout); + + let maybe_spirv_std_def = cargo_tree_string + .lines() + .find(|line| line.contains("spirv-std")); + + let Some(spirv_std_def) = maybe_spirv_std_def else { + panic!(""); + }; + + Self::parse_spirv_std_source_and_version(spirv_std_def) + } + + /// Parse a string like: + /// `spirv-std v0.9.0 (https://github.com/EmbarkStudios/rust-gpu#54f6978c) (*)` + /// Which would return: + /// `("54f6978c", SpirvSource::Git("https://github.com/EmbarkStudios/rust-gpu"))` + fn parse_spirv_std_source_and_version(spirv_std_def: &str) -> Self { + let parts: Vec = spirv_std_def.split_whitespace().map(String::from).collect(); + let version = parts + .get(1) + .expect("Couldn't find `spirv_std` version in shader crate") + .to_owned(); + let mut source = Self::CratesIO(version); + + if parts.len() > 2 { + let revision_marker = "#"; + let mut source_string = parts.get(2).unwrap().to_owned(); + source_string = source_string.replace(['(', ')'], ""); + if source_string.contains(revision_marker) { + let source_parts: Vec = source_string + .split(revision_marker) + .map(String::from) + .collect(); + let repo = source_parts.first().unwrap().to_owned(); + let revision = source_parts.last().unwrap().to_owned(); + source = Self::Git((repo, revision)); + } + } + + log::debug!("Parsed `rust-gpu` source and version: {source:?}"); + + source + } + + /// `git clone` the `rust-gpu` repo. We use it to get the required Rust toolchain to compile + /// the shader. + fn ensure_repo_is_installed(&self) { + if self.to_dirname().exists() { + log::debug!( + "Not cloning `rust-gpu` repo ({}) as it already exists at {}", + self.to_repo(), + self.to_dirname().to_string_lossy().as_ref(), + ); + return; + } + + log::debug!( + "Cloning `rust-gpu` repo {} to {}", + self.to_repo(), + self.to_dirname().to_string_lossy().as_ref(), + ); + + std::process::Command::new("git") + .args([ + "clone", + self.to_repo().as_ref(), + self.to_dirname().to_string_lossy().as_ref(), + ]) + .output() + .unwrap(); + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn parsing_spirv_std_dep_for_shader_template() { + let shader_template_path = crate::test::shader_crate_template_path(); + let source = SpirvSource::get_spirv_std_dep_defintiion(&shader_template_path); + assert_eq!(source, SpirvSource::CratesIO("v0.9.0".to_owned())); + } + + #[test] + fn parsing_spirv_std_dep_for_git_source() { + let definition = "spirv-std v9.9.9 (https://github.com/Rust-GPU/rust-gpu#54f6978c) (*)"; + let source = SpirvSource::parse_spirv_std_source_and_version(definition); + assert_eq!( + source, + SpirvSource::Git(( + "https://github.com/Rust-GPU/rust-gpu".to_owned(), + "54f6978c".to_owned() + )) + ); + } +} diff --git a/crates/cargo-gpu/src/toml.rs b/crates/cargo-gpu/src/toml.rs index ab360ef..cf76f7d 100644 --- a/crates/cargo-gpu/src/toml.rs +++ b/crates/cargo-gpu/src/toml.rs @@ -37,25 +37,9 @@ pub struct Toml { impl Toml { /// Entrypoint pub fn run(&self) { - // Find the path to the toml file to use - let path = if self.path.is_file() && self.path.ends_with(".toml") { - self.path.clone() - } else { - let path = self.path.join("Cargo.toml"); - if path.is_file() { - path - } else { - log::error!("toml file '{}' is not a file", self.path.display()); - panic!("toml file '{}' is not a file", self.path.display()); - } - }; - - log::info!("using toml file '{}'", path.display()); + let (path, toml) = Self::parse_cargo_toml(self.path.clone()); // Determine if this is a workspace's Cargo.toml or a crate's Cargo.toml - let contents = std::fs::read_to_string(&path).unwrap(); - let toml: toml::Table = toml::from_str(&contents).unwrap(); - let (toml_type, table) = if toml.contains_key("workspace") { let table = Self::get_metadata_rustgpu_table(&toml, "workspace") .unwrap_or_else(|| { @@ -134,6 +118,29 @@ impl Toml { } } + /// Parse the contents of the shader's `Cargo.toml` + pub fn parse_cargo_toml(mut path: std::path::PathBuf) -> (std::path::PathBuf, toml::Table) { + // Find the path to the toml file to use + let parsed_path = if path.is_file() && path.ends_with(".toml") { + path + } else { + path = path.join("Cargo.toml"); + if path.is_file() { + path + } else { + log::error!("toml file '{}' is not a file", path.display()); + panic!("toml file '{}' is not a file", path.display()); + } + }; + + log::info!("using toml file '{}'", parsed_path.display()); + + let contents = std::fs::read_to_string(&parsed_path).unwrap(); + let toml: toml::Table = toml::from_str(&contents).unwrap(); + + (parsed_path, toml) + } + /// Parse the `[package.metadata.rust-gpu]` section. fn get_metadata_rustgpu_table<'toml>( toml: &'toml toml::Table, diff --git a/crates/spirv-builder-cli/Cargo.toml b/crates/spirv-builder-cli/Cargo.toml index 67f9ee8..c89b824 100644 --- a/crates/spirv-builder-cli/Cargo.toml +++ b/crates/spirv-builder-cli/Cargo.toml @@ -4,6 +4,11 @@ version = "0.1.0" edition = "2021" [lib] +path = "src/lib.rs" + +[[bin]] +name = "spirv-builder-cli" +path = "src/main.rs" [dependencies] env_home = "0.1.0" @@ -13,18 +18,6 @@ serde = "1.0.214" serde_json = "1.0.132" toml = "0.8.19" -[dependencies.spirv-builder-pre-cli] -package = "spirv-builder" -optional = true -git = "https://github.com/Rust-GPU/rust-gpu" -rev = "4c633aec" - -[dependencies.spirv-builder-0_10] -package = "spirv-builder" -optional = true -git = "https://github.com/Rust-GPU/rust-gpu" -rev = "60dcb82" - [features] default = ["spirv-builder-0_10"] # The `spirv-builder` before `cargo gpu` existed. It has an incompatible `SpirvBuilder` interface. @@ -32,3 +25,19 @@ spirv-builder-pre-cli = ["dep:spirv-builder-pre-cli"] # The first version that introduced `cargo gpu`. It has some extra `.builder()` args that make # dynamically changing build dependencies easier. spirv-builder-0_10 = ["dep:spirv-builder-0_10"] + +# NB: All the `${AUTO-REPLACE*}` tokens in each feature get replaced with the same values. +# This is because only one feature can ever be used at once and it makes it easier to just +# replace each token rather than figure out to what feature each token belongs. + +[dependencies.spirv-builder-pre-cli] +package = "spirv-builder" +optional = true +git = "https://github.com/Rust-GPU/rust-gpu" # ${AUTO-REPLACE-SOURCE} +rev = "4c633aec" # ${AUTO-REPLACE-VERSION} + +[dependencies.spirv-builder-0_10] +package = "spirv-builder" +optional = true +git = "https://github.com/Rust-GPU/rust-gpu" # ${AUTO-REPLACE-SOURCE} +rev = "60dcb82" # ${AUTO-REPLACE-VERSION}