Skip to content

Make cargo-gpu a library, usable from build scripts #71

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 2, 2025
Merged

Conversation

Firestar99
Copy link
Member

@Firestar99 Firestar99 commented May 12, 2025

Requires #75 (and base retarget)

Build script API example:

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

    // install the toolchain and build the `rustc_codegen_spirv` codegen backend with it
    let backend = cargo_gpu::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");
    builder.print_metadata = MetadataPrintout::DependencyOnly;
    builder.spirv_metadata = SpirvMetadata::Full;
    let spv_result = builder.build()?;
    let path_to_spv = spv_result.module.unwrap_single();

    // emit path to wgsl into env var, used in `quad.rs` like this:
    // > 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(())
}

based on https://github.com/Firestar99/colorbubble/blob/eedd20ecc8155f9be15bc95b0fcd68b5002b94f3/build.rs

closes #67
closes #73

@Firestar99 Firestar99 force-pushed the library branch 2 times, most recently from a4d7b41 to 51eb0ac Compare May 26, 2025 16:46
@Firestar99 Firestar99 changed the base branch from main to updating-target-json-support May 28, 2025 11:57
@Firestar99 Firestar99 force-pushed the updating-target-json-support branch from 09edda2 to af18f7f Compare May 28, 2025 14:06
@Firestar99 Firestar99 force-pushed the updating-target-json-support branch 2 times, most recently from 8fc529a to fb10e19 Compare May 28, 2025 17:51
@Firestar99 Firestar99 force-pushed the updating-target-json-support branch from fb10e19 to fd5da3c Compare May 30, 2025 08:44
@Firestar99 Firestar99 force-pushed the library branch 2 times, most recently from 9557a7e to 0c0e40b Compare May 30, 2025 15:40
@Firestar99 Firestar99 marked this pull request as ready for review June 2, 2025 10:21
@Firestar99
Copy link
Member Author

@tombh @schell Feel free to review this one as well after #75 is merged (so that the base branch is updated to main)

/// Represents a functional backend installation, whether it was cached or just installed.
#[derive(Clone, Debug)]
#[expect(
clippy::exhaustive_structs,
Copy link
Collaborator

@tombh tombh Jun 2, 2025

Choose a reason for hiding this comment

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

This lint always confuses me. So I could be wrong. But I don't think it's to do with whether new private members will be added. Ideally, if this code is being called by anything outside of the project (let's say from outside the workspace for sake of argument), then it's useful to add #[non_exhaustive] which prevents external users from doing things that could unexpectedly break backwards compatibility, like struct construction, wildcard match (for enums), etc.

If the struct is only ever used within the crate, then the better solution here is to use pub (crate) (this will prevent the lint from triggering).

If the struct is only ever used by rust-gpu, then we can handle the fallout from "unexpected" new fields. In which case we can change the current reason = ... to something like, "this struct is only ever used by rust-gpu".

Copy link
Member Author

Choose a reason for hiding this comment

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

I would like people to be able to use the struct update syntax, but #[non_exhaustive] prevents that, as you could add private fields in the future, which would prevent external crates from using it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you elaborate on the private fields thing? As far as I understand it's about any new fields being added.

I completely agree that struct construction is a nice thing to have. But I think it's important to consider the expectations of library versioning. Basically I think if we're not using #[non_exhaustive] for public structs then we should take semantic versioning seriously, because of cargo's flexibility around the patch version. I think it's easier to use #[non_exhaustive] than to have to carefully consider minor/patch versions all the time.

Copy link
Member Author

Choose a reason for hiding this comment

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

You can only use struct update syntax on structs where you have access to all members of said struct. For an external crate, that means all members must be public. But if the struct has #[non_exhaustive] on it, you say that this struct may change in minor revisions, including you potentially adding private fields to it. So the rust devs decided that you cannot use struct update syntax on a struct that is non-exhaustive.

Copy link
Member Author

Choose a reason for hiding this comment

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

Also we'd have to enforce it on SpirvBuilder as well

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ohh, right that makes sense. Thanks.

So is the basic argument about whether we have a nice developer experience or whether we maintain good semantic versioning? I don't think we can have both, and in general we should lean towards being good crate ecosystem citizens. I don't know how much work it would be to support non_exhaustive, if it's a lot then yeah that's a fine reason to not worry about it. If there's another reason not to use it then I'm not seeing it yet. Like I say, the lint always confuses me so I'd like to learn.

Copy link
Member Author

@Firestar99 Firestar99 Jun 2, 2025

Choose a reason for hiding this comment

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

You'd then have to write this instead: (which isn't a big deal, I just like struct update syntax)

let mut builder = backend.to_spirv_builder(shader_crate, "spirv-unknown-vulkan1.2");
builder.print_metadata = MetadataPrintout::DependencyOnly;
builder.spirv_metadata = SpirvMetadata::Full;

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sure, I get that, and I agree it's much more ergonomic. But the other side is that we're neglecting Rust library conventions. In which case I don't see how developer aesthetics is a relevant point in the context of application maintenance. Here's an overly dramatic way to put it: nice code isn't a reason to unexpectedly break builds.

I don't want to stop you doing this! I just want to make sure I understand.

Copy link
Member Author

Choose a reason for hiding this comment

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

I made it #[non_exhaustive], though it'd need SpirvBuilder to also be. Let's do that in a followup

impl InstalledBackend {
/// Creates a new `SpirvBuilder` configured to use this installed backend.
#[expect(
clippy::missing_panics_doc,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is InstalledBackend truly completely public? Or just within the crate? If it's just public within the crate then pub (crate) removes the missing_panic_docs lint warning.

Copy link
Member Author

Choose a reason for hiding this comment

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

From the example above, backend: InstalledBackend.

    // install the toolchain and build the `rustc_codegen_spirv` codegen backend with it
    let backend = cargo_gpu::Install::from_shader_crate(shader_crate.clone()).run()?;
    [...]
    backend.to_spirv_builder(shader_crate, "spirv-unknown-vulkan1.2")

I like to give external crates access to the paths, in case they need to hack something. Though you could argue that we may want to #[non_exhaustive] that one, as in theory external crates shouldn't be touching it, but just call this function on it to create a new SpirvBuilder.

@tombh
Copy link
Collaborator

tombh commented Jun 2, 2025

Oh, you wrote after #75 is merged. Hopefully the comments are relevant anyway.

@Firestar99
Copy link
Member Author

I just want to make sure you don't hit the merge button, as it's not required to even get an approved review to merge into non-master branches.

Base automatically changed from updating-target-json-support to main June 2, 2025 14:42
@Firestar99 Firestar99 merged commit b497234 into main Jun 2, 2025
6 checks passed
@Firestar99 Firestar99 deleted the library branch June 2, 2025 15:42
@Firestar99
Copy link
Member Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

discussion: ask for toolchain installation cargo-gpu as a library
2 participants