Skip to content

Add host.runner for wrapping host build target executions (e.g. build scripts) #16591

@weihanglo

Description

@weihanglo

Problem

Cargo has rustc-wrapper for wrapping rustc,
but there is no way to wrap other host executables like build scripts and proc macros.

This makes several use cases awkward or impossible:

  • Distributed builds:
    Tools like distcc/sccache work as compiler wrappers that intercept invocations,
    decide whether to run locally or remotely, and handle the handoff.
    For build scripts, we'd want the same pattern to avoid wrapping the entire cargo
    or modifying cargo source code.

  • Build system integration:
    Build system like Nix keeps dependencies in a read-only store for reproducibility.
    Some build scripts try to write to their own source directory not just OUT_DIR,
    which breaks in build sandbox (granted, they shouldn't do that in the first place).
    Existing workarounds are like copying all vendored sources to a writable temp directory
    before every build, or patch the build scripts directly.
    This is usually expensive and not recommended (see Crane docs).
    With a wrapper,
    Build system could intercept the build script, redirect writes,
    or fail fast with a clear error instead of subtle breakage.

  • Debugging:
    Build scripts buffer their output and only show it at the end.
    If a build script crashes or hangs, you don't see what it was doing.
    A wrapper could stream output earlier, add timestamps,
    catch crashes and provide stack traces.
    This gives a good playground for experimenting diferent build script output for Cargo itself.

  • Environment control:
    Currently you'd set env vars globally for the whole Cargo process,
    which affects rustc too.
    While rustc has an unstable --env-set to control what env!() sees,
    build scripts don't have equivalent mechanisms yet.

  • Process isolation (i.e., sandboxing:
    Tools like bubblewrap or seccomp filters can restrict filesystem/network access.
    Without a wrapper, you'd need to wrap the entire cargo process which affects rustc too.
    A wrapper lets you apply restrictions per build script.

Context

The Cargo team has discussed full sandboxing of build scripts and proc-macros multiple times.
The general consensus has been that built-in sandboxing is likely out of scope for Cargo at this moment,
due to its complexity and hard to satisfy different needs with a single solution.
The focus right now be on reducing reliance on build scripts and making them easier to audit.

In a recent Cargo team discussion https://github.com/rust-lang/cargo-team/blob/0cf1414e1012445262a335a6b1c2b11983d31639/meetings/sync-meeting/2026-01-20.md#build-script-wrapper,
the team agreed with a host runner unstable feature to offer a low-level hook for experimentation.
External tools can try different approaches without Cargo committing to any particular design.

Proposed Solution

Add host.runner config that wraps host build targets like build scripts during the build

This follows the existing pattern of [host] config,
which currently supports linker and rustflags.

It is similar to how target.<triple>.runner works for cargo test/cargo run executables
but for host build targets.

Config example:

[host]
runner = ["wrapper", "--custom-flag"]

Invocation:

wrapper --custom-flag <actual-build-script-executable> <args...>

The goal is to enable experimentation,
not to commit to stabilizing the current design.
Different use cases may need different APIs,
and we should gather real-world feedback before deciding what to stabilize,
or different features for different use cases
For example, we might want first-class support for distributed builds to avoid process-forking cost.

Implementation plan

This can reuse the existing -Zhost-config unstable feature.

The infrastructure is already in place:

  • TargetConfig struct has a runner field
  • load_host_triple() already loads [host] config including runner but unused
  • [host] already applies to build scripts and proc-macros

Main work needed:

  • Add tests documenting the current behavior
  • In custom_build.rs, use host.runner when executing build scripts, and update test snapshots.
  • Update unstable documentation for -Zhost-config
  • Announce in #t-cargo/build-integration for tools to start experiment

Limitations

  • Proc-macro wrapping:
    Proc-macros can't be wrapped by Cargo, as they're loaded and executed by rustc directly.
    This can still establish patterns for future rustc changes.

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-build-scriptsArea: build.rs scriptsA-build-systemArea: build system integrationC-feature-requestCategory: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`S-acceptedStatus: Issue or feature is accepted, and has a team member available to help mentor or reviewZ-host-configNightly: host-config

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions