diff --git a/internal/rosa/rosa.go b/internal/rosa/rosa.go index e49bba5..7c0e3d3 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..., + )), + })..., + ) +}