internal/rosa: remove legacy exec helper

This change also adds a fourth stage to the bootstrap machinery, offering more correct toolchain validation that works around LLVM dynamic linking flaws.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
cat
2026-07-30 00:18:22 +09:00
parent 7908fb9583
commit 6487b41d92
16 changed files with 178 additions and 179 deletions
+121 -133
View File
@@ -121,6 +121,12 @@ const (
// Std denotes the standard Rosa OS toolchain.
Std
// Stage3 denotes the stage3 Rosa OS toolchain built on [Std]. Software
// built on this toolchain should be identical to [Std]. This is generally
// for validation of LLVM 3-stage nondeterminism only, due to dynamic
// linking complications.
Stage3
// _stageEnd is the total number of stages available and does not denote a
// valid toolchain.
_stageEnd
@@ -149,7 +155,7 @@ func (t Stage) isIntermediate() bool {
// isStd returns whether t is considered functionally equivalent to [Std].
func (t Stage) isStd() bool {
switch t {
case stageStdGentoo, Std:
case stageStdGentoo, Std, Stage3:
return true
default:
return false
@@ -198,17 +204,6 @@ const scriptName = "all"
// build script under.
var absCureScript = AbsSystem.Append(scriptName)
const (
// TExclusive denotes an exclusive [pkg.Artifact].
TExclusive = 1 << iota
// TEarly hints for an early variant of toybox to be used when available.
TEarly
// TNoToolchain excludes the LLVM toolchain.
TNoToolchain
// THostNet arranges for a [pkg.KindExecNet] to be created.
THostNet
)
var (
_stage0Dist = H("stage0-dist")
_mksh = H("mksh")
@@ -218,102 +213,6 @@ var (
_patch = H("patch")
)
// New returns a [pkg.Artifact] based on a [Toolchain] via s.
func (t Toolchain) New(
name string,
flag int,
extra []pkg.Artifact,
knownChecksum *pkg.Checksum,
env []string,
script string,
paths ...pkg.ExecPath,
) pkg.Artifact {
const lcMessages = "LC_MESSAGES=C.UTF-8"
var support []pkg.Artifact
switch t.stage {
case stageGentoo, stageEarly:
name += "-boot"
support = append(support, extra...)
support = append(support, NewEtc(true))
if t.stage == stageEarly {
_, a := t.MustLoad(_stage0Dist)
support = append(support, a)
} else if t.gentooStage3 == nil {
panic(os.ErrInvalid)
} else {
support = append(support,
t.gentooStage3,
gentooOverlay{t.gentooStage3},
)
env = append(env,
"CC=clang",
"CXX=clang++",
)
}
env = fixupEnviron(env, []string{
EnvTriple + "=" + t.triple(),
lcMessages,
"LDFLAGS=" + t.earlyLDFLAGS(true),
}, "/system/bin",
"/usr/bin",
)
case stageIntermediateGentoo, stageStdGentoo,
stageIntermediate, Std:
if t.stage.isIntermediate() {
name += "-std"
}
toybox := _toybox
if flag&TEarly != 0 {
toybox = _toyboxEarly
}
base := _llvm
if flag&TNoToolchain != 0 {
base = _musl
}
support = slices.Concat(extra, t.S.New(t.stage-1).Append([]pkg.Artifact{
NewEtc(false),
},
base,
_mksh,
toybox,
))
env = fixupEnviron(env, []string{
EnvTriple + "=" + t.triple(),
lcMessages,
}, "/system/bin", "/bin")
default:
panic("unsupported toolchain " + strconv.Itoa(int(t.stage)))
}
return pkg.NewExec(
name, t.arch, knownChecksum, pkg.ExecTimeoutMax,
flag&THostNet != 0,
flag&TExclusive != 0,
fhs.AbsRoot, env,
absCureScript,
nil,
slices.Concat([]pkg.ExecPath{pkg.Path(
fhs.AbsRoot, true,
support...,
), pkg.Path(
absCureScript, false,
pkg.NewFile(scriptName, []byte(
"#!/system/bin/sh\n"+
"set -eu -o pipefail\n"+
script,
)),
)}, paths)...,
)
}
// HasStageEarly returns whether a stage0 distribution is available.
func (s *S) HasStageEarly() (ok bool) {
func() {
@@ -364,7 +263,7 @@ function apply {
}
}
return t.NewPackage(name+"-src", Unversioned, nil, &PackageAttr{
return t.New(name+"-src", Unversioned, nil, &PackageAttr{
Paths: paths,
}, &GenericHelper{
Build: buf.String(),
@@ -379,7 +278,7 @@ const helperInPlace = "\x00"
// Helper is a build system helper for [Toolchain.NewPackage].
type Helper interface {
// extra returns helper-specific dependencies.
extra(flag int) P
extra() P
// wantsChmod returns whether the source directory should be made writable.
wantsChmod() bool
@@ -421,12 +320,19 @@ type PackageAttr struct {
// Whether to replace /usr/bin/ with a symlink to /bin/.
PopulateUsrBin bool
// Hint for an early variant of toybox to be used when available.
Early bool
// Exclude the LLVM toolchain.
NoToolchain bool
// Whether the resulting [pkg.Artifact] is exclusive.
Exclusive bool
// Whether to create [pkg.KindExecNet] instead of [pkg.KindExec].
HostNet bool
// Unregistered extras.
Extra []pkg.Artifact
// Passed through to [Toolchain.New], before source.
Paths []pkg.ExecPath
// Passed through to [Toolchain.New].
Flag int
}
// pa holds whether an [ArtifactH] is present.
@@ -467,8 +373,8 @@ func (t Toolchain) Append(a []pkg.Artifact, handles ...ArtifactH) []pkg.Artifact
return a
}
// NewPackage constructs a [pkg.Artifact] via a build system helper.
func (t Toolchain) NewPackage(
// New constructs a [pkg.Artifact] via a build system helper.
func (t Toolchain) New(
name, version string,
source pkg.Artifact,
attr *PackageAttr,
@@ -486,25 +392,96 @@ func (t Toolchain) NewPackage(
if version != Unversioned {
rn = name + "-" + version
}
root := make([]pkg.Artifact, 0, 1<<3+len(attr.Extra)+len(extra))
root = append(root, attr.Extra...)
const lcMessages = "LC_MESSAGES=C.UTF-8"
srn := rn
env := slices.Clone(attr.Env)
var extraBoot []ArtifactH
switch t.stage {
case stageGentoo, stageEarly:
srn += "-boot"
root = append(root, NewEtc(true))
if t.stage == stageEarly {
extraBoot = append(extraBoot, _stage0Dist)
} else if t.gentooStage3 == nil {
panic(os.ErrInvalid)
} else {
env = append(env,
"CC=clang",
"CXX=clang++",
)
}
env = fixupEnviron(env, []string{
EnvTriple + "=" + t.triple(),
lcMessages,
"LDFLAGS=" + t.earlyLDFLAGS(true),
}, "/system/bin",
"/usr/bin",
)
case stageIntermediateGentoo, stageStdGentoo,
stageIntermediate, Std, Stage3:
if t.stage.isIntermediate() {
srn += "-std"
}
if t.stage == Stage3 {
srn += "-stage4"
}
toybox := _toybox
if attr.Early {
toybox = _toyboxEarly
}
base := _llvm
if attr.NoToolchain {
base = _musl
}
root = append(root, NewEtc(false))
extraBoot = append(extraBoot,
base,
_mksh,
toybox,
)
env = fixupEnviron(env, []string{
EnvTriple + "=" + t.triple(),
lcMessages,
}, "/system/bin", "/bin")
default:
panic("unsupported toolchain " + strconv.Itoa(int(t.stage)))
}
wantsChmod, wantsWrite := helper.wantsChmod(), helper.wantsWrite()
extraRes := make([]pkg.Artifact, 0, 1<<3+len(attr.Extra)+len(extra))
extraRes = append(extraRes, attr.Extra...)
{
pv := paGet()
for _, p := range helper.extra(attr.Flag) {
extraRes = t.appendHandle(extraRes, pv, p)
for _, p := range helper.extra() {
root = t.appendHandle(root, pv, p)
}
for _, p := range extra {
extraRes = t.appendHandle(extraRes, pv, p)
root = t.appendHandle(root, pv, p)
}
boot := t.S.New(t.stage - 1)
for _, p := range extraBoot {
root = boot.appendHandle(root, pv, p)
}
paPut(pv)
}
var (
scriptEarly string
sourceSuffix string
)
if t.stage == stageGentoo {
root = append(root,
t.gentooStage3,
gentooOverlay{t.gentooStage3},
)
}
var scriptEarly string
dir, create := helper.wantsDir()
helperScriptEarly := helper.scriptEarly()
if attr.EnterSource ||
@@ -529,7 +506,7 @@ cd '/usr/src/` + name + `/'
if source != nil {
paths = slices.Concat(attr.Paths, []pkg.ExecPath{
pkg.Path(AbsUsrSrc.Append(
name+sourceSuffix,
name,
), attr.Writable || wantsWrite, t.NewPatchedSource(
rn, source, !attr.Chmod && !wantsChmod, attr.Patches...,
)),
@@ -556,14 +533,25 @@ ln -s ../bin /usr/`
scriptEarly += " /bin/)\n"
}
return t.New(
rn,
attr.Flag,
extraRes,
attr.KnownChecksum,
attr.Env,
scriptEarly+helper.script(t, name),
paths...,
return pkg.NewExec(
srn, t.arch, attr.KnownChecksum, pkg.ExecTimeoutMax,
attr.HostNet,
attr.Exclusive,
fhs.AbsRoot, env,
absCureScript,
nil,
slices.Concat([]pkg.ExecPath{pkg.Path(
fhs.AbsRoot, true,
root...,
), pkg.Path(
absCureScript, false,
pkg.NewFile(scriptName, []byte(
"#!/system/bin/sh\n"+
"set -eu -o pipefail\n"+
scriptEarly+helper.script(t, name),
)),
)}, paths)...,
)
}
@@ -581,7 +569,7 @@ type GenericHelper struct {
var _ Helper = new(GenericHelper)
// extra is a noop.
func (*GenericHelper) extra(int) P { return nil }
func (*GenericHelper) extra() P { return nil }
// wantsChmod returns false.
func (*GenericHelper) wantsChmod() bool { return false }