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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 22 additions & 46 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"crates/cargo-gpu",
"crates/cargo-gpu-install",
"crates/xtask",
]

Expand All @@ -14,11 +15,20 @@ exclude = [

resolver = "2"

[workspace.package]
version = "0.1.0"
edition = "2021"
repository = "https://github.com/Rust-GPU/cargo-gpu"
readme = "./README.md"
keywords = ["gpu", "compiler", "rust-gpu"]
license = "MIT OR Apache-2.0"

[workspace.dependencies]
spirv-builder = { git = "https://github.com/Rust-GPU/rust-gpu", rev = "823b0c275e3eb8ab44ada5a6d989bdb57e8f9409", default-features = false }
cargo-gpu-install = { path = "./crates/cargo-gpu-install" }
anyhow = "1.0.98"
clap = { version = "4.5.41", features = ["derive"] }
crossterm = "0.29.0"
crossterm = { version = "0.29.0", default-features = false, features = ["events", "windows"] }
directories = "6.0.0"
env_logger = "0.11.8"
log = "0.4"
Expand Down
34 changes: 34 additions & 0 deletions crates/cargo-gpu-install/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "cargo-gpu-install"
version.workspace = true
edition.workspace = true
description = "Install rust-gpu and it's required toolchain automatically"
repository.workspace = true
readme.workspace = true
keywords.workspace = true
license.workspace = true

[features]
clap = ["dep:clap", "spirv-builder/clap"]
watch = ["spirv-builder/watch"]
test = ["dep:tempfile"]
tty = ["dep:crossterm"]

[dependencies]
cargo_metadata.workspace = true
anyhow.workspace = true
spirv-builder.workspace = true
clap = { workspace = true, optional = true }
directories.workspace = true
log.workspace = true
serde.workspace = true
crossterm = { workspace = true, optional = true }
tempfile = { workspace = true, optional = true }

[dev-dependencies]
test-log.workspace = true
cargo_metadata = { workspace = true, features = ["builder"] }
cargo-util-schemas = "0.8.2"

[lints]
workspace = true
43 changes: 43 additions & 0 deletions crates/cargo-gpu-install/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# cargo-gpu-install

`cargo-gpu-install` is the install action of `cargo-gpu`, separated into its own crate. It's intended to be used
by build scripts and other binaries that need automated installation of rust-gpu and it's required toolchain,
without having to pull all the other cli dependencies of the full `cargo-gpu` (like clap).

## Example

This is an example build script meant to be placed in your "main" std crate, to build a secondary no-std "shader" crate.
But you can just as well use this in your executable directly, with some minor adjustments.
```rust,no_run
# use std::path::PathBuf;
# use cargo_gpu_install::install::Install;
# use cargo_gpu_install::spirv_builder::SpirvMetadata;

pub fn main() -> Result<(), Box<dyn std::error::Error>> {
// path to your shader crate
let shader_crate = PathBuf::from("./shaders");

// install the toolchain and build the `rustc_codegen_spirv` codegen backend with it
let backend = Install::from_shader_crate(shader_crate.clone()).run()?;

// build the shader crate
let mut builder = backend.to_spirv_builder(shader_crate, "spirv-unknown-vulkan1.2");
// set to true when you're in a build script, false when outside one
builder.build_script.defaults = true;
Copy link
Member Author

@Firestar99 Firestar99 Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is already adjusted for Rust-GPU/rust-gpu#488, even though it is not yet in this branch. So this will fail doctests (which we don't test in ci...)

// enable debug information in the shaders
builder.spirv_metadata = SpirvMetadata::Full;
let spv_result = builder.build()?;
let path_to_spv = spv_result.module.unwrap_single();

// emit path to the artifact into env var, use it anywhere in your crate like:
// > include_str!(env!("MY_SHADER_PATH"))
println!(
"cargo::rustc-env=MY_SHADER_PATH={}",
path_to_spv.display()
);

// you could also generate some rust source code into the `std::env::var("OUT_DIR")` dir
// and use `include!(concat!(env!("OUT_DIR"), "/shader_symbols.rs"));` to include it
Ok(())
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,23 @@ 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 {
/// Cargo target to compile.
///
/// Conflicts with `--shader-crate`.
#[clap(short, long, conflicts_with("shader_crate"))]
#[cfg_attr(feature = "clap", clap(short, long, conflicts_with("shader_crate")))]
pub package: Option<String>,

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

#[expect(
Expand All @@ -75,30 +79,30 @@ 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<String>,

/// 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)]
#[cfg_attr(feature = "clap", clap(long, verbatim_doc_comment))]
pub spirv_builder_version: Option<String>,

/// 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
Expand All @@ -122,7 +126,7 @@ pub struct Install {
/// 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)]
#[cfg_attr(feature = "clap", clap(long, action, verbatim_doc_comment))]
pub force_overwrite_lockfiles_v4_to_v3: bool,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! toolchain installation logic

use anyhow::Context as _;
#[cfg(feature = "tty")]
use crossterm::tty::IsTty as _;

use crate::user_output;
Expand Down Expand Up @@ -98,6 +99,20 @@ pub fn ensure_toolchain_and_components_exist(
Ok(())
}

#[cfg(not(feature = "tty"))]
/// Prompt user if they want to install a new Rust toolchain.
fn get_consent_for_toolchain_install(
_prompt: &str,
skip_toolchain_install_consent: bool,
) -> anyhow::Result<()> {
if skip_toolchain_install_consent {
Ok(())
} else {
no_tty()
}
}

#[cfg(feature = "tty")]
/// Prompt user if they want to install a new Rust toolchain.
fn get_consent_for_toolchain_install(
prompt: &str,
Expand All @@ -108,10 +123,7 @@ fn get_consent_for_toolchain_install(
}

if !std::io::stdout().is_tty() {
user_output!("No TTY detected so can't ask for consent to install Rust toolchain.");
log::error!("Attempted to ask for consent when there's no TTY");
#[expect(clippy::exit, reason = "can't ask for user consent if there's no TTY")]
std::process::exit(1);
no_tty()
}

log::debug!("asking for consent to install the required toolchain");
Expand Down Expand Up @@ -143,3 +155,10 @@ fn get_consent_for_toolchain_install(
std::process::exit(0);
}
}

fn no_tty() -> ! {
user_output!("No TTY detected so can't ask for consent to install Rust toolchain.");
log::error!("Attempted to ask for consent when there's no TTY");
#[expect(clippy::exit, reason = "can't ask for user consent if there's no TTY")]
std::process::exit(1);
}
Loading