Skip to content

Allow setting RUSTC_BOOTSTRAP=1 on specific crates in the dependency graph from the top level of the build #6627

Closed
@hsivonen

Description

@hsivonen

The feature being requested

Please add a cargo capability to designate from the top level of the build which specific crates in the dependency graph are compiled with RUSTC_BOOTSTRAP=1 even when using a non-nightly compiler

Use cases

A piece of software that depends on many Rust crates (such as Firefox) tracks the stable release channel for the Rust compiler. Yet, in some case, it makes sense to use an unstable rust feature nonetheless.

  1. There is a temporally substantial (not just a couple of Rust release trains but even years) mismatch between Rust stabilization priorities and the shipping needs of the piece of software that is using Rust code. The most obvious example of this is portable packed SIMD in Firefox.
  2. A more temporary but immediate need comes up to use a Rust feature that has an active stabilization plan a couple release trains ahead of the Rust stabilization schedule. In the case of Firefox, some allocator-related customizations where of this type.

Note that making the first use case go away by stabilizing packed portable SIMD (which would be great!) does not make this feature request moot, since it seems reasonable to expect that the second kind of situation will keep arising from time to time even if there are periods in between when nightly features aren't needed.

Alternative solutions and what's wrong with them

Just use nightly Rust

The general tendency of Rust seems to be that if you want to use a nightly feature, you should be using nightly Rust. This poses problems:

  1. If the piece of software using Rust is not only compiled for distribution by its developers but also by Linux and BSD distributions (as Firefox is), it is not scalable for each upstream project to designate its own snapshot of the Rust toolchain for the Linux and BSD distributions to use. In fact, for some distributions even the notion that building Firefox with in-archive compilers requires packaging the (Rust stable-channel) Rust toolchain on a rolling basis is a big departure from how they usually do things. Having upstream software require the packaging of upstream-designated snapshots of nightly Rust would probably not be well received.
  2. Using nightly Rust means giving up on the value that the Rust release train model is supposed to provide. That is, the Rust release train model is supposed to catch regressions that land in nightly before they reach the stable channel. If there truly is such quality assurance value in Rust's train model, it seems bad to ask projects to give up that value for all code they compile if in a vast amount of code they have a couple of specific things that need nightly-only features.
  3. Especially given the first point, "just use nightly Rust" does not really work as a temporary solution to a temporary problem (like the allocator-related customization in Firefox), because in the context of downstream packaging, the timescales to plan for such a change (if feasible at all; see the first point) would likely exceed the time period when a given nightly feature is temporarily needed. Notably, "just use nightly Rustis not just a quick matter ofrustup default nightly` for software that gets packaged downstream.

Additionally, the "just use nighly Rust" solution has the problems of the next solution as well.

Set RUSTC_BOOTSTRAP=1 for the whole build system

This is what Firefox used to do.

The main problem with this solution is that it's very easy to casually introduce more dependencies on nightly Rust features than was intended. Specifically, if nightly features are enabled for specific purpose that's well-known to people who take care of toolchain issues in the project, there is some chance of tracking the status of each nightly feature of interest in Rust development. If, however, developers casually introduce more dependencies on nightly features, soon no one is tracking which nightly features the project as a whole depends on ending there may be unpleasant surprises if nightly feature breaks and the person who casually started using it hasn't been tracking the status of the feature and hasn't been prepared to deal with the nightly feature changing.

In general, to the extent it is some sort of Rust position that production users of Rust should not be relying on nightly features, it seems incongruent with that position to suggest solutions that easily lead production users to depend on arbitrary nightly features when their use case is about depending on a very small number of them.

Notably, unintentionally adding a dependency on a nightly feature is not necessarily a matter of a developer "unintentionally" typing #![feature(foo)] in the code they are writing: #![feature(foo)] may be buried in a dependency, and with nightly features being behind cargo features in dependencies, it may be even less obvious what nightly features are enabled.

Use println!("cargo:rustc-env=RUSTC_BOOTSTRAP=1"); from build.rs

This is what Firefox does now.

This hack avoids the problems of the above two solutions by scoping RUSTC_BOOTSTRAP=1 to specific crates. It's own problems fundamentally arise from the hack being syntactically part of an individual crate while the the need for the hack is a configuration concern of the top-level application.

In a non-cargo vendor scenario, putting application-level configuration in a specific crate basically requires maintaining a git fork the crate with an extra change set just for the configuration and the ceremony of telling cargo to replace the crates.io instance of a crate with the forked git instance. Even in a cargo vendor situation is not a matter of maintaining a patch that always gets applied after re-running cargo vendor: cargo requires more ceremony to opt out of hash checking for the vendor crates.

Even if the effort required to maintain application-level configuration as a change to an individual crate is relatively mild, the path of least maintenance burden still is to make the change in the crates.io crate itself. This poses the risk of other applications unknowingly becoming reliant on configuration meant for a different application. This is probably harmless for crates that are blatantly obviously nightly-only, such as previously the simd crate or presently the packed_simd crate. It can be problematic, however, on crates that conditionally depend on such crates. For example, encoding_rs has a cargo feature simd-accel for enabling the dependency on, depending on encoding_rs version, simd or packed_simd and gaining the associated performance benefits. While it should be obvious that this is a cargo feature as opposed to the default state of things precisely because there is some tradeoff (in this case, trading compatibility with future Rust toolchains for performance today), the full implications of the trade-off might not be obvious and someone might enable simd-accel with a stable-channel toolchain and be unhappy later when it toolchain update breaks the build.

Preferred solution

My preference for solving this would be adding the ability to use the top-level Cargo.toml to indicate which crates in the dependency graph are to be compiled with nightly features enabled. While naming is a total bikeshed, I suggest putting something very obvious in the syntax along the lines of enable_features_that_future_rust_releases_may_break instead of relying on short but less obvious terms like nightly or bootstrap.

There is other per-crate configuration tha the top-level Cargo.toml should be able to control, such as the optimization level. In principle, it would be good for the solution for this feature request to be consistent with other such configuration, but I also wish that this feature request doesn't get deferred indefinitely due to consistency concerns.

Alternative solution

It has been suggested that instead of introducing syntax into the the top-level Cargo.toml the RUSTC_BOOTSTRAP environment variable should be extended to take a list of crate names as an alternative to taking the value 1. While this would address the use case, I consider it better design to recognize the use case and to provide top-level Cargo.toml syntax (that makes the downsides clear) along with other per-crate configuration than to make RUSTC_BOOTSTRAP more magic with a special-case design that sort of tries to avoid recognizing the use case by deliberately keeping the mechanism obscure.

Potential downsides

A potential downside is that people use enable_features_that_future_rust_releases_may_break and are still unhappy when breakage happens. However, the standard approach of telling people to "just use nightly Rust" already has this problem.

The best solution to avoiding unhappiness from changes to features that are explicitly subject to change is to work actively on "subject to change" features so that they actually don't appear de facto unchanging and so that they spend a relatively short time in the "subject to change" state. Fixing that kind of general prioritization issue is outside the scope of this feature request, though.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-feature-requestCategory: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions