internal/rosa: remove legacy exec helper
Test / Hakurei (race detector) (push) Successful in 9m33s
Test / Sandbox (push) Successful in 5m27s
Test / Sandbox (race detector) (push) Successful in 8m21s
Test / ShareFS (push) Successful in 10m32s
Test / Create distribution (push) Successful in 1m51s
Test / Hakurei (push) Successful in 3m5s
Test / Flake checks (push) Successful in 1m28s

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
+14 -4
View File
@@ -616,7 +616,7 @@ func main() {
{ {
c.NewCommand( c.NewCommand(
"stage3", "stage4",
"Check for toolchain 3-stage non-determinism", "Check for toolchain 3-stage non-determinism",
func(args []string) (err error) { func(args []string) (err error) {
var ( var (
@@ -636,21 +636,31 @@ func main() {
if err = cm.Do(func(cache *pkg.Cache) (err error) { if err = cm.Do(func(cache *pkg.Cache) (err error) {
_, llvm := rosa.Native().New(rosa.Std - 1).Load(_llvm) _, llvm := rosa.Native().New(rosa.Std - 1).Load(_llvm)
pathname, checksum[0], err = cache.Cure(llvm) pathname, _, err = cache.Cure(llvm)
return return
}); err != nil { }); err != nil {
return return
} }
log.Println("stage2:", pathname) log.Println("stage2:", pathname)
if err = cm.Do(func(cache *pkg.Cache) (err error) { if err = cm.Do(func(cache *pkg.Cache) (err error) {
_, llvm := rosa.Native().New(rosa.Std).Load(_llvm) _, llvm := rosa.Native().New(rosa.Std).Load(_llvm)
pathname, checksum[1], err = cache.Cure(llvm) pathname, checksum[0], err = cache.Cure(llvm)
return return
}); err != nil { }); err != nil {
return return
} }
log.Println("stage3:", pathname) log.Println("stage3:", pathname)
if err = cm.Do(func(cache *pkg.Cache) (err error) {
_, llvm := rosa.Native().New(rosa.Stage3).Load(_llvm)
pathname, checksum[1], err = cache.Cure(llvm)
return
}); err != nil {
return
}
log.Println("stage4:", pathname)
if checksum[0] != checksum[1] { if checksum[0] != checksum[1] {
err = &pkg.ChecksumMismatchError{ err = &pkg.ChecksumMismatchError{
Got: checksum[0].Value(), Got: checksum[0].Value(),
@@ -658,7 +668,7 @@ func main() {
} }
} else { } else {
log.Println( log.Println(
"stage2 is identical to stage3", "stage3 is identical to stage4",
"("+pkg.Encode(checksum[0].Value())+")", "("+pkg.Encode(checksum[0].Value())+")",
) )
} }
+1 -1
View File
@@ -35,7 +35,7 @@ type CMakeHelper struct {
var _ Helper = new(CMakeHelper) var _ Helper = new(CMakeHelper)
// extra returns the cmake handle alongside either ninja or make. // extra returns the cmake handle alongside either ninja or make.
func (attr *CMakeHelper) extra(int) P { func (attr *CMakeHelper) extra() P {
if attr != nil && attr.Make { if attr != nil && attr.Make {
return P{_cmake, _make} return P{_cmake, _make}
} }
+3 -3
View File
@@ -17,14 +17,14 @@ func (t Toolchain) NewViaGit(
url, rev string, url, rev string,
checksum pkg.Checksum, checksum pkg.Checksum,
) pkg.Artifact { ) pkg.Artifact {
return t.NewPackage(strings.TrimSuffix( return t.New(strings.TrimSuffix(
path.Base(url), path.Base(url),
".git", ".git",
)+"-src", path.Base(rev), nil, &PackageAttr{ )+"-src", path.Base(rev), nil, &PackageAttr{
KnownChecksum: &checksum, KnownChecksum: &checksum,
Flag: THostNet, HostNet: true,
Paths: []pkg.ExecPath{resolvconf()}, Paths: []pkg.ExecPath{resolvconf()},
}, &GenericHelper{ }, &GenericHelper{
Build: ` Build: `
git \ git \
+2 -2
View File
@@ -13,7 +13,7 @@ func (t Toolchain) newGo(
script string, script string,
boot ...pkg.Artifact, boot ...pkg.Artifact,
) pkg.Artifact { ) pkg.Artifact {
return t.NewPackage("go", version, newTar( return t.New("go", version, newTar(
"https://go.dev/dl/go"+version+".src.tar.gz", "https://go.dev/dl/go"+version+".src.tar.gz",
checksum, checksum,
pkg.Gzip, pkg.Gzip,
@@ -75,7 +75,7 @@ func init() {
) )
switch t.arch { switch t.arch {
case "amd64": case "amd64":
bootstrapEarly = []pkg.Artifact{t.NewPackage("go", "1.4-bootstrap", newTar( bootstrapEarly = []pkg.Artifact{t.New("go", "1.4-bootstrap", newTar(
"https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz", "https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz",
"8o9JL_ToiQKadCTb04nvBDkp8O1xiWOolAxVEqaTGodieNe4lOFEjlOxN3bwwe23", "8o9JL_ToiQKadCTb04nvBDkp8O1xiWOolAxVEqaTGodieNe4lOFEjlOxN3bwwe23",
pkg.Gzip, pkg.Gzip,
+2 -2
View File
@@ -184,8 +184,8 @@ func init() {
}...) }...)
} }
return &meta, t.NewPackage("llvm", meta.Version, source, &PackageAttr{ return &meta, t.New("llvm", meta.Version, source, &PackageAttr{
Flag: TExclusive, Exclusive: true,
Bin: []string{ Bin: []string{
"chmod", "chmod",
"mkdir", "mkdir",
+7 -6
View File
@@ -52,15 +52,16 @@ type MakeHelper struct {
var _ Helper = new(MakeHelper) var _ Helper = new(MakeHelper)
// extra returns make and other optional dependencies. // extra returns make and other optional dependencies.
func (attr *MakeHelper) extra(flag int) P { func (attr *MakeHelper) extra() P {
extra := P{_make} if attr == nil || !attr.OmitDefaults {
if (attr == nil || !attr.OmitDefaults) && flag&TEarly == 0 { return P{
extra = append(extra, _make,
_awk, _awk,
_coreutils, _coreutils,
) }
} }
return extra return P{_make}
} }
// wantsChmod returns whether the build system needs to be generated. // wantsChmod returns whether the build system needs to be generated.
+1 -1
View File
@@ -35,7 +35,7 @@ type MesonHelper struct {
var _ Helper = new(MesonHelper) var _ Helper = new(MesonHelper)
// extra returns hardcoded meson runtime dependencies. // extra returns hardcoded meson runtime dependencies.
func (attr *MesonHelper) extra(int) P { func (attr *MesonHelper) extra() P {
if !attr.isPython() { if !attr.isPython() {
return P{_muon} return P{_muon}
} }
+2 -1
View File
@@ -10,7 +10,9 @@ package bison {
compress = gzip; compress = gzip;
}; };
toyboxEarly = true;
exec = make { exec = make {
omitDefaults = true;
check = [ check = [
"TESTSUITEFLAGS=" + jobsFlagE + "' " + skipGNUTests { "TESTSUITEFLAGS=" + jobsFlagE + "' " + skipGNUTests {
tests = [ tests = [
@@ -24,6 +26,5 @@ package bison {
]; ];
}; };
toyboxEarly = true;
inputs = [ m4 ]; inputs = [ m4 ];
} }
+3 -1
View File
@@ -18,5 +18,7 @@ package byacc {
writable = true; writable = true;
chmod = true; chmod = true;
exec = make {}; exec = make {
omitDefaults = true;
};
} }
+12 -4
View File
@@ -17,9 +17,11 @@ package m4 {
chmod +w tests/test-c32ispunct.sh && echo '#!/bin/sh' > tests/test-c32ispunct.sh chmod +w tests/test-c32ispunct.sh && echo '#!/bin/sh' > tests/test-c32ispunct.sh
`; `;
exec = make {};
toyboxEarly = true; toyboxEarly = true;
exec = make {
omitDefaults = true;
};
inputs = [ inputs = [
diffutils, diffutils,
@@ -194,7 +196,9 @@ test_disable '#!/bin/sh' tests/cmp
`; `;
toyboxEarly = true; toyboxEarly = true;
exec = make {}; exec = make {
omitDefaults = true;
};
} }
package patch { package patch {
@@ -218,7 +222,9 @@ test_disable '#!/bin/sh' tests/need-filename
`; `;
toyboxEarly = true; toyboxEarly = true;
exec = make {}; exec = make {
omitDefaults = true;
};
} }
package grep { package grep {
@@ -328,6 +334,7 @@ package bash {
toyboxEarly = true; toyboxEarly = true;
exec = make { exec = make {
omitDefaults = true;
configure = { configure = {
"without-bash-malloc"; "without-bash-malloc";
}; };
@@ -364,6 +371,7 @@ test_disable 'int main(){return 0;}' gnulib-tests/test-lchown.c
`; `;
exec = make { exec = make {
omitDefaults = true;
configure = { configure = {
"enable-single-binary": "symlinks"; "enable-single-binary": "symlinks";
}; };
+1
View File
@@ -25,6 +25,7 @@ package kernel-headers {
toyboxEarly = true; toyboxEarly = true;
exec = make { exec = make {
omitDefaults = true;
skipConfigure = true; skipConfigure = true;
make = [ make = [
"-f /usr/src/kernel-headers/Makefile", "-f /usr/src/kernel-headers/Makefile",
+1
View File
@@ -15,6 +15,7 @@ package rsync {
toyboxEarly = true; toyboxEarly = true;
exec = make { exec = make {
omitDefaults = true;
configure = { configure = {
"disable-openssl"; "disable-openssl";
"disable-xxhash"; "disable-xxhash";
+1 -1
View File
@@ -9,7 +9,7 @@ type MakeMakerHelper struct {
} }
// extra returns perl. // extra returns perl.
func (*MakeMakerHelper) extra(int) P { return P{_perl, _make} } func (*MakeMakerHelper) extra() P { return P{_perl, _make} }
// wantsChmod returns true. // wantsChmod returns true.
func (*MakeMakerHelper) wantsChmod() bool { return true } func (*MakeMakerHelper) wantsChmod() bool { return true }
+1 -1
View File
@@ -26,7 +26,7 @@ type PipHelper struct {
var _ Helper = new(PipHelper) var _ Helper = new(PipHelper)
// extra returns python, or pytest if defaults are assumed. // extra returns python, or pytest if defaults are assumed.
func (attr *PipHelper) extra(int) P { func (attr *PipHelper) extra() P {
if attr == nil || (!attr.SkipCheck && attr.Check == "") { if attr == nil || (!attr.SkipCheck && attr.Check == "") {
return P{_pythonPyTest} return P{_pythonPyTest}
} }
+121 -133
View File
@@ -121,6 +121,12 @@ const (
// Std denotes the standard Rosa OS toolchain. // Std denotes the standard Rosa OS toolchain.
Std 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 // _stageEnd is the total number of stages available and does not denote a
// valid toolchain. // valid toolchain.
_stageEnd _stageEnd
@@ -149,7 +155,7 @@ func (t Stage) isIntermediate() bool {
// isStd returns whether t is considered functionally equivalent to [Std]. // isStd returns whether t is considered functionally equivalent to [Std].
func (t Stage) isStd() bool { func (t Stage) isStd() bool {
switch t { switch t {
case stageStdGentoo, Std: case stageStdGentoo, Std, Stage3:
return true return true
default: default:
return false return false
@@ -198,17 +204,6 @@ const scriptName = "all"
// build script under. // build script under.
var absCureScript = AbsSystem.Append(scriptName) 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 ( var (
_stage0Dist = H("stage0-dist") _stage0Dist = H("stage0-dist")
_mksh = H("mksh") _mksh = H("mksh")
@@ -218,102 +213,6 @@ var (
_patch = H("patch") _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. // HasStageEarly returns whether a stage0 distribution is available.
func (s *S) HasStageEarly() (ok bool) { func (s *S) HasStageEarly() (ok bool) {
func() { 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, Paths: paths,
}, &GenericHelper{ }, &GenericHelper{
Build: buf.String(), Build: buf.String(),
@@ -379,7 +278,7 @@ const helperInPlace = "\x00"
// Helper is a build system helper for [Toolchain.NewPackage]. // Helper is a build system helper for [Toolchain.NewPackage].
type Helper interface { type Helper interface {
// extra returns helper-specific dependencies. // extra returns helper-specific dependencies.
extra(flag int) P extra() P
// wantsChmod returns whether the source directory should be made writable. // wantsChmod returns whether the source directory should be made writable.
wantsChmod() bool wantsChmod() bool
@@ -421,12 +320,19 @@ type PackageAttr struct {
// Whether to replace /usr/bin/ with a symlink to /bin/. // Whether to replace /usr/bin/ with a symlink to /bin/.
PopulateUsrBin bool 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. // Unregistered extras.
Extra []pkg.Artifact Extra []pkg.Artifact
// Passed through to [Toolchain.New], before source. // Passed through to [Toolchain.New], before source.
Paths []pkg.ExecPath Paths []pkg.ExecPath
// Passed through to [Toolchain.New].
Flag int
} }
// pa holds whether an [ArtifactH] is present. // pa holds whether an [ArtifactH] is present.
@@ -467,8 +373,8 @@ func (t Toolchain) Append(a []pkg.Artifact, handles ...ArtifactH) []pkg.Artifact
return a return a
} }
// NewPackage constructs a [pkg.Artifact] via a build system helper. // New constructs a [pkg.Artifact] via a build system helper.
func (t Toolchain) NewPackage( func (t Toolchain) New(
name, version string, name, version string,
source pkg.Artifact, source pkg.Artifact,
attr *PackageAttr, attr *PackageAttr,
@@ -486,25 +392,96 @@ func (t Toolchain) NewPackage(
if version != Unversioned { if version != Unversioned {
rn = name + "-" + version 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() 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() pv := paGet()
for _, p := range helper.extra(attr.Flag) { for _, p := range helper.extra() {
extraRes = t.appendHandle(extraRes, pv, p) root = t.appendHandle(root, pv, p)
} }
for _, p := range extra { 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) paPut(pv)
} }
var ( if t.stage == stageGentoo {
scriptEarly string root = append(root,
sourceSuffix string t.gentooStage3,
) gentooOverlay{t.gentooStage3},
)
}
var scriptEarly string
dir, create := helper.wantsDir() dir, create := helper.wantsDir()
helperScriptEarly := helper.scriptEarly() helperScriptEarly := helper.scriptEarly()
if attr.EnterSource || if attr.EnterSource ||
@@ -529,7 +506,7 @@ cd '/usr/src/` + name + `/'
if source != nil { if source != nil {
paths = slices.Concat(attr.Paths, []pkg.ExecPath{ paths = slices.Concat(attr.Paths, []pkg.ExecPath{
pkg.Path(AbsUsrSrc.Append( pkg.Path(AbsUsrSrc.Append(
name+sourceSuffix, name,
), attr.Writable || wantsWrite, t.NewPatchedSource( ), attr.Writable || wantsWrite, t.NewPatchedSource(
rn, source, !attr.Chmod && !wantsChmod, attr.Patches..., rn, source, !attr.Chmod && !wantsChmod, attr.Patches...,
)), )),
@@ -556,14 +533,25 @@ ln -s ../bin /usr/`
scriptEarly += " /bin/)\n" scriptEarly += " /bin/)\n"
} }
return t.New( return pkg.NewExec(
rn, srn, t.arch, attr.KnownChecksum, pkg.ExecTimeoutMax,
attr.Flag, attr.HostNet,
extraRes, attr.Exclusive,
attr.KnownChecksum, fhs.AbsRoot, env,
attr.Env, absCureScript,
scriptEarly+helper.script(t, name), nil,
paths...,
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) var _ Helper = new(GenericHelper)
// extra is a noop. // extra is a noop.
func (*GenericHelper) extra(int) P { return nil } func (*GenericHelper) extra() P { return nil }
// wantsChmod returns false. // wantsChmod returns false.
func (*GenericHelper) wantsChmod() bool { return false } func (*GenericHelper) wantsChmod() bool { return false }
+6 -19
View File
@@ -989,9 +989,6 @@ func (ctx *evalContext) pf(
attr PackageAttr attr PackageAttr
patches []string patches []string
files []KV files []KV
excl bool
early bool
minimal bool
anitya int64 anitya int64
kc any kc any
@@ -1020,9 +1017,9 @@ func (ctx *evalContext) pf(
k("populateUsrBin"): &attr.PopulateUsrBin, k("populateUsrBin"): &attr.PopulateUsrBin,
k("patches"): &patches, k("patches"): &patches,
k("files"): &files, k("files"): &files,
k("exclusive"): &excl, k("exclusive"): &attr.Exclusive,
k("toyboxEarly"): &early, k("toyboxEarly"): &attr.Early,
k("minimal"): &minimal, k("minimal"): &attr.NoToolchain,
k("checksum"): &kc, k("checksum"): &kc,
k("output"): &output, k("output"): &output,
@@ -1116,16 +1113,6 @@ func (ctx *evalContext) pf(
) )
} }
if excl {
attr.Flag |= TExclusive
}
if early {
attr.Flag |= TEarly
}
if minimal {
attr.Flag |= TNoToolchain
}
if kc != nil { if kc != nil {
checksum, ok := kc.(string) checksum, ok := kc.(string)
if !ok { if !ok {
@@ -1162,7 +1149,7 @@ func (ctx *evalContext) pf(
} }
} }
v = cachedArtifact{&meta, ctx.t.NewPackage( v = cachedArtifact{&meta, ctx.t.New(
meta.Name, meta.Name,
meta.Version, meta.Version,
source, source,
@@ -1175,8 +1162,8 @@ func (ctx *evalContext) pf(
} }
var ( var (
// ErrToplevel is returned by [S.Evaluate] when encountering a toplevel // ErrToplevel is returned by [S.RegisterAzalea] when encountering a
// expression other than a package declaration. // toplevel expression other than a package declaration.
ErrToplevel = errors.New("top level must only contain package declarations") ErrToplevel = errors.New("top level must only contain package declarations")
) )