Funny story: The way my compiler ( https://github.com/neat-lang/neat ) used to build is, three years ago there was an initial minimal compiler that was written in D. And every time you checked out the Neat repo on a new system, it had a file with a list of breaking commits, and it would:
- git clone itself in a subfolder
- git checkout and build the initial D compiler
- install it in a temporary prefix
- git checkout the first breaking commit and build it with the initial compiler
- install it over the previous compiler
- git checkout ...
and so on. In normal operation all these stages would be cached, so before I abandoned this approach, I think I was up to a hundred or so intermediate versions.
Eventually I started doing git releases, which needed a better solution. So since I have an optional C backend, I just build the compiler with the C backend, then zip up all the C files to make the release. Then to bootstrap from it, I just do (effectively) gcc *.c -o build/neat_bootstrap.
That's a lot of steps in your bootstrap chain. I think tying it to releases can make it easier, but it's good your process is automated.
Rust has a long chain too, but rustc only uses features itself, that the previous release supports. Releases are every 6 weeks.
Go had been bootstrapping from 1.4 (the last C compiler release), until the release of 1.20 this year, when the bootstrap compiler was bumped to 1.17.13 and will be bumped yearly [0]. That meant go1.4 had to be able to compile new versions, keeping the compiler from using new features in itself for 8 years. Notably, this now allows for generics to be used in the compiler.
I did that too, many compilers ago! Of course, I broke something somewhere and forgot where.
My compiler tastes have changed since, and I think next time I'll maintain a basic bootstrap from Scheme or Rust, but I always thought it was a neat way to do it.
- git clone itself in a subfolder
- git checkout and build the initial D compiler
- install it in a temporary prefix
- git checkout the first breaking commit and build it with the initial compiler
- install it over the previous compiler
- git checkout ...
and so on. In normal operation all these stages would be cached, so before I abandoned this approach, I think I was up to a hundred or so intermediate versions.
Eventually I started doing git releases, which needed a better solution. So since I have an optional C backend, I just build the compiler with the C backend, then zip up all the C files to make the release. Then to bootstrap from it, I just do (effectively) gcc *.c -o build/neat_bootstrap.
edit: Ah, here it is: https://github.com/Neat-Lang/neat/blob/v0.1.6/bootstrap.sh