Skip to content

Provide a way for build scripts to invoke rustc the way cargo would #11244

Closed as not planned
@RalfJung

Description

@RalfJung

Problem

It is a common pattern in build scripts to run a rustc 'build probe': compile some code with rustc, see if that works, and then enable some cfg flags depending on the result. Crates use this to automatically detect if some API they optionally use is available on the current compiler. Examples of this include anhow, thiserror, eyre, and the autocfg crate which abstracts that pattern into a library.

However, invoking rustc in the right way is non-trivial. build scripts have to remember to check RUSTC and RUSTC_WRAPPER, to pass on the RUSTFLAGS (using CARGO_ENCODED_RUSTFLAGS where possible so spaces are preserved properly), and to set --target based on the TARGET env var. And even then the final rustc invocation is not always the same as what cargo itself uses, causing issues like the one described here: it is impossible to tell, from the information a build script has, whether cargo will pass --target to rustc or not, and there are some situations (rustc bootstrap and Miri, for instance) where this really matters. As a consequence, the aforementioned crates sometimes break in rustc bootstrap and Miri when they are used as dependency of a proc macro.

Proposed Solution

Cargo should just tell the build script how it will invoke rustc. I'm imagining an env var like CARGO_ENCODED_RUSTC (name to be bikeshed), which build scripts could use like this:

use std::path::Path;
use std::env;
use std::fs;
use std::process::Command;

fn probe(probe: &str) -> bool {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let probefile = Path::new(&out_dir).join("probe.rs");
    fs::write(&probefile, probe).expect("failed to write in OUT_DIR");

    let rustc = env::var("CARGO_ENCODED_RUSTC").unwrap();
    let mut rustc = rustc.split('\x1f');
    let mut cmd = Command::new(rustc.next().unwrap());
    cmd
        .args(rustc)
        .arg("--crate-name=build_probe")
        .arg("--crate-type=lib")
        .arg("--emit=metadata")
        .arg("--out-dir")
        .arg(out_dir)
        .arg(probefile);
    cmd.status().expect("failed to invoke rustc").success()
}

Notes

Does this need an RFC?

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-build-scriptsArea: build.rs scriptsC-feature-requestCategory: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`S-needs-designStatus: Needs someone to work further on the design for the feature or fix. NOT YET accepted.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions