Switching Toolchains in build.rs: Use Nightly for Data Generation and Stable for Main Compilation

Hi everyone,

I'm working on a Rust project where I need to:

  1. Compile a Rust tool within build.rs using the nightly toolchain (due to some compiler hackery that requires nightly).
  2. Use this tool to generate some data and store it in std::env::temp_dir.
  3. Include this generated data in the main crate during compilation, which should be compiled with the stable toolchain.

I've tried multiple approaches to set the toolchain as described in the rustup overrides guide. However, every time I run the main crate's build.rs with the stable toolchain, it also tries to compile the tool (meant for data generation) with the stable toolchain, which fails due to the nightly dependencies.

Here is a simplified version of my build.rs:

use std::{path::PathBuf, process::Command};

fn main() {
    let output = Command::new("cargo")
        .arg("+nightly")
        .arg("run")
        .current_dir(PathBuf::from("/absolute/path/to/tool"))
        .output()
        .unwrap();

    println!("cargo:warning={output:?}");
}

I've also tried setting the toolchain using rustup default, rustup override, and the environment variable RUSTUP_TOOLCHAIN. Interestingly, if I run this code outside build.rs (e.g., in src/main.rs), it works perfectly. This leads me to believe there's something specific about build.rs that I'm missing.

Has anyone encountered this issue before or have any ideas on how to properly switch toolchains in build.rs? Any help or pointers would be greatly appreciated!

Thank you!

1 Like

Putting a rustup toolchain file where you specify you want to use nightly toolchain into your tool package's root directory should do the job, though on my machine your snippet with +nightly as an argument for Cargo works, too.

1 Like

You're right, I hadn't tried a minimal example of the problem like this:

// `main_crate/build.rs`
fn main() {
    let main_crate_out = std::process::Command::new("rustc")
        .arg("-vV")
        .output()
        .unwrap();

    println!("cargo:warning=MAIN CRATE {main_crate_out:?}");

    let tool_out = std::process::Command::new("cargo")
        .arg("+nightly")
        .arg("run")
        .current_dir("../tool")
        .output()
        .unwrap();

    println!("cargo:warning=TOOL {tool_out:?}");
}

// `tool/src/main.rs`
fn main() {
    let out = std::process::Command::new("rustc")
        .arg("-vV")
        .output()
        .unwrap();

    println!("{out:?}");
}

This works as expected. So, the problem seems to be related to the tool I am using: rust gpu. I guess I need to look into the inner workings of the crate.

By the way, you should use rustup run nightly cargo run ... instead of cargo +nightly run .... The former works as long as rustup is installed at all, whereas the latter might not if PATH isn't configured to prefer rustup's proxies.

2 Likes

I found a solution; it's not elegant, but it works.

In one of the crates of rust-gpu, they use the RUSTC environment variable (specifically here: code line). I compiled this crate locally and added debug prints before that line, similar to what I did earlier with Command::new("rustc").arg("-vV"). Note: I used the plain command rustc instead of the RUSTC environment variable. This correctly prints the desired nightly toolchain, but it crashes afterward because RUSTC still points to the stable toolchain.

My workaround is to run the cargo build command with env_remove("RUSTC"). This works because, in the mentioned code line, it defaults to rustc, which correctly uses the nightly toolchain.

The final question is: Why is the RUSTC environment variable not updated? According to the Cargo documentation, it should be updated if I understand correctly.

Cargo sets RUSTC only for build scripts, not for commands like cargo run or cargo build (it only reads it in those cases, defaulting to rustc if the variable is not present or otherwise overridden by Cargo's configuration). So if you invoke Cargo from a build script as a subcommand, it will have the RUSTC variable present that was set by Cargo for the build script.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.