Files
hakurei/internal/rosa/rosa.go
Ophestra d258dea0bf
All checks were successful
Test / Create distribution (push) Successful in 49s
Test / Sandbox (push) Successful in 2m52s
Test / ShareFS (push) Successful in 4m40s
Test / Sandbox (race detector) (push) Successful in 5m22s
Test / Hpkg (push) Successful in 5m20s
Test / Hakurei (push) Successful in 5m41s
Test / Hakurei (race detector) (push) Successful in 7m36s
Test / Flake checks (push) Successful in 1m44s
internal/rosa: bootstrap on gentoo stage3
This contains a fully working musl+llvm toolchain and many build systems in a pretty small package.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-01-19 00:51:49 +09:00

224 lines
5.6 KiB
Go

// 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)
}
}
// triplet returns the Rosa OS host triple corresponding to [runtime.GOARCH].
func triplet() string {
return linuxArch() + "-rosa-linux-musl"
}
const (
// EnvTriplet holds the return value of triplet.
EnvTriplet = "ROSA_TRIPLE"
// EnvRefCFLAGS holds toolchain-specific reference CFLAGS.
EnvRefCFLAGS = "ROSA_CFLAGS"
// EnvRefCXXFLAGS holds toolchain-specific reference CXXFLAGS.
EnvRefCXXFLAGS = "ROSA_CXXFLAGS"
)
// ldflags returns LDFLAGS corresponding to triplet.
func ldflags(static bool) string {
s := "LDFLAGS=" +
"-fuse-ld=lld " +
"-L/system/lib -Wl,-rpath=/system/lib " +
"-L/system/lib/" + triplet() + " " +
"-Wl,-rpath=/system/lib/" + triplet() + " " +
"-rtlib=compiler-rt " +
"-unwindlib=libunwind " +
"-Wl,--as-needed"
if !static {
s += " -Wl,--dynamic-linker=/system/lib/ld-musl-x86_64.so.1"
}
return s
}
// cflags is reference CFLAGS for the Rosa OS toolchain.
const cflags = "-Qunused-arguments " +
"-isystem/system/include"
// cxxflags returns reference CXXFLAGS for the Rosa OS toolchain corresponding
// to [runtime.GOARCH].
func cxxflags() string {
return "--start-no-unused-arguments " +
"-stdlib=libc++ " +
"--end-no-unused-arguments " +
"-isystem/system/include/c++/v1 " +
"-isystem/system/include/" + triplet() + "/c++/v1 " +
"-isystem/system/include "
}
// 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
// toolchainStage3 denotes the Gentoo stage3 toolchain. Special care must be
// taken to compile correctly against this toolchain.
toolchainStage3
)
// 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 {
const lcMessages = "LC_MESSAGES=C.UTF-8"
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")
case toolchainStage3:
const (
version = "20260111T160052Z"
checksum = "c5_FwMnRN8RZpTdBLGYkL4RR8ampdaZN2JbkgrFLe8-QHQAVQy08APVvIL6eT7KW"
)
path = fhs.AbsRoot.Append("bin", "bash")
args[0] = "bash"
support = slices.Concat([]pkg.Artifact{
cureEtc{},
toolchainBusybox.New("stage3-"+version, nil, nil, nil, `
tar -C /work -xf /usr/src/stage3.tar.xz
rm -rf /work/dev/ /work/proc/
ln -vs ../usr/bin /work/bin
`, pkg.Path(AbsUsrSrc.Append("stage3.tar.xz"), false,
pkg.NewHTTPGet(
nil, "https://distfiles.gentoo.org/releases/"+
runtime.GOARCH+"/autobuilds/"+version+
"/stage3-"+runtime.GOARCH+"-musl-llvm-"+version+".tar.xz",
mustDecode(checksum),
),
)),
}, extra)
env = fixupEnviron(env, []string{
EnvTriplet + "=" + triplet(),
lcMessages,
EnvRefCFLAGS + "=" + cflags,
EnvRefCXXFLAGS + "=" + cxxflags(),
ldflags(true),
}, "/system/bin",
"/usr/bin",
"/usr/lib/llvm/21/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)...,
)
}