// Package rosa provides Rosa OS toolchain artifacts and miscellaneous software. package rosa import ( "log" "runtime" "slices" "strconv" "strings" "hakurei.app/container/fhs" "hakurei.app/internal/pkg" ) const ( // kindEtc is the kind of [pkg.Artifact] of cureEtc. kindEtc = iota + pkg.KindCustomOffset // kindBusyboxBin is the kind of [pkg.Artifact] of busyboxBin. kindBusyboxBin ) // mustDecode is like [pkg.MustDecode], but replaces the zero value and prints // a warning. func mustDecode(s string) pkg.Checksum { var fallback = pkg.Checksum{} if s == "" { log.Println( "falling back to", pkg.Encode(fallback), "for unpopulated checksum", ) return fallback } return pkg.MustDecode(s) } var ( // AbsUsrSrc is the conventional directory to place source code under. AbsUsrSrc = fhs.AbsUsr.Append("src") // AbsSystem is the Rosa OS installation prefix. AbsSystem = fhs.AbsRoot.Append("system") ) // linuxArch returns the architecture name used by linux corresponding to // [runtime.GOARCH]. func linuxArch() string { switch runtime.GOARCH { case "amd64": return "x86_64" default: panic("unsupported target " + runtime.GOARCH) } } // Toolchain denotes the infrastructure to compile a [pkg.Artifact] on. type Toolchain uintptr const ( // toolchainBusybox denotes a busybox installation from the busyboxBin // binary distribution. This is for decompressing unsupported formats. toolchainBusybox Toolchain = iota ) // lastIndexFunc is like [strings.LastIndexFunc] but for [slices]. func lastIndexFunc[S ~[]E, E any](s S, f func(E) bool) (i int) { if i = slices.IndexFunc(s, f); i < 0 { return } if i0 := lastIndexFunc[S](s[i+1:], f); i0 >= 0 { i = i0 } return } // fixupEnviron fixes up PATH, prepends extras and returns the resulting slice. func fixupEnviron(env, extras []string, paths ...string) []string { const pathPrefix = "PATH=" pathVal := strings.Join(paths, ":") if i := lastIndexFunc(env, func(s string) bool { return strings.HasPrefix(s, pathPrefix) }); i < 0 { env = append(env, pathPrefix+pathVal) } else { if len(env[i]) == len(pathPrefix) { env[i] = pathPrefix + pathVal } else { env[i] += ":" + pathVal } } return append(extras, env...) } // absCureScript is the absolute pathname [Toolchain.New] places the fixed-up // build script under. var absCureScript = fhs.AbsUsrBin.Append(".cure-script") // New returns a [pkg.Artifact] compiled on this toolchain. func (t Toolchain) New( name string, extra []pkg.Artifact, checksum *pkg.Checksum, env []string, script string, paths ...pkg.ExecPath, ) pkg.Artifact { var ( path = AbsSystem.Append("bin", "busybox") args = []string{"hush", absCureScript.String()} support []pkg.Artifact ) switch t { case toolchainBusybox: support = slices.Concat([]pkg.Artifact{newBusyboxBin()}, extra) env = fixupEnviron(env, nil, "/system/bin") default: panic("unsupported toolchain " + strconv.Itoa(int(t))) } return pkg.NewExec( name, checksum, pkg.ExecTimeoutMax, fhs.AbsRoot, env, path, args, slices.Concat([]pkg.ExecPath{pkg.Path( fhs.AbsRoot, true, support..., ), pkg.Path( absCureScript, false, pkg.NewFile(".cure-script", []byte("set -e\n"+script)), )}, paths)..., ) }