From 0061d11f939157e181a908f50b0ffb4c991e0375 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Tue, 10 Feb 2026 21:24:25 +0900 Subject: [PATCH] internal/rosa: use self-hosted stage0 This removes the bootstrap dependency on Gentoo stage3 tarball. Signed-off-by: Ophestra --- cmd/mbf/main.go | 34 ++++++++++++++-- internal/rosa/rosa.go | 90 +++++++++++++++++++++++++++-------------- internal/rosa/stage0.go | 36 ++++++++++++++++- 3 files changed, 125 insertions(+), 35 deletions(-) diff --git a/cmd/mbf/main.go b/cmd/mbf/main.go index 83bb671..9828a8f 100644 --- a/cmd/mbf/main.go +++ b/cmd/mbf/main.go @@ -111,15 +111,31 @@ func main() { { var ( + flagGentoo string + flagChecksum string + flagStage0 bool ) c.NewCommand( "stage3", "Check for toolchain 3-stage non-determinism", func(args []string) (err error) { - _, _, _, stage1 := (rosa.Std - 2).NewLLVM() - _, _, _, stage2 := (rosa.Std - 1).NewLLVM() - _, _, _, stage3 := rosa.Std.NewLLVM() + std := rosa.Std + if flagGentoo != "" { + std -= 3 // magic number to discourage misuse + + var checksum pkg.Checksum + if len(flagChecksum) != 0 { + if err = pkg.Decode(&checksum, flagChecksum); err != nil { + return + } + } + rosa.SetGentooStage3(flagGentoo, checksum) + } + + _, _, _, stage1 := (std - 2).NewLLVM() + _, _, _, stage2 := (std - 1).NewLLVM() + _, _, _, stage3 := std.NewLLVM() var ( pathname *check.Absolute checksum [2]unique.Handle[pkg.Checksum] @@ -153,7 +169,7 @@ func main() { if flagStage0 { if pathname, _, err = cache.Cure( - rosa.Std.Load(rosa.Stage0), + std.Load(rosa.Stage0), ); err != nil { return err } @@ -163,6 +179,16 @@ func main() { return }, ). + Flag( + &flagGentoo, + "gentoo", command.StringFlag(""), + "Bootstrap from a Gentoo stage3 tarball", + ). + Flag( + &flagChecksum, + "checksum", command.StringFlag(""), + "Checksum of Gentoo stage3 tarball", + ). Flag( &flagStage0, "stage0", command.BoolFlag(false), diff --git a/internal/rosa/rosa.go b/internal/rosa/rosa.go index b82b09a..e9e720f 100644 --- a/internal/rosa/rosa.go +++ b/internal/rosa/rosa.go @@ -2,6 +2,7 @@ package rosa import ( + "errors" "log" "runtime" "slices" @@ -101,9 +102,23 @@ func earlyCXXFLAGS() string { type Toolchain uintptr const ( - // toolchainBusybox denotes a busybox installation from the busyboxBin - // binary distribution. This is for decompressing unsupported formats. - toolchainBusybox Toolchain = iota + // _toolchainBusybox denotes a busybox installation from the busyboxBin + // binary distribution. This is defined as a toolchain to make use of the + // toolchain abstractions to preprocess toolchainGentoo and is not a real, + // functioning toolchain. It does not contain any compilers. + _toolchainBusybox Toolchain = iota + + // toolchainGentoo denotes the toolchain in a Gentoo stage3 tarball. Special + // care must be taken to compile correctly against this toolchain. + toolchainGentoo + + // toolchainIntermediateGentoo is like to toolchainIntermediate, but + // compiled against toolchainGentoo. + toolchainIntermediateGentoo + + // toolchainStdGentoo is like Std, but bootstrapped from toolchainGentoo. + // This toolchain creates the first [Stage0] distribution. + toolchainStdGentoo // toolchainStage0 denotes the stage0 toolchain. Special care must be taken // to compile correctly against this toolchain. @@ -125,7 +140,7 @@ const ( // stage0Concat concatenates s and values. If the current toolchain is // toolchainStage0, stage0Concat returns s as is. func stage0Concat[S ~[]E, E any](t Toolchain, s S, values ...E) S { - if t == toolchainStage0 { + if t == toolchainStage0 || t == toolchainGentoo { return s } return slices.Concat(s, values) @@ -134,7 +149,7 @@ func stage0Concat[S ~[]E, E any](t Toolchain, s S, values ...E) S { // stage0ExclConcat concatenates s and values. If the current toolchain is not // toolchainStage0, stage0ExclConcat returns s as is. func stage0ExclConcat[S ~[]E, E any](t Toolchain, s S, values ...E) S { - if t == toolchainStage0 { + if t == toolchainStage0 || t == toolchainGentoo { return slices.Concat(s, values) } return s @@ -182,6 +197,25 @@ const ( TEarly ) +var ( + // gentooStage3 is the url of a Gentoo stage3 tarball. + gentooStage3 string + // gentooStage3Checksum is the expected checksum of gentooStage3. + gentooStage3Checksum pkg.Checksum +) + +// SetGentooStage3 sets the Gentoo stage3 tarball url and checksum. It panics +// if given zero values or if these values have already been set. +func SetGentooStage3(url string, checksum pkg.Checksum) { + if gentooStage3 != "" { + panic(errors.New("attempting to set Gentoo stage3 url twice")) + } + if url == "" { + panic(errors.New("attempting to set Gentoo stage3 url to the zero value")) + } + gentooStage3, gentooStage3Checksum = url, checksum +} + // New returns a [pkg.Artifact] compiled on this toolchain. func (t Toolchain) New( name string, @@ -197,49 +231,45 @@ func (t Toolchain) New( var support []pkg.Artifact switch t { - case toolchainBusybox: + case _toolchainBusybox: name += "-early" support = slices.Concat([]pkg.Artifact{newBusyboxBin()}, extra) env = fixupEnviron(env, nil, "/system/bin") - case toolchainStage0: + case toolchainGentoo, toolchainStage0: name += "-boot" - var seed string - switch runtime.GOARCH { - case "amd64": - seed = "c5_FwMnRN8RZpTdBLGYkL4RR8ampdaZN2JbkgrFLe8-QHQAVQy08APVvIL6eT7KW" - case "arm64": - seed = "79uRbRI44PyknQQ9RlFUQrwqplup7vImiIk6klefL8TN-fT42TXMS_v4XszwexCb" - - default: - panic("unsupported target " + runtime.GOARCH) - } - support = slices.Concat([]pkg.Artifact{ - cureEtc{}, - toolchainBusybox.New("stage0", 0, nil, nil, nil, ` -tar -C /work -xf /usr/src/stage0.tar.xz + support = append(support, cureEtc{}) + if t == toolchainStage0 { + support = append(support, NewStage0()) + } else { + support = append(support, _toolchainBusybox.New("gentoo", 0, nil, nil, nil, ` +tar -C /work -xf /usr/src/stage3.tar.xz rm -rf /work/dev/ /work/proc/ ln -vs ../usr/bin /work/bin mkdir -vp /work/system/bin -ln -vs ../../bin/sh /work/system/bin -`, pkg.Path(AbsUsrSrc.Append("stage0.tar.xz"), false, +(cd /work/system/bin && ln -vs \ + ../../bin/sh \ + ../../usr/lib/llvm/*/bin/* \ + .) +`, pkg.Path(AbsUsrSrc.Append("stage3.tar.xz"), false, pkg.NewHTTPGet( - nil, "https://basement.gensokyo.uk/seed/"+seed, - mustDecode(seed), + nil, gentooStage3, + gentooStage3Checksum, ), - )), - }, extra) + ))) + } + support = slices.Concat(support, extra) env = fixupEnviron(env, []string{ EnvTriplet + "=" + triplet(), lcMessages, "LDFLAGS=" + earlyLDFLAGS(true), }, "/system/bin", "/usr/bin", - "/usr/lib/llvm/21/bin", ) - case toolchainIntermediate, Std: - if t < Std { + case toolchainIntermediateGentoo, toolchainStdGentoo, + toolchainIntermediate, Std: + if t == toolchainIntermediateGentoo || t == toolchainIntermediate { name += "-std" } diff --git a/internal/rosa/stage0.go b/internal/rosa/stage0.go index 7b34725..faa25e3 100644 --- a/internal/rosa/stage0.go +++ b/internal/rosa/stage0.go @@ -1,6 +1,11 @@ package rosa -import "hakurei.app/internal/pkg" +import ( + "runtime" + "sync" + + "hakurei.app/internal/pkg" +) func (t Toolchain) newStage0() pkg.Artifact { musl, compilerRT, runtimes, clang := t.NewLLVM() @@ -37,3 +42,32 @@ tar \ `) } func init() { artifactsF[Stage0] = Toolchain.newStage0 } + +var ( + // stage0 stores the tarball unpack artifact. + stage0 pkg.Artifact + // stage0Once is for lazy initialisation of stage0. + stage0Once sync.Once +) + +// NewStage0 returns a stage0 distribution created from curing [Stage0]. +func NewStage0() pkg.Artifact { + stage0Once.Do(func() { + var seed string + switch runtime.GOARCH { + case "amd64": + seed = "tqM1Li15BJ-uFG8zU-XjgFxoN_kuzh1VxrSDVUVa0vGmo-NeWapSftH739sY8EAg" + + default: + panic("unsupported target " + runtime.GOARCH) + } + + stage0 = pkg.NewHTTPGetTar( + nil, "https://hakurei.app/seed/20260210/"+ + "stage0-"+triplet()+".tar.bz2", + mustDecode(seed), + pkg.TarBzip2, + ) + }) + return stage0 +}