diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 9c418110..5a710c97 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -19,6 +19,23 @@ jobs: - ubuntu-latest - macos-latest - windows-latest + rust-gpu-version: [latest] + include: + # As well as testing on each OS, we also want to test to make sure we're still supporting + # older versions of `rust-gpu`. However, we can assume that these tests are already okay + # across platforms, so we only need to test on Linux, the chepeast in terms of minutes. + # + # `0.7.0` currently fails building `spirv-builder-cli` with: + # """ + # package `is_terminal_polyfill v1.70.1` cannot be built because it requires rustc + # 1.70.0 or newer, while the currently active rustc version is 1.69.0-nightly + # """ + # It's probably easily fixable. But also `0.7.0` was released in April 2023, so there's + # unlikely many users of it? + - os: ubuntu-latest + rust-gpu-version: 0.8.0 + - os: ubuntu-latest + rust-gpu-version: 0.9.0 runs-on: ${{ matrix.os }} defaults: run: @@ -35,9 +52,8 @@ jobs: rustup update - run: cargo test - name: Run a full build - run: cargo xtask test-build + run: cargo xtask test-build --rust-gpu-version ${{ matrix.rust-gpu-version }} - lints: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 738d6d1f..9f81952c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,8 @@ flamegraph.svg # Compiled shader assets from running tests crates/shader-crate-template/shaders + +# Build artefacts used by CI tests +tmp/* +crates/shader-crate-template/manifest.json +crates/shader-crate-template/rust_gpu_shader_crate_template.spv diff --git a/Cargo.lock b/Cargo.lock index 9885f04d..bd63d763 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,6 +105,7 @@ dependencies = [ "spirv-builder-cli", "test-log", "toml", + "version_check", ] [[package]] @@ -124,9 +125,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", @@ -134,9 +135,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", @@ -146,9 +147,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", @@ -158,9 +159,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "colorchoice" @@ -301,9 +302,9 @@ checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heck" -version = "0.5.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -769,9 +770,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.11.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" @@ -955,6 +956,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/crates/cargo-gpu/Cargo.toml b/crates/cargo-gpu/Cargo.toml index 05c1b33d..8373ef4a 100644 --- a/crates/cargo-gpu/Cargo.toml +++ b/crates/cargo-gpu/Cargo.toml @@ -23,6 +23,7 @@ toml.workspace = true chrono.workspace = true http.workspace = true crossterm.workspace = true +version_check = "0.9.5" [dev-dependencies] test-log.workspace = true diff --git a/crates/cargo-gpu/src/build.rs b/crates/cargo-gpu/src/build.rs index 3dc97dcb..d32dd248 100644 --- a/crates/cargo-gpu/src/build.rs +++ b/crates/cargo-gpu/src/build.rs @@ -22,7 +22,7 @@ pub struct Build { impl Build { /// Entrypoint - #[expect(clippy::too_many_lines, reason = "these lines are fine")] + #[expect(clippy::too_many_lines, reason = "It's not too confusing")] pub fn run(&mut self) -> anyhow::Result<()> { let spirv_builder_cli_path = self.install.run()?; @@ -150,7 +150,6 @@ impl Build { ); std::fs::remove_file(spirv_manifest)?; } - Ok(()) } } diff --git a/crates/cargo-gpu/src/install.rs b/crates/cargo-gpu/src/install.rs index 505de378..d6c286ba 100644 --- a/crates/cargo-gpu/src/install.rs +++ b/crates/cargo-gpu/src/install.rs @@ -104,13 +104,14 @@ pub struct Install { impl Install { /// 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) -> anyhow::Result { + fn spirv_cli(&self, shader_crate_path: &std::path::Path) -> anyhow::Result { SpirvCli::new( shader_crate_path, self.spirv_install.spirv_builder_source.clone(), self.spirv_install.spirv_builder_version.clone(), self.spirv_install.rust_toolchain.clone(), self.spirv_install.auto_install_rust_toolchain, + self.spirv_install.force_overwrite_lockfiles_v4_to_v3, ) } diff --git a/crates/cargo-gpu/src/main.rs b/crates/cargo-gpu/src/main.rs index 990246c0..b46cf5bd 100644 --- a/crates/cargo-gpu/src/main.rs +++ b/crates/cargo-gpu/src/main.rs @@ -134,7 +134,7 @@ fn run() -> anyhow::Result<()> { "installing with final merged arguments: {:#?}", command.install ); - let _: std::path::PathBuf = command.install.run()?; + command.install.run()?; } Command::Build(build) => { let shader_crate_path = build.install.spirv_install.shader_crate; @@ -150,7 +150,7 @@ fn run() -> anyhow::Result<()> { command.run()?; } else { command.run()?; - } + }; } Command::Show(show) => show.run()?, Command::DumpUsage => dump_full_usage_for_readme()?, @@ -274,6 +274,23 @@ 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") @@ -304,21 +321,4 @@ mod test { } std::fs::remove_dir_all(cache_dir).unwrap(); } - - pub 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(()) - } } diff --git a/crates/cargo-gpu/src/spirv_cli.rs b/crates/cargo-gpu/src/spirv_cli.rs index 38dfb869..abe2e1f4 100644 --- a/crates/cargo-gpu/src/spirv_cli.rs +++ b/crates/cargo-gpu/src/spirv_cli.rs @@ -1,10 +1,16 @@ //! 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. +use std::io::Write as _; + use anyhow::Context as _; use crate::spirv_source::SpirvSource; +/// `Cargo.lock` manifest version 4 became the default in Rust 1.83.0. Conflicting manifest +/// versions between the workspace and the shader crate, can cause problems. +const RUST_VERSION_THAT_USES_V4_CARGO_LOCKS: &str = "1.83.0"; + /// Cargo dependency for `spirv-builder` and the rust toolchain channel. #[derive(Debug, Clone)] pub struct SpirvCli { @@ -23,6 +29,8 @@ pub struct SpirvCli { pub channel: String, /// The date of the pinned version of `rust-gpu` pub date: chrono::NaiveDate, + /// `Cargo.lock`s that have had their manifest versions changed by us and need changing back. + pub cargo_lock_files_with_changed_manifest_versions: Vec, /// Has the user overridden the toolchain consent prompt is_toolchain_install_consent: bool, } @@ -40,15 +48,39 @@ impl core::fmt::Display for SpirvCli { impl SpirvCli { /// Create instance pub fn new( - shader_crate_path: &std::path::PathBuf, + shader_crate_path: &std::path::Path, maybe_rust_gpu_source: Option, maybe_rust_gpu_version: Option, maybe_rust_gpu_channel: Option, is_toolchain_install_consent: bool, + is_force_overwrite_lockfiles_v4_to_v3: bool, ) -> anyhow::Result { + let mut cargo_lock_files_with_changed_manifest_versions = vec![]; + + let maybe_shader_crate_lock = + Self::ensure_workspace_rust_version_doesnt_conflict_with_shader( + shader_crate_path, + is_force_overwrite_lockfiles_v4_to_v3, + )?; + + if let Some(shader_crate_lock) = maybe_shader_crate_lock { + cargo_lock_files_with_changed_manifest_versions.push(shader_crate_lock); + } + let (default_rust_gpu_source, rust_gpu_date, default_rust_gpu_channel) = SpirvSource::get_rust_gpu_deps_from_shader(shader_crate_path)?; + let maybe_workspace_crate_lock = + Self::ensure_shader_rust_version_doesnt_conflict_with_any_cargo_locks( + shader_crate_path, + default_rust_gpu_channel.clone(), + is_force_overwrite_lockfiles_v4_to_v3, + )?; + + if let Some(workspace_crate_lock) = maybe_workspace_crate_lock { + cargo_lock_files_with_changed_manifest_versions.push(workspace_crate_lock); + } + 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()); @@ -66,6 +98,7 @@ impl SpirvCli { channel: maybe_rust_gpu_channel.unwrap_or(default_rust_gpu_channel), date: rust_gpu_date, is_toolchain_install_consent, + cargo_lock_files_with_changed_manifest_versions, }) } @@ -171,7 +204,6 @@ impl SpirvCli { crate::user_output!("{prompt} [y/n]: "); let input = crossterm::event::read()?; crossterm::terminal::disable_raw_mode()?; - crate::user_output!("{:?}\n", input); if let crossterm::event::Event::Key(crossterm::event::KeyEvent { code: crossterm::event::KeyCode::Char('y'), @@ -184,6 +216,242 @@ impl SpirvCli { std::process::exit(0); } } + + /// See docs for `force_overwrite_lockfiles_v4_to_v3` flag for why we do this. + fn ensure_workspace_rust_version_doesnt_conflict_with_shader( + shader_crate_path: &std::path::Path, + is_force_overwrite_lockfiles_v4_to_v3: bool, + ) -> anyhow::Result> { + log::debug!("Ensuring no v3/v4 `Cargo.lock` conflicts from workspace Rust..."); + let workspace_rust_version = Self::get_rustc_version(None)?; + if version_check::Version::at_least( + &workspace_rust_version, + RUST_VERSION_THAT_USES_V4_CARGO_LOCKS, + ) { + log::debug!( + "user's Rust is v{}, so no v3/v4 conflicts possible.", + workspace_rust_version + ); + return Ok(None); + } + + Self::handle_conflicting_cargo_lock_v4( + shader_crate_path, + is_force_overwrite_lockfiles_v4_to_v3, + )?; + + if is_force_overwrite_lockfiles_v4_to_v3 { + Ok(Some(shader_crate_path.join("Cargo.lock"))) + } else { + Ok(None) + } + } + + /// See docs for `force_overwrite_lockfiles_v4_to_v3` flag for why we do this. + fn ensure_shader_rust_version_doesnt_conflict_with_any_cargo_locks( + shader_crate_path: &std::path::Path, + channel: String, + is_force_overwrite_lockfiles_v4_to_v3: bool, + ) -> anyhow::Result> { + log::debug!("Ensuring no v3/v4 `Cargo.lock` conflicts from shader's Rust..."); + let shader_rust_version = Self::get_rustc_version(Some(channel))?; + if version_check::Version::at_least( + &shader_rust_version, + RUST_VERSION_THAT_USES_V4_CARGO_LOCKS, + ) { + log::debug!( + "shader's Rust is v{}, so no v3/v4 conflicts possible.", + shader_rust_version + ); + return Ok(None); + } + + log::debug!( + "shader's Rust is v{}, so checking both shader and workspace `Cargo.lock` manifest versions...", + shader_rust_version + ); + + if shader_crate_path.join("Cargo.lock").exists() { + // Note that we don't return the `Cargo.lock` here (so that it's marked for reversion + // after the build), because we can be sure that updating it now is actually updating it + // to the state it should have been all along. Therefore it doesn't need reverting once + // fixed. + Self::handle_conflicting_cargo_lock_v4( + shader_crate_path, + is_force_overwrite_lockfiles_v4_to_v3, + )?; + } + + if let Some(workspace_root) = Self::get_workspace_root(shader_crate_path)? { + Self::handle_conflicting_cargo_lock_v4( + workspace_root, + is_force_overwrite_lockfiles_v4_to_v3, + )?; + return Ok(Some(workspace_root.join("Cargo.lock"))); + } + + Ok(None) + } + + /// Get the path to the shader crate's workspace, if it has one. We can't use the traditional + /// `cargo metadata` because if the workspace has a conflicting `Cargo.lock` manifest version + /// then that command won't work. Instead we do an old school recursive file tree walk. + fn get_workspace_root( + shader_crate_path: &std::path::Path, + ) -> anyhow::Result> { + let shader_cargo_toml = std::fs::read_to_string(shader_crate_path.join("Cargo.toml"))?; + if !shader_cargo_toml.contains("workspace = true") { + return Ok(None); + } + + let mut current_path = shader_crate_path; + #[expect(clippy::default_numeric_fallback, reason = "It's just a loop")] + for _ in 0..15 { + if let Some(parent_path) = current_path.parent() { + if parent_path.join("Cargo.lock").exists() { + return Ok(Some(parent_path)); + } + current_path = parent_path; + } else { + break; + } + } + + Ok(None) + } + + /// When Rust < 1.83.0 is being used an error will occur if it tries to parse `Cargo.lock` + /// files that use lockfile manifest version 4. Here we check and handle that. + fn handle_conflicting_cargo_lock_v4( + folder: &std::path::Path, + is_force_overwrite_lockfiles_v4_to_v3: bool, + ) -> anyhow::Result<()> { + let shader_cargo_lock_path = folder.join("Cargo.lock"); + let shader_cargo_lock = std::fs::read_to_string(shader_cargo_lock_path.clone())?; + let third_line = shader_cargo_lock.lines().nth(2).context("")?; + if third_line.contains("version = 4") { + Self::handle_v3v4_conflict( + &shader_cargo_lock_path, + is_force_overwrite_lockfiles_v4_to_v3, + )?; + return Ok(()); + } + if third_line.contains("version = 3") { + return Ok(()); + } + anyhow::bail!( + "Unrecognized `Cargo.lock` manifest version at: {}", + folder.display() + ) + } + + /// Handle conflicting `Cargo.lock` manifest versions by either overwriting the manifest + /// version or exiting with advice on how to handle the conflict. + fn handle_v3v4_conflict( + offending_cargo_lock: &std::path::Path, + is_force_overwrite_lockfiles_v4_to_v3: bool, + ) -> anyhow::Result<()> { + if !is_force_overwrite_lockfiles_v4_to_v3 { + Self::exit_with_v3v4_hack_suggestion(); + } + + Self::replace_cargo_lock_manifest_version(offending_cargo_lock, "4", "3")?; + + Ok(()) + } + + /// Once all install and builds have completed put their manifest versions back to how they + /// were. + pub fn revert_cargo_lock_manifest_versions(&self) -> anyhow::Result<()> { + for offending_cargo_lock in &self.cargo_lock_files_with_changed_manifest_versions { + log::debug!("Reverting: {}", offending_cargo_lock.display()); + Self::replace_cargo_lock_manifest_version(offending_cargo_lock, "3", "4")?; + } + + Ok(()) + } + + /// Replace the manifest version, eg `version = 4`, in a `Cargo.lock` file. + fn replace_cargo_lock_manifest_version( + offending_cargo_lock: &std::path::Path, + from_version: &str, + to_version: &str, + ) -> anyhow::Result<()> { + log::warn!( + "Replacing manifest version 'version = {}' with 'version = {}' in: {}", + from_version, + to_version, + offending_cargo_lock.display() + ); + let old_contents = std::fs::read_to_string(offending_cargo_lock)?; + let new_contents = old_contents.replace( + &format!("\nversion = {from_version}\n"), + &format!("\nversion = {to_version}\n"), + ); + + let mut file = std::fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(offending_cargo_lock)?; + file.write_all(new_contents.as_bytes())?; + + Ok(()) + } + + /// Exit and give the user advice on how to deal with the infamous v3/v4 Cargo lockfile version + /// problem. + #[expect(clippy::non_ascii_literal, reason = "It's CLI output")] + fn exit_with_v3v4_hack_suggestion() { + crate::user_output!( + "Conflicting `Cargo.lock` versions detected ⚠️\n\ + Because `cargo gpu` uses a dedicated Rust toolchain for compiling shaders\n\ + it's possible that the `Cargo.lock` manifest version of the shader crate\n\ + does not match the `Cargo.lock` manifest version of the workspace. This is\n\ + due to a change in the defaults introduced in Rust 1.83.0.\n\ + \n\ + One way to resolve this is to force the workspace to use the same version\n\ + of Rust as required by the shader. However that is not often ideal or even\n\ + possible. Another way is to exlude the shader from the workspace. This is\n\ + also not ideal if you have many shaders sharing config from the workspace.\n\ + \n\ + Therefore `cargo gpu build/install` offers a workaround with the argument:\n\ + --force-overwrite-lockfiles-v4-to-v3\n\ + \n\ + See `cargo gpu build --help` for more information.\n\ + " + ); + std::process::exit(1); + } + + /// Get the version of `rustc`. + fn get_rustc_version( + maybe_toolchain: Option, + ) -> anyhow::Result { + let mut maybe_current_env_toolchain: Option = None; + if let Some(toolchain) = maybe_toolchain { + maybe_current_env_toolchain = std::env::var_os("RUSTUP_TOOLCHAIN"); + std::env::set_var("RUSTUP_TOOLCHAIN", toolchain); + } + + let Some(version) = version_check::Version::read() else { + anyhow::bail!("Couldn't get `rustc --version`"); + }; + + if let Some(current_env_toolchain) = maybe_current_env_toolchain { + std::env::set_var("RUSTUP_TOOLCHAIN", current_env_toolchain); + } + + Ok(version) + } +} + +impl Drop for SpirvCli { + fn drop(&mut self) { + let result = self.revert_cargo_lock_manifest_versions(); + if let Err(error) = result { + log::error!("Couldn't revert some or all of the shader `Cargo.lock` files: {error}"); + } + } } #[cfg(test)] @@ -195,7 +463,7 @@ mod test { let shader_template_path = crate::test::shader_crate_template_path(); // TODO: This downloads the `rust-gpu` repo which slows the test down. Can we avoid that // just to get the sanity check? - let spirv = SpirvCli::new(&shader_template_path, None, None, None, true).unwrap(); + let spirv = SpirvCli::new(&shader_template_path, None, None, None, true, false).unwrap(); let dir = spirv.cached_checkout_path().unwrap(); let name = dir .file_name() diff --git a/crates/cargo-gpu/src/spirv_source.rs b/crates/cargo-gpu/src/spirv_source.rs index 498e20ea..ec4526e6 100644 --- a/crates/cargo-gpu/src/spirv_source.rs +++ b/crates/cargo-gpu/src/spirv_source.rs @@ -50,18 +50,20 @@ impl core::fmt::Display for SpirvSource { 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, + pub fn get_rust_gpu_deps_from_shader>( + shader_crate_path: F, ) -> anyhow::Result<(Self, chrono::NaiveDate, String)> { - let rust_gpu_source = Self::get_spirv_std_dep_definition(shader_crate_path)?; - + let rust_gpu_source = Self::get_spirv_std_dep_definition(shader_crate_path.as_ref())?; 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}"); + log::debug!( + "Parsed version, date and toolchain channel from shader-defined `rust-gpu`: \ + {rust_gpu_source:?}, {date}, {channel}" + ); Ok((rust_gpu_source, date, channel)) } @@ -91,6 +93,30 @@ impl SpirvSource { Ok(crate::cache_dir()?.join("rust-gpu-repo").join(dir)) } + /// Make sure shader crate path is absolute and canonical. + fn shader_crate_path_canonical( + shader_crate_path: &mut std::path::PathBuf, + ) -> anyhow::Result<()> { + let cwd = std::env::current_dir().context("no cwd")?; + let mut canonical_path = shader_crate_path.clone(); + + if !canonical_path.is_absolute() { + canonical_path = cwd.join(canonical_path); + } + canonical_path + .canonicalize() + .context("could not get absolute path to shader crate")?; + + if !canonical_path.is_dir() { + log::error!("{shader_crate_path:?} is not a directory, aborting"); + anyhow::bail!("{shader_crate_path:?} is not a directory"); + }; + + *shader_crate_path = canonical_path; + + Ok(()) + } + /// Checkout the `rust-gpu` repo to the requested version. fn checkout(&self) -> anyhow::Result<()> { log::debug!( @@ -171,31 +197,27 @@ impl SpirvSource { Ok(channel.to_string().replace('"', "")) } - /// Get the shader crate's `spirv_std = ...` definition in its `Cargo.toml` + /// Get the shader crate's resolved `spirv_std = ...` definition in its `Cargo.toml`/`Cargo.lock` pub fn get_spirv_std_dep_definition( - shader_crate_path: &std::path::PathBuf, + shader_crate_path: &std::path::Path, ) -> anyhow::Result { - let cwd = std::env::current_dir().context("no cwd")?; - let exec_path = if shader_crate_path.is_absolute() { - shader_crate_path.clone() - } else { - cwd.join(shader_crate_path) - } - .canonicalize() - .context("could not get absolute path to shader crate")?; - if !exec_path.is_dir() { - log::error!("{exec_path:?} is not a directory, aborting"); - anyhow::bail!("{exec_path:?} is not a directory"); - } + let canonical_shader_path = shader_crate_path.to_path_buf(); + Self::shader_crate_path_canonical(&mut canonical_shader_path.clone())?; - log::debug!("Running `cargo tree` on {}", exec_path.display()); + log::debug!( + "Running `cargo tree` on {}", + canonical_shader_path.display() + ); let output_cargo_tree = std::process::Command::new("cargo") - .current_dir(&exec_path) + .current_dir(canonical_shader_path.clone()) .args(["tree", "--workspace", "--prefix", "none"]) .output()?; anyhow::ensure!( output_cargo_tree.status.success(), - "could not query shader's `Cargo.toml` for `spirv-std` dependency" + format!( + "could not query shader's `Cargo.toml` for `spirv-std` dependency: {}", + String::from_utf8(output_cargo_tree.stderr)? + ) ); let cargo_tree_string = String::from_utf8_lossy(&output_cargo_tree.stdout); @@ -205,7 +227,7 @@ impl SpirvSource { log::trace!(" found {maybe_spirv_std_def:?}"); let Some(spirv_std_def) = maybe_spirv_std_def else { - anyhow::bail!("`spirv-std` not found in shader's `Cargo.toml` at {exec_path:?}:\n{cargo_tree_string}"); + anyhow::bail!("`spirv-std` not found in shader's `Cargo.toml` at {canonical_shader_path:?}:\n{cargo_tree_string}"); }; Self::parse_spirv_std_source_and_version(spirv_std_def) @@ -239,8 +261,8 @@ impl SpirvSource { // So here we'll parse the fragment out of the source string by hand let uri = source_string.parse::()?; let maybe_hash = if source_string.contains('#') { - let splits = source_string.split('#'); - splits.last().map(std::borrow::ToOwned::to_owned) + let mut splits = source_string.split('#'); + splits.next_back().map(std::borrow::ToOwned::to_owned) } else { None }; @@ -322,6 +344,7 @@ impl SpirvSource { crate::user_output!("Cloning `rust-gpu` repo...\n"); + // TODO: do something else when testing, to help speed things up. let output_clone = std::process::Command::new("git") .args([ "clone", diff --git a/crates/shader-crate-template/Cargo.lock b/crates/shader-crate-template/Cargo.lock index 8a308596..8a7a320e 100644 --- a/crates/shader-crate-template/Cargo.lock +++ b/crates/shader-crate-template/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "autocfg" diff --git a/crates/shader-crate-template/Cargo.toml b/crates/shader-crate-template/Cargo.toml index 1261d9ff..fb72e0ee 100644 --- a/crates/shader-crate-template/Cargo.toml +++ b/crates/shader-crate-template/Cargo.toml @@ -8,7 +8,7 @@ crate-type = ["rlib", "cdylib"] # Dependencies for CPU and GPU code [dependencies] -# "v0.9" doesn't seem to compile on windows? +# TODO: use a simple crate version once v0.10.0 is released spirv-std = { git = "https://github.com/Rust-GPU/rust-gpu", rev = "82a0f69" } # Dependencies for GPU code @@ -92,3 +92,25 @@ manifest-file = "manifest.json" auto-install-rust-toolchain = false # Force `spirv-builder-cli` and `rustc_codegen_spirv` to be rebuilt. force-spirv-cli-rebuild = false +# There is a tricky situation where a shader crate that depends on workspace config can have +# a different `Cargo.lock` lockfile version from the the workspace's `Cargo.lock`. This can +# prevent builds when an old Rust toolchain doesn't recognise the newer lockfile version. +# +# The ideal way to resolve this would be to match the shader crate's toolchain with the +# workspace's toolchain. However, that is not always possible. Another solution is to +# `exclude = [...]` the problematic shader crate from the workspace. This also may not be a +# suitable solution if there are a number of shader crates all sharing similar config and +# you don't want to have to copy/paste and maintain that config across all the shaders. +# +# So a somewhat hacky workaround is to have `cargo gpu` overwrite lockfile versions. Enabling +# this flag will only come into effect if there are a mix of v3/v4 lockfiles. It will also +# only overwrite versions for the duration of a build. It will attempt to return the versions +# to their original values once the build is finished. However, of course, unexpected errors +# can occur and the overwritten values can remain. Hence why this behaviour is not enabled by +# default. +# +# This hack is possible because the change from v3 to v4 only involves a minor change to the +# way source URLs are encoded. See these PRs for more details: +# * https://github.com/rust-lang/cargo/pull/12280 +# * https://github.com/rust-lang/cargo/pull/14595 +force-overwrite-lockfiles-v4-to-v3 = false diff --git a/crates/spirv-builder-cli/Cargo.lock b/crates/spirv-builder-cli/Cargo.lock index 9c8789ca..ae537474 100644 --- a/crates/spirv-builder-cli/Cargo.lock +++ b/crates/spirv-builder-cli/Cargo.lock @@ -63,11 +63,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -97,18 +98,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" dependencies = [ "serde", ] [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -118,9 +119,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.37" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -135,9 +136,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.23" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", @@ -145,9 +146,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", @@ -157,21 +158,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "colorchoice" @@ -210,7 +211,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -225,7 +226,7 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98e71ae4df57d214182a2e5cb90230c0192c6ddfcaa05c36453d46a54713e10" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "stable_deref_trait", ] @@ -312,15 +313,15 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" -version = "0.5.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -346,12 +347,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", ] [[package]] @@ -408,9 +409,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" @@ -449,9 +450,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.162" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libredox" @@ -529,18 +530,18 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -574,9 +575,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -729,35 +730,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -820,7 +821,7 @@ dependencies = [ "bytemuck", "derive_more", "elsa", - "indexmap 2.6.0", + "indexmap 2.7.0", "internal-iterator", "itertools", "lazy_static", @@ -848,7 +849,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "serde", ] @@ -923,9 +924,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "strsim" -version = "0.11.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" @@ -940,9 +941,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -985,7 +986,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -994,9 +995,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "utf8parse" @@ -1251,9 +1252,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] diff --git a/crates/spirv-builder-cli/Cargo.toml b/crates/spirv-builder-cli/Cargo.toml index 2bb6680e..2cbd1350 100644 --- a/crates/spirv-builder-cli/Cargo.toml +++ b/crates/spirv-builder-cli/Cargo.toml @@ -4,7 +4,10 @@ version = "0.1.0" edition = "2021" [dependencies] -clap = { version = "4.4.8", features = ["derive"] } +# `clap = "=4.4.8"` pin is needed to support older Rust toolchains. +# If `clap` gets too old, then it can be split into 2 versions, gated by a feature, like `spirv` is. +# Or more generally we should think about how to deprecate supporting old version of `rust-gpu`. +clap = { version = "=4.4.8", features = ["derive"] } env_home = "0.1.0" env_logger = "0.10" log = "0.4" diff --git a/crates/spirv-builder-cli/src/args.rs b/crates/spirv-builder-cli/src/args.rs index 8ce6a8b6..dad50d65 100644 --- a/crates/spirv-builder-cli/src/args.rs +++ b/crates/spirv-builder-cli/src/args.rs @@ -176,4 +176,28 @@ pub struct InstallArgs { /// Assume "yes" to "Install Rust toolchain: [y/n]" prompt. #[clap(long, action)] pub auto_install_rust_toolchain: bool, + + /// There is a tricky situation where a shader crate that depends on workspace config can have + /// a different `Cargo.lock` lockfile version from the the workspace's `Cargo.lock`. This can + /// prevent builds when an old Rust toolchain doesn't recognise the newer lockfile version. + /// + /// The ideal way to resolve this would be to match the shader crate's toolchain with the + /// workspace's toolchain. However, that is not always possible. Another solution is to + /// `exclude = [...]` the problematic shader crate from the workspace. This also may not be a + /// suitable solution if there are a number of shader crates all sharing similar config and + /// you don't want to have to copy/paste and maintain that config across all the shaders. + /// + /// So a somewhat hacky workaround is to have `cargo gpu` overwrite lockfile versions. Enabling + /// this flag will only come into effect if there are a mix of v3/v4 lockfiles. It will also + /// only overwrite versions for the duration of a build. It will attempt to return the versions + /// to their original values once the build is finished. However, of course, unexpected errors + /// can occur and the overwritten values can remain. Hence why this behaviour is not enabled by + /// default. + /// + /// This hack is possible because the change from v3 to v4 only involves a minor change to the + /// way source URLs are encoded. See these PRs for more details: + /// * https://github.com/rust-lang/cargo/pull/12280 + /// * https://github.com/rust-lang/cargo/pull/14595 + #[clap(long, action, verbatim_doc_comment)] + pub force_overwrite_lockfiles_v4_to_v3: bool, } diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index f486fde4..3f4d0905 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -1,16 +1,26 @@ //! Project/repository utilities. -#![allow(clippy::shadow_reuse, reason = "sometimes its nice")] -#![allow(clippy::unwrap_used, reason = "sometimes its good")] -#![allow(clippy::unwrap_in_result, reason = "sometimes that's what you want")] +#![allow( + clippy::shadow_reuse, + clippy::unwrap_used, + clippy::unwrap_in_result, + reason = "This is just a workflow tool" +)] use anyhow::Context as _; use clap::Parser as _; +/// Path to the shader crate +const SHADER_CRATE_PATH: &str = "crates/shader-crate-template"; + /// Our xtask commands. #[derive(Debug, clap::Parser)] enum Cli { /// Run a test build of the shader-crate-template project. - TestBuild, + TestBuild { + /// Build using the specified version of `spirv-std`. + #[clap(long)] + rust_gpu_version: Option, + }, } fn cmd(args: impl IntoIterator>) -> anyhow::Result<()> { @@ -34,6 +44,8 @@ fn cmd(args: impl IntoIterator>) -> anyhow::Result<()> { struct ShaderCrateTemplateCargoTomlWriter { /// Original string original_shader_crate_template_str: String, + /// Original lockfile + original_shader_crate_lock_file: String, /// Parsed toml table table: toml::Table, } @@ -41,26 +53,46 @@ struct ShaderCrateTemplateCargoTomlWriter { impl Drop for ShaderCrateTemplateCargoTomlWriter { fn drop(&mut self) { log::info!("reverting overwrite of Cargo.toml"); - std::fs::write(Self::PATH, &self.original_shader_crate_template_str).unwrap(); + std::fs::write( + format!("{SHADER_CRATE_PATH}/Cargo.toml"), + &self.original_shader_crate_template_str, + ) + .unwrap(); + log::info!("reverting overwrite of Cargo.lock"); + std::fs::write( + format!("{SHADER_CRATE_PATH}/Cargo.lock"), + &self.original_shader_crate_lock_file, + ) + .unwrap(); } } impl ShaderCrateTemplateCargoTomlWriter { - /// Path to the Cargo.toml - const PATH: &str = "crates/shader-crate-template/Cargo.toml"; - /// Create a new one fn new() -> Self { - let original_shader_crate_template_str = std::fs::read_to_string(Self::PATH).unwrap(); + let original_shader_crate_template_str = + std::fs::read_to_string(format!("{SHADER_CRATE_PATH}/Cargo.toml")).unwrap(); let table = toml::from_str::(&original_shader_crate_template_str).unwrap(); + let original_shader_crate_lock_file = + std::fs::read_to_string(format!("{SHADER_CRATE_PATH}/Cargo.lock")).unwrap(); Self { original_shader_crate_template_str, + original_shader_crate_lock_file, table, } } - /// Replace the output-dir - fn replace_output_dir(&mut self, path: impl AsRef) -> anyhow::Result<()> { + /// Get the `[dependencies]` section of the shader's `Cargo.toml`. + fn get_cargo_dependencies_table(&mut self) -> &mut toml::Table { + self.table + .get_mut("dependencies") + .unwrap() + .as_table_mut() + .unwrap() + } + + /// Get the `[package.metadata.rust-gpu.build]` section of the shader's `Cargo.toml`. + fn get_rust_gpu_table(&mut self) -> &mut toml::Table { let package = self .table .get_mut("package") @@ -68,21 +100,42 @@ impl ShaderCrateTemplateCargoTomlWriter { .as_table_mut() .unwrap(); let metadata = package.get_mut("metadata").unwrap().as_table_mut().unwrap(); - let rust_gpu = metadata + metadata .get_mut("rust-gpu") .unwrap() .as_table_mut() - .unwrap(); - let build = rust_gpu.get_mut("build").unwrap().as_table_mut().unwrap(); - let output_dir = build.get_mut("output-dir").unwrap(); - *output_dir = toml::Value::String(format!("{}", path.as_ref().display())); + .unwrap() + } + + /// Write any temporary changes to the shader crate's `Cargo.toml` that are needed to run e2e + /// tests. + fn write_shader_crate_cargo_toml_changes(&self) -> anyhow::Result<()> { std::fs::write( - Self::PATH, + format!("{SHADER_CRATE_PATH}/Cargo.toml"), toml::to_string_pretty(&self.table).context("could not serialize")?, ) .context("could not overwrite path")?; Ok(()) } + + /// Replace the output-dir + fn replace_output_dir(&mut self, path: impl AsRef) -> anyhow::Result<()> { + let rust_gpu = self.get_rust_gpu_table(); + let build = rust_gpu.get_mut("build").unwrap().as_table_mut().unwrap(); + let output_dir = build.get_mut("output-dir").unwrap(); + *output_dir = toml::Value::String(format!("{}", path.as_ref().display())); + self.write_shader_crate_cargo_toml_changes()?; + Ok(()) + } + + /// Replace the `spirv-std` dependency version + fn replace_spirv_std_version(&mut self, version: String) -> anyhow::Result<()> { + let dependencies = self.get_cargo_dependencies_table(); + let spirv_std = dependencies.get_mut("spirv-std").unwrap(); + *spirv_std = toml::Value::String(version); + self.write_shader_crate_cargo_toml_changes()?; + Ok(()) + } } /// Run the xtask. @@ -92,7 +145,9 @@ fn main() { let cli = Cli::parse(); match cli { - Cli::TestBuild => { + Cli::TestBuild { + rust_gpu_version: maybe_rust_gpu_version, + } => { log::info!("installing cargo gpu"); cmd(["cargo", "install", "--path", "crates/cargo-gpu"]).unwrap(); @@ -102,8 +157,9 @@ fn main() { "gpu", "install", "--shader-crate", - "crates/shader-crate-template", + SHADER_CRATE_PATH, "--auto-install-rust-toolchain", + "--force-overwrite-lockfiles-v4-to-v3", ]) .unwrap(); @@ -111,13 +167,23 @@ fn main() { let mut overwriter = ShaderCrateTemplateCargoTomlWriter::new(); overwriter.replace_output_dir(dir.path()).unwrap(); + if let Some(rust_gpu_version) = maybe_rust_gpu_version { + if rust_gpu_version != "latest" { + overwriter + .replace_spirv_std_version(rust_gpu_version) + .unwrap(); + } + } + cmd([ "cargo", "gpu", "build", "--shader-crate", - "crates/shader-crate-template", + SHADER_CRATE_PATH, + "--auto-install-rust-toolchain", "--force-spirv-cli-rebuild", + "--force-overwrite-lockfiles-v4-to-v3", ]) .unwrap();