stage0 in a post-rust-1.0 world


(Reposted/reworded from mailing list)

I’ve been messing about with what a (Debian) distro package for rust might look like, and thinking about how it compares to bootstrapping C/C++ on new architectures/distros.

Specifically, once we reach 1.0:

  • Will the rust stage1 source be limited to using “stable” rust language features? (somewhat like gcc stage1 can’t use gcc-specific features)
  • What sort of version skew between stage0 and stage1 are we expecting to support? (Can we compile rust1.1 using rust1.0? What about rust2.0?)

Additionally, I currently see numerous “breaking” changes transitioned via #[cfg(stage0)]. The assumption is that stage0 is older than the breaking change, and stage1+ is newer than the change. Amongst other things, this means that you can’t build stage0 again using the compiler that you’ve just built :frowning:

I see there’s already been discussion towards other mechanisms for tagging version-specific code(*). I’ve read what I can find, but I’m new to the community so mostly I’m just looking to get caught up on the conversations around this - and register a vote for “we need to have a story for this for 1.0, and not later”.


  • Gus

Perfecting Rust Packaging
How to distro-package a (Cargo-using) Rust executable?

stage0 for version 1.X will not be the 1.X-1 release until every feature that the compiler wants to use is in a stable release, or the compiler is rewritten to not use any features. I suspect over time, as Rust matures and the compiler settles down, we’ll eventually reach a point where rustc can be built from any Rust >= 1.Y. Building the standard library will probably never ever be done with a release compiler, though, because libcore in particular wants lots of things like lang items and intrinsics that are unlikely to be stabilized.

It’s a sticky issue.


this is true of the c standard library as well, right- you can’t (efficiently) implement it in c. so how does stuff like gcc get packaged? it seems like the distinction is that it has no release channels, so intrinsics and extensions are available even to ‘stable’ builds of the compiler…


gcc has a similar stage1/stage2 split. Stage1 uses standard C (no gcc extensions) and so is reasonably portable. The stage1 compiler is used to build stage2, libgcc, etc - so all that can assume full gcc features (but still no floating point emulation and other support that comes in later with stage2+libgcc). I think the equivalent in Rust would be stage1 restricting itself to some widely available subset of language features (I suggested “stable” above); and stage2 being able to use all compiler features, but not libstd and friends.

Re packaging: Debian has a somewhat overly-restrictive policy of always building packages on the native architecture (not cross-compiling). When bootstrapping a new architecture, this gets overlooked in order to break the C compiler dependency cycle - a cross-compiler from an existing architecture or a toolchain found outside Debian (yocto is a popular choice) is used to build the first gcc package and the small set of “build-essential” packages. The build-essential (including gcc) packages are rebuilt on the native arch before uploading to the Debian archive - to ensure consistency with future rebuilds. Once these are available, the new architecture can (theoretically) build everything else.

For Rust packaging, we have two variations of the exact same bootstrapping problem, with the exact same solutions: Bootstrapping a new architecture needs a cross-compiler similar to above, and bootstrapping Rust itself can use a Rust compiler provided outside Debian. This leaves us with “only” the problem of maintaining the status-quo on an existing architecture - and you can see that I’m looking for a way to build the next compiler revision using an existing compiler without having to resort to a stage0 opaque blob I found on the internet every time. At the moment, I’m considering patching in my own unofficial support for language version attributes, using it to replace a bunch of the cfg(stage0) guards, and see how far I can stretch the specific version of stage0 required for stage1.


I’m a little worried about necroing here but I’m really curious to know how far you got with patching this as it seems rather difficult.

As far as I can tell, rebuilding must be done with something non stable to get a compiler to build the stable one because of libcore’s dependence on unstable features.


Yeah, I’m a bit slower than you - it wasn’t until I started playing with the ‘beta’ releases that I realised just how big a show-stopper not having any way to opt-in to unstable features was going to be.

Currently we’re building the Debian rustc package with the usual stage0 opaque blob bundled along with the .deb source.

I guess future improvements might be:

  • Build/maintain a separate rustc-nightly package that is released at least often enough that the chore of patching semvers ranges is manageable.
  • Rewrite Rust stage1 to only require a semi-recent Rust stable.

I still think the latter is the only viable long-term goal, but not volunteering to do either at the moment :confused:

Incidentally, the lack of opt-in to unstable features means we also can’t package a rustc1.0-contemporary cargo with rustc1.0, which is disappointing. I think the enforced-release-trains thing is a good approach - but at the moment stable is a bit unpleasant while everyone is still addicted to rust-nightly.


Note that Cargo should be able to build on stable nightly very soon:

(not sure if the cherry-pick into 1.1 is going to happen or not)


I’m still not sure what I’m going to do while the long-term rewrite is not being actively discussed. Because the build environment intentionally doesn’t let network connections out, I may just package up the source0 blob and then make it a build requirement to the real rust package. Then hopefully I just use --enable-local-rust --llvm-root=/ and everything works like a charm.


@bryteise: Just arrange for the right stage0 compiler .tar.gz to already be in dl/ and don’t give --enable-local-rust - then src/etc/ (called from mk/ will do the right thing.

In the Debian packaging, we use a short script to download the x86+i386 stage0 tarballs for us during packaging, some of the packaging details make sure that gets put in the right place, and then the Rust build system just works.


Oh awesome, thanks so much for the advice!