aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-03-01 22:58:39 +0900
committerOphestra <cat@gensokyo.uk>2026-03-01 23:39:43 +0900
commit2baa9df1331721ea6e0cbd384784fcb97101b8dc (patch)
treeaf1acf2e4a35e421b9d3406586ea80180907dd51
parent51d3df2419e26f15d780c98629a357473855ebd4 (diff)
internal/rosa: general helper abstraction
This greatly increases code sharing and makes implementations far simpler. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--internal/rosa/rosa.go151
1 files changed, 151 insertions, 0 deletions
diff --git a/internal/rosa/rosa.go b/internal/rosa/rosa.go
index e49bba5b..7c0e3d39 100644
--- a/internal/rosa/rosa.go
+++ b/internal/rosa/rosa.go
@@ -392,3 +392,154 @@ cat /usr/src/` + name + `-patches/* | \
t.Load(Patch),
), nil, nil, script, paths...)
}
+
+// helperInPlace is a special directory value for omitting the cd statement.
+const helperInPlace = "\x00"
+
+// Helper is a build system helper for [Toolchain.NewPackage].
+type Helper interface {
+ // name returns the value passed to the name argument of [Toolchain.New].
+ name(name, version string) string
+ // extra returns helper-specific dependencies.
+ extra(flag int) []PArtifact
+
+ // wantsChmod returns whether the source directory should be made writable.
+ wantsChmod() bool
+ // wantsWrite returns whether the source directory should be mounted writable.
+ wantsWrite() bool
+ // scriptEarly returns the helper-specific segment of cure script that goes
+ // before the cd statement.
+ scriptEarly() string
+ // createDir returns whether the path returned by wantsDir should be created.
+ createDir() bool
+ // wantsDir returns the directory to enter before script.
+ //
+ // The zero value implies source directory if [PackageAttr.ScriptEarly] is
+ // also empty. The special value helperInPlace omits the cd statement.
+ wantsDir() string
+ // script returns the helper-specific segment of cure script.
+ script(name string) string
+}
+
+const (
+ // sourceTarXZ denotes a source tarball to be decompressed using [XZ].
+ sourceTarXZ = 1 + iota
+)
+
+// PackageAttr holds build-system-agnostic attributes.
+type PackageAttr struct {
+ // Mount the source tree writable.
+ Writable bool
+ // Do not pass through [Toolchain.NewPatchedSource].
+ Chmod bool
+ // Unconditionally enter source directory early.
+ EnterSource bool
+
+ // Additional environment variables.
+ Env []string
+ // Runs before script emitted by [Helper]. Enters source if non-empty.
+ ScriptEarly string
+
+ // Passed to [Toolchain.NewPatchedSource].
+ Patches [][2]string
+ // Kind of source artifact.
+ SourceKind int
+
+ // Dependencies not provided by stage0.
+ NonStage0 []pkg.Artifact
+
+ // Passed through to [Toolchain.New], before source.
+ Paths []pkg.ExecPath
+ // Passed through to [Toolchain.New].
+ Flag int
+}
+
+// NewPackage constructs a [pkg.Artifact] via a build system helper.
+func (t Toolchain) NewPackage(
+ name, version string,
+ source pkg.Artifact,
+ attr *PackageAttr,
+ helper Helper,
+ extra ...PArtifact,
+) pkg.Artifact {
+ if attr == nil {
+ attr = new(PackageAttr)
+ }
+
+ if name == "" || version == "" {
+ panic("name must be non-empty")
+ }
+ if source == nil {
+ panic("source must be non-nil")
+ }
+ wantsChmod, wantsWrite := helper.wantsChmod(), helper.wantsWrite()
+ if attr.SourceKind > 0 &&
+ (attr.Writable || attr.Chmod || wantsChmod || wantsWrite || len(attr.Patches) > 0) {
+ panic("source processing requested on a non-unpacked kind")
+ }
+
+ dc := len(attr.NonStage0)
+ if !t.isStage0() {
+ dc += 1<<3 + len(extra)
+ }
+
+ extraRes := make([]pkg.Artifact, 0, dc)
+ extraRes = append(extraRes, attr.NonStage0...)
+ if !t.isStage0() {
+ for _, p := range helper.extra(attr.Flag) {
+ extraRes = append(extraRes, t.Load(p))
+ }
+ for _, p := range extra {
+ extraRes = append(extraRes, t.Load(p))
+ }
+ }
+
+ var scriptEarly string
+
+ var sourceSuffix string
+ switch attr.SourceKind {
+ case sourceTarXZ:
+ sourceSuffix = ".tar.xz"
+ scriptEarly += `
+tar -C /usr/src/ -xf '/usr/src/` + name + `.tar.xz'
+mv '/usr/src/` + name + `-` + version + `' '/usr/src/` + name + `'
+`
+ break
+ }
+
+ dir := helper.wantsDir()
+ helperScriptEarly := helper.scriptEarly()
+ if attr.EnterSource ||
+ dir == "" ||
+ attr.ScriptEarly != "" ||
+ helperScriptEarly != "" {
+ scriptEarly += `
+cd '/usr/src/` + name + `/'
+`
+ }
+ scriptEarly += attr.ScriptEarly + helperScriptEarly
+ if dir != "" && dir != helperInPlace {
+ if helper.createDir() {
+ scriptEarly += "\nmkdir -p " + dir
+ }
+ scriptEarly += "\ncd " + dir + "\n"
+ } else if !attr.EnterSource && attr.ScriptEarly == "" {
+ panic("cannot remain in root")
+ }
+
+ return t.New(
+ helper.name(name, version),
+ attr.Flag,
+ extraRes,
+ nil,
+ attr.Env,
+ scriptEarly+helper.script(name),
+ slices.Concat(attr.Paths, []pkg.ExecPath{
+ pkg.Path(AbsUsrSrc.Append(
+ name+sourceSuffix,
+ ), attr.Writable || wantsWrite, t.NewPatchedSource(
+ name, version, source, !attr.Chmod && !wantsChmod, attr.Patches...,
+ )),
+ })...,
+ )
+}