Compare commits
44 Commits
8a26521f5b
...
v0.3.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
d61faa09eb
|
|||
|
50153788ef
|
|||
|
c84fe63217
|
|||
|
eb67e5e0a8
|
|||
|
948afe33e5
|
|||
|
76c657177d
|
|||
|
4356f978aa
|
|||
|
4f17dad645
|
|||
|
68b7d41c65
|
|||
|
e48f303e38
|
|||
|
f1fd406b82
|
|||
|
53b1de3395
|
|||
|
92dcadbf27
|
|||
|
0bd6a18326
|
|||
|
67d592c337
|
|||
|
fdc8a8419b
|
|||
|
122cfbf63a
|
|||
|
504f5d28fe
|
|||
|
3eadd5c580
|
|||
|
4d29333807
|
|||
|
e1533fa4c6
|
|||
|
9a74d5273d
|
|||
|
2abc8c454e
|
|||
|
fecb963e85
|
|||
|
cd9da57f20
|
|||
|
c6a95f5a6a
|
|||
|
228489371d
|
|||
|
490471d22b
|
|||
|
763d2572fe
|
|||
|
bb1b6beb87
|
|||
|
3224a7da63
|
|||
|
8a86cf74ee
|
|||
|
e34a59e332
|
|||
|
861801597d
|
|||
|
334578fdde
|
|||
|
20790af71e
|
|||
|
43b8a40fc0
|
|||
|
87c3059214
|
|||
|
6956dfc31a
|
|||
|
d9ebaf20f8
|
|||
|
acee0b3632
|
|||
|
5e55a796df
|
|||
|
f6eaf76ec9
|
|||
|
5c127a7035
|
105
cmd/mbf/main.go
105
cmd/mbf/main.go
@@ -47,13 +47,13 @@ func main() {
|
||||
}()
|
||||
|
||||
var (
|
||||
flagVerbose bool
|
||||
flagCures int
|
||||
flagBase string
|
||||
flagTShift int
|
||||
flagQuiet bool
|
||||
flagCures int
|
||||
flagBase string
|
||||
flagTShift int
|
||||
)
|
||||
c := command.New(os.Stderr, log.Printf, "mbf", func([]string) (err error) {
|
||||
msg.SwapVerbose(flagVerbose)
|
||||
msg.SwapVerbose(!flagQuiet)
|
||||
|
||||
var base *check.Absolute
|
||||
if flagBase, err = filepath.Abs(flagBase); err != nil {
|
||||
@@ -62,16 +62,19 @@ func main() {
|
||||
return
|
||||
}
|
||||
if cache, err = pkg.Open(ctx, msg, flagCures, base); err == nil {
|
||||
if flagTShift < 0 || flagTShift > 31 {
|
||||
flagTShift = 31
|
||||
if flagTShift < 0 {
|
||||
cache.SetThreshold(0)
|
||||
} else if flagTShift > 31 {
|
||||
cache.SetThreshold(1 << 31)
|
||||
} else {
|
||||
cache.SetThreshold(1 << flagTShift)
|
||||
}
|
||||
cache.SetThreshold(1 << flagTShift)
|
||||
}
|
||||
return
|
||||
}).Flag(
|
||||
&flagVerbose,
|
||||
"v", command.BoolFlag(false),
|
||||
"Print cure messages to the console",
|
||||
&flagQuiet,
|
||||
"q", command.BoolFlag(false),
|
||||
"Do not print cure messages",
|
||||
).Flag(
|
||||
&flagCures,
|
||||
"cures", command.IntFlag(0),
|
||||
@@ -82,7 +85,7 @@ func main() {
|
||||
"Directory to store cured artifacts",
|
||||
).Flag(
|
||||
&flagTShift,
|
||||
"tshift", command.IntFlag(31),
|
||||
"tshift", command.IntFlag(-1),
|
||||
"Dependency graph size exponent, to the power of 2",
|
||||
)
|
||||
|
||||
@@ -143,24 +146,86 @@ func main() {
|
||||
if len(args) != 1 {
|
||||
return errors.New("cure requires 1 argument")
|
||||
}
|
||||
var a pkg.Artifact
|
||||
var p rosa.PArtifact
|
||||
switch args[0] {
|
||||
case "acl":
|
||||
p = rosa.ACL
|
||||
case "attr":
|
||||
p = rosa.Attr
|
||||
case "autoconf":
|
||||
p = rosa.Autoconf
|
||||
case "bash":
|
||||
p = rosa.Bash
|
||||
case "busybox":
|
||||
a = rosa.Std.NewBusybox()
|
||||
case "musl":
|
||||
a = rosa.Std.NewMusl(nil)
|
||||
p = rosa.Busybox
|
||||
case "cmake":
|
||||
p = rosa.CMake
|
||||
case "coreutils":
|
||||
p = rosa.Coreutils
|
||||
case "diffutils":
|
||||
p = rosa.Diffutils
|
||||
case "gettext":
|
||||
p = rosa.Gettext
|
||||
case "git":
|
||||
a = rosa.Std.NewGit()
|
||||
p = rosa.Git
|
||||
case "go":
|
||||
a = rosa.Std.NewGo()
|
||||
p = rosa.Go
|
||||
case "gperf":
|
||||
p = rosa.Gperf
|
||||
case "hakurei":
|
||||
p = rosa.Hakurei
|
||||
case "kernel-headers":
|
||||
p = rosa.KernelHeaders
|
||||
case "libXau":
|
||||
p = rosa.LibXau
|
||||
case "libexpat":
|
||||
p = rosa.Libexpat
|
||||
case "libseccomp":
|
||||
p = rosa.Libseccomp
|
||||
case "libxml2":
|
||||
p = rosa.Libxml2
|
||||
case "libffi":
|
||||
p = rosa.Libffi
|
||||
case "libgd":
|
||||
p = rosa.Libgd
|
||||
case "m4":
|
||||
p = rosa.M4
|
||||
case "make":
|
||||
p = rosa.Make
|
||||
case "meson":
|
||||
p = rosa.Meson
|
||||
case "ninja":
|
||||
p = rosa.Ninja
|
||||
case "patch":
|
||||
p = rosa.Patch
|
||||
case "perl":
|
||||
p = rosa.Perl
|
||||
case "pkg-config":
|
||||
p = rosa.PkgConfig
|
||||
case "python":
|
||||
p = rosa.Python
|
||||
case "rsync":
|
||||
a = rosa.Std.NewRsync()
|
||||
p = rosa.Rsync
|
||||
case "setuptools":
|
||||
p = rosa.Setuptools
|
||||
case "wayland":
|
||||
p = rosa.Wayland
|
||||
case "wayland-protocols":
|
||||
p = rosa.WaylandProtocols
|
||||
case "xcb":
|
||||
p = rosa.XCB
|
||||
case "xcb-proto":
|
||||
p = rosa.XCBProto
|
||||
case "xproto":
|
||||
p = rosa.Xproto
|
||||
case "zlib":
|
||||
p = rosa.Zlib
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported artifact %q", args[0])
|
||||
}
|
||||
|
||||
pathname, _, err := cache.Cure(a)
|
||||
pathname, _, err := cache.Cure(rosa.Std.Load(p))
|
||||
if err == nil {
|
||||
log.Println(pathname)
|
||||
}
|
||||
|
||||
12
dist/install.sh
vendored
12
dist/install.sh
vendored
@@ -1,12 +1,12 @@
|
||||
#!/bin/sh
|
||||
cd "$(dirname -- "$0")" || exit 1
|
||||
|
||||
install -vDm0755 "bin/hakurei" "${HAKUREI_INSTALL_PREFIX}/usr/bin/hakurei"
|
||||
install -vDm0755 "bin/sharefs" "${HAKUREI_INSTALL_PREFIX}/usr/bin/sharefs"
|
||||
install -vDm0755 "bin/hakurei" "${DESTDIR}/usr/bin/hakurei"
|
||||
install -vDm0755 "bin/sharefs" "${DESTDIR}/usr/bin/sharefs"
|
||||
|
||||
install -vDm4511 "bin/hsu" "${HAKUREI_INSTALL_PREFIX}/usr/bin/hsu"
|
||||
if [ ! -f "${HAKUREI_INSTALL_PREFIX}/etc/hsurc" ]; then
|
||||
install -vDm0400 "hsurc.default" "${HAKUREI_INSTALL_PREFIX}/etc/hsurc"
|
||||
install -vDm4511 "bin/hsu" "${DESTDIR}/usr/bin/hsu"
|
||||
if [ ! -f "${DESTDIR}/etc/hsurc" ]; then
|
||||
install -vDm0400 "hsurc.default" "${DESTDIR}/etc/hsurc"
|
||||
fi
|
||||
|
||||
install -vDm0644 "comp/_hakurei" "${HAKUREI_INSTALL_PREFIX}/usr/share/zsh/site-functions/_hakurei"
|
||||
install -vDm0644 "comp/_hakurei" "${DESTDIR}/usr/share/zsh/site-functions/_hakurei"
|
||||
@@ -25,8 +25,7 @@ var (
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
if os.Getenv("GO_TEST_SKIP_ACL") == "1" {
|
||||
t.Log("acl test skipped")
|
||||
t.SkipNow()
|
||||
t.Skip("acl test skipped")
|
||||
}
|
||||
|
||||
testFilePath := path.Join(t.TempDir(), testFileName)
|
||||
@@ -143,6 +142,7 @@ func (c *getFAclInvocation) run(name string) error {
|
||||
}
|
||||
|
||||
c.cmd = exec.Command("getfacl", "--omit-header", "--absolute-names", "--numeric", name)
|
||||
c.cmd.Stderr = os.Stderr
|
||||
|
||||
scanErr := make(chan error, 1)
|
||||
if p, err := c.cmd.StdoutPipe(); err != nil {
|
||||
@@ -254,7 +254,7 @@ func getfacl(t *testing.T, name string) []*getFAclResp {
|
||||
t.Fatalf("getfacl: error = %v", err)
|
||||
}
|
||||
if len(c.pe) != 0 {
|
||||
t.Errorf("errors encountered parsing getfacl output\n%s", errors.Join(c.pe...).Error())
|
||||
t.Errorf("errors encountered parsing getfacl output\n%s", errors.Join(c.pe...))
|
||||
}
|
||||
return c.val
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ type ExecPath struct {
|
||||
P *check.Absolute
|
||||
// Artifacts to mount on the pathname, must contain at least one [Artifact].
|
||||
// If there are multiple entries or W is true, P is set up as an overlay
|
||||
// mount, and entries of A must not implement [File].
|
||||
// mount, and entries of A must not implement [FileArtifact].
|
||||
A []Artifact
|
||||
// Whether to make the mount point writable via the temp directory.
|
||||
W bool
|
||||
@@ -105,6 +105,9 @@ type execArtifact struct {
|
||||
// equivalent to execTimeoutDefault. This value is never encoded in Params
|
||||
// because it cannot affect outcome.
|
||||
timeout time.Duration
|
||||
|
||||
// Caller-supplied exclusivity value, returned as is by IsExclusive.
|
||||
exclusive bool
|
||||
}
|
||||
|
||||
var _ fmt.Stringer = new(execArtifact)
|
||||
@@ -123,7 +126,7 @@ var _ KnownChecksum = new(execNetArtifact)
|
||||
func (a *execNetArtifact) Checksum() Checksum { return a.checksum }
|
||||
|
||||
// Kind returns the hardcoded [Kind] constant.
|
||||
func (a *execNetArtifact) Kind() Kind { return KindExecNet }
|
||||
func (*execNetArtifact) Kind() Kind { return KindExecNet }
|
||||
|
||||
// Params is [Checksum] concatenated with [KindExec] params.
|
||||
func (a *execNetArtifact) Params(ctx *IContext) {
|
||||
@@ -157,13 +160,14 @@ func (a *execNetArtifact) Cure(f *FContext) error {
|
||||
// negative timeout value is equivalent tp [ExecTimeoutDefault], a timeout value
|
||||
// greater than [ExecTimeoutMax] is equivalent to [ExecTimeoutMax].
|
||||
//
|
||||
// The user-facing name is not accessible from the container and does not
|
||||
// affect curing outcome. Because of this, it is omitted from parameter data
|
||||
// for computing identifier.
|
||||
// The user-facing name and exclusivity value are not accessible from the
|
||||
// container and does not affect curing outcome. Because of this, it is omitted
|
||||
// from parameter data for computing identifier.
|
||||
func NewExec(
|
||||
name string,
|
||||
checksum *Checksum,
|
||||
timeout time.Duration,
|
||||
exclusive bool,
|
||||
|
||||
dir *check.Absolute,
|
||||
env []string,
|
||||
@@ -181,7 +185,7 @@ func NewExec(
|
||||
if timeout > ExecTimeoutMax {
|
||||
timeout = ExecTimeoutMax
|
||||
}
|
||||
a := execArtifact{name, paths, dir, env, pathname, args, timeout}
|
||||
a := execArtifact{name, paths, dir, env, pathname, args, timeout, exclusive}
|
||||
if checksum == nil {
|
||||
return &a
|
||||
}
|
||||
@@ -189,7 +193,7 @@ func NewExec(
|
||||
}
|
||||
|
||||
// Kind returns the hardcoded [Kind] constant.
|
||||
func (a *execArtifact) Kind() Kind { return KindExec }
|
||||
func (*execArtifact) Kind() Kind { return KindExec }
|
||||
|
||||
// Params writes paths, executable pathname and args.
|
||||
func (a *execArtifact) Params(ctx *IContext) {
|
||||
@@ -237,6 +241,9 @@ func (a *execArtifact) Dependencies() []Artifact {
|
||||
return slices.Concat(artifacts...)
|
||||
}
|
||||
|
||||
// IsExclusive returns the caller-supplied exclusivity value.
|
||||
func (a *execArtifact) IsExclusive() bool { return a.exclusive }
|
||||
|
||||
// String returns the caller-supplied reporting name.
|
||||
func (a *execArtifact) String() string { return a.name }
|
||||
|
||||
@@ -259,6 +266,10 @@ func scanVerbose(
|
||||
) {
|
||||
defer close(done)
|
||||
s := bufio.NewScanner(r)
|
||||
s.Buffer(
|
||||
make([]byte, bufio.MaxScanTokenSize),
|
||||
bufio.MaxScanTokenSize<<12,
|
||||
)
|
||||
for s.Scan() {
|
||||
msg.Verbose(prefix, s.Text())
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestExec(t *testing.T) {
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-offline", nil, 0,
|
||||
"exec-offline", nil, 0, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -62,7 +62,7 @@ func TestExec(t *testing.T) {
|
||||
), ignorePathname, wantChecksumOffline, nil},
|
||||
|
||||
{"error passthrough", pkg.NewExec(
|
||||
"", nil, 0,
|
||||
"", nil, 0, true,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -85,7 +85,7 @@ func TestExec(t *testing.T) {
|
||||
}},
|
||||
|
||||
{"invalid paths", pkg.NewExec(
|
||||
"", nil, 0,
|
||||
"", nil, 0, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -98,7 +98,7 @@ func TestExec(t *testing.T) {
|
||||
// check init failure passthrough
|
||||
var exitError *exec.ExitError
|
||||
if _, _, err := c.Cure(pkg.NewExec(
|
||||
"", nil, 0,
|
||||
"", nil, 0, false,
|
||||
pkg.AbsWork,
|
||||
nil,
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -120,7 +120,7 @@ func TestExec(t *testing.T) {
|
||||
)
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-net", &wantChecksum, 0,
|
||||
"exec-net", &wantChecksum, 0, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -152,7 +152,7 @@ func TestExec(t *testing.T) {
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-overlay-root", nil, 0,
|
||||
"exec-overlay-root", nil, 0, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -178,7 +178,7 @@ func TestExec(t *testing.T) {
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-overlay-work", nil, 0,
|
||||
"exec-overlay-work", nil, 0, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||
check.MustAbs("/work/bin/testtool"),
|
||||
@@ -209,7 +209,7 @@ func TestExec(t *testing.T) {
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-multiple-layers", nil, 0,
|
||||
"exec-multiple-layers", nil, 0, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -239,7 +239,9 @@ func TestExec(t *testing.T) {
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return os.MkdirAll(t.GetWorkDir().String(), 0700)
|
||||
},
|
||||
}}, 1<<5 /* concurrent cache hits */), cure: func(f *pkg.FContext) error {
|
||||
}}, 1<<5 /* concurrent cache hits */),
|
||||
|
||||
cure: func(f *pkg.FContext) error {
|
||||
work := f.GetWorkDir()
|
||||
if err := os.MkdirAll(work.String(), 0700); err != nil {
|
||||
return err
|
||||
@@ -260,7 +262,7 @@ func TestExec(t *testing.T) {
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-layer-promotion", nil, 0,
|
||||
"exec-layer-promotion", nil, 0, true,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"bytes"
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// A fileArtifact is an [Artifact] that cures into data known ahead of time.
|
||||
@@ -24,10 +25,10 @@ var _ KnownChecksum = new(fileArtifactNamed)
|
||||
// String returns the caller-supplied reporting name.
|
||||
func (a *fileArtifactNamed) String() string { return a.name }
|
||||
|
||||
// NewFile returns a [File] that cures into a caller-supplied byte slice.
|
||||
// NewFile returns a [FileArtifact] that cures into a caller-supplied byte slice.
|
||||
//
|
||||
// Caller must not modify data after NewFile returns.
|
||||
func NewFile(name string, data []byte) File {
|
||||
func NewFile(name string, data []byte) FileArtifact {
|
||||
f := fileArtifact(data)
|
||||
if name != "" {
|
||||
return &fileArtifactNamed{f, name}
|
||||
@@ -36,13 +37,16 @@ func NewFile(name string, data []byte) File {
|
||||
}
|
||||
|
||||
// Kind returns the hardcoded [Kind] constant.
|
||||
func (a *fileArtifact) Kind() Kind { return KindFile }
|
||||
func (*fileArtifact) Kind() Kind { return KindFile }
|
||||
|
||||
// Params writes the result of Cure.
|
||||
func (a *fileArtifact) Params(ctx *IContext) { ctx.GetHash().Write(*a) }
|
||||
|
||||
// Dependencies returns a nil slice.
|
||||
func (a *fileArtifact) Dependencies() []Artifact { return nil }
|
||||
func (*fileArtifact) Dependencies() []Artifact { return nil }
|
||||
|
||||
// IsExclusive returns false: Cure returns a prepopulated buffer.
|
||||
func (*fileArtifact) IsExclusive() bool { return false }
|
||||
|
||||
// Checksum computes and returns the checksum of caller-supplied data.
|
||||
func (a *fileArtifact) Checksum() Checksum {
|
||||
@@ -52,4 +56,6 @@ func (a *fileArtifact) Checksum() Checksum {
|
||||
}
|
||||
|
||||
// Cure returns the caller-supplied data.
|
||||
func (a *fileArtifact) Cure(context.Context) ([]byte, error) { return *a, nil }
|
||||
func (a *fileArtifact) Cure(*RContext) (io.ReadCloser, error) {
|
||||
return io.NopCloser(bytes.NewReader(*a)), nil
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"sync"
|
||||
"unique"
|
||||
)
|
||||
|
||||
// An httpArtifact is an [Artifact] backed by a [http] url string. The method is
|
||||
@@ -17,38 +15,32 @@ type httpArtifact struct {
|
||||
// Caller-supplied url string.
|
||||
url string
|
||||
|
||||
// Caller-supplied checksum of the response body. This is validated during
|
||||
// curing and the first call to Data.
|
||||
checksum Checksum
|
||||
// Caller-supplied checksum of the response body. This is validated when
|
||||
// closing the [io.ReadCloser] returned by Cure.
|
||||
checksum unique.Handle[Checksum]
|
||||
|
||||
// doFunc is the Do method of [http.Client] supplied by the caller.
|
||||
doFunc func(req *http.Request) (*http.Response, error)
|
||||
|
||||
// Response body read to EOF.
|
||||
data []byte
|
||||
|
||||
// Synchronises access to data.
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var _ KnownChecksum = new(httpArtifact)
|
||||
var _ fmt.Stringer = new(httpArtifact)
|
||||
|
||||
// NewHTTPGet returns a new [File] backed by the supplied client. A GET request
|
||||
// is set up for url. If c is nil, [http.DefaultClient] is used instead.
|
||||
// NewHTTPGet returns a new [FileArtifact] backed by the supplied client. A GET
|
||||
// request is set up for url. If c is nil, [http.DefaultClient] is used instead.
|
||||
func NewHTTPGet(
|
||||
c *http.Client,
|
||||
url string,
|
||||
checksum Checksum,
|
||||
) File {
|
||||
) FileArtifact {
|
||||
if c == nil {
|
||||
c = http.DefaultClient
|
||||
}
|
||||
return &httpArtifact{url: url, checksum: checksum, doFunc: c.Do}
|
||||
return &httpArtifact{url: url, checksum: unique.Make(checksum), doFunc: c.Do}
|
||||
}
|
||||
|
||||
// Kind returns the hardcoded [Kind] constant.
|
||||
func (a *httpArtifact) Kind() Kind { return KindHTTPGet }
|
||||
func (*httpArtifact) Kind() Kind { return KindHTTPGet }
|
||||
|
||||
// Params writes the backing url string. Client is not represented as it does
|
||||
// not affect [Cache.Cure] outcome.
|
||||
@@ -57,10 +49,13 @@ func (a *httpArtifact) Params(ctx *IContext) {
|
||||
}
|
||||
|
||||
// Dependencies returns a nil slice.
|
||||
func (a *httpArtifact) Dependencies() []Artifact { return nil }
|
||||
func (*httpArtifact) Dependencies() []Artifact { return nil }
|
||||
|
||||
// IsExclusive returns false: Cure returns as soon as a response is received.
|
||||
func (*httpArtifact) IsExclusive() bool { return false }
|
||||
|
||||
// Checksum returns the caller-supplied checksum.
|
||||
func (a *httpArtifact) Checksum() Checksum { return a.checksum }
|
||||
func (a *httpArtifact) Checksum() Checksum { return a.checksum.Value() }
|
||||
|
||||
// String returns [path.Base] over the backing url.
|
||||
func (a *httpArtifact) String() string { return path.Base(a.url) }
|
||||
@@ -73,11 +68,13 @@ func (e ResponseStatusError) Error() string {
|
||||
return "the requested URL returned non-OK status: " + http.StatusText(int(e))
|
||||
}
|
||||
|
||||
// do sends the caller-supplied request on the caller-supplied [http.Client]
|
||||
// and reads its response body to EOF and returns the resulting bytes.
|
||||
func (a *httpArtifact) do(ctx context.Context) (data []byte, err error) {
|
||||
// Cure sends the http request and returns the resulting response body reader
|
||||
// wrapped to perform checksum validation. It is valid but not encouraged to
|
||||
// close the resulting [io.ReadCloser] before it is read to EOF, as that causes
|
||||
// Close to block until all remaining data is consumed and validated.
|
||||
func (a *httpArtifact) Cure(r *RContext) (rc io.ReadCloser, err error) {
|
||||
var req *http.Request
|
||||
req, err = http.NewRequestWithContext(ctx, http.MethodGet, a.url, nil)
|
||||
req, err = http.NewRequestWithContext(r.Unwrap(), http.MethodGet, a.url, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -92,35 +89,6 @@ func (a *httpArtifact) do(ctx context.Context) (data []byte, err error) {
|
||||
return nil, ResponseStatusError(resp.StatusCode)
|
||||
}
|
||||
|
||||
if data, err = io.ReadAll(resp.Body); err != nil {
|
||||
_ = resp.Body.Close()
|
||||
return
|
||||
}
|
||||
|
||||
err = resp.Body.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Cure completes the http request and returns the resulting response body read
|
||||
// to EOF. Data does not interact with the filesystem.
|
||||
func (a *httpArtifact) Cure(ctx context.Context) (data []byte, err error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
if a.data != nil {
|
||||
// validated by cache or a previous call to Data
|
||||
return a.data, nil
|
||||
}
|
||||
|
||||
if data, err = a.do(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h := sha512.New384()
|
||||
h.Write(data)
|
||||
if got := (Checksum)(h.Sum(nil)); got != a.checksum {
|
||||
return nil, &ChecksumMismatchError{got, a.checksum}
|
||||
}
|
||||
a.data = data
|
||||
rc = r.NewMeasuredReader(resp.Body, a.checksum)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ package pkg_test
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
"unique"
|
||||
"unsafe"
|
||||
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/internal/pkg"
|
||||
@@ -31,15 +33,27 @@ func TestHTTPGet(t *testing.T) {
|
||||
|
||||
checkWithCache(t, []cacheTestCase{
|
||||
{"direct", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
var r pkg.RContext
|
||||
rCacheVal := reflect.ValueOf(&r).Elem().FieldByName("cache")
|
||||
reflect.NewAt(
|
||||
rCacheVal.Type(),
|
||||
unsafe.Pointer(rCacheVal.UnsafeAddr()),
|
||||
).Elem().Set(reflect.ValueOf(c))
|
||||
|
||||
f := pkg.NewHTTPGet(
|
||||
&client,
|
||||
"file:///testdata",
|
||||
testdataChecksum.Value(),
|
||||
)
|
||||
if got, err := f.Cure(t.Context()); err != nil {
|
||||
var got []byte
|
||||
if rc, err := f.Cure(&r); err != nil {
|
||||
t.Fatalf("Cure: error = %v", err)
|
||||
} else if got, err = io.ReadAll(rc); err != nil {
|
||||
t.Fatalf("ReadAll: error = %v", err)
|
||||
} else if string(got) != testdata {
|
||||
t.Fatalf("Cure: %x, want %x", got, testdata)
|
||||
} else if err = rc.Close(); err != nil {
|
||||
t.Fatalf("Close: error = %v", err)
|
||||
}
|
||||
|
||||
// check direct validation
|
||||
@@ -51,8 +65,21 @@ func TestHTTPGet(t *testing.T) {
|
||||
wantErrMismatch := &pkg.ChecksumMismatchError{
|
||||
Got: testdataChecksum.Value(),
|
||||
}
|
||||
if _, err := f.Cure(t.Context()); !reflect.DeepEqual(err, wantErrMismatch) {
|
||||
t.Fatalf("Cure: error = %#v, want %#v", err, wantErrMismatch)
|
||||
if rc, err := f.Cure(&r); err != nil {
|
||||
t.Fatalf("Cure: error = %v", err)
|
||||
} else if got, err = io.ReadAll(rc); err != nil {
|
||||
t.Fatalf("ReadAll: error = %v", err)
|
||||
} else if string(got) != testdata {
|
||||
t.Fatalf("Cure: %x, want %x", got, testdata)
|
||||
} else if err = rc.Close(); !reflect.DeepEqual(err, wantErrMismatch) {
|
||||
t.Fatalf("Close: error = %#v, want %#v", err, wantErrMismatch)
|
||||
}
|
||||
|
||||
// check fallback validation
|
||||
if rc, err := f.Cure(&r); err != nil {
|
||||
t.Fatalf("Cure: error = %v", err)
|
||||
} else if err = rc.Close(); !reflect.DeepEqual(err, wantErrMismatch) {
|
||||
t.Fatalf("Close: error = %#v, want %#v", err, wantErrMismatch)
|
||||
}
|
||||
|
||||
// check direct response error
|
||||
@@ -62,12 +89,19 @@ func TestHTTPGet(t *testing.T) {
|
||||
pkg.Checksum{},
|
||||
)
|
||||
wantErrNotFound := pkg.ResponseStatusError(http.StatusNotFound)
|
||||
if _, err := f.Cure(t.Context()); !reflect.DeepEqual(err, wantErrNotFound) {
|
||||
if _, err := f.Cure(&r); !reflect.DeepEqual(err, wantErrNotFound) {
|
||||
t.Fatalf("Cure: error = %#v, want %#v", err, wantErrNotFound)
|
||||
}
|
||||
}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C")},
|
||||
|
||||
{"cure", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
var r pkg.RContext
|
||||
rCacheVal := reflect.ValueOf(&r).Elem().FieldByName("cache")
|
||||
reflect.NewAt(
|
||||
rCacheVal.Type(),
|
||||
unsafe.Pointer(rCacheVal.UnsafeAddr()),
|
||||
).Elem().Set(reflect.ValueOf(c))
|
||||
|
||||
f := pkg.NewHTTPGet(
|
||||
&client,
|
||||
"file:///testdata",
|
||||
@@ -85,10 +119,15 @@ func TestHTTPGet(t *testing.T) {
|
||||
t.Fatalf("Cure: %x, want %x", checksum.Value(), testdataChecksum.Value())
|
||||
}
|
||||
|
||||
if got, err := f.Cure(t.Context()); err != nil {
|
||||
var got []byte
|
||||
if rc, err := f.Cure(&r); err != nil {
|
||||
t.Fatalf("Cure: error = %v", err)
|
||||
} else if got, err = io.ReadAll(rc); err != nil {
|
||||
t.Fatalf("ReadAll: error = %v", err)
|
||||
} else if string(got) != testdata {
|
||||
t.Fatalf("Cure: %x, want %x", got, testdata)
|
||||
} else if err = rc.Close(); err != nil {
|
||||
t.Fatalf("Close: error = %v", err)
|
||||
}
|
||||
|
||||
// check load from cache
|
||||
@@ -97,10 +136,14 @@ func TestHTTPGet(t *testing.T) {
|
||||
"file:///testdata",
|
||||
testdataChecksum.Value(),
|
||||
)
|
||||
if got, err := f.Cure(t.Context()); err != nil {
|
||||
if rc, err := f.Cure(&r); err != nil {
|
||||
t.Fatalf("Cure: error = %v", err)
|
||||
} else if got, err = io.ReadAll(rc); err != nil {
|
||||
t.Fatalf("ReadAll: error = %v", err)
|
||||
} else if string(got) != testdata {
|
||||
t.Fatalf("Cure: %x, want %x", got, testdata)
|
||||
} else if err = rc.Close(); err != nil {
|
||||
t.Fatalf("Close: error = %v", err)
|
||||
}
|
||||
|
||||
// check error passthrough
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha512"
|
||||
@@ -147,14 +148,15 @@ func (t *TContext) GetWorkDir() *check.Absolute { return t.work }
|
||||
// create it if they wish to use it, using [os.MkdirAll].
|
||||
func (t *TContext) GetTempDir() *check.Absolute { return t.temp }
|
||||
|
||||
// Open tries to open [Artifact] for reading. If a implements [File], its data
|
||||
// might be used directly, eliminating the roundtrip to vfs. Otherwise, it must
|
||||
// cure into a directory containing a single regular file.
|
||||
// Open tries to open [Artifact] for reading. If a implements [FileArtifact],
|
||||
// its reader might be used directly, eliminating the roundtrip to vfs.
|
||||
// Otherwise, it must cure into a directory containing a single regular file.
|
||||
//
|
||||
// If err is nil, the caller is responsible for closing the resulting
|
||||
// [io.ReadCloser].
|
||||
// If err is nil, the caller must close the resulting [io.ReadCloser] and return
|
||||
// its error, if any. Failure to read r to EOF may result in a spurious
|
||||
// [ChecksumMismatchError], or the underlying implementation may block on Close.
|
||||
func (t *TContext) Open(a Artifact) (r io.ReadCloser, err error) {
|
||||
if f, ok := a.(File); ok {
|
||||
if f, ok := a.(FileArtifact); ok {
|
||||
return t.cache.openFile(f)
|
||||
}
|
||||
|
||||
@@ -213,6 +215,20 @@ func (f *FContext) GetArtifact(a Artifact) (
|
||||
panic(InvalidLookupError(f.cache.Ident(a).Value()))
|
||||
}
|
||||
|
||||
// RContext is passed to [FileArtifact.Cure] and provides helper methods useful
|
||||
// for curing the [FileArtifact].
|
||||
//
|
||||
// Methods of RContext are safe for concurrent use. RContext is valid
|
||||
// until [FileArtifact.Cure] returns.
|
||||
type RContext struct {
|
||||
// Address of underlying [Cache], should be zeroed or made unusable after
|
||||
// [FileArtifact.Cure] returns and must not be exposed directly.
|
||||
cache *Cache
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying [context.Context].
|
||||
func (r *RContext) Unwrap() context.Context { return r.cache.ctx }
|
||||
|
||||
// An Artifact is a read-only reference to a piece of data that may be created
|
||||
// deterministically but might not currently be available in memory or on the
|
||||
// filesystem.
|
||||
@@ -237,6 +253,24 @@ type Artifact interface {
|
||||
//
|
||||
// Result must remain identical across multiple invocations.
|
||||
Dependencies() []Artifact
|
||||
|
||||
// IsExclusive returns whether the [Artifact] is exclusive. Exclusive
|
||||
// artifacts might not run in parallel with each other, and are still
|
||||
// subject to the cures limit.
|
||||
//
|
||||
// Some implementations may saturate the CPU for a nontrivial amount of
|
||||
// time. Curing multiple such implementations simultaneously causes
|
||||
// significant CPU scheduler overhead. An exclusive artifact will generally
|
||||
// not be cured alongside another exclusive artifact, thus alleviating this
|
||||
// overhead.
|
||||
//
|
||||
// Note that [Cache] reserves the right to still cure exclusive
|
||||
// artifacts concurrently as this is not a synchronisation primitive but
|
||||
// an optimisation one. Implementations are forbidden from accessing global
|
||||
// state regardless of exclusivity.
|
||||
//
|
||||
// Result must remain identical across multiple invocations.
|
||||
IsExclusive() bool
|
||||
}
|
||||
|
||||
// FloodArtifact refers to an [Artifact] requiring its entire dependency graph
|
||||
@@ -274,7 +308,7 @@ func Flood(a Artifact) iter.Seq[Artifact] {
|
||||
//
|
||||
// TrivialArtifact is unable to cure any other [Artifact] and it cannot access
|
||||
// pathnames. This type of [Artifact] is primarily intended for dependency-less
|
||||
// artifacts or direct dependencies that only consists of [File].
|
||||
// artifacts or direct dependencies that only consists of [FileArtifact].
|
||||
type TrivialArtifact interface {
|
||||
// Cure cures the current [Artifact] to the working directory obtained via
|
||||
// [TContext.GetWorkDir].
|
||||
@@ -309,16 +343,19 @@ type KnownChecksum interface {
|
||||
Checksum() Checksum
|
||||
}
|
||||
|
||||
// A File refers to an [Artifact] backed by a single file.
|
||||
type File interface {
|
||||
// Cure returns the full contents of [File]. If [File] implements
|
||||
// [KnownChecksum], Cure is responsible for validating any data it produces
|
||||
// and must return [ChecksumMismatchError] if validation fails.
|
||||
// FileArtifact refers to an [Artifact] backed by a single file.
|
||||
type FileArtifact interface {
|
||||
// Cure returns [io.ReadCloser] of the full contents of [FileArtifact]. If
|
||||
// [FileArtifact] implements [KnownChecksum], Cure is responsible for
|
||||
// validating any data it produces and must return [ChecksumMismatchError]
|
||||
// if validation fails. This error is conventionally returned during the
|
||||
// first call to Close, but may be returned during any call to Read before
|
||||
// EOF, or by Cure itself.
|
||||
//
|
||||
// Callers must not modify the returned byte slice.
|
||||
// Callers are responsible for closing the resulting [io.ReadCloser].
|
||||
//
|
||||
// Result must remain identical across multiple invocations.
|
||||
Cure(ctx context.Context) ([]byte, error)
|
||||
Cure(r *RContext) (io.ReadCloser, error)
|
||||
|
||||
Artifact
|
||||
}
|
||||
@@ -413,9 +450,9 @@ type pendingArtifactDep struct {
|
||||
// Cache is a support layer that implementations of [Artifact] can use to store
|
||||
// cured [Artifact] data in a content addressed fashion.
|
||||
type Cache struct {
|
||||
// Work for curing dependency [Artifact] is sent here and cured concurrently
|
||||
// while subject to the cures limit. Invalid after the context is canceled.
|
||||
cureDep chan<- *pendingArtifactDep
|
||||
// Cures of any variant of [Artifact] sends to cures before entering the
|
||||
// implementation and receives an equal amount of elements after.
|
||||
cures chan struct{}
|
||||
|
||||
// [context.WithCancel] over caller-supplied context, used by [Artifact] and
|
||||
// all dependency curing goroutines.
|
||||
@@ -430,7 +467,7 @@ type Cache struct {
|
||||
// Directory where all [Cache] related files are placed.
|
||||
base *check.Absolute
|
||||
|
||||
// Whether to validate [File.Cure] for a [KnownChecksum] file. This
|
||||
// Whether to validate [FileArtifact.Cure] for a [KnownChecksum] file. This
|
||||
// significantly reduces performance.
|
||||
strict bool
|
||||
// Maximum size of a dependency graph.
|
||||
@@ -453,6 +490,11 @@ type Cache struct {
|
||||
// Synchronises access to ident and corresponding filesystem entries.
|
||||
identMu sync.RWMutex
|
||||
|
||||
// Synchronises entry into exclusive artifacts for the cure method.
|
||||
exclMu sync.Mutex
|
||||
// Buffered I/O free list, must not be accessed directly.
|
||||
bufioPool sync.Pool
|
||||
|
||||
// Unlocks the on-filesystem cache. Must only be called from Close.
|
||||
unlock func()
|
||||
// Synchronises calls to Close.
|
||||
@@ -573,8 +615,8 @@ func (e *ChecksumMismatchError) Error() string {
|
||||
// found and removed from the underlying storage of [Cache].
|
||||
type ScrubError struct {
|
||||
// Content-addressed entries not matching their checksum. This can happen
|
||||
// if an incorrect [File] implementation was cured against a non-strict
|
||||
// [Cache].
|
||||
// if an incorrect [FileArtifact] implementation was cured against
|
||||
// a non-strict [Cache].
|
||||
ChecksumMismatches []ChecksumMismatchError
|
||||
// Dangling identifier symlinks. This can happen if the content-addressed
|
||||
// entry was removed while scrubbing due to a checksum mismatch.
|
||||
@@ -910,10 +952,11 @@ func (c *Cache) finaliseIdent(
|
||||
close(done)
|
||||
}
|
||||
|
||||
// openFile tries to load [File] from [Cache], and if that fails, obtains it via
|
||||
// [File.Cure] instead. Notably, it does not cure [File]. If err is nil, the
|
||||
// caller is responsible for closing the resulting [io.ReadCloser].
|
||||
func (c *Cache) openFile(f File) (r io.ReadCloser, err error) {
|
||||
// openFile tries to load [FileArtifact] from [Cache], and if that fails,
|
||||
// obtains it via [FileArtifact.Cure] instead. Notably, it does not cure
|
||||
// [FileArtifact] to the filesystem. If err is nil, the caller is responsible
|
||||
// for closing the resulting [io.ReadCloser].
|
||||
func (c *Cache) openFile(f FileArtifact) (r io.ReadCloser, err error) {
|
||||
if kc, ok := f.(KnownChecksum); ok {
|
||||
c.checksumMu.RLock()
|
||||
r, err = os.Open(c.base.Append(
|
||||
@@ -943,11 +986,7 @@ func (c *Cache) openFile(f File) (r io.ReadCloser, err error) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
var data []byte
|
||||
if data, err = f.Cure(c.ctx); err != nil {
|
||||
return
|
||||
}
|
||||
r = io.NopCloser(bytes.NewReader(data))
|
||||
return f.Cure(&RContext{c})
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1102,6 +1141,14 @@ func (c *Cache) Cure(a Artifact) (
|
||||
checksum unique.Handle[Checksum],
|
||||
err error,
|
||||
) {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
err = c.ctx.Err()
|
||||
return
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
if c.threshold > 0 {
|
||||
var n uintptr
|
||||
for range Flood(a) {
|
||||
@@ -1114,7 +1161,7 @@ func (c *Cache) Cure(a Artifact) (
|
||||
c.msg.Verbosef("visited %d artifacts", n)
|
||||
}
|
||||
|
||||
return c.cure(a)
|
||||
return c.cure(a, true)
|
||||
}
|
||||
|
||||
// CureError wraps a non-nil error returned attempting to cure an [Artifact].
|
||||
@@ -1187,8 +1234,133 @@ func (e *DependencyCureError) Error() string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// enterCure must be called before entering an [Artifact] implementation.
|
||||
func (c *Cache) enterCure(a Artifact, curesExempt bool) error {
|
||||
if a.IsExclusive() {
|
||||
c.exclMu.Lock()
|
||||
}
|
||||
if curesExempt {
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case c.cures <- struct{}{}:
|
||||
return nil
|
||||
|
||||
case <-c.ctx.Done():
|
||||
if a.IsExclusive() {
|
||||
c.exclMu.Unlock()
|
||||
}
|
||||
return c.ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// exitCure must be called after exiting an [Artifact] implementation.
|
||||
func (c *Cache) exitCure(a Artifact, curesExempt bool) {
|
||||
if a.IsExclusive() {
|
||||
c.exclMu.Unlock()
|
||||
}
|
||||
if curesExempt {
|
||||
return
|
||||
}
|
||||
|
||||
<-c.cures
|
||||
}
|
||||
|
||||
// getWriter is like [bufio.NewWriter] but for bufioPool.
|
||||
func (c *Cache) getWriter(w io.Writer) *bufio.Writer {
|
||||
bw := c.bufioPool.Get().(*bufio.Writer)
|
||||
bw.Reset(w)
|
||||
return bw
|
||||
}
|
||||
|
||||
// measuredReader implements [io.ReadCloser] and measures the checksum during
|
||||
// Close. If the underlying reader is not read to EOF, Close blocks until all
|
||||
// remaining data is consumed and validated.
|
||||
type measuredReader struct {
|
||||
// Underlying reader. Never exposed directly.
|
||||
r io.ReadCloser
|
||||
// For validating checksum. Never exposed directly.
|
||||
h hash.Hash
|
||||
// Buffers writes to h, initialised by [Cache]. Never exposed directly.
|
||||
hbw *bufio.Writer
|
||||
// Expected checksum, compared during Close.
|
||||
want unique.Handle[Checksum]
|
||||
|
||||
// For accessing free lists.
|
||||
c *Cache
|
||||
|
||||
// Set up via [io.TeeReader] by [Cache].
|
||||
io.Reader
|
||||
}
|
||||
|
||||
// Close reads the underlying [io.ReadCloser] to EOF, closes it and measures its
|
||||
// outcome. It returns a [ChecksumMismatchError] for an unexpected checksum.
|
||||
func (mr *measuredReader) Close() (err error) {
|
||||
if mr.hbw == nil || mr.Reader == nil {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
err = mr.hbw.Flush()
|
||||
mr.c.putWriter(mr.hbw)
|
||||
mr.hbw, mr.Reader = nil, nil
|
||||
if err != nil {
|
||||
_ = mr.r.Close()
|
||||
return
|
||||
}
|
||||
var n int64
|
||||
if n, err = io.Copy(mr.h, mr.r); err != nil {
|
||||
_ = mr.r.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
mr.c.msg.Verbosef("missed %d bytes on measured reader", n)
|
||||
}
|
||||
|
||||
if err = mr.r.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := mr.c.getIdentBuf()
|
||||
mr.h.Sum(buf[:0])
|
||||
|
||||
if got := Checksum(buf[:]); got != mr.want.Value() {
|
||||
err = &ChecksumMismatchError{
|
||||
Got: got,
|
||||
Want: mr.want.Value(),
|
||||
}
|
||||
}
|
||||
|
||||
mr.c.putIdentBuf(buf)
|
||||
return
|
||||
}
|
||||
|
||||
// newMeasuredReader implements [RContext.NewMeasuredReader].
|
||||
func (c *Cache) newMeasuredReader(
|
||||
r io.ReadCloser,
|
||||
checksum unique.Handle[Checksum],
|
||||
) io.ReadCloser {
|
||||
mr := measuredReader{r: r, h: sha512.New384(), want: checksum, c: c}
|
||||
mr.hbw = c.getWriter(mr.h)
|
||||
mr.Reader = io.TeeReader(r, mr.hbw)
|
||||
return &mr
|
||||
}
|
||||
|
||||
// NewMeasuredReader returns an [io.ReadCloser] implementing behaviour required
|
||||
// by [FileArtifact]. The resulting [io.ReadCloser] holds a buffer originating
|
||||
// from [Cache] and must be closed to return this buffer.
|
||||
func (r *RContext) NewMeasuredReader(
|
||||
rc io.ReadCloser,
|
||||
checksum unique.Handle[Checksum],
|
||||
) io.ReadCloser {
|
||||
return r.cache.newMeasuredReader(rc, checksum)
|
||||
}
|
||||
|
||||
// putWriter adds bw to bufioPool.
|
||||
func (c *Cache) putWriter(bw *bufio.Writer) { c.bufioPool.Put(bw) }
|
||||
|
||||
// cure implements Cure without checking the full dependency graph.
|
||||
func (c *Cache) cure(a Artifact) (
|
||||
func (c *Cache) cure(a Artifact, curesExempt bool) (
|
||||
pathname *check.Absolute,
|
||||
checksum unique.Handle[Checksum],
|
||||
err error,
|
||||
@@ -1283,8 +1455,8 @@ func (c *Cache) cure(a Artifact) (
|
||||
}()
|
||||
}
|
||||
|
||||
// cure File outside type switch to skip TContext initialisation
|
||||
if f, ok := a.(File); ok {
|
||||
// cure FileArtifact outside type switch to skip TContext initialisation
|
||||
if f, ok := a.(FileArtifact); ok {
|
||||
if checksumFi != nil {
|
||||
if !checksumFi.Mode().IsRegular() {
|
||||
// unreachable
|
||||
@@ -1293,63 +1465,96 @@ func (c *Cache) cure(a Artifact) (
|
||||
return
|
||||
}
|
||||
|
||||
var data []byte
|
||||
data, err = f.Cure(c.ctx)
|
||||
if err != nil {
|
||||
work := c.base.Append(dirWork, ids)
|
||||
var w *os.File
|
||||
if w, err = os.OpenFile(
|
||||
work.String(),
|
||||
os.O_CREATE|os.O_EXCL|os.O_WRONLY,
|
||||
0400,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if checksumPathname == nil {
|
||||
h := sha512.New384()
|
||||
h.Write(data)
|
||||
buf := c.getIdentBuf()
|
||||
h.Sum(buf[:0])
|
||||
checksum = unique.Make(Checksum(buf[:]))
|
||||
checksums = Encode(Checksum(buf[:]))
|
||||
c.putIdentBuf(buf)
|
||||
checksumPathname = c.base.Append(
|
||||
dirChecksum,
|
||||
checksums,
|
||||
)
|
||||
} else if c.IsStrict() {
|
||||
h := sha512.New384()
|
||||
h.Write(data)
|
||||
if got := Checksum(h.Sum(nil)); got != checksum.Value() {
|
||||
err = &ChecksumMismatchError{
|
||||
Got: got,
|
||||
Want: checksum.Value(),
|
||||
}
|
||||
return
|
||||
defer func() {
|
||||
closeErr := w.Close()
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
|
||||
removeErr := os.Remove(work.String())
|
||||
if err == nil && !errors.Is(removeErr, os.ErrNotExist) {
|
||||
err = removeErr
|
||||
}
|
||||
}()
|
||||
|
||||
var r io.ReadCloser
|
||||
if err = c.enterCure(a, curesExempt); err != nil {
|
||||
return
|
||||
}
|
||||
r, err = f.Cure(&RContext{c})
|
||||
if err == nil {
|
||||
if checksumPathname == nil || c.IsStrict() {
|
||||
h := sha512.New384()
|
||||
hbw := c.getWriter(h)
|
||||
_, err = io.Copy(w, io.TeeReader(r, hbw))
|
||||
flushErr := hbw.Flush()
|
||||
c.putWriter(hbw)
|
||||
if err == nil {
|
||||
err = flushErr
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
buf := c.getIdentBuf()
|
||||
h.Sum(buf[:0])
|
||||
|
||||
if checksumPathname == nil {
|
||||
checksum = unique.Make(Checksum(buf[:]))
|
||||
checksums = Encode(Checksum(buf[:]))
|
||||
} else if c.IsStrict() {
|
||||
if got := Checksum(buf[:]); got != checksum.Value() {
|
||||
err = &ChecksumMismatchError{
|
||||
Got: got,
|
||||
Want: checksum.Value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.putIdentBuf(buf)
|
||||
|
||||
if checksumPathname == nil {
|
||||
checksumPathname = c.base.Append(
|
||||
dirChecksum,
|
||||
checksums,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_, err = io.Copy(w, r)
|
||||
}
|
||||
|
||||
closeErr := r.Close()
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
}
|
||||
c.exitCure(a, curesExempt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.checksumMu.Lock()
|
||||
var w *os.File
|
||||
w, err = os.OpenFile(
|
||||
if err = os.Rename(
|
||||
work.String(),
|
||||
checksumPathname.String(),
|
||||
os.O_CREATE|os.O_EXCL|os.O_WRONLY,
|
||||
0400,
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
c.checksumMu.Unlock()
|
||||
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
closeErr := w.Close()
|
||||
timeErr := zeroTimes(checksumPathname.String())
|
||||
c.checksumMu.Unlock()
|
||||
|
||||
if err == nil {
|
||||
err = timeErr
|
||||
}
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1365,7 +1570,12 @@ func (c *Cache) cure(a Artifact) (
|
||||
switch ca := a.(type) {
|
||||
case TrivialArtifact:
|
||||
defer t.destroy(&err)
|
||||
if err = ca.Cure(&t); err != nil {
|
||||
if err = c.enterCure(a, curesExempt); err != nil {
|
||||
return
|
||||
}
|
||||
err = ca.Cure(&t)
|
||||
c.exitCure(a, curesExempt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
break
|
||||
@@ -1381,14 +1591,7 @@ func (c *Cache) cure(a Artifact) (
|
||||
var errsMu sync.Mutex
|
||||
for i, d := range deps {
|
||||
pending := pendingArtifactDep{d, &res[i], &errs, &errsMu, &wg}
|
||||
select {
|
||||
case c.cureDep <- &pending:
|
||||
break
|
||||
|
||||
case <-c.ctx.Done():
|
||||
err = c.ctx.Err()
|
||||
return
|
||||
}
|
||||
go pending.cure(c)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
@@ -1401,7 +1604,12 @@ func (c *Cache) cure(a Artifact) (
|
||||
}
|
||||
|
||||
defer f.destroy(&err)
|
||||
if err = ca.Cure(&f); err != nil {
|
||||
if err = c.enterCure(a, curesExempt); err != nil {
|
||||
return
|
||||
}
|
||||
err = ca.Cure(&f)
|
||||
c.exitCure(a, curesExempt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
break
|
||||
@@ -1486,7 +1694,7 @@ func (pending *pendingArtifactDep) cure(c *Cache) {
|
||||
defer pending.Done()
|
||||
|
||||
var err error
|
||||
pending.resP.pathname, pending.resP.checksum, err = c.cure(pending.a)
|
||||
pending.resP.pathname, pending.resP.checksum, err = c.cure(pending.a, false)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
@@ -1501,6 +1709,7 @@ func (c *Cache) Close() {
|
||||
c.closeOnce.Do(func() {
|
||||
c.cancel()
|
||||
c.wg.Wait()
|
||||
close(c.cures)
|
||||
c.unlock()
|
||||
})
|
||||
}
|
||||
@@ -1536,6 +1745,10 @@ func open(
|
||||
base *check.Absolute,
|
||||
lock bool,
|
||||
) (*Cache, error) {
|
||||
if cures < 1 {
|
||||
cures = runtime.NumCPU()
|
||||
}
|
||||
|
||||
for _, name := range []string{
|
||||
dirIdentifier,
|
||||
dirChecksum,
|
||||
@@ -1548,6 +1761,8 @@ func open(
|
||||
}
|
||||
|
||||
c := Cache{
|
||||
cures: make(chan struct{}, cures),
|
||||
|
||||
msg: msg,
|
||||
base: base,
|
||||
|
||||
@@ -1556,9 +1771,8 @@ func open(
|
||||
identPending: make(map[unique.Handle[ID]]<-chan struct{}),
|
||||
}
|
||||
c.ctx, c.cancel = context.WithCancel(ctx)
|
||||
cureDep := make(chan *pendingArtifactDep, cures)
|
||||
c.cureDep = cureDep
|
||||
c.identPool.New = func() any { return new(extIdent) }
|
||||
c.bufioPool.New = func() any { return new(bufio.Writer) }
|
||||
|
||||
if lock || !testing.Testing() {
|
||||
if unlock, err := lockedfile.MutexAt(
|
||||
@@ -1572,23 +1786,5 @@ func open(
|
||||
c.unlock = func() {}
|
||||
}
|
||||
|
||||
if cures < 1 {
|
||||
cures = runtime.NumCPU()
|
||||
}
|
||||
for i := 0; i < cures; i++ {
|
||||
c.wg.Go(func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
|
||||
case pending := <-cureDep:
|
||||
pending.cure(&c)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ type overrideIdent struct {
|
||||
|
||||
func (a overrideIdent) ID() pkg.ID { return a.id }
|
||||
|
||||
// overrideIdentFile overrides the ID method of [File].
|
||||
// overrideIdentFile overrides the ID method of [FileArtifact].
|
||||
type overrideIdentFile struct {
|
||||
id pkg.ID
|
||||
pkg.File
|
||||
pkg.FileArtifact
|
||||
}
|
||||
|
||||
func (a overrideIdentFile) ID() pkg.ID { return a.id }
|
||||
@@ -61,10 +61,10 @@ type knownIdentArtifact interface {
|
||||
pkg.TrivialArtifact
|
||||
}
|
||||
|
||||
// A knownIdentFile implements [pkg.KnownIdent] and [File]
|
||||
// A knownIdentFile implements [pkg.KnownIdent] and [FileArtifact]
|
||||
type knownIdentFile interface {
|
||||
pkg.KnownIdent
|
||||
pkg.File
|
||||
pkg.FileArtifact
|
||||
}
|
||||
|
||||
// overrideChecksum overrides the Checksum method of [Artifact].
|
||||
@@ -75,7 +75,7 @@ type overrideChecksum struct {
|
||||
|
||||
func (a overrideChecksum) Checksum() pkg.Checksum { return a.checksum }
|
||||
|
||||
// overrideChecksumFile overrides the Checksum method of [File].
|
||||
// overrideChecksumFile overrides the Checksum method of [FileArtifact].
|
||||
type overrideChecksumFile struct {
|
||||
checksum pkg.Checksum
|
||||
knownIdentFile
|
||||
@@ -96,12 +96,14 @@ func (a *stubArtifact) Kind() pkg.Kind { return a.kind }
|
||||
func (a *stubArtifact) Params(ctx *pkg.IContext) { ctx.GetHash().Write(a.params) }
|
||||
func (a *stubArtifact) Dependencies() []pkg.Artifact { return a.deps }
|
||||
func (a *stubArtifact) Cure(t *pkg.TContext) error { return a.cure(t) }
|
||||
func (*stubArtifact) IsExclusive() bool { return false }
|
||||
|
||||
// A stubArtifactF implements [FloodArtifact] with hardcoded behaviour.
|
||||
type stubArtifactF struct {
|
||||
kind pkg.Kind
|
||||
params []byte
|
||||
deps []pkg.Artifact
|
||||
excl bool
|
||||
|
||||
cure func(f *pkg.FContext) error
|
||||
}
|
||||
@@ -110,8 +112,9 @@ func (a *stubArtifactF) Kind() pkg.Kind { return a.kind }
|
||||
func (a *stubArtifactF) Params(ctx *pkg.IContext) { ctx.GetHash().Write(a.params) }
|
||||
func (a *stubArtifactF) Dependencies() []pkg.Artifact { return a.deps }
|
||||
func (a *stubArtifactF) Cure(f *pkg.FContext) error { return a.cure(f) }
|
||||
func (a *stubArtifactF) IsExclusive() bool { return a.excl }
|
||||
|
||||
// A stubFile implements [File] with hardcoded behaviour.
|
||||
// A stubFile implements [FileArtifact] with hardcoded behaviour.
|
||||
type stubFile struct {
|
||||
data []byte
|
||||
err error
|
||||
@@ -119,7 +122,9 @@ type stubFile struct {
|
||||
stubArtifact
|
||||
}
|
||||
|
||||
func (a *stubFile) Cure(context.Context) ([]byte, error) { return a.data, a.err }
|
||||
func (a *stubFile) Cure(*pkg.RContext) (io.ReadCloser, error) {
|
||||
return io.NopCloser(bytes.NewReader(a.data)), a.err
|
||||
}
|
||||
|
||||
// newStubFile returns an implementation of [pkg.File] with hardcoded behaviour.
|
||||
func newStubFile(
|
||||
@@ -128,7 +133,7 @@ func newStubFile(
|
||||
sum *pkg.Checksum,
|
||||
data []byte,
|
||||
err error,
|
||||
) pkg.File {
|
||||
) pkg.FileArtifact {
|
||||
f := overrideIdentFile{id, &stubFile{data, err, stubArtifact{
|
||||
kind,
|
||||
nil,
|
||||
@@ -283,7 +288,7 @@ func checkWithCache(t *testing.T, testCases []cacheTestCase) {
|
||||
msg.SwapVerbose(testing.Verbose())
|
||||
|
||||
var scrubFunc func() error // scrub after hashing
|
||||
if c, err := pkg.Open(t.Context(), msg, 0, base); err != nil {
|
||||
if c, err := pkg.Open(t.Context(), msg, 1<<4, base); err != nil {
|
||||
t.Fatalf("Open: error = %v", err)
|
||||
} else {
|
||||
t.Cleanup(c.Close)
|
||||
@@ -561,6 +566,10 @@ func TestCache(t *testing.T) {
|
||||
stub.UniqueError
|
||||
}{UniqueError: 0xbad},
|
||||
)},
|
||||
|
||||
cure: func(f *pkg.FContext) error {
|
||||
panic("attempting to cure impossible artifact")
|
||||
},
|
||||
}, nil, pkg.Checksum{}, &pkg.DependencyCureError{
|
||||
{
|
||||
Ident: unique.Make(pkg.ID{0xff, 3}),
|
||||
|
||||
@@ -24,7 +24,7 @@ const (
|
||||
TarBzip2
|
||||
)
|
||||
|
||||
// A tarArtifact is an [Artifact] unpacking a tarball backed by a [File].
|
||||
// A tarArtifact is an [Artifact] unpacking a tarball backed by a [FileArtifact].
|
||||
type tarArtifact struct {
|
||||
// Caller-supplied backing tarball.
|
||||
f Artifact
|
||||
@@ -80,6 +80,9 @@ func (a *tarArtifact) Dependencies() []Artifact {
|
||||
return []Artifact{a.f}
|
||||
}
|
||||
|
||||
// IsExclusive returns false: decompressor and tar reader are fully sequential.
|
||||
func (a *tarArtifact) IsExclusive() bool { return false }
|
||||
|
||||
// A DisallowedTypeflagError describes a disallowed typeflag encountered while
|
||||
// unpacking a tarball.
|
||||
type DisallowedTypeflagError byte
|
||||
@@ -97,12 +100,11 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
|
||||
}
|
||||
|
||||
defer func(f io.ReadCloser) {
|
||||
closeErr := tr.Close()
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
err = tr.Close()
|
||||
}
|
||||
|
||||
closeErr = f.Close()
|
||||
closeErr := f.Close()
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
@@ -175,7 +177,10 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
|
||||
break
|
||||
|
||||
case tar.TypeLink:
|
||||
if err = os.Link(header.Linkname, pathname.String()); err != nil {
|
||||
if err = os.Link(
|
||||
temp.Append(header.Linkname).String(),
|
||||
pathname.String(),
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
break
|
||||
|
||||
98
internal/rosa/acl.go
Normal file
98
internal/rosa/acl.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newAttr() pkg.Artifact {
|
||||
const (
|
||||
version = "2.5.2"
|
||||
checksum = "YWEphrz6vg1sUMmHHVr1CRo53pFXRhq_pjN-AlG8UgwZK1y6m7zuDhxqJhD0SV0l"
|
||||
)
|
||||
return t.New("attr-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(Perl),
|
||||
}, nil, nil, `
|
||||
ln -s ../../system/bin/perl /usr/bin
|
||||
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/attr/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("attr"), true, t.NewPatchedSource(
|
||||
"attr", version, pkg.NewHTTPGetTar(
|
||||
nil,
|
||||
"https://download.savannah.nongnu.org/releases/attr/"+
|
||||
"attr-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
), true, [2]string{"libgen-basename", `From 8a80d895dfd779373363c3a4b62ecce5a549efb2 Mon Sep 17 00:00:00 2001
|
||||
From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me>
|
||||
Date: Sat, 30 Mar 2024 10:17:10 +0100
|
||||
Subject: tools/attr.c: Add missing libgen.h include for basename(3)
|
||||
|
||||
Fixes compilation issue with musl and modern C99 compilers.
|
||||
|
||||
See: https://bugs.gentoo.org/926294
|
||||
---
|
||||
tools/attr.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/tools/attr.c b/tools/attr.c
|
||||
index f12e4af..6a3c1e9 100644
|
||||
--- a/tools/attr.c
|
||||
+++ b/tools/attr.c
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
+#include <libgen.h>
|
||||
|
||||
#include <attr/attributes.h>
|
||||
|
||||
--
|
||||
cgit v1.1`}, [2]string{"musl-errno", `diff --git a/test/attr.test b/test/attr.test
|
||||
index 6ce2f9b..e9bde92 100644
|
||||
--- a/test/attr.test
|
||||
+++ b/test/attr.test
|
||||
@@ -11,7 +11,7 @@ Try various valid and invalid names
|
||||
|
||||
$ touch f
|
||||
$ setfattr -n user -v value f
|
||||
- > setfattr: f: Operation not supported
|
||||
+ > setfattr: f: Not supported
|
||||
|
||||
$ setfattr -n user. -v value f
|
||||
> setfattr: f: Invalid argument
|
||||
`},
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Attr] = Toolchain.newAttr }
|
||||
|
||||
func (t Toolchain) newACL() pkg.Artifact {
|
||||
const (
|
||||
version = "2.3.2"
|
||||
checksum = "-fY5nwH4K8ZHBCRXrzLdguPkqjKI6WIiGu4dBtrZ1o0t6AIU73w8wwJz_UyjIS0P"
|
||||
)
|
||||
return t.New("acl-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
|
||||
t.Load(Attr),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/acl/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
--enable-static
|
||||
make "-j$(nproc)"
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("acl"), true, pkg.NewHTTPGetTar(
|
||||
nil,
|
||||
"https://download.savannah.nongnu.org/releases/acl/"+
|
||||
"acl-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[ACL] = Toolchain.newACL }
|
||||
70
internal/rosa/all.go
Normal file
70
internal/rosa/all.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
// PArtifact is a lazily-initialised [pkg.Artifact] preset.
|
||||
type PArtifact int
|
||||
|
||||
const (
|
||||
ACL PArtifact = iota
|
||||
Attr
|
||||
Autoconf
|
||||
Bash
|
||||
Busybox
|
||||
CMake
|
||||
Coreutils
|
||||
Diffutils
|
||||
Gettext
|
||||
Git
|
||||
Go
|
||||
Gperf
|
||||
Hakurei
|
||||
KernelHeaders
|
||||
LibXau
|
||||
Libexpat
|
||||
Libffi
|
||||
Libgd
|
||||
Libseccomp
|
||||
Libxml2
|
||||
M4
|
||||
Make
|
||||
Meson
|
||||
Ninja
|
||||
Patch
|
||||
Perl
|
||||
PkgConfig
|
||||
Python
|
||||
Rsync
|
||||
Setuptools
|
||||
Wayland
|
||||
WaylandProtocols
|
||||
XCB
|
||||
XCBProto
|
||||
Xproto
|
||||
Zlib
|
||||
|
||||
// _presetEnd is the total number of presets and does not denote a preset.
|
||||
_presetEnd
|
||||
)
|
||||
|
||||
var (
|
||||
// artifactsF is an array of functions for the result of [PArtifact].
|
||||
artifactsF [_presetEnd]func(t Toolchain) pkg.Artifact
|
||||
|
||||
// artifacts stores the result of artifactsF.
|
||||
artifacts [_toolchainEnd][len(artifactsF)]pkg.Artifact
|
||||
// artifactsOnce is for lazy initialisation of artifacts.
|
||||
artifactsOnce [_toolchainEnd][len(artifactsF)]sync.Once
|
||||
)
|
||||
|
||||
// Load returns the resulting [pkg.Artifact] of [PArtifact].
|
||||
func (t Toolchain) Load(p PArtifact) pkg.Artifact {
|
||||
artifactsOnce[t][p].Do(func() {
|
||||
artifacts[t][p] = artifactsF[p](t)
|
||||
})
|
||||
return artifacts[t][p]
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
// busyboxBin is a busybox binary distribution installed under bin/busybox.
|
||||
type busyboxBin struct {
|
||||
// Underlying busybox binary.
|
||||
bin pkg.File
|
||||
bin pkg.FileArtifact
|
||||
}
|
||||
|
||||
// Kind returns the hardcoded [pkg.Kind] value.
|
||||
@@ -25,6 +25,9 @@ func (a busyboxBin) Kind() pkg.Kind { return kindBusyboxBin }
|
||||
// Params is a noop.
|
||||
func (a busyboxBin) Params(*pkg.IContext) {}
|
||||
|
||||
// IsExclusive returns false: Cure performs a trivial filesystem write.
|
||||
func (busyboxBin) IsExclusive() bool { return false }
|
||||
|
||||
// Dependencies returns the underlying busybox [pkg.File].
|
||||
func (a busyboxBin) Dependencies() []pkg.Artifact {
|
||||
return []pkg.Artifact{a.bin}
|
||||
@@ -80,7 +83,8 @@ func newBusyboxBin() pkg.Artifact {
|
||||
checksum = "L7OBIsPu9enNHn7FqpBT1kOg_mCLNmetSeNMA3i4Y60Z5jTgnlX3qX3zcQtLx5AB"
|
||||
)
|
||||
return pkg.NewExec(
|
||||
"busybox-bin-"+version, nil, pkg.ExecTimeoutMax, fhs.AbsRoot, []string{
|
||||
"busybox-bin-"+version, nil, pkg.ExecTimeoutMax, false,
|
||||
fhs.AbsRoot, []string{
|
||||
"PATH=/system/bin",
|
||||
},
|
||||
AbsSystem.Append("bin", "busybox"),
|
||||
@@ -100,26 +104,21 @@ func newBusyboxBin() pkg.Artifact {
|
||||
)
|
||||
}
|
||||
|
||||
// NewBusybox returns a [pkg.Artifact] containing a dynamically linked busybox
|
||||
// installation usable within the [Toolchain] it is compiled against.
|
||||
func (t Toolchain) NewBusybox() pkg.Artifact {
|
||||
func (t Toolchain) newBusybox() pkg.Artifact {
|
||||
const (
|
||||
version = "1.37.0"
|
||||
checksum = "Ial94Tnt7esJ_YEeb0AxunVL6MGYFyOw7Rtu2o87CXCi1TLrc6rlznVsN1rZk7it"
|
||||
)
|
||||
|
||||
extra := []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
t.NewKernelHeaders(),
|
||||
}
|
||||
var env []string
|
||||
|
||||
if t == toolchainStage3 {
|
||||
extra = nil
|
||||
env = append(env, "EXTRA_LDFLAGS=-static")
|
||||
}
|
||||
|
||||
return t.New("busybox-"+version, extra, nil, slices.Concat([]string{
|
||||
return t.New("busybox-"+version, false, stage3Concat(t, []pkg.Artifact{},
|
||||
t.Load(Make),
|
||||
t.Load(KernelHeaders),
|
||||
), nil, slices.Concat([]string{
|
||||
"ROSA_BUSYBOX_ENABLE=" + strings.Join([]string{
|
||||
"STATIC",
|
||||
"PIE",
|
||||
@@ -297,7 +296,6 @@ config_disable() {
|
||||
cat > /bin/gcc << EOF
|
||||
exec clang \
|
||||
-Wno-ignored-optimization-argument \
|
||||
${ROSA_CFLAGS} \
|
||||
${LDFLAGS} \
|
||||
\$@
|
||||
EOF
|
||||
@@ -358,3 +356,4 @@ index 64e752f4b..40f5ba7f7 100644
|
||||
}`)),
|
||||
))
|
||||
}
|
||||
func init() { artifactsF[Busybox] = Toolchain.newBusybox }
|
||||
|
||||
@@ -8,15 +8,14 @@ import (
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
// NewCMake returns a [pkg.Artifact] containing an installation of CMake.
|
||||
func (t Toolchain) NewCMake() pkg.Artifact {
|
||||
func (t Toolchain) newCMake() pkg.Artifact {
|
||||
const (
|
||||
version = "4.2.1"
|
||||
checksum = "Y3OdbMsob6Xk2y1DCME6z4Fryb5_TkFD7knRT8dTNIRtSqbiCJyyDN9AxggN_I75"
|
||||
)
|
||||
return t.New("cmake-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
t.NewKernelHeaders(),
|
||||
return t.New("cmake-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(KernelHeaders),
|
||||
}, nil, nil, `
|
||||
# expected to be writable in the copy made during bootstrap
|
||||
chmod -R +w /usr/src/cmake/Tests
|
||||
@@ -37,6 +36,7 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[CMake] = Toolchain.newCMake }
|
||||
|
||||
// CMakeAttr holds the project-specific attributes that will be applied to a new
|
||||
// [pkg.Artifact] compiled via CMake.
|
||||
@@ -59,6 +59,9 @@ type CMakeAttr struct {
|
||||
|
||||
// Override the default installation prefix [AbsSystem].
|
||||
Prefix *check.Absolute
|
||||
|
||||
// Return an exclusive artifact.
|
||||
Exclusive bool
|
||||
}
|
||||
|
||||
// NewViaCMake returns a [pkg.Artifact] for compiling and installing via CMake.
|
||||
@@ -81,14 +84,6 @@ func (t Toolchain) NewViaCMake(
|
||||
panic("CACHE must be non-empty")
|
||||
}
|
||||
|
||||
cmakeExtras := []pkg.Artifact{
|
||||
t.NewCMake(),
|
||||
t.NewNinja(),
|
||||
}
|
||||
if t == toolchainStage3 {
|
||||
cmakeExtras = nil
|
||||
}
|
||||
|
||||
scriptEarly := attr.ScriptEarly
|
||||
if attr.Writable {
|
||||
scriptEarly = `
|
||||
@@ -102,9 +97,9 @@ chmod -R +w "${ROSA_SOURCE}"
|
||||
}
|
||||
|
||||
sourcePath := AbsUsrSrc.Append(name)
|
||||
return t.New(name+"-"+variant+"-"+version, slices.Concat(
|
||||
attr.Extra,
|
||||
cmakeExtras,
|
||||
return t.New(name+"-"+variant+"-"+version, attr.Exclusive, stage3Concat(t, attr.Extra,
|
||||
t.Load(CMake),
|
||||
t.Load(Ninja),
|
||||
), nil, slices.Concat([]string{
|
||||
"ROSA_SOURCE=" + sourcePath.String(),
|
||||
"ROSA_CMAKE_SOURCE=" + sourcePath.Append(attr.Append...).String(),
|
||||
|
||||
@@ -89,6 +89,9 @@ func (cureEtc) Kind() pkg.Kind { return kindEtc }
|
||||
// Params is a noop.
|
||||
func (cureEtc) Params(*pkg.IContext) {}
|
||||
|
||||
// IsExclusive returns false: Cure performs a few trivial filesystem writes.
|
||||
func (cureEtc) IsExclusive() bool { return false }
|
||||
|
||||
// Dependencies returns a slice containing the backing iana-etc release.
|
||||
func (a cureEtc) Dependencies() []pkg.Artifact {
|
||||
if a.iana != nil {
|
||||
@@ -98,7 +101,12 @@ func (a cureEtc) Dependencies() []pkg.Artifact {
|
||||
}
|
||||
|
||||
// String returns a hardcoded reporting name.
|
||||
func (cureEtc) String() string { return "cure-etc" }
|
||||
func (a cureEtc) String() string {
|
||||
if a.iana == nil {
|
||||
return "cure-etc-minimal"
|
||||
}
|
||||
return "cure-etc"
|
||||
}
|
||||
|
||||
// newIANAEtc returns an unpacked iana-etc release.
|
||||
func newIANAEtc() pkg.Artifact {
|
||||
|
||||
@@ -4,25 +4,20 @@ import (
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
// NewGit returns a [pkg.Artifact] containing an installation of git.
|
||||
func (t Toolchain) NewGit() pkg.Artifact {
|
||||
func (t Toolchain) newGit() pkg.Artifact {
|
||||
const (
|
||||
version = "2.52.0"
|
||||
checksum = "uH3J1HAN_c6PfGNJd2OBwW4zo36n71wmkdvityYnrh8Ak0D1IifiAvEWz9Vi9DmS"
|
||||
)
|
||||
extra := []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
t.NewPerl(),
|
||||
t.NewM4(),
|
||||
t.NewAutoconf(),
|
||||
t.NewGettext(),
|
||||
return t.New("git-"+version, false, stage3Concat(t, []pkg.Artifact{},
|
||||
t.Load(Make),
|
||||
t.Load(Perl),
|
||||
t.Load(M4),
|
||||
t.Load(Autoconf),
|
||||
t.Load(Gettext),
|
||||
|
||||
t.NewZlib(),
|
||||
}
|
||||
if t == toolchainStage3 {
|
||||
extra = nil
|
||||
}
|
||||
return t.New("git-"+version, extra, nil, nil, `
|
||||
t.Load(Zlib),
|
||||
), nil, nil, `
|
||||
chmod -R +w /usr/src/git && cd /usr/src/git
|
||||
make configure
|
||||
./configure --prefix=/system
|
||||
@@ -35,3 +30,4 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Git] = Toolchain.newGit }
|
||||
|
||||
@@ -2,13 +2,12 @@ package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
// NewMake returns a [pkg.Artifact] containing an installation of GNU Make.
|
||||
func (t Toolchain) NewMake() pkg.Artifact {
|
||||
func (t Toolchain) newMake() pkg.Artifact {
|
||||
const (
|
||||
version = "4.4.1"
|
||||
checksum = "YS_B07ZcAy9PbaK5_vKGj64SrxO2VMpnMKfc9I0Q9IC1rn0RwOH7802pJoj2Mq4a"
|
||||
)
|
||||
return t.New("make-"+version, nil, nil, nil, `
|
||||
return t.New("make-"+version, false, nil, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/make/configure \
|
||||
--prefix=/system \
|
||||
@@ -23,15 +22,15 @@ cd "$(mktemp -d)"
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Make] = Toolchain.newMake }
|
||||
|
||||
// NewM4 returns a [pkg.Artifact] containing an installation of GNU M4.
|
||||
func (t Toolchain) NewM4() pkg.Artifact {
|
||||
func (t Toolchain) newM4() pkg.Artifact {
|
||||
const (
|
||||
version = "1.4.20"
|
||||
checksum = "RT0_L3m4Co86bVBY3lCFAEs040yI1WdeNmRylFpah8IZovTm6O4wI7qiHJN3qsW9"
|
||||
)
|
||||
return t.New("m4-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
return t.New("m4-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd /usr/src/m4
|
||||
chmod +w tests/test-c32ispunct.sh && echo '#!/bin/sh' > tests/test-c32ispunct.sh
|
||||
@@ -49,23 +48,26 @@ make DESTDIR=/work install
|
||||
pkg.TarBzip2,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[M4] = Toolchain.newM4 }
|
||||
|
||||
// NewAutoconf returns a [pkg.Artifact] containing an installation of GNU Autoconf.
|
||||
func (t Toolchain) NewAutoconf() pkg.Artifact {
|
||||
func (t Toolchain) newAutoconf() pkg.Artifact {
|
||||
const (
|
||||
version = "2.72"
|
||||
checksum = "-c5blYkC-xLDer3TWEqJTyh1RLbOd1c5dnRLKsDnIrg_wWNOLBpaqMY8FvmUFJ33"
|
||||
)
|
||||
return t.New("autoconf-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
t.NewM4(),
|
||||
t.NewPerl(),
|
||||
return t.New("autoconf-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(M4),
|
||||
t.Load(Perl),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/autoconf/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}"
|
||||
make "-j$(nproc)" check
|
||||
make \
|
||||
"-j$(nproc)" \
|
||||
TESTSUITEFLAGS="-j$(nproc)" \
|
||||
check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("autoconf"), false, pkg.NewHTTPGetTar(
|
||||
nil,
|
||||
@@ -74,15 +76,15 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Autoconf] = Toolchain.newAutoconf }
|
||||
|
||||
// NewGettext returns a [pkg.Artifact] containing an installation of GNU gettext.
|
||||
func (t Toolchain) NewGettext() pkg.Artifact {
|
||||
func (t Toolchain) newGettext() pkg.Artifact {
|
||||
const (
|
||||
version = "0.26"
|
||||
checksum = "IMu7yDZX7xL5UO1ZxXc-iBMbY9LLEUlOroyuSlHMZwg9MKtxG7HIm8F2LheDua0y"
|
||||
)
|
||||
return t.New("gettext-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
return t.New("gettext-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd /usr/src/gettext
|
||||
test_disable() { chmod +w "$2" && echo "$1" > "$2"; }
|
||||
@@ -110,15 +112,15 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Gettext] = Toolchain.newGettext }
|
||||
|
||||
// NewDiffutils returns a [pkg.Artifact] containing an installation of GNU diffutils.
|
||||
func (t Toolchain) NewDiffutils() pkg.Artifact {
|
||||
func (t Toolchain) newDiffutils() pkg.Artifact {
|
||||
const (
|
||||
version = "3.12"
|
||||
checksum = "9J5VAq5oA7eqwzS1Yvw-l3G5o-TccUrNQR3PvyB_lgdryOFAfxtvQfKfhdpquE44"
|
||||
)
|
||||
return t.New("diffutils-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
return t.New("diffutils-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd /usr/src/diffutils
|
||||
test_disable() { chmod +w "$2" && echo "$1" > "$2"; }
|
||||
@@ -139,15 +141,44 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Diffutils] = Toolchain.newDiffutils }
|
||||
|
||||
// NewBash returns a [pkg.Artifact] containing an installation of GNU Bash.
|
||||
func (t Toolchain) NewBash() pkg.Artifact {
|
||||
func (t Toolchain) newPatch() pkg.Artifact {
|
||||
const (
|
||||
version = "2.8"
|
||||
checksum = "MA0BQc662i8QYBD-DdGgyyfTwaeALZ1K0yusV9rAmNiIsQdX-69YC4t9JEGXZkeR"
|
||||
)
|
||||
return t.New("patch-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd /usr/src/patch
|
||||
test_disable() { chmod +w "$2" && echo "$1" > "$2"; }
|
||||
|
||||
test_disable '#!/bin/sh' tests/ed-style
|
||||
test_disable '#!/bin/sh' tests/need-filename
|
||||
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/patch/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}"
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("patch"), true, pkg.NewHTTPGetTar(
|
||||
nil,
|
||||
"https://ftp.gnu.org/gnu/patch/patch-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Patch] = Toolchain.newPatch }
|
||||
|
||||
func (t Toolchain) newBash() pkg.Artifact {
|
||||
const (
|
||||
version = "5.3"
|
||||
checksum = "4LQ_GRoB_ko-Ih8QPf_xRKA02xAm_TOxQgcJLmFDT6udUPxTAWrsj-ZNeuTusyDq"
|
||||
)
|
||||
return t.New("bash-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
return t.New("bash-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/bash/configure \
|
||||
@@ -163,18 +194,18 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Bash] = Toolchain.newBash }
|
||||
|
||||
// NewCoreutils returns a [pkg.Artifact] containing an installation of GNU Coreutils.
|
||||
func (t Toolchain) NewCoreutils() pkg.Artifact {
|
||||
func (t Toolchain) newCoreutils() pkg.Artifact {
|
||||
const (
|
||||
version = "9.9"
|
||||
checksum = "B1_TaXj1j5aiVIcazLWu8Ix03wDV54uo2_iBry4qHG6Y-9bjDpUPlkNLmU_3Nvw6"
|
||||
)
|
||||
return t.New("coreutils-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
t.NewPerl(),
|
||||
return t.New("coreutils-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(Perl),
|
||||
|
||||
t.NewKernelHeaders(),
|
||||
t.Load(KernelHeaders),
|
||||
}, nil, nil, `
|
||||
cd /usr/src/coreutils
|
||||
test_disable() { chmod +w "$2" && echo "$1" > "$2"; }
|
||||
@@ -194,3 +225,26 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Coreutils] = Toolchain.newCoreutils }
|
||||
|
||||
func (t Toolchain) newGperf() pkg.Artifact {
|
||||
const (
|
||||
version = "3.3"
|
||||
checksum = "RtIy9pPb_Bb8-31J2Nw-rRGso2JlS-lDlVhuNYhqR7Nt4xM_nObznxAlBMnarJv7"
|
||||
)
|
||||
return t.New("gperf-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/gperf/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}"
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("gperf"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://ftp.gnu.org/pub/gnu/gperf/gperf-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Gperf] = Toolchain.newGperf }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"slices"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
@@ -9,14 +10,14 @@ import (
|
||||
// newGoBootstrap returns the Go bootstrap toolchain.
|
||||
func (t Toolchain) newGoBootstrap() pkg.Artifact {
|
||||
const checksum = "8o9JL_ToiQKadCTb04nvBDkp8O1xiWOolAxVEqaTGodieNe4lOFEjlOxN3bwwe23"
|
||||
return t.New("go1.4-bootstrap", []pkg.Artifact{
|
||||
t.NewBash(),
|
||||
return t.New("go1.4-bootstrap", false, []pkg.Artifact{
|
||||
t.Load(Bash),
|
||||
}, nil, []string{
|
||||
"CGO_ENABLED=0",
|
||||
}, `
|
||||
mkdir -p /var/tmp
|
||||
cp -r /usr/src/go1.4-bootstrap /work
|
||||
cd /work/go1.4-bootstrap/src
|
||||
cp -r /usr/src/go /work
|
||||
cd /work/go/src
|
||||
chmod -R +w ..
|
||||
|
||||
ln -s ../system/bin/busybox /bin/pwd
|
||||
@@ -31,8 +32,11 @@ rm \
|
||||
syscall/creds_test.go \
|
||||
net/multicast_test.go
|
||||
|
||||
CC="${CC} ${LDFLAGS}" ./all.bash
|
||||
`, pkg.Path(AbsUsrSrc.Append("go1.4-bootstrap"), false, pkg.NewHTTPGetTar(
|
||||
./all.bash
|
||||
cd /work/
|
||||
mkdir system/
|
||||
mv go/ system/
|
||||
`, pkg.Path(AbsUsrSrc.Append("go"), false, pkg.NewHTTPGetTar(
|
||||
nil, "https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
@@ -42,22 +46,30 @@ CC="${CC} ${LDFLAGS}" ./all.bash
|
||||
// newGo returns a specific version of the Go toolchain.
|
||||
func (t Toolchain) newGo(
|
||||
version, checksum string,
|
||||
boot pkg.Artifact,
|
||||
env ...string,
|
||||
env []string,
|
||||
script string,
|
||||
extra ...pkg.Artifact,
|
||||
) pkg.Artifact {
|
||||
return t.New("go"+version, []pkg.Artifact{
|
||||
boot,
|
||||
}, nil, slices.Concat([]string{
|
||||
return t.New("go"+version, false, slices.Concat([]pkg.Artifact{
|
||||
t.Load(Bash),
|
||||
}, extra), nil, slices.Concat([]string{
|
||||
"CC=cc",
|
||||
"GOCACHE=/tmp/gocache",
|
||||
"GOROOT_BOOTSTRAP=/system/go",
|
||||
"CGO_" + ldflags(false) + " -O2 -g",
|
||||
"TMPDIR=/dev/shm/go",
|
||||
}, env), `
|
||||
mkdir /work/system
|
||||
mkdir /work/system "${TMPDIR}"
|
||||
cp -r /usr/src/go /work/system
|
||||
cd /work/system/go/src
|
||||
chmod -R +w ..
|
||||
sed -i 's/bash run.bash/sh run.bash/' all.bash
|
||||
sh make.bash
|
||||
`+script+`
|
||||
./all.bash
|
||||
|
||||
mkdir /work/system/bin
|
||||
ln -s \
|
||||
../go/bin/go \
|
||||
../go/bin/gofmt \
|
||||
/work/system/bin
|
||||
`, pkg.Path(AbsUsrSrc.Append("go"), false, pkg.NewHTTPGetTar(
|
||||
nil, "https://go.dev/dl/go"+version+".src.tar.gz",
|
||||
mustDecode(checksum),
|
||||
@@ -65,28 +77,50 @@ sh make.bash
|
||||
)))
|
||||
}
|
||||
|
||||
// NewGo returns a [pkg.Artifact] containing the Go toolchain.
|
||||
func (t Toolchain) NewGo() pkg.Artifact {
|
||||
func (t Toolchain) newGoLatest() pkg.Artifact {
|
||||
go119 := t.newGo(
|
||||
"1.19",
|
||||
"9_e0aFHsIkVxWVGsp9T2RvvjOc3p4n9o9S8tkNe9Cvgzk_zI2FhRQB7ioQkeAAro",
|
||||
t.newGoBootstrap(),
|
||||
"GOROOT_BOOTSTRAP=/go1.4-bootstrap",
|
||||
[]string{"CGO_ENABLED=0"}, `
|
||||
rm \
|
||||
crypto/tls/handshake_client_test.go
|
||||
`, t.newGoBootstrap(),
|
||||
)
|
||||
|
||||
go121 := t.newGo(
|
||||
"1.21.13",
|
||||
"YtrDka402BOAEwywx03Vz4QlVwoBiguJHzG7PuythMCPHXS8CVMLvzmvgEbu4Tzu",
|
||||
go119,
|
||||
[]string{"CGO_ENABLED=0"}, `
|
||||
sed -i \
|
||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
||||
|
||||
rm \
|
||||
crypto/tls/handshake_client_test.go \
|
||||
crypto/tls/handshake_server_test.go
|
||||
`, go119,
|
||||
)
|
||||
|
||||
go123 := t.newGo(
|
||||
"1.23.12",
|
||||
"wcI32bl1tkqbgcelGtGWPI4RtlEddd-PTd76Eb-k7nXA5LbE9yTNdIL9QSOOxMOs",
|
||||
go121,
|
||||
nil, `
|
||||
sed -i \
|
||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
||||
`, go121,
|
||||
)
|
||||
|
||||
go125 := t.newGo(
|
||||
"1.25.6",
|
||||
"x0z430qoDvQbbw_fftjW0rh_GSoh0VJhPzttWk_0hj9yz9AKOjuwRMupF_Q0dbt7",
|
||||
go123,
|
||||
nil, `
|
||||
sed -i \
|
||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
||||
`, go123,
|
||||
)
|
||||
|
||||
return go125
|
||||
}
|
||||
func init() { artifactsF[Go] = Toolchain.newGoLatest }
|
||||
|
||||
83
internal/rosa/hakurei.go
Normal file
83
internal/rosa/hakurei.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
func (t Toolchain) newHakurei() pkg.Artifact {
|
||||
const (
|
||||
version = "0.3.3"
|
||||
checksum = "iMN9qzDB000noZ6dOHh_aSdrhRZPopjyWHd0KFVjxjQLQstAOvLYZEZ74btlL0pu"
|
||||
)
|
||||
return t.New("hakurei-"+version, false, []pkg.Artifact{
|
||||
t.Load(Go),
|
||||
t.Load(PkgConfig),
|
||||
|
||||
t.Load(KernelHeaders),
|
||||
t.Load(Libseccomp),
|
||||
t.Load(ACL),
|
||||
t.Load(Attr),
|
||||
|
||||
t.Load(Xproto),
|
||||
t.Load(LibXau),
|
||||
t.Load(XCBProto),
|
||||
t.Load(XCB),
|
||||
|
||||
t.Load(Libffi),
|
||||
t.Load(Libexpat),
|
||||
t.Load(Libxml2),
|
||||
t.Load(Wayland),
|
||||
t.Load(WaylandProtocols),
|
||||
}, nil, []string{
|
||||
"GOCACHE=/tmp/gocache",
|
||||
"CC=clang -O3 -Werror",
|
||||
}, `
|
||||
echo '# Building test helper (hostname).'
|
||||
go build -v -o /bin/hostname /usr/src/hostname/main.go
|
||||
echo
|
||||
|
||||
chmod -R +w /usr/src/hakurei
|
||||
cd /usr/src/hakurei
|
||||
mkdir -p /work/system/{bin,libexec/hakurei}
|
||||
|
||||
echo '# Building hakurei.'
|
||||
go generate -v ./...
|
||||
go build -trimpath -v -o /work/system/libexec/hakurei -ldflags="-s -w
|
||||
-buildid=
|
||||
-extldflags=-static
|
||||
-X hakurei.app/internal/info.buildVersion='v`+version+`'
|
||||
-X hakurei.app/internal/info.hakureiPath=/system/bin/hakurei
|
||||
-X hakurei.app/internal/info.hsuPath=/system/bin/hsu
|
||||
-X main.hakureiPath=/system/bin/hakurei" ./...
|
||||
echo
|
||||
|
||||
echo '# Testing hakurei.'
|
||||
go test -ldflags='-buildid= -extldflags=-static' ./...
|
||||
echo
|
||||
|
||||
mv \
|
||||
/work/system/libexec/hakurei/{hakurei,hpkg} \
|
||||
/work/system/bin
|
||||
`, pkg.Path(AbsUsrSrc.Append("hakurei"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://git.gensokyo.uk/security/hakurei/archive/"+
|
||||
"v"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)), pkg.Path(AbsUsrSrc.Append("hostname", "main.go"), false, pkg.NewFile(
|
||||
"hostname.go",
|
||||
[]byte(`
|
||||
package main
|
||||
|
||||
import "os"
|
||||
|
||||
func main() {
|
||||
if name, err := os.Hostname(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
os.Stdout.WriteString(name)
|
||||
}
|
||||
}
|
||||
`),
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Hakurei] = Toolchain.newHakurei }
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
|
||||
// newKernel is a helper for interacting with Kbuild.
|
||||
func (t Toolchain) newKernel(
|
||||
exclusive bool,
|
||||
patches [][2]string,
|
||||
script string,
|
||||
extra ...pkg.Artifact,
|
||||
) pkg.Artifact {
|
||||
@@ -15,27 +17,28 @@ func (t Toolchain) newKernel(
|
||||
version = "6.18.5"
|
||||
checksum = "-V1e1WWl7HuePkmm84sSKF7nLuHfUs494uNMzMqXEyxcNE_PUE0FICL0oGWn44mM"
|
||||
)
|
||||
return t.New("kernel-"+version, slices.Concat([]pkg.Artifact{
|
||||
t.NewMake(),
|
||||
return t.New("kernel-"+version, exclusive, slices.Concat([]pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, extra), nil, nil, `
|
||||
export LLVM=1
|
||||
export HOSTCFLAGS="${ROSA_CFLAGS}"
|
||||
export HOSTLDFLAGS="${LDFLAGS}"
|
||||
chmod -R +w /usr/src/linux && cd /usr/src/linux
|
||||
`+script, pkg.Path(AbsUsrSrc.Append("linux"), true, pkg.NewHTTPGetTar(
|
||||
nil,
|
||||
"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/"+
|
||||
"snapshot/linux-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
cd /usr/src/linux
|
||||
`+script, pkg.Path(AbsUsrSrc.Append("linux"), true, t.NewPatchedSource(
|
||||
"kernel", version, pkg.NewHTTPGetTar(
|
||||
nil,
|
||||
"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/"+
|
||||
"snapshot/linux-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
), false, patches...,
|
||||
)))
|
||||
}
|
||||
|
||||
// NewKernelHeaders returns a [pkg.Artifact] containing kernel headers.
|
||||
func (t Toolchain) NewKernelHeaders() pkg.Artifact {
|
||||
return t.newKernel(`
|
||||
func (t Toolchain) newKernelHeaders() pkg.Artifact {
|
||||
return t.newKernel(false, nil, `
|
||||
make "-j$(nproc)" \
|
||||
INSTALL_HDR_PATH=/work/system \
|
||||
headers_install
|
||||
`, t.NewRsync())
|
||||
`, t.Load(Rsync))
|
||||
}
|
||||
func init() { artifactsF[KernelHeaders] = Toolchain.newKernelHeaders }
|
||||
|
||||
33
internal/rosa/libexpat.go
Normal file
33
internal/rosa/libexpat.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
func (t Toolchain) newLibexpat() pkg.Artifact {
|
||||
const (
|
||||
version = "2.7.3"
|
||||
checksum = "GmkoD23nRi9cMT0cgG1XRMrZWD82UcOMzkkvP1gkwSFWCBgeSXMuoLpa8-v8kxW-"
|
||||
)
|
||||
return t.New("libexpat-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(Bash),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/libexpat/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("libexpat"), false, pkg.NewHTTPGetTar(
|
||||
nil, "https://github.com/libexpat/libexpat/releases/download/"+
|
||||
"R_"+strings.ReplaceAll(version, ".", "_")+"/"+
|
||||
"expat-"+version+".tar.bz2",
|
||||
mustDecode(checksum),
|
||||
pkg.TarBzip2,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Libexpat] = Toolchain.newLibexpat }
|
||||
@@ -2,20 +2,20 @@ package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
// NewLibffi returns a [pkg.Artifact] containing an installation of libffi.
|
||||
func (t Toolchain) NewLibffi() pkg.Artifact {
|
||||
func (t Toolchain) newLibffi() pkg.Artifact {
|
||||
const (
|
||||
version = "3.4.5"
|
||||
checksum = "apIJzypF4rDudeRoI_n3K7N-zCeBLTbQlHRn9NSAZqdLAWA80mR0gXPTpHsL7oMl"
|
||||
)
|
||||
return t.New("libffi-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
t.NewKernelHeaders(),
|
||||
return t.New("libffi-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(KernelHeaders),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/libffi/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}"
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("libffi"), false, pkg.NewHTTPGetTar(
|
||||
@@ -26,3 +26,4 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Libffi] = Toolchain.newLibffi }
|
||||
|
||||
33
internal/rosa/libgd.go
Normal file
33
internal/rosa/libgd.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newLibgd() pkg.Artifact {
|
||||
const (
|
||||
version = "2.3.3"
|
||||
checksum = "8T-sh1_FJT9K9aajgxzh8ot6vWIF-xxjcKAHvTak9MgGUcsFfzP8cAvvv44u2r36"
|
||||
)
|
||||
return t.New("libgd-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
|
||||
t.Load(Zlib),
|
||||
}, nil, []string{
|
||||
"TMPDIR=/dev/shm/gd",
|
||||
}, `
|
||||
mkdir /dev/shm/gd
|
||||
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/libgd/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("libgd"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://github.com/libgd/libgd/releases/download/"+
|
||||
"gd-"+version+"/libgd-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Libgd] = Toolchain.newLibgd }
|
||||
36
internal/rosa/libseccomp.go
Normal file
36
internal/rosa/libseccomp.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
func (t Toolchain) newLibseccomp() pkg.Artifact {
|
||||
const (
|
||||
version = "2.6.0"
|
||||
checksum = "mMu-iR71guPjFbb31u-YexBaanKE_nYPjPux-vuBiPfS_0kbwJdfCGlkofaUm-EY"
|
||||
)
|
||||
return t.New("libseccomp-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(Bash),
|
||||
t.Load(Gperf),
|
||||
|
||||
t.Load(KernelHeaders),
|
||||
}, nil, nil, `
|
||||
ln -s ../system/bin/bash /bin/bash
|
||||
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/libseccomp/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("libseccomp"), false, pkg.NewHTTPGetTar(
|
||||
nil,
|
||||
"https://github.com/seccomp/libseccomp/releases/download/"+
|
||||
"v"+version+"/libseccomp-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Libseccomp] = Toolchain.newLibseccomp }
|
||||
35
internal/rosa/libxml2.go
Normal file
35
internal/rosa/libxml2.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
func (t Toolchain) newLibxml2() pkg.Artifact {
|
||||
const (
|
||||
version = "2.15.1"
|
||||
checksum = "pYzAR3cNrEHezhEMirgiq7jbboLzwMj5GD7SQp0jhSIMdgoU4G9oU9Gxun3zzUIU"
|
||||
)
|
||||
return t.New("libxml2-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd /usr/src/
|
||||
tar xf libxml2.tar.xz
|
||||
mv libxml2-`+version+` libxml2
|
||||
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/libxml2/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("libxml2.tar.xz"), false, pkg.NewHTTPGet(
|
||||
nil, "https://download.gnome.org/sources/libxml2/"+
|
||||
strings.Join(strings.Split(version, ".")[:2], ".")+
|
||||
"/libxml2-"+version+".tar.xz",
|
||||
mustDecode(checksum),
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Libxml2] = Toolchain.newLibxml2 }
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/internal/pkg"
|
||||
@@ -72,8 +73,8 @@ func llvmFlagName(flag int) string {
|
||||
}
|
||||
}
|
||||
|
||||
// newLLVM returns a [pkg.Artifact] containing a LLVM variant.
|
||||
func (t Toolchain) newLLVM(variant string, attr *llvmAttr) pkg.Artifact {
|
||||
// newLLVMVariant returns a [pkg.Artifact] containing a LLVM variant.
|
||||
func (t Toolchain) newLLVMVariant(variant string, attr *llvmAttr) pkg.Artifact {
|
||||
const (
|
||||
version = "21.1.8"
|
||||
checksum = "8SUpqDkcgwOPsqHVtmf9kXfFeVmjVxl4LMn-qSE1AI_Xoeju-9HaoPNGtidyxyka"
|
||||
@@ -124,24 +125,12 @@ func (t Toolchain) newLLVM(variant string, attr *llvmAttr) pkg.Artifact {
|
||||
)
|
||||
}
|
||||
|
||||
extra := []pkg.Artifact{
|
||||
t.NewLibffi(),
|
||||
t.NewPython(),
|
||||
t.NewPerl(),
|
||||
t.NewDiffutils(),
|
||||
t.NewBash(),
|
||||
t.NewCoreutils(),
|
||||
|
||||
t.NewKernelHeaders(),
|
||||
}
|
||||
if t == toolchainStage3 {
|
||||
extra = nil
|
||||
}
|
||||
|
||||
if attr.flags&llvmProjectClang != 0 {
|
||||
cache = append(cache,
|
||||
[2]string{"CLANG_DEFAULT_LINKER", "lld"},
|
||||
[2]string{"CLANG_DEFAULT_CXX_STDLIB", "libc++"},
|
||||
[2]string{"CLANG_DEFAULT_RTLIB", "compiler-rt"},
|
||||
[2]string{"CLANG_DEFAULT_UNWINDLIB", "libunwind"},
|
||||
)
|
||||
}
|
||||
if attr.flags&llvmProjectLld != 0 {
|
||||
@@ -181,35 +170,26 @@ cp -r /system/include /usr/include && rm -rf /system/include
|
||||
)
|
||||
}
|
||||
|
||||
source := pkg.NewHTTPGetTar(
|
||||
nil, "https://github.com/llvm/llvm-project/archive/refs/tags/"+
|
||||
"llvmorg-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)
|
||||
|
||||
patches := make([]pkg.ExecPath, len(attr.patches)+1)
|
||||
for i, p := range attr.patches {
|
||||
patches[i+1] = pkg.Path(
|
||||
AbsUsrSrc.Append("llvm-patches", p[0]+".patch"), false,
|
||||
pkg.NewFile(p[0], []byte(p[1])),
|
||||
)
|
||||
}
|
||||
patches[0] = pkg.Path(AbsUsrSrc.Append("llvmorg"), false, source)
|
||||
if len(patches) > 1 {
|
||||
source = t.New(
|
||||
"llvmorg-patched", nil, nil, nil, `
|
||||
cp -r /usr/src/llvmorg/. /work/.
|
||||
chmod -R +w /work && cd /work
|
||||
cat /usr/src/llvm-patches/* | patch -p 1
|
||||
`, patches...,
|
||||
)
|
||||
}
|
||||
|
||||
return t.NewViaCMake("llvm", version, variant, source, &CMakeAttr{
|
||||
return t.NewViaCMake("llvm", version, variant, t.NewPatchedSource(
|
||||
"llvmorg", version, pkg.NewHTTPGetTar(
|
||||
nil, "https://github.com/llvm/llvm-project/archive/refs/tags/"+
|
||||
"llvmorg-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
), true, attr.patches...,
|
||||
), &CMakeAttr{
|
||||
Cache: slices.Concat(cache, attr.cmake),
|
||||
Append: cmakeAppend,
|
||||
Extra: slices.Concat(attr.extra, extra),
|
||||
Extra: stage3Concat(t, attr.extra,
|
||||
t.Load(Libffi),
|
||||
t.Load(Python),
|
||||
t.Load(Perl),
|
||||
t.Load(Diffutils),
|
||||
t.Load(Bash),
|
||||
t.Load(Coreutils),
|
||||
|
||||
t.Load(KernelHeaders),
|
||||
),
|
||||
Prefix: attr.prefix,
|
||||
|
||||
Env: slices.Concat([]string{
|
||||
@@ -217,11 +197,13 @@ cat /usr/src/llvm-patches/* | patch -p 1
|
||||
"ROSA_LLVM_RUNTIMES=" + strings.Join(runtimes, ";"),
|
||||
}, attr.env),
|
||||
ScriptEarly: scriptEarly, Script: script + attr.script,
|
||||
|
||||
Exclusive: true,
|
||||
})
|
||||
}
|
||||
|
||||
// NewLLVM returns LLVM toolchain across multiple [pkg.Artifact].
|
||||
func (t Toolchain) NewLLVM() (musl, compilerRT, runtimes, clang pkg.Artifact) {
|
||||
// newLLVM returns LLVM toolchain across multiple [pkg.Artifact].
|
||||
func (t Toolchain) newLLVM() (musl, compilerRT, runtimes, clang pkg.Artifact) {
|
||||
var target string
|
||||
switch runtime.GOARCH {
|
||||
case "386", "amd64":
|
||||
@@ -237,10 +219,10 @@ func (t Toolchain) NewLLVM() (musl, compilerRT, runtimes, clang pkg.Artifact) {
|
||||
{"LLVM_ENABLE_LIBXML2", "OFF"},
|
||||
}
|
||||
|
||||
compilerRT = t.newLLVM("compiler-rt", &llvmAttr{
|
||||
env: []string{
|
||||
ldflags(false),
|
||||
},
|
||||
compilerRT = t.newLLVMVariant("compiler-rt", &llvmAttr{
|
||||
env: stage3ExclConcat(t, []string{},
|
||||
"LDFLAGS="+earlyLDFLAGS(false),
|
||||
),
|
||||
cmake: [][2]string{
|
||||
// libc++ not yet available
|
||||
{"CMAKE_CXX_COMPILER_TARGET", ""},
|
||||
@@ -281,20 +263,21 @@ ln -s \
|
||||
|
||||
musl = t.NewMusl(&MuslAttr{
|
||||
Extra: []pkg.Artifact{compilerRT},
|
||||
Env: []string{
|
||||
ldflags(false),
|
||||
Env: stage3ExclConcat(t, []string{
|
||||
"CC=clang",
|
||||
"LIBCC=/system/lib/clang/21/lib/" +
|
||||
triplet() + "/libclang_rt.builtins.a",
|
||||
"AR=ar",
|
||||
"RANLIB=ranlib",
|
||||
},
|
||||
"LDFLAGS="+earlyLDFLAGS(false),
|
||||
),
|
||||
})
|
||||
|
||||
runtimes = t.newLLVM("runtimes", &llvmAttr{
|
||||
env: []string{
|
||||
ldflags(false),
|
||||
},
|
||||
runtimes = t.newLLVMVariant("runtimes", &llvmAttr{
|
||||
env: stage3ExclConcat(t, []string{},
|
||||
"LDFLAGS="+earlyLDFLAGS(false),
|
||||
),
|
||||
flags: llvmRuntimeLibunwind | llvmRuntimeLibcxx | llvmRuntimeLibcxxABI,
|
||||
cmake: slices.Concat([][2]string{
|
||||
// libc++ not yet available
|
||||
@@ -310,13 +293,13 @@ ln -s \
|
||||
},
|
||||
})
|
||||
|
||||
clang = t.newLLVM("clang", &llvmAttr{
|
||||
clang = t.newLLVMVariant("clang", &llvmAttr{
|
||||
flags: llvmProjectClang | llvmProjectLld,
|
||||
env: []string{
|
||||
"CFLAGS=" + cflags,
|
||||
"CXXFLAGS=" + cxxflags(),
|
||||
ldflags(false),
|
||||
},
|
||||
env: stage3ExclConcat(t, []string{},
|
||||
"CFLAGS="+earlyCFLAGS,
|
||||
"CXXFLAGS="+earlyCXXFLAGS(),
|
||||
"LDFLAGS="+earlyLDFLAGS(false),
|
||||
),
|
||||
cmake: slices.Concat([][2]string{
|
||||
{"LLVM_TARGETS_TO_BUILD", target},
|
||||
{"CMAKE_CROSSCOMPILING", "OFF"},
|
||||
@@ -326,24 +309,17 @@ ln -s \
|
||||
musl,
|
||||
compilerRT,
|
||||
runtimes,
|
||||
t.NewGit(),
|
||||
t.Load(Git),
|
||||
},
|
||||
script: `
|
||||
ln -s clang /work/system/bin/cc
|
||||
ln -s clang++ /work/system/bin/c++
|
||||
|
||||
ninja check-all
|
||||
`,
|
||||
|
||||
patches: [][2]string{
|
||||
{"xfail-broken-tests", `diff --git a/clang/test/Driver/hexagon-toolchain-linux.c b/clang/test/Driver/hexagon-toolchain-linux.c
|
||||
index e791353cca07..4efaf3948054 100644
|
||||
--- a/clang/test/Driver/hexagon-toolchain-linux.c
|
||||
+++ b/clang/test/Driver/hexagon-toolchain-linux.c
|
||||
@@ -1,3 +1,5 @@
|
||||
+// XFAIL: target={{.*-rosa-linux-musl}}
|
||||
+
|
||||
// UNSUPPORTED: system-windows
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
diff --git a/clang/test/Modules/timestamps.c b/clang/test/Modules/timestamps.c
|
||||
{"xfail-broken-tests", `diff --git a/clang/test/Modules/timestamps.c b/clang/test/Modules/timestamps.c
|
||||
index 50fdce630255..4b4465a75617 100644
|
||||
--- a/clang/test/Modules/timestamps.c
|
||||
+++ b/clang/test/Modules/timestamps.c
|
||||
@@ -353,9 +329,131 @@ index 50fdce630255..4b4465a75617 100644
|
||||
/// Verify timestamps that gets embedded in the module
|
||||
#include <c-header.h>
|
||||
|
||||
`},
|
||||
|
||||
{"path-system-include", `diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
index cdbf21fb9026..dd052858700d 100644
|
||||
--- a/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
@@ -773,6 +773,12 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
|
||||
addExternCSystemInclude(
|
||||
DriverArgs, CC1Args,
|
||||
concat(SysRoot, "/usr/include", MultiarchIncludeDir));
|
||||
+ if (!MultiarchIncludeDir.empty() &&
|
||||
+ D.getVFS().exists(concat(SysRoot, "/system/include", MultiarchIncludeDir)))
|
||||
+ addExternCSystemInclude(
|
||||
+ DriverArgs, CC1Args,
|
||||
+ concat(SysRoot, "/system/include", MultiarchIncludeDir));
|
||||
+
|
||||
|
||||
if (getTriple().getOS() == llvm::Triple::RTEMS)
|
||||
return;
|
||||
@@ -783,6 +789,7 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
|
||||
addExternCSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/include"));
|
||||
|
||||
addExternCSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/include"));
|
||||
+ addExternCSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/system/include"));
|
||||
|
||||
if (!DriverArgs.hasArg(options::OPT_nobuiltininc) && getTriple().isMusl())
|
||||
addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude);
|
||||
`},
|
||||
|
||||
{"path-system-libraries", `diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
|
||||
index 8d3775de9be5..1e126e2d6f24 100644
|
||||
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
|
||||
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
|
||||
@@ -463,6 +463,15 @@ void tools::AddLinkerInputs(const ToolChain &TC, const InputInfoList &Inputs,
|
||||
if (!TC.isCrossCompiling())
|
||||
addDirectoryList(Args, CmdArgs, "-L", "LIBRARY_PATH");
|
||||
|
||||
+ const std::string RosaSuffix = "-rosa-linux-musl";
|
||||
+ if (TC.getTripleString().size() > RosaSuffix.size() &&
|
||||
+ std::equal(RosaSuffix.rbegin(), RosaSuffix.rend(), TC.getTripleString().rbegin())) {
|
||||
+ CmdArgs.push_back("-rpath");
|
||||
+ CmdArgs.push_back("/system/lib");
|
||||
+ CmdArgs.push_back("-rpath");
|
||||
+ CmdArgs.push_back(("/system/lib/" + TC.getTripleString()).c_str());
|
||||
+ }
|
||||
+
|
||||
for (const auto &II : Inputs) {
|
||||
// If the current tool chain refers to an OpenMP offloading host, we
|
||||
// should ignore inputs that refer to OpenMP offloading devices -
|
||||
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
index 8ac8d4eb9181..795995bb53cb 100644
|
||||
--- a/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
@@ -324,6 +324,7 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
|
||||
Generic_GCC::AddMultilibPaths(D, SysRoot, "libo32", MultiarchTriple, Paths);
|
||||
addPathIfExists(D, concat(SysRoot, "/libo32"), Paths);
|
||||
addPathIfExists(D, concat(SysRoot, "/usr/libo32"), Paths);
|
||||
+ addPathIfExists(D, concat(SysRoot, "/system/libo32"), Paths);
|
||||
}
|
||||
Generic_GCC::AddMultilibPaths(D, SysRoot, OSLibDir, MultiarchTriple, Paths);
|
||||
|
||||
@@ -343,16 +344,20 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
|
||||
|
||||
addPathIfExists(D, concat(SysRoot, "/usr/lib", MultiarchTriple), Paths);
|
||||
addPathIfExists(D, concat(SysRoot, "/usr", OSLibDir), Paths);
|
||||
+ addPathIfExists(D, concat(SysRoot, "/system/lib", MultiarchTriple), Paths);
|
||||
+ addPathIfExists(D, concat(SysRoot, "/system", OSLibDir), Paths);
|
||||
if (IsRISCV) {
|
||||
StringRef ABIName = tools::riscv::getRISCVABI(Args, Triple);
|
||||
addPathIfExists(D, concat(SysRoot, "/", OSLibDir, ABIName), Paths);
|
||||
addPathIfExists(D, concat(SysRoot, "/usr", OSLibDir, ABIName), Paths);
|
||||
+ addPathIfExists(D, concat(SysRoot, "/system", OSLibDir, ABIName), Paths);
|
||||
}
|
||||
|
||||
Generic_GCC::AddMultiarchPaths(D, SysRoot, OSLibDir, Paths);
|
||||
|
||||
addPathIfExists(D, concat(SysRoot, "/lib"), Paths);
|
||||
addPathIfExists(D, concat(SysRoot, "/usr/lib"), Paths);
|
||||
+ addPathIfExists(D, concat(SysRoot, "/system/lib"), Paths);
|
||||
}
|
||||
|
||||
ToolChain::RuntimeLibType Linux::GetDefaultRuntimeLibType() const {
|
||||
@@ -457,6 +462,11 @@ std::string Linux::getDynamicLinker(const ArgList &Args) const {
|
||||
return Triple.isArch64Bit() ? "/system/bin/linker64" : "/system/bin/linker";
|
||||
}
|
||||
if (Triple.isMusl()) {
|
||||
+ const std::string RosaSuffix = "-rosa-linux-musl";
|
||||
+ if (Triple.str().size() > RosaSuffix.size() &&
|
||||
+ std::equal(RosaSuffix.rbegin(), RosaSuffix.rend(), Triple.str().rbegin()))
|
||||
+ return "/system/bin/linker";
|
||||
+
|
||||
std::string ArchName;
|
||||
bool IsArm = false;
|
||||
|
||||
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
|
||||
index 64324a3f8b01..15ce70b68217 100644
|
||||
--- a/clang/tools/clang-installapi/Options.cpp
|
||||
+++ b/clang/tools/clang-installapi/Options.cpp
|
||||
@@ -515,7 +515,7 @@ bool Options::processFrontendOptions(InputArgList &Args) {
|
||||
FEOpts.FwkPaths = std::move(FrameworkPaths);
|
||||
|
||||
// Add default framework/library paths.
|
||||
- PathSeq DefaultLibraryPaths = {"/usr/lib", "/usr/local/lib"};
|
||||
+ PathSeq DefaultLibraryPaths = {"/usr/lib", "/system/lib", "/usr/local/lib"};
|
||||
PathSeq DefaultFrameworkPaths = {"/Library/Frameworks",
|
||||
"/System/Library/Frameworks"};
|
||||
|
||||
`},
|
||||
},
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
// llvm stores the result of Toolchain.newLLVM.
|
||||
llvm [_toolchainEnd][4]pkg.Artifact
|
||||
// llvmOnce is for lazy initialisation of llvm.
|
||||
llvmOnce [_toolchainEnd]sync.Once
|
||||
)
|
||||
|
||||
// NewLLVM returns LLVM toolchain across multiple [pkg.Artifact].
|
||||
func (t Toolchain) NewLLVM() (musl, compilerRT, runtimes, clang pkg.Artifact) {
|
||||
llvmOnce[t].Do(func() {
|
||||
llvm[t][0], llvm[t][1], llvm[t][2], llvm[t][3] = t.newLLVM()
|
||||
})
|
||||
return llvm[t][0], llvm[t][1], llvm[t][2], llvm[t][3]
|
||||
}
|
||||
|
||||
27
internal/rosa/meson.go
Normal file
27
internal/rosa/meson.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newMeson() pkg.Artifact {
|
||||
const (
|
||||
version = "1.10.1"
|
||||
checksum = "w895BXF_icncnXatT_OLCFe2PYEtg4KrKooMgUYdN-nQVvbFX3PvYWHGEpogsHtd"
|
||||
)
|
||||
return t.New("meson-"+version, false, []pkg.Artifact{
|
||||
t.Load(Python),
|
||||
t.Load(Setuptools),
|
||||
}, nil, nil, `
|
||||
cd /usr/src/meson
|
||||
chmod -R +w meson.egg-info
|
||||
python3 setup.py \
|
||||
install \
|
||||
--prefix=/system \
|
||||
--root=/work
|
||||
`, pkg.Path(AbsUsrSrc.Append("meson"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://github.com/mesonbuild/meson/releases/download/"+
|
||||
version+"/meson-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Meson] = Toolchain.newMeson }
|
||||
@@ -29,24 +29,21 @@ func (t Toolchain) NewMusl(attr *MuslAttr) pkg.Artifact {
|
||||
|
||||
target := "install"
|
||||
script := `
|
||||
mv -v /work/lib/* /work/system/lib
|
||||
rmdir -v /work/lib/
|
||||
mkdir -p /work/system/bin
|
||||
COMPAT_LINKER_NAME="ld-musl-$(uname -m).so.1"
|
||||
ln -vs ../lib/libc.so /work/system/bin/linker
|
||||
ln -vs ../lib/libc.so /work/system/bin/ldd
|
||||
ln -vs libc.so "/work/system/lib/${COMPAT_LINKER_NAME}"
|
||||
rm -v "/work/lib/${COMPAT_LINKER_NAME}"
|
||||
rmdir -v /work/lib
|
||||
`
|
||||
if attr.Headers {
|
||||
target = "install-headers"
|
||||
script = ""
|
||||
}
|
||||
|
||||
extra := []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
}
|
||||
if t == toolchainStage3 {
|
||||
extra = nil
|
||||
}
|
||||
|
||||
return t.New("musl-"+version, slices.Concat(
|
||||
attr.Extra,
|
||||
extra,
|
||||
return t.New("musl-"+version, false, stage3Concat(t, attr.Extra,
|
||||
t.Load(Make),
|
||||
), nil, slices.Concat([]string{
|
||||
"ROSA_MUSL_TARGET=" + target,
|
||||
}, attr.Env), `
|
||||
|
||||
@@ -2,15 +2,14 @@ package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
// NewNinja returns a [pkg.Artifact] containing an installation of Ninja.
|
||||
func (t Toolchain) NewNinja() pkg.Artifact {
|
||||
func (t Toolchain) newNinja() pkg.Artifact {
|
||||
const (
|
||||
version = "1.13.2"
|
||||
checksum = "ygKWMa0YV2lWKiFro5hnL-vcKbc_-RACZuPu0Io8qDvgQlZ0dxv7hPNSFkt4214v"
|
||||
)
|
||||
return t.New("ninja-"+version, []pkg.Artifact{
|
||||
t.NewCMake(),
|
||||
t.NewPython(),
|
||||
return t.New("ninja-"+version, false, []pkg.Artifact{
|
||||
t.Load(CMake),
|
||||
t.Load(Python),
|
||||
}, nil, nil, `
|
||||
chmod -R +w /usr/src/ninja/
|
||||
mkdir -p /work/system/bin/ && cd /work/system/bin/
|
||||
@@ -33,3 +32,4 @@ python3 /usr/src/ninja/configure.py \
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Ninja] = Toolchain.newNinja }
|
||||
|
||||
@@ -2,26 +2,30 @@ package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
// NewPerl returns a [pkg.Artifact] containing an installation of perl.
|
||||
func (t Toolchain) NewPerl() pkg.Artifact {
|
||||
func (t Toolchain) newPerl() pkg.Artifact {
|
||||
const (
|
||||
version = "5.42.0"
|
||||
checksum = "2KR7Jbpk-ZVn1a30LQRwbgUvg2AXlPQZfzrqCr31qD5-yEsTwVQ_W76eZH-EdxM9"
|
||||
)
|
||||
return t.New("perl-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
return t.New("perl-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
chmod -R +w /usr/src/perl && cd /usr/src/perl
|
||||
|
||||
echo 'print STDOUT "1..0 # Skip broken test\n";' > ext/Pod-Html/t/htmldir3.t
|
||||
|
||||
./Configure \
|
||||
-des \
|
||||
-Dprefix=/system \
|
||||
-Dcc="${CC}" \
|
||||
-Dcc="clang" \
|
||||
-Dcflags='--std=gnu99' \
|
||||
-Dldflags="${LDFLAGS}" \
|
||||
-Doptimize='-O2 -fno-strict-aliasing' \
|
||||
-Duseithreads
|
||||
make "-j$(nproc)" # test
|
||||
make \
|
||||
"-j$(nproc)" \
|
||||
TEST_JOBS=256 \
|
||||
test_harness
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("perl"), true, pkg.NewHTTPGetTar(
|
||||
nil,
|
||||
@@ -30,3 +34,4 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Perl] = Toolchain.newPerl }
|
||||
|
||||
29
internal/rosa/pkg-config.go
Normal file
29
internal/rosa/pkg-config.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newPkgConfig() pkg.Artifact {
|
||||
const (
|
||||
version = "0.29.2"
|
||||
checksum = "gi7yAvkwo20Inys1tHbeYZ3Wjdm5VPkrnO0Q6_QZPCAwa1zrA8F4a63cdZDd-717"
|
||||
)
|
||||
return t.New("pkg-config-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/pkg-config/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
CFLAGS='-Wno-int-conversion' \
|
||||
--with-internal-glib
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("pkg-config"), true, pkg.NewHTTPGetTar(
|
||||
nil,
|
||||
"https://pkgconfig.freedesktop.org/releases/"+
|
||||
"pkg-config-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[PkgConfig] = Toolchain.newPkgConfig }
|
||||
@@ -6,8 +6,7 @@ import (
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
// NewPython returns a [pkg.Artifact] containing an installation of Python.
|
||||
func (t Toolchain) NewPython() pkg.Artifact {
|
||||
func (t Toolchain) newPython() pkg.Artifact {
|
||||
const (
|
||||
version = "3.14.2"
|
||||
checksum = "7nZunVMGj0viB-CnxpcRego2C90X5wFsMTgsoewd5z-KSZY2zLuqaBwG-14zmKys"
|
||||
@@ -33,12 +32,16 @@ func (t Toolchain) NewPython() pkg.Artifact {
|
||||
// breaks on llvm
|
||||
"test_dbm_gnu",
|
||||
}
|
||||
return t.New("python-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
t.NewZlib(),
|
||||
t.NewLibffi(),
|
||||
return t.New("python-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(Zlib),
|
||||
t.Load(Libffi),
|
||||
}, nil, []string{
|
||||
"EXTRATESTOPTS=-j0 -x " + strings.Join(skipTests, " -x "),
|
||||
|
||||
// _ctypes appears to infer something from the linker name
|
||||
"LDFLAGS=-Wl,--dynamic-linker=/system/lib/" +
|
||||
"ld-musl-" + linuxArch() + ".so.1",
|
||||
}, `
|
||||
# test_synopsis_sourceless assumes this is writable and checks __pycache__
|
||||
chmod -R +w /usr/src/python/
|
||||
@@ -46,7 +49,8 @@ chmod -R +w /usr/src/python/
|
||||
export HOME="$(mktemp -d)"
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/python/configure \
|
||||
--prefix=/system
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}"
|
||||
make "-j$(nproc)"
|
||||
make test
|
||||
make DESTDIR=/work install
|
||||
@@ -58,3 +62,26 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Python] = Toolchain.newPython }
|
||||
|
||||
func (t Toolchain) newSetuptools() pkg.Artifact {
|
||||
const (
|
||||
version = "80.10.1"
|
||||
checksum = "p3rlwEmy1krcUH1KabprQz1TCYjJ8ZUjOQknQsWh3q-XEqLGEd3P4VrCc7ouHGXU"
|
||||
)
|
||||
return t.New("setuptools-"+version, false, []pkg.Artifact{
|
||||
t.Load(Python),
|
||||
}, nil, nil, `
|
||||
pip3 install \
|
||||
--no-index \
|
||||
--prefix=/system \
|
||||
--root=/work \
|
||||
/usr/src/setuptools
|
||||
`, pkg.Path(AbsUsrSrc.Append("setuptools"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://github.com/pypa/setuptools/archive/refs/tags/"+
|
||||
"v"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Setuptools] = Toolchain.newSetuptools }
|
||||
|
||||
@@ -63,16 +63,11 @@ func triplet() string {
|
||||
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 " +
|
||||
// earlyLDFLAGS returns LDFLAGS corresponding to triplet.
|
||||
func earlyLDFLAGS(static bool) string {
|
||||
s := "-fuse-ld=lld " +
|
||||
"-L/system/lib -Wl,-rpath=/system/lib " +
|
||||
"-L/system/lib/" + triplet() + " " +
|
||||
"-Wl,-rpath=/system/lib/" + triplet() + " " +
|
||||
@@ -80,18 +75,18 @@ func ldflags(static bool) string {
|
||||
"-unwindlib=libunwind " +
|
||||
"-Wl,--as-needed"
|
||||
if !static {
|
||||
s += " -Wl,--dynamic-linker=/system/lib/ld-musl-x86_64.so.1"
|
||||
s += " -Wl,--dynamic-linker=/system/bin/linker"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// cflags is reference CFLAGS for the Rosa OS toolchain.
|
||||
const cflags = "-Qunused-arguments " +
|
||||
// earlyCFLAGS is reference CFLAGS for the stage3 toolchain.
|
||||
const earlyCFLAGS = "-Qunused-arguments " +
|
||||
"-isystem/system/include"
|
||||
|
||||
// cxxflags returns reference CXXFLAGS for the Rosa OS toolchain corresponding
|
||||
// to [runtime.GOARCH].
|
||||
func cxxflags() string {
|
||||
// earlyCXXFLAGS returns reference CXXFLAGS for the stage3 toolchain
|
||||
// corresponding to [runtime.GOARCH].
|
||||
func earlyCXXFLAGS() string {
|
||||
return "--start-no-unused-arguments " +
|
||||
"-stdlib=libc++ " +
|
||||
"--end-no-unused-arguments " +
|
||||
@@ -119,8 +114,30 @@ const (
|
||||
|
||||
// Std denotes the standard Rosa OS toolchain.
|
||||
Std
|
||||
|
||||
// _toolchainEnd is the total number of toolchains available and does not
|
||||
// denote a valid toolchain.
|
||||
_toolchainEnd
|
||||
)
|
||||
|
||||
// stage3Concat concatenates s and values. If the current toolchain is
|
||||
// toolchainStage3, stage3Concat returns s as is.
|
||||
func stage3Concat[S ~[]E, E any](t Toolchain, s S, values ...E) S {
|
||||
if t == toolchainStage3 {
|
||||
return s
|
||||
}
|
||||
return slices.Concat(s, values)
|
||||
}
|
||||
|
||||
// stage3ExclConcat concatenates s and values. If the current toolchain is not
|
||||
// toolchainStage3, stage3ExclConcat returns s as is.
|
||||
func stage3ExclConcat[S ~[]E, E any](t Toolchain, s S, values ...E) S {
|
||||
if t == toolchainStage3 {
|
||||
return slices.Concat(s, values)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -159,6 +176,7 @@ var absCureScript = fhs.AbsUsrBin.Append(".cure-script")
|
||||
// New returns a [pkg.Artifact] compiled on this toolchain.
|
||||
func (t Toolchain) New(
|
||||
name string,
|
||||
exclusive bool,
|
||||
extra []pkg.Artifact,
|
||||
checksum *pkg.Checksum,
|
||||
env []string,
|
||||
@@ -175,10 +193,12 @@ func (t Toolchain) New(
|
||||
)
|
||||
switch t {
|
||||
case toolchainBusybox:
|
||||
name += "-early"
|
||||
support = slices.Concat([]pkg.Artifact{newBusyboxBin()}, extra)
|
||||
env = fixupEnviron(env, nil, "/system/bin")
|
||||
|
||||
case toolchainStage3:
|
||||
name += "-boot"
|
||||
const (
|
||||
version = "20260111T160052Z"
|
||||
checksum = "c5_FwMnRN8RZpTdBLGYkL4RR8ampdaZN2JbkgrFLe8-QHQAVQy08APVvIL6eT7KW"
|
||||
@@ -187,7 +207,7 @@ func (t Toolchain) New(
|
||||
args[0] = "bash"
|
||||
support = slices.Concat([]pkg.Artifact{
|
||||
cureEtc{},
|
||||
toolchainBusybox.New("stage3-"+version, nil, nil, nil, `
|
||||
toolchainBusybox.New("stage3-"+version, false, nil, nil, nil, `
|
||||
tar -C /work -xf /usr/src/stage3.tar.xz
|
||||
rm -rf /work/dev/ /work/proc/
|
||||
ln -vs ../usr/bin /work/bin
|
||||
@@ -203,16 +223,17 @@ ln -vs ../usr/bin /work/bin
|
||||
env = fixupEnviron(env, []string{
|
||||
EnvTriplet + "=" + triplet(),
|
||||
lcMessages,
|
||||
|
||||
EnvRefCFLAGS + "=" + cflags,
|
||||
EnvRefCXXFLAGS + "=" + cxxflags(),
|
||||
ldflags(true),
|
||||
"LDFLAGS=" + earlyLDFLAGS(true),
|
||||
}, "/system/bin",
|
||||
"/usr/bin",
|
||||
"/usr/lib/llvm/21/bin",
|
||||
)
|
||||
|
||||
case toolchainIntermediate, Std:
|
||||
if t < Std {
|
||||
name += "-std"
|
||||
}
|
||||
|
||||
boot := t - 1
|
||||
musl, compilerRT, runtimes, clang := boot.NewLLVM()
|
||||
support = slices.Concat(extra, []pkg.Artifact{
|
||||
@@ -221,19 +242,12 @@ ln -vs ../usr/bin /work/bin
|
||||
compilerRT,
|
||||
runtimes,
|
||||
clang,
|
||||
boot.NewBusybox(),
|
||||
boot.Load(Busybox),
|
||||
})
|
||||
env = fixupEnviron(env, []string{
|
||||
EnvTriplet + "=" + triplet(),
|
||||
lcMessages,
|
||||
|
||||
// autotools projects act up with CFLAGS
|
||||
"CC=clang " + cflags,
|
||||
EnvRefCFLAGS + "=" + cflags,
|
||||
"CXX=clang++ " + cxxflags(),
|
||||
EnvRefCXXFLAGS + "=" + cxxflags(),
|
||||
ldflags(false),
|
||||
|
||||
"AR=ar",
|
||||
"RANLIB=ranlib",
|
||||
"LIBCC=/system/lib/clang/21/lib/" + triplet() +
|
||||
@@ -245,7 +259,7 @@ ln -vs ../usr/bin /work/bin
|
||||
}
|
||||
|
||||
return pkg.NewExec(
|
||||
name, checksum, pkg.ExecTimeoutMax,
|
||||
name, checksum, pkg.ExecTimeoutMax, exclusive,
|
||||
fhs.AbsRoot, env,
|
||||
path, args,
|
||||
|
||||
@@ -258,3 +272,43 @@ ln -vs ../usr/bin /work/bin
|
||||
)}, paths)...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewPatchedSource returns [pkg.Artifact] of source with patches applied. If
|
||||
// passthrough is true, source is returned as is for zero length patches.
|
||||
func (t Toolchain) NewPatchedSource(
|
||||
name, version string,
|
||||
source pkg.Artifact,
|
||||
passthrough bool,
|
||||
patches ...[2]string,
|
||||
) pkg.Artifact {
|
||||
if passthrough && len(patches) == 0 {
|
||||
return source
|
||||
}
|
||||
|
||||
paths := make([]pkg.ExecPath, len(patches)+1)
|
||||
for i, p := range patches {
|
||||
paths[i+1] = pkg.Path(
|
||||
AbsUsrSrc.Append(name+"-patches", p[0]+".patch"), false,
|
||||
pkg.NewFile(p[0]+".patch", []byte(p[1])),
|
||||
)
|
||||
}
|
||||
paths[0] = pkg.Path(AbsUsrSrc.Append(name), false, source)
|
||||
|
||||
aname := name + "-" + version + "-src"
|
||||
script := `
|
||||
cp -r /usr/src/` + name + `/. /work/.
|
||||
chmod -R +w /work && cd /work
|
||||
`
|
||||
if len(paths) > 1 {
|
||||
script += `
|
||||
cat /usr/src/` + name + `-patches/* | \
|
||||
patch \
|
||||
-p 1 \
|
||||
--ignore-whitespace
|
||||
`
|
||||
aname += "-patched"
|
||||
}
|
||||
return t.New(aname, false, stage3Concat(t, []pkg.Artifact{},
|
||||
t.Load(Patch),
|
||||
), nil, nil, script, paths...)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@ package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
// NewRsync returns a [pkg.Artifact] containing an installation of rsync.
|
||||
func (t Toolchain) NewRsync() pkg.Artifact {
|
||||
func (t Toolchain) newRsync() pkg.Artifact {
|
||||
const (
|
||||
version = "3.4.1"
|
||||
checksum = "VBlTsBWd9z3r2-ex7GkWeWxkUc5OrlgDzikAC0pK7ufTjAJ0MbmC_N04oSVTGPiv"
|
||||
)
|
||||
return t.New("rsync-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
return t.New("rsync-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/rsync/configure --prefix=/system \
|
||||
@@ -27,3 +26,4 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Rsync] = Toolchain.newRsync }
|
||||
|
||||
82
internal/rosa/wayland.go
Normal file
82
internal/rosa/wayland.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newWayland() pkg.Artifact {
|
||||
const (
|
||||
version = "1.24.0"
|
||||
checksum = "JxgLiFRRGw2D3uhVw8ZeDbs3V7K_d4z_ypDog2LBqiA_5y2vVbUAk5NT6D5ozm0m"
|
||||
)
|
||||
return t.New("wayland-"+version, false, []pkg.Artifact{
|
||||
t.Load(Python),
|
||||
t.Load(Meson),
|
||||
t.Load(PkgConfig),
|
||||
t.Load(CMake),
|
||||
t.Load(Ninja),
|
||||
|
||||
t.Load(Libffi),
|
||||
t.Load(Libexpat),
|
||||
t.Load(Libxml2),
|
||||
}, nil, nil, `
|
||||
cd /usr/src/wayland
|
||||
chmod +w tests tests/sanity-test.c
|
||||
echo 'int main(){}' > tests/sanity-test.c
|
||||
|
||||
cd "$(mktemp -d)"
|
||||
meson setup \
|
||||
--reconfigure \
|
||||
--buildtype=release \
|
||||
--prefix=/system \
|
||||
--prefer-static \
|
||||
-Ddocumentation=false \
|
||||
-Dtests=true \
|
||||
-Ddefault_library=both \
|
||||
. /usr/src/wayland
|
||||
meson compile
|
||||
meson test
|
||||
meson install \
|
||||
--destdir=/work
|
||||
`, pkg.Path(AbsUsrSrc.Append("wayland"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://gitlab.freedesktop.org/wayland/wayland/"+
|
||||
"-/archive/"+version+"/wayland-"+version+".tar.bz2",
|
||||
mustDecode(checksum),
|
||||
pkg.TarBzip2,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Wayland] = Toolchain.newWayland }
|
||||
|
||||
func (t Toolchain) newWaylandProtocols() pkg.Artifact {
|
||||
const (
|
||||
version = "1.47"
|
||||
checksum = "B_NodZ7AQfCstcx7kgbaVjpkYOzbAQq0a4NOk-SA8bQixAE20FY3p1-6gsbPgHn9"
|
||||
)
|
||||
return t.New("wayland-protocols-"+version, false, []pkg.Artifact{
|
||||
t.Load(Python),
|
||||
t.Load(Meson),
|
||||
t.Load(PkgConfig),
|
||||
t.Load(CMake),
|
||||
t.Load(Ninja),
|
||||
|
||||
t.Load(Wayland),
|
||||
t.Load(Libffi),
|
||||
t.Load(Libexpat),
|
||||
t.Load(Libxml2),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
meson setup \
|
||||
--reconfigure \
|
||||
--buildtype=release \
|
||||
--prefix=/system \
|
||||
--prefer-static \
|
||||
. /usr/src/wayland-protocols
|
||||
meson compile
|
||||
meson install \
|
||||
--destdir=/work
|
||||
`, pkg.Path(AbsUsrSrc.Append("wayland-protocols"), false, pkg.NewHTTPGetTar(
|
||||
nil, "https://gitlab.freedesktop.org/wayland/wayland-protocols/"+
|
||||
"-/archive/"+version+"/wayland-protocols-"+version+".tar.bz2",
|
||||
mustDecode(checksum),
|
||||
pkg.TarBzip2,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[WaylandProtocols] = Toolchain.newWaylandProtocols }
|
||||
52
internal/rosa/x.go
Normal file
52
internal/rosa/x.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newXproto() pkg.Artifact {
|
||||
const (
|
||||
version = "7.0.23"
|
||||
checksum = "goxwWxV0jZ_3pNczXFltZWHAhq92x-aEreUGyp5Ns8dBOoOmgbpeNIu1nv0Zx07z"
|
||||
)
|
||||
return t.New("xproto-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(PkgConfig),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/xproto/configure \
|
||||
--prefix=/system \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("xproto"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://www.x.org/releases/X11R7.7/src/proto/xproto-"+version+".tar.bz2",
|
||||
mustDecode(checksum),
|
||||
pkg.TarBzip2,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Xproto] = Toolchain.newXproto }
|
||||
|
||||
func (t Toolchain) newLibXau() pkg.Artifact {
|
||||
const (
|
||||
version = "1.0.7"
|
||||
checksum = "bm768RoZZnHRe9VjNU1Dw3BhfE60DyS9D_bgSR-JLkEEyUWT_Hb_lQripxrXto8j"
|
||||
)
|
||||
return t.New("libXau-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(PkgConfig),
|
||||
|
||||
t.Load(Xproto),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/libXau/configure \
|
||||
--prefix=/system \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("libXau"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://www.x.org/releases/X11R7.7/src/lib/"+
|
||||
"libXau-"+version+".tar.bz2",
|
||||
mustDecode(checksum),
|
||||
pkg.TarBzip2,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[LibXau] = Toolchain.newLibXau }
|
||||
56
internal/rosa/xcb.go
Normal file
56
internal/rosa/xcb.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newXCBProto() pkg.Artifact {
|
||||
const (
|
||||
version = "1.17.0"
|
||||
checksum = "_NtbKaJ_iyT7XiJz25mXQ7y-niTzE8sHPvLXZPcqtNoV_-vTzqkezJ8Hp2U1enCv"
|
||||
)
|
||||
return t.New("xcb-proto-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(Python),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/xcb-proto/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("xcb-proto"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://xcb.freedesktop.org/dist/xcb-proto-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[XCBProto] = Toolchain.newXCBProto }
|
||||
|
||||
func (t Toolchain) newXCB() pkg.Artifact {
|
||||
const (
|
||||
version = "1.17.0"
|
||||
checksum = "hjjsc79LpWM_hZjNWbDDS6qRQUXREjjekS6UbUsDq-RR1_AjgNDxhRvZf-1_kzDd"
|
||||
)
|
||||
return t.New("xcb-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
t.Load(Python),
|
||||
t.Load(PkgConfig),
|
||||
|
||||
t.Load(XCBProto),
|
||||
t.Load(Xproto),
|
||||
t.Load(LibXau),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
/usr/src/xcb/configure \
|
||||
--prefix=/system \
|
||||
--build="${ROSA_TRIPLE}" \
|
||||
--enable-static
|
||||
make "-j$(nproc)" check
|
||||
make DESTDIR=/work install
|
||||
`, pkg.Path(AbsUsrSrc.Append("xcb"), true, pkg.NewHTTPGetTar(
|
||||
nil, "https://xcb.freedesktop.org/dist/libxcb-"+version+".tar.gz",
|
||||
mustDecode(checksum),
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[XCB] = Toolchain.newXCB }
|
||||
@@ -2,17 +2,16 @@ package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
// NewZlib returns a new [pkg.Artifact] containing an installation of zlib.
|
||||
func (t Toolchain) NewZlib() pkg.Artifact {
|
||||
func (t Toolchain) newZlib() pkg.Artifact {
|
||||
const (
|
||||
version = "1.3.1"
|
||||
checksum = "E-eIpNzE8oJ5DsqH4UuA_0GDKuQF5csqI8ooDx2w7Vx-woJ2mb-YtSbEyIMN44mH"
|
||||
)
|
||||
return t.New("zlib-"+version, []pkg.Artifact{
|
||||
t.NewMake(),
|
||||
return t.New("zlib-"+version, false, []pkg.Artifact{
|
||||
t.Load(Make),
|
||||
}, nil, nil, `
|
||||
cd "$(mktemp -d)"
|
||||
CFLAGS="${CFLAGS} -fPIC" /usr/src/zlib/configure \
|
||||
CC="clang -fPIC" /usr/src/zlib/configure \
|
||||
--prefix /system
|
||||
make "-j$(nproc)" test
|
||||
make DESTDIR=/work install
|
||||
@@ -23,3 +22,4 @@ make DESTDIR=/work install
|
||||
pkg.TarGzip,
|
||||
)))
|
||||
}
|
||||
func init() { artifactsF[Zlib] = Toolchain.newZlib }
|
||||
|
||||
@@ -35,7 +35,7 @@ package
|
||||
|
||||
|
||||
*Default:*
|
||||
` <derivation hakurei-static-x86_64-unknown-linux-musl-0.3.3> `
|
||||
` <derivation hakurei-static-x86_64-unknown-linux-musl-0.3.4> `
|
||||
|
||||
|
||||
|
||||
@@ -805,7 +805,7 @@ package
|
||||
|
||||
|
||||
*Default:*
|
||||
` <derivation hakurei-hsu-0.3.3> `
|
||||
` <derivation hakurei-hsu-0.3.4> `
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "hakurei";
|
||||
version = "0.3.3";
|
||||
version = "0.3.4";
|
||||
|
||||
srcFiltered = builtins.path {
|
||||
name = "${pname}-src";
|
||||
|
||||
Reference in New Issue
Block a user