2 Commits
Author SHA1 Message Date
cat bfd922fde3 internal/rosa/package/kernel: 6.12.94 to 6.18.37
Test / Create distribution (push) Successful in 2m51s
Test / Sandbox (push) Successful in 7m38s
Test / ShareFS (push) Successful in 8m59s
Test / Hakurei (push) Successful in 9m6s
Test / Sandbox (race detector) (push) Successful in 9m53s
Test / Hakurei (race detector) (push) Successful in 11m7s
Test / Flake checks (push) Successful in 1m16s
Finally jumping from 6.12 to 6.18, and the patches can be dropped.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-07-01 02:00:31 +09:00
cat b121f74b4e internal/rosa/package/kernel: 6.12.94 to 6.18.37
Finally jumping from 6.12 to 6.18, and the patches can be dropped.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-07-01 01:59:18 +09:00
277 changed files with 1679 additions and 20284 deletions
-1
View File
@@ -1 +0,0 @@
*.az linguist-language=Azalea
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Build for release - name: Build for release
run: nix build --print-out-paths --print-build-logs ./test#dist run: nix build --print-out-paths --print-build-logs .#dist
- name: Release - name: Release
uses: https://gitea.com/actions/release-action@main uses: https://gitea.com/actions/release-action@main
+9 -9
View File
@@ -12,7 +12,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run NixOS test - name: Run NixOS test
run: nix build --out-link "result" --print-out-paths --print-build-logs ./test#checks.x86_64-linux.hakurei run: nix build --out-link "result" --print-out-paths --print-build-logs .#checks.x86_64-linux.hakurei
- name: Upload test output - name: Upload test output
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
@@ -29,7 +29,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run NixOS test - name: Run NixOS test
run: nix build --out-link "result" --print-out-paths --print-build-logs ./test#checks.x86_64-linux.race run: nix build --out-link "result" --print-out-paths --print-build-logs .#checks.x86_64-linux.race
- name: Upload test output - name: Upload test output
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
@@ -46,7 +46,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run NixOS test - name: Run NixOS test
run: nix build --out-link "result" --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox run: nix build --out-link "result" --print-out-paths --print-build-logs .#checks.x86_64-linux.sandbox
- name: Upload test output - name: Upload test output
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
@@ -63,7 +63,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run NixOS test - name: Run NixOS test
run: nix build --out-link "result" --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox-race run: nix build --out-link "result" --print-out-paths --print-build-logs .#checks.x86_64-linux.sandbox-race
- name: Upload test output - name: Upload test output
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
@@ -80,7 +80,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run NixOS test - name: Run NixOS test
run: nix build --out-link "result" --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sharefs run: nix build --out-link "result" --print-out-paths --print-build-logs .#checks.x86_64-linux.sharefs
- name: Upload test output - name: Upload test output
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
@@ -103,7 +103,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run checks - name: Run checks
run: nix --print-build-logs --experimental-features 'nix-command flakes' flake check ./test run: nix --print-build-logs --experimental-features 'nix-command flakes' flake check
dist: dist:
name: Create distribution name: Create distribution
@@ -116,9 +116,9 @@ jobs:
id: build-test id: build-test
run: >- run: >-
export HAKUREI_REV="$(git rev-parse --short HEAD)" && export HAKUREI_REV="$(git rev-parse --short HEAD)" &&
sed -i.old 's/version = /version = "0.0.0-'$HAKUREI_REV'"; # version = /' test/package.nix && sed -i.old 's/version = /version = "0.0.0-'$HAKUREI_REV'"; # version = /' package.nix &&
nix build --print-out-paths --print-build-logs ./test#dist && nix build --print-out-paths --print-build-logs .#dist &&
mv test/{package.nix.old,package.nix} && mv package.nix.old package.nix &&
echo "rev=$HAKUREI_REV" >> $GITHUB_OUTPUT echo "rev=$HAKUREI_REV" >> $GITHUB_OUTPUT
- name: Upload test build - name: Upload test build
-1
View File
@@ -1,5 +1,4 @@
# produced by tools and text editors # produced by tools and text editors
*.swp
*.qcow2 *.qcow2
*.test *.test
*.out *.out
+6 -1
View File
@@ -11,7 +11,7 @@
<a href="https://git.gensokyo.uk/rosa/hakurei/actions"><img src="https://git.gensokyo.uk/rosa/hakurei/actions/workflows/test.yml/badge.svg?branch=staging&style=flat-square" alt="Gitea Workflow Status" /></a> <a href="https://git.gensokyo.uk/rosa/hakurei/actions"><img src="https://git.gensokyo.uk/rosa/hakurei/actions/workflows/test.yml/badge.svg?branch=staging&style=flat-square" alt="Gitea Workflow Status" /></a>
<br/> <br/>
<a href="https://git.gensokyo.uk/rosa/hakurei/releases"><img src="https://img.shields.io/gitea/v/release/rosa/hakurei?gitea_url=https%3A%2F%2Fgit.gensokyo.uk&color=purple" alt="Release" /></a> <a href="https://git.gensokyo.uk/rosa/hakurei/releases"><img src="https://img.shields.io/gitea/v/release/rosa/hakurei?gitea_url=https%3A%2F%2Fgit.gensokyo.uk&color=purple" alt="Release" /></a>
<a href="https://git.gensokyo.uk/rosa/hakurei/src/branch/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-pink" alt="MIT License" /></a> <a href="https://goreportcard.com/report/hakurei.app"><img src="https://goreportcard.com/badge/hakurei.app" alt="Go Report Card" /></a>
<a href="https://hakurei.app"><img src="https://img.shields.io/website?url=https%3A%2F%2Fhakurei.app" alt="Website" /></a> <a href="https://hakurei.app"><img src="https://img.shields.io/website?url=https%3A%2F%2Fhakurei.app" alt="Website" /></a>
</p> </p>
@@ -58,3 +58,8 @@ package [internal/rosa](https://pkg.go.dev/hakurei.app/internal/rosa).
New dependencies will generally not be added. Patches adding new dependencies New dependencies will generally not be added. Patches adding new dependencies
are very likely to be rejected. are very likely to be rejected.
## NixOS Module (deprecated)
The NixOS module is in maintenance mode and will be removed once planterette is
feature-complete. Full module documentation can be found [here](options.md).
-17
View File
@@ -4,7 +4,6 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"io" "io"
"os"
"strconv" "strconv"
"strings" "strings"
@@ -336,19 +335,3 @@ func parse(
return &c, nil return &c, nil
} }
// list prints names of non-hidden entries in pathname.
func list(pathname *check.Absolute, dir bool) error {
dents, err := os.ReadDir(pathname.String())
if err != nil {
return err
}
for _, dent := range dents {
name := dent.Name()
if (dent.IsDir() != dir) || (len(name) > 0 && name[0] == '.') {
continue
}
fmt.Println(dent.Name())
}
return nil
}
+23 -2
View File
@@ -7,6 +7,7 @@ package main
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io" "io"
"log" "log"
"os" "os"
@@ -76,7 +77,17 @@ func main() {
"enter", "Enter mutable state template", "enter", "Enter mutable state template",
func(args []string) error { func(args []string) error {
if len(args) != 1 { if len(args) != 1 {
return list(template, true) dents, err := os.ReadDir(template.String())
if err != nil {
return err
}
for _, dent := range dents {
if !dent.IsDir() {
continue
}
fmt.Println(dent.Name())
}
return nil
} }
config := hst.Config{ config := hst.Config{
@@ -148,7 +159,17 @@ func main() {
"run", "Start the named application", "run", "Start the named application",
func(args []string) error { func(args []string) error {
if len(args) < 1 { if len(args) < 1 {
return list(base.Append("app"), false) dents, err := os.ReadDir(base.Append("app").String())
if err != nil {
return err
}
for _, dent := range dents {
if dent.IsDir() {
continue
}
fmt.Println(dent.Name())
}
return nil
} }
var config *hst.Config var config *hst.Config
+1 -1
View File
@@ -1 +1 @@
v0.4.6 v0.4.5
+3 -3
View File
@@ -57,7 +57,7 @@ func main() {
verbose := os.Getenv("VERBOSE") != "" verbose := os.Getenv("VERBOSE") != ""
runTests := os.Getenv("HAKUREI_DIST_MAKE") == "" runTests := os.Getenv("HAKUREI_DIST_MAKE") == ""
version = getenv("HAKUREI_VERSION", strings.TrimSpace(version)) version = getenv("HAKUREI_VERSION", strings.TrimSpace(version))
prefix := getenv("PREFIX", "/usr/local") prefix := getenv("PREFIX", "/usr")
destdir := getenv("DESTDIR", "dist") destdir := getenv("DESTDIR", "dist")
if verbose { if verbose {
@@ -217,7 +217,7 @@ func main() {
mustWriteFile("comp/", nil, os.ModeDir|0755) mustWriteFile("comp/", nil, os.ModeDir|0755)
mustWriteFile("comp/_hakurei", comp, 0644) mustWriteFile("comp/_hakurei", comp, 0644)
mustWriteFile("install.sh", []byte(`#!/bin/sh -e mustWriteFile("install.sh", []byte(`#!/bin/sh -e
cd "$(dirname -- "$0")" cd "$(dirname -- "$0")" || exit 1
install -vDm0755 "bin/hakurei" "${DESTDIR}`+prefix+`/bin/hakurei" install -vDm0755 "bin/hakurei" "${DESTDIR}`+prefix+`/bin/hakurei"
install -vDm0755 "bin/sharefs" "${DESTDIR}`+prefix+`/bin/sharefs" install -vDm0755 "bin/sharefs" "${DESTDIR}`+prefix+`/bin/sharefs"
@@ -230,7 +230,7 @@ fi
install -vDm0644 "comp/_hakurei" "${DESTDIR}`+prefix+`/share/zsh/site-functions/_hakurei" install -vDm0644 "comp/_hakurei" "${DESTDIR}`+prefix+`/share/zsh/site-functions/_hakurei"
`), 0755) `), 0755)
mustWriteFromPath("LICENSE", "LICENSE", 0) mustWriteFromPath("README.md", "README.md", 0)
mustWriteFile("hsurc.default", []byte("1000 0"), 0400) mustWriteFile("hsurc.default", []byte("1000 0"), 0400)
mustWriteFromPath("bin/hsu", filepath.Join(s, "hsu"), 04511) mustWriteFromPath("bin/hsu", filepath.Join(s, "hsu"), 04511)
for _, name := range []string{ for _, name := range []string{
+6 -9
View File
@@ -19,9 +19,10 @@ func TestHelp(t *testing.T) {
want string want string
}{ }{
{ {
"main", []string{}, `usage: hakurei [-h | --help] [-v] [--insecure] [--json] <command> [<args>] "main", []string{}, `
Usage: hakurei [-h | --help] [-v] [--insecure] [--json] COMMAND [OPTIONS]
commands: Commands:
run Load and start container from configuration file run Load and start container from configuration file
exec Configure and start a permissive container exec Configure and start a permissive container
show Show live or local instance configuration show Show live or local instance configuration
@@ -34,14 +35,10 @@ commands:
`, `,
}, },
{ {
"exec", []string{"exec", "-h"}, `usage: hakurei exec [-h | --help] [--dbus-config <value>] "exec", []string{"exec", "-h"}, `
[--dbus-system <value>] [--mpris] [--dbus-log] Usage: hakurei exec [-h | --help] [--dbus-config <value>] [--dbus-system <value>] [--mpris] [--dbus-log] [--id <value>] [-a <int>] [-g <value>] [-d <value>] [-u <value>] [--policy <value>] [--priority <int>] [--private-runtime] [--private-tmpdir] [--wayland] [-X] [--dbus] [--pipewire] [--pulse] COMMAND [OPTIONS]
[--id <value>] [-a <int>] [-g <value>] [-d <value>]
[-u <value>] [--policy <value>] [--priority <int>]
[--private-runtime] [--private-tmpdir] [--wayland] [-X]
[--dbus] [--pipewire] [--pulse] <command> [<args>]
flags: Flags:
-X Enable direct connection to X11 -X Enable direct connection to X11
-a int -a int
Application identity Application identity
+1 -1
View File
@@ -8,7 +8,7 @@ buildGoModule {
pname = "${hakurei.pname}-hsu"; pname = "${hakurei.pname}-hsu";
inherit (hakurei) version; inherit (hakurei) version;
src = ../cmd/hsu; src = ./.;
inherit (hakurei) vendorHash; inherit (hakurei) vendorHash;
env.CGO_ENABLED = 0; env.CGO_ENABLED = 0;
+9 -65
View File
@@ -5,7 +5,6 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"testing" "testing"
"hakurei.app/check" "hakurei.app/check"
@@ -22,66 +21,41 @@ type cache struct {
// Should generally not be used directly. // Should generally not be used directly.
c *pkg.Cache c *pkg.Cache
attr pkg.CacheAttr cures, jobs int
// Primarily to work around missing landlock LSM. // Primarily to work around missing landlock LSM.
hostAbstract bool hostAbstract bool
// Set SCHED_IDLE. // Set SCHED_IDLE.
idle bool idle bool
// Unset [pkg.CSuppressInit]. // Unset [pkg.CSuppressInit].
verboseInit bool verboseInit bool
// Whether to enable output colours.
color bool
// Unset [pkg.CExternShallow].
deep bool
// Loaded artifact of [rosa.QEMU]. // Loaded artifact of [rosa.QEMU].
qemu pkg.Artifact qemu pkg.Artifact
base, mirror string base, mirror string
} }
// writeTitle sets title of the terminal connected to [message.Msg].
func writeTitle(msg message.Msg, s string) {
msg.Suspend()
w := msg.GetLogger().Writer()
_, _ = w.Write([]byte("\x1b]0;" + s + "\a"))
msg.Resume()
}
// open opens the underlying [pkg.Cache]. // open opens the underlying [pkg.Cache].
func (cache *cache) open() (err error) { func (cache *cache) open() (err error) {
if cache.c != nil { if cache.c != nil {
return os.ErrInvalid return os.ErrInvalid
} }
var hostname string
if hostname, err = os.Hostname(); err != nil {
return
}
var base *check.Absolute var base *check.Absolute
if cache.base, err = filepath.Abs(cache.base); err != nil { if cache.base, err = filepath.Abs(cache.base); err != nil {
return return
} else if base, err = check.NewAbs(cache.base); err != nil { } else if base, err = check.NewAbs(cache.base); err != nil {
return return
} }
var notify chan bool
var flags int
if cache.idle { if cache.idle {
cache.attr.Flags |= pkg.CSchedIdle flags |= pkg.CSchedIdle
} }
if cache.hostAbstract { if cache.hostAbstract {
cache.attr.Flags |= pkg.CHostAbstract flags |= pkg.CHostAbstract
} }
if !cache.verboseInit { if !cache.verboseInit {
cache.attr.Flags |= pkg.CSuppressInit flags |= pkg.CSuppressInit
}
if !cache.deep {
cache.attr.Flags |= pkg.CExternShallow
}
if cache.color {
cache.attr.Flags |= pkg.CColourOutput
notify = make(chan bool, 1<<12)
cache.attr.Notify = notify
} }
done := make(chan struct{}) done := make(chan struct{})
@@ -103,8 +77,10 @@ func (cache *cache) open() (err error) {
cache.c, err = pkg.Open( cache.c, err = pkg.Open(
cache.ctx, cache.ctx,
cache.msg, cache.msg,
flags,
cache.cures,
cache.jobs,
base, base,
&cache.attr,
) )
if err != nil { if err != nil {
return return
@@ -119,40 +95,13 @@ func (cache *cache) open() (err error) {
return return
} }
var r rosa.Remote var r rosa.Remote
if r, err = rosa.NewRemote(http.DefaultClient, cache.mirror, pub); err != nil { if r, err = rosa.NewRemote(cache.mirror, pub, http.DefaultClient); err != nil {
cache.c.Close() cache.c.Close()
return err return err
} }
cache.c.SetExternal(r) cache.c.SetExternal(r)
} }
if cache.attr.Notify != nil {
cache.msg.Suspend()
_, _ = cache.msg.GetLogger().Writer().Write([]byte("\x1b[22;0t"))
cache.msg.Resume()
prefix := hostname + ": mbf "
go func() {
var n, nc uint64
for enter := range notify {
if enter {
nc++
} else {
nc--
n++
}
if nc == 0 {
writeTitle(cache.msg, prefix[:len(prefix)-1])
continue
}
writeTitle(cache.msg, prefix+
"("+strconv.FormatUint(nc, 10)+
" running, "+strconv.FormatUint(n, 10)+" complete)")
}
}()
}
if cache.qemu != nil { if cache.qemu != nil {
var pathname *check.Absolute var pathname *check.Absolute
pathname, _, err = cache.c.Cure(cache.qemu) pathname, _, err = cache.c.Cure(cache.qemu)
@@ -173,11 +122,6 @@ func (cache *cache) open() (err error) {
func (cache *cache) Close() { func (cache *cache) Close() {
if cache.c != nil { if cache.c != nil {
cache.c.Close() cache.c.Close()
if cache.attr.Notify != nil {
cache.msg.Suspend()
_, _ = cache.msg.GetLogger().Writer().Write([]byte("\x1b[23;0t"))
cache.msg.Resume()
}
} }
} }
+5 -5
View File
@@ -28,8 +28,8 @@ func TestNoReply(t *testing.T) {
c, err := pkg.Open( c, err := pkg.Open(
t.Context(), t.Context(),
message.New(log.New(os.Stderr, "cir: ", 0)), message.New(log.New(os.Stderr, "cir: ", 0)),
0, 0, 0,
check.MustAbs(t.TempDir()), check.MustAbs(t.TempDir()),
nil,
) )
if err != nil { if err != nil {
t.Fatalf("Open: error = %v", err) t.Fatalf("Open: error = %v", err)
@@ -63,7 +63,7 @@ func TestNoReply(t *testing.T) {
} }
<-done <-done
wantIdent := pkg.MustDecode("bpVsJ69hdKPBntMh_u777g7-quFNVmMlBJQOlM_thiQa9JnOFLAbe5OlR4zNjZ32") wantIdent := pkg.MustDecode("fiZf-ZY_Yq6qxJNrHbMiIPYCsGkUiKCRsZrcSELXTqZWtCnESlHmzV5ThhWWGGYG")
if gotIdent := c.Ident(a).Value(); gotIdent != wantIdent { if gotIdent := c.Ident(a).Value(); gotIdent != wantIdent {
t.Errorf( t.Errorf(
"cureFromIR: %s, want %s", "cureFromIR: %s, want %s",
@@ -124,7 +124,7 @@ func TestDaemon(t *testing.T) {
cancel() cancel()
<-done <-done
const want = "bpVsJ69hdKPBntMh_u777g7-quFNVmMlBJQOlM_thiQa9JnOFLAbe5OlR4zNjZ32" const want = "fiZf-ZY_Yq6qxJNrHbMiIPYCsGkUiKCRsZrcSELXTqZWtCnESlHmzV5ThhWWGGYG"
if got := filepath.Base(p.String()); got != want { if got := filepath.Base(p.String()); got != want {
t.Errorf("cureRemote: %s, want %s", got, want) t.Errorf("cureRemote: %s, want %s", got, want)
} }
@@ -132,8 +132,8 @@ func TestDaemon(t *testing.T) {
wantLog := []string{ wantLog := []string{
"", "",
"daemon: aborting all pending cures", "daemon: aborting all pending cures",
"daemon: attempting to cancel invalid artifact 58Vy_5beLZCvZX__KzRKyIE6vATleaZP1ZBxbqoWnekz9aZ1zkxNgcIUFI5V1_Jf", "daemon: attempting to cancel invalid artifact kQm9fmnCmXST1-MMmxzcau2oKZCXXrlZydo4PkeV5hO_2PKfeC8t98hrbV_ZZx_j",
"daemon: fulfilled artifact " + want, "daemon: fulfilled artifact fiZf-ZY_Yq6qxJNrHbMiIPYCsGkUiKCRsZrcSELXTqZWtCnESlHmzV5ThhWWGGYG",
} }
gotLog := strings.Split(buf.String(), "\n") gotLog := strings.Split(buf.String(), "\n")
slices.Sort(gotLog) slices.Sort(gotLog)
+1 -4
View File
@@ -21,10 +21,7 @@ func commandInfo(
r *rosa.Report, r *rosa.Report,
) (err error) { ) (err error) {
if len(args) == 0 { if len(args) == 0 {
for _, h := range rosa.Native().CollectAll() { return errors.New("info requires at least 1 argument")
fmt.Println(h)
}
return nil
} }
// recovered by HandleAccess // recovered by HandleAccess
+39 -209
View File
@@ -27,7 +27,6 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
@@ -92,11 +91,6 @@ func main() {
var cm cache var cm cache
defer func() { cm.Close() }() defer func() { cm.Close() }()
if cm.color = ext.Isatty(syscall.Stderr) &&
os.Getenv("NO_COLOR") == "" &&
os.Getenv("TERM") != "dumb"; cm.color {
log.SetPrefix("\x1b[2mmbf: \x1b[0m")
}
var ( var (
flagQuiet bool flagQuiet bool
@@ -208,17 +202,13 @@ func main() {
"v", command.BoolFlag(false), "v", command.BoolFlag(false),
"Do not suppress verbose output from init", "Do not suppress verbose output from init",
).Flag( ).Flag(
&cm.attr.Cures, &cm.cures,
"cures", command.IntFlag(0), "cures", command.IntFlag(0),
"Maximum number of dependencies to cure at any given time", "Maximum number of dependencies to cure at any given time",
).Flag( ).Flag(
&cm.attr.Jobs, &cm.jobs,
"jobs", command.IntFlag(0), "jobs", command.IntFlag(0),
"Preferred number of jobs to run, when applicable", "Preferred number of jobs to run, when applicable",
).Flag(
&cm.attr.Load,
"load-average", command.IntFlag(0),
"Preferred load average number, when applicable",
).Flag( ).Flag(
&cm.base, &cm.base,
"d", command.StringFlag("$MBF_CACHE_DIR"), "d", command.StringFlag("$MBF_CACHE_DIR"),
@@ -227,11 +217,6 @@ func main() {
&cm.mirror, &cm.mirror,
"r", command.StringFlag("$MBF_REMOTE"), "r", command.StringFlag("$MBF_REMOTE"),
"URL of mirror service", "URL of mirror service",
).Flag(
&cm.deep,
"deep", command.BoolFlag(false),
"Cure all inputs even when the required artifact is available "+
"via the mirror service",
).Flag( ).Flag(
&cm.idle, &cm.idle,
"sched-idle", command.BoolFlag(false), "sched-idle", command.BoolFlag(false),
@@ -283,7 +268,7 @@ func main() {
if _, err := io.Copy(h, os.Stdin); err != nil { if _, err := io.Copy(h, os.Stdin); err != nil {
return err return err
} }
fmt.Println(pkg.Encode(pkg.Checksum(h.Sum(nil)))) log.Println(pkg.Encode(pkg.Checksum(h.Sum(nil))))
return nil return nil
}, },
) )
@@ -554,9 +539,7 @@ func main() {
if base, err := os.OpenRoot(cm.base); err != nil { if base, err := os.OpenRoot(cm.base); err != nil {
return err return err
} else { } else {
var mux http.ServeMux h = rosa.NewMirror(msg, base.FS(), key)
rosa.NewMirror(msg, base.FS(), key).Register(&mux)
h = &mux
} }
server := http.Server{Addr: args[0], Handler: h} server := http.Server{Addr: args[0], Handler: h}
@@ -578,52 +561,28 @@ func main() {
}, },
) )
c.NewCommand(
"snapshot",
"Populate an empty directory with a static mirror snapshot",
func(args []string) error {
if len(args) != 1 {
return errors.New("snapshot requires 1 argument")
}
var key ed25519.PrivateKey
if p, err := os.ReadFile(filepath.Join(cm.base, "ed25519")); err != nil {
return err
} else if len(p) != ed25519.PrivateKeySize {
return errors.New("invalid private key")
} else {
key = p
}
var m rosa.Mirror
if base, err := os.OpenRoot(cm.base); err != nil {
return err
} else {
m = rosa.NewMirror(msg, base.FS(), key)
}
retry:
root, err := os.OpenRoot(args[0])
if err != nil {
if os.IsNotExist(err) {
if err = os.MkdirAll(args[0], 0755); err != nil {
return err
}
goto retry
}
return err
}
go func() { <-ctx.Done(); _ = root.Close() }()
return m.Populate(root)
},
)
{ {
var (
flagGentoo string
flagChecksum string
)
c.NewCommand( c.NewCommand(
"stage4", "stage3",
"Check for toolchain 3-stage non-determinism", "Check for toolchain 3-stage non-determinism",
func(args []string) (err error) { func(args []string) (err error) {
s := rosa.Std
if flagGentoo != "" {
s -= 3 // magic number to discourage misuse
var checksum pkg.Checksum
if len(flagChecksum) != 0 {
if err = pkg.Decode(&checksum, flagChecksum); err != nil {
return
}
}
rosa.Native().SetGentooStage3(flagGentoo, checksum)
}
var ( var (
pathname *check.Absolute pathname *check.Absolute
checksum [2]unique.Handle[pkg.Checksum] checksum [2]unique.Handle[pkg.Checksum]
@@ -631,7 +590,7 @@ func main() {
_llvm := rosa.H("llvm") _llvm := rosa.H("llvm")
if err = cm.Do(func(cache *pkg.Cache) (err error) { if err = cm.Do(func(cache *pkg.Cache) (err error) {
_, llvm := rosa.Native().New(rosa.Std - 2).Load(_llvm) _, llvm := rosa.Native().New(s - 2).Load(_llvm)
pathname, _, err = cache.Cure(llvm) pathname, _, err = cache.Cure(llvm)
return return
}); err != nil { }); err != nil {
@@ -640,31 +599,21 @@ func main() {
log.Println("stage1:", pathname) log.Println("stage1:", pathname)
if err = cm.Do(func(cache *pkg.Cache) (err error) { if err = cm.Do(func(cache *pkg.Cache) (err error) {
_, llvm := rosa.Native().New(rosa.Std - 1).Load(_llvm) _, llvm := rosa.Native().New(s - 1).Load(_llvm)
pathname, _, err = cache.Cure(llvm)
return
}); err != nil {
return
}
log.Println("stage2:", pathname)
if err = cm.Do(func(cache *pkg.Cache) (err error) {
_, llvm := rosa.Native().New(rosa.Std).Load(_llvm)
pathname, checksum[0], err = cache.Cure(llvm) pathname, checksum[0], err = cache.Cure(llvm)
return return
}); err != nil { }); err != nil {
return return
} }
log.Println("stage3:", pathname) log.Println("stage2:", pathname)
if err = cm.Do(func(cache *pkg.Cache) (err error) { if err = cm.Do(func(cache *pkg.Cache) (err error) {
_, llvm := rosa.Native().New(rosa.Stage3).Load(_llvm) _, llvm := rosa.Native().New(s).Load(_llvm)
pathname, checksum[1], err = cache.Cure(llvm) pathname, checksum[1], err = cache.Cure(llvm)
return return
}); err != nil { }); err != nil {
return return
} }
log.Println("stage4:", pathname) log.Println("stage3:", pathname)
if checksum[0] != checksum[1] { if checksum[0] != checksum[1] {
err = &pkg.ChecksumMismatchError{ err = &pkg.ChecksumMismatchError{
@@ -673,34 +622,23 @@ func main() {
} }
} else { } else {
log.Println( log.Println(
"stage3 is identical to stage4", "stage2 is identical to stage3",
"("+pkg.Encode(checksum[0].Value())+")", "("+pkg.Encode(checksum[0].Value())+")",
) )
} }
return return
}, },
).Flag(
&flagGentoo,
"gentoo", command.StringFlag(""),
"Bootstrap from a Gentoo stage3 tarball",
).Flag(
&flagChecksum,
"checksum", command.StringFlag(""),
"Checksum of Gentoo stage3 tarball",
) )
} }
c.NewCommand(
"all",
command.UsageInternal,
func([]string) error {
all := rosa.Native().CollectAll()
a := make(pkg.Collect, len(all))
for i, h := range all {
_, a[i] = rosa.Native().Std().Load(h)
}
return cm.Do(func(cache *pkg.Cache) (err error) {
_, _, err = cache.Cure(&a)
if pkg.IsCollected(err) {
err = nil
}
return
})
},
)
{ {
var ( var (
flagDump string flagDump string
@@ -710,13 +648,9 @@ func main() {
flagNoReply bool flagNoReply bool
flagFaults bool flagFaults bool
flagPop bool flagPop bool
flagRebuild bool
flagBoot bool flagBoot bool
flagStd bool flagStd bool
flagGentoo string
flagChecksum string
) )
c.NewCommand( c.NewCommand(
"cure", "cure",
@@ -733,18 +667,6 @@ func main() {
t -= 1 t -= 1
} }
if flagGentoo != "" {
var checksum pkg.Checksum
if len(flagChecksum) != 0 {
if err := pkg.Decode(&checksum, flagChecksum); err != nil {
return err
}
}
rosa.Native().SetGentooStage3(flagGentoo, checksum)
t -= 3 // magic number to discourage misuse
}
_, a := rosa.Native().New(t).Load(rosa.ArtifactH(unique.Make(args[0]))) _, a := rosa.Native().New(t).Load(rosa.ArtifactH(unique.Make(args[0])))
if a == nil { if a == nil {
return fmt.Errorf("unknown artifact %q", args[0]) return fmt.Errorf("unknown artifact %q", args[0])
@@ -754,17 +676,13 @@ func main() {
default: default:
var pathname *check.Absolute var pathname *check.Absolute
err := cm.Do(func(cache *pkg.Cache) (err error) { err := cm.Do(func(cache *pkg.Cache) (err error) {
if flagRebuild {
pathname, _, err = cache.CureNew(a)
} else {
pathname, _, err = cache.Cure(a) pathname, _, err = cache.Cure(a)
}
return return
}) })
if err != nil { if err != nil {
return err return err
} }
fmt.Println(pathname) log.Println(pathname)
if flagExport != "" { if flagExport != "" {
msg.Verbosef("exporting %s to %s...", args[0], flagExport) msg.Verbosef("exporting %s to %s...", args[0], flagExport)
@@ -813,7 +731,7 @@ func main() {
ctx, ctx,
a, a,
"", true, os.Stdin, os.Stdout, os.Stderr, "", true, os.Stdin, os.Stdout, os.Stderr,
rosa.AbsSystem.Append("bin", "sh"), rosa.AbsSystem.Append("bin", "mksh"),
"sh", "sh",
) )
}) })
@@ -825,7 +743,7 @@ func main() {
} }
pathname, err := cureRemote(ctx, &addr, a, flags) pathname, err := cureRemote(ctx, &addr, a, flags)
if !flagNoReply && err == nil { if !flagNoReply && err == nil {
fmt.Println(pathname) log.Println(pathname)
} }
if errors.Is(err, context.Canceled) { if errors.Is(err, context.Canceled) {
@@ -919,97 +837,9 @@ func main() {
&flagPop, &flagPop,
"pop", command.BoolFlag(false), "pop", command.BoolFlag(false),
"Display and destroy the most recent fault entry", "Display and destroy the most recent fault entry",
).Flag(
&flagRebuild,
"rebuild", command.BoolFlag(false),
"Always enter the artifact implementation",
).Flag(
&flagGentoo,
"gentoo", command.StringFlag(""),
"Bootstrap from a Gentoo stage3 tarball",
).Flag(
&flagChecksum,
"checksum", command.StringFlag(""),
"Checksum of Gentoo stage3 tarball",
) )
} }
c.NewCommand(
"validate",
"Repeatedly cure named artifact to validate determinism",
func(args []string) error {
stage := rosa.Std
if len(args) == 3 {
switch args[0] {
case "boot":
stage -= 2
case "std":
stage -= 1
default:
return fmt.Errorf("invalid stage %q", args[0])
}
args = args[1:]
}
if len(args) != 2 {
return errors.New("validate requires 2 arguments")
}
n, err := strconv.Atoi(args[0])
if err != nil {
return fmt.Errorf("invalid passes %q", args[0])
}
if n < 2 {
return errors.New("validate must rebuild at least twice")
}
_, a := rosa.Native().New(stage).Load(rosa.ArtifactH(unique.Make(args[1])))
if a == nil {
return fmt.Errorf("unknown artifact %q", args[1])
}
if !a.IsExclusive() && cm.attr.Jobs < 1 {
cm.attr.Jobs = runtime.NumCPU()/n + 1
}
res := make([]unique.Handle[pkg.Checksum], n)
if err = cm.Do(func(cache *pkg.Cache) error {
errs := make([]error, n)
var wg sync.WaitGroup
for i := range n {
wg.Go(func() {
_, res[i], errs[i] = cache.CureNew(a)
})
}
wg.Wait()
return errors.Join(errs...)
}); err != nil {
return err
}
for i := range n - 1 {
if res[i] == res[i+1] {
continue
}
var buf strings.Builder
buf.WriteString("nondeterminism:")
for _, o := range res {
buf.WriteString("\n\t")
buf.WriteString(filepath.Join(
cm.base,
"checksum",
pkg.Encode(o.Value()),
))
}
return errors.New(buf.String())
}
return nil
},
)
cleanC := c.New("clean", "Remove unused entries from the cache") cleanC := c.New("clean", "Remove unused entries from the cache")
cleanC.NewCommand( cleanC.NewCommand(
"fault", "fault",
+2 -3
View File
@@ -13,7 +13,6 @@ func New(output io.Writer, logf LogFunc, name string, early HandlerFunc) Command
return c return c
} }
// newNode initialises a subcommand tree and returns its address.
func newNode(output io.Writer, logf LogFunc, name, usage string) *node { func newNode(output io.Writer, logf LogFunc, name, usage string) *node {
n := &node{ n := &node{
name: name, usage: usage, name: name, usage: usage,
@@ -23,8 +22,8 @@ func newNode(output io.Writer, logf LogFunc, name, usage string) *node {
n.set.SetOutput(output) n.set.SetOutput(output)
n.set.Usage = func() { n.set.Usage = func() {
_ = n.writeHelp() _ = n.writeHelp()
if len(n.suffix) > 0 { if n.suffix.Len() > 0 {
_, _ = fmt.Fprintln(output, "flags:") _, _ = fmt.Fprintln(output, "Flags:")
n.set.PrintDefaults() n.set.PrintDefaults()
_, _ = fmt.Fprintln(output) _, _ = fmt.Fprintln(output)
} }
+4 -16
View File
@@ -18,14 +18,8 @@ func TestBuild(t *testing.T) {
t.Run("direct zero length", func(t *testing.T) { t.Run("direct zero length", func(t *testing.T) {
wantPanic := "invalid subcommand" wantPanic := "invalid subcommand"
t.Run("zero length name", func(t *testing.T) { t.Run("zero length name", func(t *testing.T) { defer checkRecover(t, "Command", wantPanic); c.Command("", "usage", stubHandler) })
defer checkRecover(t, "Command", wantPanic) t.Run("zero length usage", func(t *testing.T) { defer checkRecover(t, "Command", wantPanic); c.Command("name", "", stubHandler) })
c.Command("", "usage", stubHandler)
})
t.Run("zero length usage", func(t *testing.T) {
defer checkRecover(t, "Command", wantPanic)
c.Command("name", "", stubHandler)
})
}) })
t.Run("direct adopt unique names", func(t *testing.T) { t.Run("direct adopt unique names", func(t *testing.T) {
@@ -40,14 +34,8 @@ func TestBuild(t *testing.T) {
t.Run("zero length", func(t *testing.T) { t.Run("zero length", func(t *testing.T) {
wantPanic := "invalid subcommand tree" wantPanic := "invalid subcommand tree"
t.Run("zero length name", func(t *testing.T) { t.Run("zero length name", func(t *testing.T) { defer checkRecover(t, "New", wantPanic); c.New("", "usage") })
defer checkRecover(t, "New", wantPanic) t.Run("zero length usage", func(t *testing.T) { defer checkRecover(t, "New", wantPanic); c.New("name", "") })
c.New("", "usage")
})
t.Run("zero length usage", func(t *testing.T) {
defer checkRecover(t, "New", wantPanic)
c.New("name", "")
})
}) })
t.Run("direct adopt unique names", func(t *testing.T) { t.Run("direct adopt unique names", func(t *testing.T) {
+6 -26
View File
@@ -6,43 +6,36 @@ import (
"strings" "strings"
) )
// UsageInternal is a special usage string that hides the command from the // UsageInternal causes the command to be hidden from help text when set as the usage string.
// generated help message. const UsageInternal = "internal"
const UsageInternal = "\x00"
type ( type (
// HandlerFunc is called when matching a directly handled subcommand tree. // HandlerFunc is called when matching a directly handled subcommand tree.
HandlerFunc = func(args []string) error HandlerFunc = func(args []string) error
// LogFunc is the function signature of a printf function. The zero value // LogFunc is the function signature of a printf function.
// implies [log.Printf].
LogFunc = func(format string, a ...any) LogFunc = func(format string, a ...any)
// FlagDefiner is a deferred flag definer value, usually encapsulating the // FlagDefiner is a deferred flag definer value, usually encapsulating the default value.
// default value.
FlagDefiner interface { FlagDefiner interface {
// Define defines the flag in set. // Define defines the flag in set.
Define(b *strings.Builder, set *flag.FlagSet, p any, name, usage string) Define(b *strings.Builder, set *flag.FlagSet, p any, name, usage string)
} }
// A Flag is satisfied by command objects capable of receiving flags.
Flag[T any] interface { Flag[T any] interface {
// Flag defines a generic flag type in Node's flag set. // Flag defines a generic flag type in Node's flag set.
Flag(p any, name string, value FlagDefiner, usage string) T Flag(p any, name string, value FlagDefiner, usage string) T
} }
// A Command is the root of a command tree.
Command interface { Command interface {
Parse(arguments []string) error Parse(arguments []string) error
// MustParse determines exit outcomes for Parse errors and calls // MustParse determines exit outcomes for Parse errors
// handleError if [HandlerFunc] returns a non-nil error. // and calls handleError if [HandlerFunc] returns a non-nil error.
MustParse(arguments []string, handleError func(error)) MustParse(arguments []string, handleError func(error))
baseNode[Command] baseNode[Command]
} }
// A Node is a subcommand under a [Command].
Node baseNode[Node] Node baseNode[Node]
baseNode[T any] interface { baseNode[T any] interface {
@@ -60,16 +53,3 @@ type (
Flag[T] Flag[T]
} }
) )
// rootNode satisfies baseNode for [Command].
type rootNode struct{ *node }
func (r rootNode) Command(name, usage string, f HandlerFunc) Command {
r.node.Command(name, usage, f)
return r
}
func (r rootNode) Flag(p any, name string, value FlagDefiner, usage string) Command {
r.node.Flag(p, name, value, usage)
return r
}
+1 -7
View File
@@ -16,13 +16,7 @@ func (e FlagError) Is(target error) bool {
} }
func (n *node) Flag(p any, name string, value FlagDefiner, usage string) Node { func (n *node) Flag(p any, name string, value FlagDefiner, usage string) Node {
var buf strings.Builder value.Define(&n.suffix, n.set, p, name, usage)
value.Define(&buf, n.set, p, name, usage)
s := buf.String()
if len(s) > 0 && s[0] == ' ' {
s = s[1:]
}
n.suffix = append(n.suffix, s)
return n return n
} }
+3 -21
View File
@@ -1,7 +1,6 @@
package command package command
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@@ -14,31 +13,14 @@ var ErrHelp = errors.New("help requested")
func (n *node) PrintHelp() { _ = n.writeHelp() } func (n *node) PrintHelp() { _ = n.writeHelp() }
func (n *node) writeHelp() error { func (n *node) writeHelp() error {
prefix := strings.Join(append(n.prefix, n.name), " ")
var buf strings.Builder
offset := 7 + len(prefix)
line := offset + 14
w := bytes.Repeat([]byte{' '}, offset+1)
w[0] = '\n'
for _, flag := range n.suffix {
line += len(flag) + 1
if line >= 80 {
line = offset + len(flag) + 1
buf.Write(w)
}
buf.WriteByte(' ')
buf.WriteString(flag)
}
if _, err := fmt.Fprintf(n.out, if _, err := fmt.Fprintf(n.out,
"usage: %s [-h | --help]%s <command> [<args>]\n", "\nUsage:\t%s [-h | --help]%s COMMAND [OPTIONS]\n",
prefix, &buf, strings.Join(append(n.prefix, n.name), " "), &n.suffix,
); err != nil { ); err != nil {
return err return err
} }
if n.child != nil { if n.child != nil {
if _, err := fmt.Fprint(n.out, "\ncommands:\n"); err != nil { if _, err := fmt.Fprint(n.out, "\nCommands:\n"); err != nil {
return err return err
} }
} }
+2 -6
View File
@@ -3,9 +3,9 @@ package command
import ( import (
"flag" "flag"
"io" "io"
"strings"
) )
// A node represents a command.
type node struct { type node struct {
child, next *node child, next *node
name, usage string name, usage string
@@ -13,16 +13,13 @@ type node struct {
out io.Writer out io.Writer
logf LogFunc logf LogFunc
// Names of commands preceding node.
prefix []string prefix []string
// Short user-facing representations of flags received by node. suffix strings.Builder
suffix []string
f HandlerFunc f HandlerFunc
set *flag.FlagSet set *flag.FlagSet
} }
// adopt adds v as the last child of n.
func (n *node) adopt(v *node) bool { func (n *node) adopt(v *node) bool {
if n.child != nil { if n.child != nil {
return n.child.append(v) return n.child.append(v)
@@ -31,7 +28,6 @@ func (n *node) adopt(v *node) bool {
return true return true
} }
// append adds v as the last sibling of n.
func (n *node) append(v *node) bool { func (n *node) append(v *node) bool {
if n.name == v.name { if n.name == v.name {
return false return false
+22 -18
View File
@@ -70,7 +70,7 @@ func TestParse(t *testing.T) {
"d=0 out of order string flag", "d=0 out of order string flag",
buildTestCommand, buildTestCommand,
[]string{"string", "--string", "64d3b4b7b21788585845060e2199a78f"}, []string{"string", "--string", "64d3b4b7b21788585845060e2199a78f"},
"flag provided but not defined: -string\nusage: test string [-h | --help] <command> [<args>]\n\n", "", "flag provided but not defined: -string\n\nUsage:\ttest string [-h | --help] COMMAND [OPTIONS]\n\n", "",
errors.New("flag provided but not defined: -string"), errors.New("flag provided but not defined: -string"),
}, },
{ {
@@ -120,7 +120,7 @@ func TestParse(t *testing.T) {
"d=1 empty sub help", "d=1 empty sub help",
buildTestCommand, buildTestCommand,
[]string{"empty", "-h"}, []string{"empty", "-h"},
"usage: test empty [-h | --help] <command> [<args>]\n\n", "", flag.ErrHelp, "\nUsage:\ttest empty [-h | --help] COMMAND [OPTIONS]\n\n", "", flag.ErrHelp,
}, },
{ {
"d=1 no match", "d=1 no match",
@@ -151,10 +151,10 @@ func TestParse(t *testing.T) {
"d=0 help", "d=0 help",
buildTestCommand, buildTestCommand,
[]string{}, []string{},
`usage: test [-h | --help] [-v] [--fail] [--string <value>] [--int <int>] `
[--repeat <value>] <command> [<args>] Usage: test [-h | --help] [-v] [--fail] [--string <value>] [--int <int>] [--repeat <value>] COMMAND [OPTIONS]
commands: Commands:
error return an error error return an error
print wraps Fprint print wraps Fprint
string print string passed by flag string print string passed by flag
@@ -171,10 +171,10 @@ commands:
"d=0 help flag", "d=0 help flag",
buildTestCommand, buildTestCommand,
[]string{"-h"}, []string{"-h"},
`usage: test [-h | --help] [-v] [--fail] [--string <value>] [--int <int>] `
[--repeat <value>] <command> [<args>] Usage: test [-h | --help] [-v] [--fail] [--string <value>] [--int <int>] [--repeat <value>] COMMAND [OPTIONS]
commands: Commands:
error return an error error return an error
print wraps Fprint print wraps Fprint
string print string passed by flag string print string passed by flag
@@ -185,7 +185,7 @@ commands:
succeed this command succeeds succeed this command succeeds
deep top level of command tree with various levels deep top level of command tree with various levels
flags: Flags:
-fail -fail
fail early fail early
-int int -int int
@@ -203,9 +203,10 @@ flags:
"d=1 help", "d=1 help",
buildTestCommand, buildTestCommand,
[]string{"join"}, []string{"join"},
`usage: test join [-h | --help] <command> [<args>] `
Usage: test join [-h | --help] COMMAND [OPTIONS]
commands: Commands:
out write result to wout out write result to wout
log log result to wlog log log result to wlog
@@ -215,9 +216,10 @@ commands:
"d=1 help flag", "d=1 help flag",
buildTestCommand, buildTestCommand,
[]string{"join", "-h"}, []string{"join", "-h"},
`usage: test join [-h | --help] <command> [<args>] `
Usage: test join [-h | --help] COMMAND [OPTIONS]
commands: Commands:
out write result to wout out write result to wout
log log result to wlog log log result to wlog
@@ -228,9 +230,10 @@ commands:
"d=2 help", "d=2 help",
buildTestCommand, buildTestCommand,
[]string{"deep", "d=2"}, []string{"deep", "d=2"},
`usage: test deep d=2 [-h | --help] <command> [<args>] `
Usage: test deep d=2 [-h | --help] COMMAND [OPTIONS]
commands: Commands:
d=3 relative third level d=3 relative third level
`, "", command.ErrHelp, `, "", command.ErrHelp,
@@ -239,9 +242,10 @@ commands:
"d=2 help flag", "d=2 help flag",
buildTestCommand, buildTestCommand,
[]string{"deep", "d=2", "-h"}, []string{"deep", "d=2", "-h"},
`usage: test deep d=2 [-h | --help] <command> [<args>] `
Usage: test deep d=2 [-h | --help] COMMAND [OPTIONS]
commands: Commands:
d=3 relative third level d=3 relative third level
`, "", flag.ErrHelp, `, "", flag.ErrHelp,
@@ -257,7 +261,7 @@ commands:
t.Errorf("Parse: error = %v; wantErr %v", err, tc.wantErr) t.Errorf("Parse: error = %v; wantErr %v", err, tc.wantErr)
} }
if got := wout.String(); got != tc.want { if got := wout.String(); got != tc.want {
t.Errorf("Parse:\n%s\nwant\n%s", got, tc.want) t.Errorf("Parse: %s want %s", got, tc.want)
} }
if gotLog := wlog.String(); gotLog != tc.wantLog { if gotLog := wlog.String(); gotLog != tc.wantLog {
t.Errorf("Parse: log = %s wantLog %s", gotLog, tc.wantLog) t.Errorf("Parse: log = %s wantLog %s", gotLog, tc.wantLog)
+14
View File
@@ -0,0 +1,14 @@
package command
// the top level node wants [Command] returned for its builder methods
type rootNode struct{ *node }
func (r rootNode) Command(name, usage string, f HandlerFunc) Command {
r.node.Command(name, usage, f)
return r
}
func (r rootNode) Flag(p any, name string, value FlagDefiner, usage string) Command {
r.node.Flag(p, name, value, usage)
return r
}
View File
+11 -11
View File
@@ -43,19 +43,19 @@
; ;
in in
{ {
hakurei = callPackage ./. { inherit system self; }; hakurei = callPackage ./test { inherit system self; };
race = callPackage ./. { race = callPackage ./test {
inherit system self; inherit system self;
withRace = true; withRace = true;
}; };
sandbox = callPackage ./sandbox { inherit self; }; sandbox = callPackage ./test/sandbox { inherit self; };
sandbox-race = callPackage ./sandbox { sandbox-race = callPackage ./test/sandbox {
inherit self; inherit self;
withRace = true; withRace = true;
}; };
sharefs = callPackage ./sharefs { inherit system self; }; sharefs = callPackage ./cmd/sharefs/test { inherit system self; };
formatting = runCommandLocal "check-formatting" { nativeBuildInputs = [ nixfmt ]; } '' formatting = runCommandLocal "check-formatting" { nativeBuildInputs = [ nixfmt ]; } ''
cd ${./.} cd ${./.}
@@ -116,7 +116,7 @@
nettools nettools
; ;
}; };
hsu = pkgs.callPackage ./hsu.nix { inherit (self.packages.${system}) hakurei; }; hsu = pkgs.callPackage ./cmd/hsu/package.nix { inherit (self.packages.${system}) hakurei; };
sharefs = pkgs.linkFarm "sharefs" { sharefs = pkgs.linkFarm "sharefs" {
"bin/sharefs" = "${hakurei}/libexec/sharefs"; "bin/sharefs" = "${hakurei}/libexec/sharefs";
"bin/mount.fuse.sharefs" = "${hakurei}/libexec/sharefs"; "bin/mount.fuse.sharefs" = "${hakurei}/libexec/sharefs";
@@ -190,11 +190,11 @@
}; };
} }
./interactive/configuration.nix ./test/interactive/configuration.nix
./interactive/vm.nix ./test/interactive/vm.nix
./interactive/hakurei.nix ./test/interactive/hakurei.nix
./interactive/trace.nix ./test/interactive/trace.nix
./interactive/raceattr.nix ./test/interactive/raceattr.nix
self.nixosModules.hakurei self.nixosModules.hakurei
home-manager.nixosModules.home-manager home-manager.nixosModules.home-manager
+2 -2
View File
@@ -303,8 +303,8 @@ func init() {
}) })
} }
// Inputs returns a slice containing the backing file. // Dependencies returns a slice containing the backing file.
func (a archiveArtifact) Inputs() []Artifact { func (a archiveArtifact) Dependencies() []Artifact {
return []Artifact{a.f} return []Artifact{a.f}
} }
+2 -2
View File
@@ -186,7 +186,7 @@ func TestArchiveArtifact(t *testing.T) {
cureMany(t, c, []cureStep{ cureMany(t, c, []cureStep{
{"sample", pkg.NewArchive( {"sample", pkg.NewArchive(
pkg.NewFile("", buf.Bytes()), pkg.NewFile("", buf.Bytes()),
), ignorePathname, expectsFS(want), pkg.WNew, nil}, ), ignorePathname, expectsFS(want), nil},
}) })
}, expectsFS{ }, expectsFS{
".": {Mode: fs.ModeDir | 0700}, ".": {Mode: fs.ModeDir | 0700},
@@ -202,7 +202,7 @@ func TestArchiveArtifact(t *testing.T) {
"checksum/CBPcoVHuVUTVRCMbRl8J30RSSzm_tyfuXaZ-HlZsanY1sY50meOVmgaWDrGKbx9F/devices/uevent": {Mode: 0600, Data: []byte("add")}, "checksum/CBPcoVHuVUTVRCMbRl8J30RSSzm_tyfuXaZ-HlZsanY1sY50meOVmgaWDrGKbx9F/devices/uevent": {Mode: 0600, Data: []byte("add")},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/-zXKfphyWM2Ko7VMVURaEMSuixNokFSe0xFbnwR2RtRtvMrcPGWalV0LIn45PXTY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CBPcoVHuVUTVRCMbRl8J30RSSzm_tyfuXaZ-HlZsanY1sY50meOVmgaWDrGKbx9F")}, "identifier/3oYyAbRJ_we7AgWo1BRcRcnxXFk3mAQ0Qui2nGQMi8GIJNJQtvUC6P2IeoA5mbjD": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CBPcoVHuVUTVRCMbRl8J30RSSzm_tyfuXaZ-HlZsanY1sY50meOVmgaWDrGKbx9F")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
"work": {Mode: fs.ModeDir | 0700}, "work": {Mode: fs.ModeDir | 0700},
+3 -12
View File
@@ -73,10 +73,7 @@ func (c *Cache) Clean(dry, inputs bool, keep ...Artifact) (
continue continue
} }
c.msg.Verbosef( c.msg.Verbosef("arranging for destruction of %s...", name)
"arranging for destruction of %s%s%s...",
c.sgrIdent, name, c.sgrRes,
)
idents = append(idents, identPair{id, name}) idents = append(idents, identPair{id, name})
} }
@@ -105,10 +102,7 @@ func (c *Cache) Clean(dry, inputs bool, keep ...Artifact) (
if err = c.parent.Err(); err != nil { if err = c.parent.Err(); err != nil {
return destroyedIdents, destroyedChecksums, err return destroyedIdents, destroyedChecksums, err
} }
c.msg.Verbosef( c.msg.Verbosef("destroying checksum %s...", name)
"destroying checksum %s%s%s...",
c.sgrIdent, name, c.sgrRes,
)
if !dry { if !dry {
if err = errors.Join(removeAll(c.base.Append( if err = errors.Join(removeAll(c.base.Append(
dirChecksum, dirChecksum,
@@ -136,10 +130,7 @@ func (c *Cache) Clean(dry, inputs bool, keep ...Artifact) (
continue continue
} }
c.msg.Verbosef( c.msg.Verbosef("destroying substitute %s...", name)
"destroying substitute %s%s%s...",
c.sgrIdent, name, c.sgrRes,
)
if !dry { if !dry {
if err = os.Remove(c.base.Append( if err = os.Remove(c.base.Append(
dirStatus, dirStatus,
+12 -12
View File
@@ -48,7 +48,7 @@ func TestClean(t *testing.T) {
"checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400}, "checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/h6qx7m0vsyhO6oLEJgY2rKa1c6RZuIZ4QXxZSh__NZJ9VU0MIj5bFB-KuaPcsfpE": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")}, "identifier/pPRjw2XYgjB5k8dYedwxTBMgHh4_v2JM_G2Vd-skQbAGOOgPsl3CGSUbEF7om_MO": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
"lock": {Mode: 0644}, "lock": {Mode: 0644},
"variant": {Mode: 0400}, "variant": {Mode: 0400},
@@ -69,7 +69,7 @@ func TestClean(t *testing.T) {
"checksum/KgZ-FjbGuU-XP2QEHInpgv-2Zn0cTH5NqFMgTU0XrSdKmSwyC-3baVs1BMCP5spk": {Mode: 0400, Data: []byte("\xfd")}, "checksum/KgZ-FjbGuU-XP2QEHInpgv-2Zn0cTH5NqFMgTU0XrSdKmSwyC-3baVs1BMCP5spk": {Mode: 0400, Data: []byte("\xfd")},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/tkrOYhQfTRaArk4nNYNjTmHQUmuJMVIoDeAKIQBm3I4uuMimBKXKMIY2gGZqwQg4": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/KgZ-FjbGuU-XP2QEHInpgv-2Zn0cTH5NqFMgTU0XrSdKmSwyC-3baVs1BMCP5spk")}, "identifier/FMwSBYw22KqM8jZryfY2ChHXpLuVDdWYyNOYdHvIVYk8ujY6UnGRm5brr2sTTfpD": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/KgZ-FjbGuU-XP2QEHInpgv-2Zn0cTH5NqFMgTU0XrSdKmSwyC-3baVs1BMCP5spk")},
"lock": {Mode: 0644}, "lock": {Mode: 0644},
"variant": {Mode: 0400}, "variant": {Mode: 0400},
@@ -122,16 +122,16 @@ func TestClean(t *testing.T) {
"checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE/result": {Mode: 0444}, "checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE/result": {Mode: 0444},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/KpxTUsUfGbsX6C6HU2j9cd9He3_tt2o2m2MgH4m0zial8lmoqSWY-Gc3KrS0k-UK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv")}, "identifier/Ef8KX6s_rS_nLgze0rj90zKyrGAvOnzyU0DL7nrYQWEG_f4a9pmUKI6HBEMD8AE8": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv")},
"identifier/xxQ43VMIS1WHMSEFnUdBNd6pulbtgiTorP_mtJpEsZENYhGBiDEL6Y2D8yqn0Sra": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")}, "identifier/xoIGLemzLF227e-w_AJcf_1Sgqh2gs3KFgqvOIWUQE-9P_y2vHBMBytL4GRGQqTb": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
"lock": {Mode: 0644}, "lock": {Mode: 0644},
"variant": {Mode: 0400}, "variant": {Mode: 0400},
"status": {Mode: fs.ModeDir | 0700}, "status": {Mode: fs.ModeDir | 0700},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
"substitute/dP-6_wIDRRouOaOF-nkBy-IaUbLdYHbOUTrptu7j_qfW01mnHSjYJ0oykUmvUd2x": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")}, "substitute/4bjS-QjGcSV4nth-W6Vg3-wolKmKgiq4Ld2oRIWcOfy6Wi41XXLAWPoo8FcDx6BH": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
"substitute/td1tf1kb3z8iUFg2k1xlHeTTEz3xR4e77WSWdv2JhjmZyY6iuvodhdT8BblDOi0E": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")}, "substitute/dzO8FEY9lu4hwRT6BfRZOX-uYGsC_5XH4jEJ7sJyThcmG9J_w1ArOAaUCGfL8wAM": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
"fault": {Mode: fs.ModeDir | 0700}, "fault": {Mode: fs.ModeDir | 0700},
"work": {Mode: fs.ModeDir | 0700}, "work": {Mode: fs.ModeDir | 0700},
@@ -199,18 +199,18 @@ func TestClean(t *testing.T) {
"checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv": {Mode: 0400, Data: []byte("kept")}, "checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv": {Mode: 0400, Data: []byte("kept")},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/KpxTUsUfGbsX6C6HU2j9cd9He3_tt2o2m2MgH4m0zial8lmoqSWY-Gc3KrS0k-UK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv")}, "identifier/Ef8KX6s_rS_nLgze0rj90zKyrGAvOnzyU0DL7nrYQWEG_f4a9pmUKI6HBEMD8AE8": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv")},
"identifier/xxQ43VMIS1WHMSEFnUdBNd6pulbtgiTorP_mtJpEsZENYhGBiDEL6Y2D8yqn0Sra": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CyDnDvF-LaeGPcSW70tPosNCoclByWkTjznUUF1DcgzlIwkN9yzz1ZFME1TlPj6W")}, "identifier/xoIGLemzLF227e-w_AJcf_1Sgqh2gs3KFgqvOIWUQE-9P_y2vHBMBytL4GRGQqTb": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CyDnDvF-LaeGPcSW70tPosNCoclByWkTjznUUF1DcgzlIwkN9yzz1ZFME1TlPj6W")},
"lock": {Mode: 0644}, "lock": {Mode: 0644},
"variant": {Mode: 0400}, "variant": {Mode: 0400},
"status": {Mode: fs.ModeDir | 0700}, "status": {Mode: fs.ModeDir | 0700},
"status/xxQ43VMIS1WHMSEFnUdBNd6pulbtgiTorP_mtJpEsZENYhGBiDEL6Y2D8yqn0Sra": {Mode: 0400, Data: []byte(statusHeader + "kept")}, "status/4bjS-QjGcSV4nth-W6Vg3-wolKmKgiq4Ld2oRIWcOfy6Wi41XXLAWPoo8FcDx6BH": {Mode: 0400, Data: []byte(statusHeader + "kept")},
"status/td1tf1kb3z8iUFg2k1xlHeTTEz3xR4e77WSWdv2JhjmZyY6iuvodhdT8BblDOi0E": {Mode: 0400, Data: []byte(statusHeader + "kept")}, "status/xoIGLemzLF227e-w_AJcf_1Sgqh2gs3KFgqvOIWUQE-9P_y2vHBMBytL4GRGQqTb": {Mode: 0400, Data: []byte(statusHeader + "kept")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
"substitute/td1tf1kb3z8iUFg2k1xlHeTTEz3xR4e77WSWdv2JhjmZyY6iuvodhdT8BblDOi0E": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CyDnDvF-LaeGPcSW70tPosNCoclByWkTjznUUF1DcgzlIwkN9yzz1ZFME1TlPj6W")}, "substitute/4bjS-QjGcSV4nth-W6Vg3-wolKmKgiq4Ld2oRIWcOfy6Wi41XXLAWPoo8FcDx6BH": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CyDnDvF-LaeGPcSW70tPosNCoclByWkTjznUUF1DcgzlIwkN9yzz1ZFME1TlPj6W")},
"fault": {Mode: fs.ModeDir | 0700}, "fault": {Mode: fs.ModeDir | 0700},
"work": {Mode: fs.ModeDir | 0700}, "work": {Mode: fs.ModeDir | 0700},
@@ -238,7 +238,7 @@ func TestClean(t *testing.T) {
base := makeBase(t) base := makeBase(t)
msg := message.New(log.New(os.Stderr, "clean: ", 0)) msg := message.New(log.New(os.Stderr, "clean: ", 0))
msg.SwapVerbose(testing.Verbose()) msg.SwapVerbose(testing.Verbose())
c, err := pkg.Open(t.Context(), msg, base, nil) c, err := pkg.Open(t.Context(), msg, 0, 0, 0, base)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+2 -29
View File
@@ -6,9 +6,6 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"hakurei.app/internal/xz"
"hakurei.app/internal/zstd"
) )
const ( const (
@@ -16,10 +13,6 @@ const (
Gzip = iota Gzip = iota
// Bzip2 denotes a stream compressed via [bzip2]. // Bzip2 denotes a stream compressed via [bzip2].
Bzip2 Bzip2
// Zstd denotes a stream compressed via [zstd].
Zstd
// XZ denotes a stream compressed via [xz].
XZ
) )
// A decompressArtifact is a [FileArtifact] decompressing a backing // A decompressArtifact is a [FileArtifact] decompressing a backing
@@ -73,8 +66,8 @@ func init() {
}) })
} }
// Inputs returns a slice containing the backing file. // Dependencies returns a slice containing the backing file.
func (a *decompressArtifact) Inputs() []Artifact { func (a *decompressArtifact) Dependencies() []Artifact {
return []Artifact{a.f} return []Artifact{a.f}
} }
@@ -98,9 +91,6 @@ func (c compoundCloser) Close() error {
return err return err
} }
// IsExecutable returns false.
func (*decompressArtifact) IsExecutable() bool { return false }
// Cure returns a decompressor [io.ReadCloser]. // Cure returns a decompressor [io.ReadCloser].
func (a *decompressArtifact) Cure(r *RContext) (io.ReadCloser, error) { func (a *decompressArtifact) Cure(r *RContext) (io.ReadCloser, error) {
sr, err := r.Open(a.f) sr, err := r.Open(a.f)
@@ -124,23 +114,6 @@ func (a *decompressArtifact) Cure(r *RContext) (io.ReadCloser, error) {
io.Closer io.Closer
}{bzip2.NewReader(br), br}, nil }{bzip2.NewReader(br), br}, nil
case Zstd:
return struct {
io.Reader
io.Closer
}{zstd.NewReader(br), br}, nil
case XZ:
var _dr io.Reader
if _dr, err = xz.NewReader(br, 0); err != nil {
_ = br.Close()
return nil, err
}
return struct {
io.Reader
io.Closer
}{_dr, br}, nil
default: default:
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
+3 -3
View File
@@ -43,7 +43,7 @@ func TestDecompress(t *testing.T) {
&client, &client,
"file:///testdata", "file:///testdata",
pkg.Checksum{0xfd}, pkg.Checksum{0xfd},
), pkg.Gzip), nil, nil, pkg.WNew, &pkg.ChecksumMismatchError{ ), pkg.Gzip), nil, nil, &pkg.ChecksumMismatchError{
Got: testdataChecksum, Got: testdataChecksum,
Want: pkg.Checksum{0xfd}, Want: pkg.Checksum{0xfd},
}}, }},
@@ -52,7 +52,7 @@ func TestDecompress(t *testing.T) {
&client, &client,
"file:///testdata", "file:///testdata",
testdataChecksum, testdataChecksum,
), pkg.Gzip), ignorePathname, expectsChecksum(sha512.Sum384([]byte{0})), pkg.WNew, nil}, ), pkg.Gzip), ignorePathname, expectsChecksum(sha512.Sum384([]byte{0})), nil},
}) })
}, expectsFS{ }, expectsFS{
".": {Mode: fs.ModeDir | 0700}, ".": {Mode: fs.ModeDir | 0700},
@@ -61,7 +61,7 @@ func TestDecompress(t *testing.T) {
"checksum/" + pkg.Encode(sha512.Sum384([]byte{0})): {Mode: 0400, Data: []byte{0}}, "checksum/" + pkg.Encode(sha512.Sum384([]byte{0})): {Mode: 0400, Data: []byte{0}},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/mae-iR_9LFT_BOEJ2IRzKsAlI754SalMjjt95fZeMd3gEyAzsrvuOXnXN0bFZJi4": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")}, "identifier/QpjkahDrz7pz-tv0eAGNXR6x9NAtTjWCK5Hr7G1cIZj9rT7bLYJWUQeLD4wamAlF": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
"work": {Mode: fs.ModeDir | 0700}, "work": {Mode: fs.ModeDir | 0700},
+16 -37
View File
@@ -30,16 +30,10 @@ import (
// AbsWork is the container pathname [TContext.GetWorkDir] is mounted on. // AbsWork is the container pathname [TContext.GetWorkDir] is mounted on.
var AbsWork = fhs.AbsRoot.Append("work/") var AbsWork = fhs.AbsRoot.Append("work/")
const ( // EnvJobs is the name of the environment variable holding a decimal
// EnvJobs is the name of the environment variable holding a decimal // representation of the preferred job count. Its value must not affect cure
// representation of the preferred job count. Its value must not affect cure // outcome.
// outcome. const EnvJobs = "CURE_JOBS"
EnvJobs = "CURE_JOBS"
// EnvLoad is the name of the environment variable holding a decimal
// representation of the preferred loadavg target. Its value must not affect
// cure outcome.
EnvLoad = "CURE_LOAD"
)
// ExecPath is a slice of [Artifact] and the [check.Absolute] pathname to make // ExecPath is a slice of [Artifact] and the [check.Absolute] pathname to make
// it available at under in the container. // it available at under in the container.
@@ -413,9 +407,9 @@ func init() {
func(r *IRReader) Artifact { return readExecArtifact(r, true) }) func(r *IRReader) Artifact { return readExecArtifact(r, true) })
} }
// Inputs returns a slice of all artifacts collected from caller-supplied // Dependencies returns a slice of all artifacts collected from caller-supplied
// [ExecPath]. // [ExecPath].
func (a *execArtifact) Inputs() []Artifact { func (a *execArtifact) Dependencies() []Artifact {
artifacts := make([][]Artifact, 0, len(a.paths)) artifacts := make([][]Artifact, 0, len(a.paths))
for _, p := range a.paths { for _, p := range a.paths {
artifacts = append(artifacts, p.A) artifacts = append(artifacts, p.A)
@@ -444,7 +438,7 @@ func scanVerbose(
msg message.Msg, msg message.Msg,
cancel context.CancelFunc, cancel context.CancelFunc,
done chan<- struct{}, done chan<- struct{},
prefix, suffix string, prefix string,
r io.Reader, r io.Reader,
) { ) {
defer close(done) defer close(done)
@@ -454,7 +448,7 @@ func scanVerbose(
bufio.MaxScanTokenSize<<12, bufio.MaxScanTokenSize<<12,
) )
for s.Scan() { for s.Scan() {
msg.Verbose(prefix, s.Text()+suffix) msg.Verbose(prefix, s.Text())
} }
if err := s.Err(); err != nil && !errors.Is(err, os.ErrClosed) { if err := s.Err(); err != nil && !errors.Is(err, os.ErrClosed) {
cancel() cancel()
@@ -477,7 +471,7 @@ const SeccompPresets = std.PresetStrict &
func (a *execArtifact) makeContainer( func (a *execArtifact) makeContainer(
ctx context.Context, ctx context.Context,
msg message.Msg, msg message.Msg,
flags, jobs, load int, flags, jobs int,
hostNet bool, hostNet bool,
temp, work *check.Absolute, temp, work *check.Absolute,
getArtifact GetArtifactFunc, getArtifact GetArtifactFunc,
@@ -514,10 +508,7 @@ func (a *execArtifact) makeContainer(
z.Quiet = flags&CSuppressInit != 0 z.Quiet = flags&CSuppressInit != 0
z.Uid, z.Gid = (1<<10)-1, (1<<10)-1 z.Uid, z.Gid = (1<<10)-1, (1<<10)-1
z.Dir, z.Path, z.Args = a.dir, a.path, a.args z.Dir, z.Path, z.Args = a.dir, a.path, a.args
z.Env = slices.Concat(a.env, []string{ z.Env = slices.Concat(a.env, []string{EnvJobs + "=" + strconv.Itoa(jobs)})
EnvJobs + "=" + strconv.Itoa(jobs),
EnvLoad + "=" + strconv.Itoa(load),
})
z.Grow(len(a.paths) + 4) z.Grow(len(a.paths) + 4)
if a.arch != runtime.GOARCH { if a.arch != runtime.GOARCH {
@@ -626,7 +617,7 @@ func (c *Cache) EnterExec(
return ErrNotExec return ErrNotExec
} }
deps := Collect(a.Inputs()) deps := Collect(a.Dependencies())
if _, _, err = c.Cure(&deps); err == nil { if _, _, err = c.Cure(&deps); err == nil {
return errors.New("unreachable") return errors.New("unreachable")
} else if !IsCollected(err) { } else if !IsCollected(err) {
@@ -658,9 +649,8 @@ func (c *Cache) EnterExec(
var z *container.Container var z *container.Container
z, err = e.makeContainer( z, err = e.makeContainer(
ctx, c.msg, ctx, c.msg,
c.attr.Flags, c.flags,
c.attr.Jobs, c.jobs,
c.attr.Load,
hostNet, hostNet,
temp, work, temp, work,
func(a Artifact) (*check.Absolute, unique.Handle[Checksum]) { func(a Artifact) (*check.Absolute, unique.Handle[Checksum]) {
@@ -703,7 +693,7 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
msg := f.GetMessage() msg := f.GetMessage()
var z *container.Container var z *container.Container
if z, err = a.makeContainer( if z, err = a.makeContainer(
ctx, msg, f.cache.attr.Flags, f.GetJobs(), f.GetLoad(), hostNet, ctx, msg, f.cache.flags, f.GetJobs(), hostNet,
f.GetTempDir(), f.GetWorkDir(), f.GetTempDir(), f.GetWorkDir(),
f.GetArtifact, f.GetArtifact,
f.cache.Ident, f.cache.Ident,
@@ -728,25 +718,14 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
brStdout, brStderr := f.cache.getReader(stdout), f.cache.getReader(stderr) brStdout, brStderr := f.cache.getReader(stdout), f.cache.getReader(stderr)
stdoutDone, stderrDone := make(chan struct{}), make(chan struct{}) stdoutDone, stderrDone := make(chan struct{}), make(chan struct{})
var suffix, prefixO, prefixE string
if f.cache.attr.Flags&CColourOutput != 0 {
suffix = "\x1b[0m"
prefixO = "\x1b[1;37m(" + a.name + ")\x1b[0m"
prefixE = "\x1b[1;97m(" + a.name + ")\x1b[0m"
} else {
prefixO = "(" + a.name + ":1)"
prefixE = "(" + a.name + ":2)"
}
go scanVerbose( go scanVerbose(
msg, cancel, stdoutDone, msg, cancel, stdoutDone,
prefixO, suffix, "("+a.name+":1)",
io.TeeReader(brStdout, status), io.TeeReader(brStdout, status),
) )
go scanVerbose( go scanVerbose(
msg, cancel, stderrDone, msg, cancel, stderrDone,
prefixE, suffix, "("+a.name+":2)",
io.TeeReader(brStderr, status), io.TeeReader(brStderr, status),
) )
defer func() { defer func() {
+21 -21
View File
@@ -84,7 +84,7 @@ func TestExec(t *testing.T) {
}, },
}), }),
pkg.MustPath("/opt", false, testtool), pkg.MustPath("/opt", false, testtool),
), ignorePathname, wantOffline, pkg.WNew, nil}, ), ignorePathname, wantOffline, nil},
{"substitution", pkg.NewExec( {"substitution", pkg.NewExec(
"exec-offline", "", new(wantOffline.hash()), 0, false, false, "exec-offline", "", new(wantOffline.hash()), 0, false, false,
@@ -108,7 +108,7 @@ func TestExec(t *testing.T) {
}, },
}), }),
pkg.MustPath("/opt", false, testtool), pkg.MustPath("/opt", false, testtool),
), ignorePathname, wantOffline, pkg.WSubstitute, nil}, ), ignorePathname, wantOffline, nil},
{"error passthrough", pkg.NewExec( {"error passthrough", pkg.NewExec(
"", "", nil, 0, false, true, "", "", nil, 0, false, true,
@@ -118,7 +118,7 @@ func TestExec(t *testing.T) {
[]string{"testtool"}, []string{"testtool"},
pkg.MustPath("/proc/nonexistent", false, failingArtifact), pkg.MustPath("/proc/nonexistent", false, failingArtifact),
), nil, nil, pkg.WNew, &pkg.DependencyCureError{ ), nil, nil, &pkg.DependencyCureError{
{ {
A: failingArtifact, A: failingArtifact,
Err: stub.UniqueError(0xcafe), Err: stub.UniqueError(0xcafe),
@@ -133,7 +133,7 @@ func TestExec(t *testing.T) {
[]string{"testtool"}, []string{"testtool"},
pkg.ExecPath{}, pkg.ExecPath{},
), nil, nil, pkg.WNew, pkg.ErrInvalidPaths}, ), nil, nil, pkg.ErrInvalidPaths},
}) })
// check init failure passthrough // check init failure passthrough
@@ -183,11 +183,11 @@ func TestExec(t *testing.T) {
"checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400, Data: []byte{}}, "checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400, Data: []byte{}},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/MEaM1v6MLRQ60JQOOKbfDzfJOdC3L8i30IgOpYbQdX68enwF0FN-P36_M4RX_0Vd": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/QwS7SmiatdqryQYgESdGw7Yw2PcpNf0vNfpvUA0t92BTlKiUjfCrXyMW17G2X77X": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")}, "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
"identifier/" + expected.Offline: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)}, "identifier/" + expected.Offline: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
"identifier/" + expected.OfflineS: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)}, "identifier/" + expected.OfflineS: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
"identifier/_OHzj_xdmfpaWfFN-_D9JpGpPlIjh84873E3Ui9xwv9PPPIt0BlzvH-cnPNeRsTl": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
@@ -225,7 +225,7 @@ func TestExec(t *testing.T) {
}, },
}), }),
pkg.MustPath("/opt", false, testtool), pkg.MustPath("/opt", false, testtool),
), ignorePathname, wantNet, pkg.WNew, nil}, ), ignorePathname, wantNet, nil},
}) })
destroyStatus(t, base, 2, 0) destroyStatus(t, base, 2, 0)
@@ -242,7 +242,7 @@ func TestExec(t *testing.T) {
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/" + expected.Net: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W")}, "identifier/" + expected.Net: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W")},
"identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")}, "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
"identifier/MEaM1v6MLRQ60JQOOKbfDzfJOdC3L8i30IgOpYbQdX68enwF0FN-P36_M4RX_0Vd": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
@@ -269,7 +269,7 @@ func TestExec(t *testing.T) {
}, },
}), }),
pkg.MustPath("/opt", false, testtool), pkg.MustPath("/opt", false, testtool),
), ignorePathname, wantOffline, pkg.WNew, nil}, ), ignorePathname, wantOffline, nil},
}) })
destroyStatus(t, base, 2, 0) destroyStatus(t, base, 2, 0)
@@ -284,7 +284,7 @@ func TestExec(t *testing.T) {
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/" + expected.OvlRoot: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)}, "identifier/" + expected.OvlRoot: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
"identifier/MEaM1v6MLRQ60JQOOKbfDzfJOdC3L8i30IgOpYbQdX68enwF0FN-P36_M4RX_0Vd": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
@@ -316,7 +316,7 @@ func TestExec(t *testing.T) {
return os.MkdirAll(t.GetWorkDir().String(), 0700) return os.MkdirAll(t.GetWorkDir().String(), 0700)
}, },
}), pkg.Path(pkg.AbsWork, false /* ignored */, testtool), }), pkg.Path(pkg.AbsWork, false /* ignored */, testtool),
), ignorePathname, wantOffline, pkg.WNew, nil}, ), ignorePathname, wantOffline, nil},
}) })
destroyStatus(t, base, 2, 0) destroyStatus(t, base, 2, 0)
@@ -331,7 +331,7 @@ func TestExec(t *testing.T) {
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/" + expected.Work: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)}, "identifier/" + expected.Work: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
"identifier/MEaM1v6MLRQ60JQOOKbfDzfJOdC3L8i30IgOpYbQdX68enwF0FN-P36_M4RX_0Vd": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
@@ -385,7 +385,7 @@ func TestExec(t *testing.T) {
}, },
}), }),
pkg.MustPath("/opt", false, testtool), pkg.MustPath("/opt", false, testtool),
), ignorePathname, wantOffline, pkg.WNew, nil}, ), ignorePathname, wantOffline, nil},
}) })
destroyStatus(t, base, 2, 0) destroyStatus(t, base, 2, 0)
@@ -403,9 +403,9 @@ func TestExec(t *testing.T) {
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")}, "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
"identifier/2uIvmnqtzAx4Ed4PWV8xjZCWeJCwfE2IcCc0evM-Nd8mDUlgSU6nLtPLcpPUcMlG": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK")}, "identifier/B-kc5iJMx8GtlCua4dz6BiJHnDAOUfPjgpbKq4e-QEn0_CZkSYs3fOA1ve06qMs2": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK")},
"identifier/" + expected.Layers: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)}, "identifier/" + expected.Layers: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
"identifier/MEaM1v6MLRQ60JQOOKbfDzfJOdC3L8i30IgOpYbQdX68enwF0FN-P36_M4RX_0Vd": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
@@ -438,7 +438,7 @@ func TestExec(t *testing.T) {
}, },
}), }),
pkg.MustPath("/opt", false, testtool), pkg.MustPath("/opt", false, testtool),
), ignorePathname, wantOffline, pkg.WNew, nil}, ), ignorePathname, wantOffline, nil},
}) })
destroyStatus(t, base, 2, 0) destroyStatus(t, base, 2, 0)
@@ -452,8 +452,8 @@ func TestExec(t *testing.T) {
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500}, "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/Vzj9qaSVBtNyBQv_PW6uojFOWTwraHoaC0C6xdaGG1q_HTvSh4KLVxRcEQU1jd2Y": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/kvJIqZo5DKFOxC2ZQ-8_nPaQzEAz9cIm3p6guO-uLqm-xaiPu7oRkSnsu411jd_U": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"identifier/MEaM1v6MLRQ60JQOOKbfDzfJOdC3L8i30IgOpYbQdX68enwF0FN-P36_M4RX_0Vd": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"identifier/" + expected.Promote: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)}, "identifier/" + expected.Promote: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
@@ -503,7 +503,7 @@ func TestExec(t *testing.T) {
".": {Mode: fs.ModeDir | 0500}, ".": {Mode: fs.ModeDir | 0500},
"check": {Mode: 0400, Data: []byte("binfmt")}, "check": {Mode: 0400, Data: []byte("binfmt")},
}, pkg.WNew, nil}, }, nil},
}) })
destroyStatus(t, base, 2, 0) destroyStatus(t, base, 2, 0)
@@ -519,9 +519,9 @@ func TestExec(t *testing.T) {
"checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3/check": {Mode: 0400, Data: []byte("binfmt")}, "checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3/check": {Mode: 0400, Data: []byte("binfmt")},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/c-HodmoEG0f_WGdst9aN-duLMH7QDDXOUi7UcyArXMeM1fc9lYKKQlTCW8ktaK8X": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3")}, "identifier/6VQTJ1lI5BmVuI1YFYJ8ClO3MRORvTTrcWFDcUU-l5Ga8EofxCxGlSTYN-u8dKj_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3")},
"identifier/_v8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV")}, "identifier/_v8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV")},
"identifier/MEaM1v6MLRQ60JQOOKbfDzfJOdC3L8i30IgOpYbQdX68enwF0FN-P36_M4RX_0Vd": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
+2 -17
View File
@@ -12,7 +12,6 @@ type fileArtifact []byte
var _ KnownChecksum = new(fileArtifact) var _ KnownChecksum = new(fileArtifact)
var _ CuresExempt = new(fileArtifact) var _ CuresExempt = new(fileArtifact)
var _ RevisionArtifact = new(fileArtifact)
// fileArtifactNamed embeds fileArtifact alongside a caller-supplied name. // fileArtifactNamed embeds fileArtifact alongside a caller-supplied name.
type fileArtifactNamed struct { type fileArtifactNamed struct {
@@ -64,8 +63,8 @@ func init() {
}) })
} }
// Inputs returns a nil slice. // Dependencies returns a nil slice.
func (*fileArtifact) Inputs() []Artifact { return nil } func (*fileArtifact) Dependencies() []Artifact { return nil }
// IsExclusive returns false: Cure returns a prepopulated buffer. // IsExclusive returns false: Cure returns a prepopulated buffer.
func (*fileArtifact) IsExclusive() bool { return false } func (*fileArtifact) IsExclusive() bool { return false }
@@ -77,20 +76,6 @@ func (a *fileArtifact) Checksum() Checksum {
return Checksum(h.Sum(nil)) return Checksum(h.Sum(nil))
} }
// Revision satisfies [RevisionArtifact] for incompatible IsExecutable behaviour.
func (*fileArtifact) Revision() uint64 { return 0 }
// IsExecutable returns whether the contents begin with shebang or the ELF
// magic number.
func (a *fileArtifact) IsExecutable() bool {
return a != nil &&
(bytes.HasPrefix(
*a, []byte{'#', '!'},
) || bytes.HasPrefix(
*a, []byte{0x7f, 'E', 'L', 'F'},
))
}
// Cure returns the caller-supplied data. // Cure returns the caller-supplied data.
func (a *fileArtifact) Cure(*RContext) (io.ReadCloser, error) { func (a *fileArtifact) Cure(*RContext) (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(*a)), nil return io.NopCloser(bytes.NewReader(*a)), nil
+5 -24
View File
@@ -12,41 +12,22 @@ func TestFile(t *testing.T) {
t.Parallel() t.Parallel()
want := expectsFile{0} want := expectsFile{0}
const ident = "N7M6W8-e55Wky_1q1a6qRKFVDw1S5XtPdB4HE2YZObG8OFum8HittyJgZUG5Ru2u"
wantShebang := expectsFile("#!/bin/sh\n")
const identShebang = "oKTGZb_TRtJf5G1iGGj4SOx1WUbI6YhmsUeEsCNvmK9tZYRwdwQf-DLFUxRPx5qm"
wantELF := expectsFile("\x7fELF\xde\xad\xbe\xef")
const identELF = "LjeHmIJgD1SiXIH8ewpGRed2_LU84NLapstnOAa_0hxLd8YY7x4KM0aApsj-RwCh"
checkWithCache(t, []cacheTestCase{ checkWithCache(t, []cacheTestCase{
{"file", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { {"file", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
cureMany(t, c, []cureStep{ cureMany(t, c, []cureStep{
{"short", pkg.NewFile("null", want), base.Append( {"short", pkg.NewFile("null", []byte{0}), base.Append(
"identifier", "identifier",
ident, "3376ALA7hIUm2LbzH2fDvRezgzod1eTK_G6XjyOgbM2u-6swvkFaF0BOwSl_juBi",
), want, pkg.WNew, nil}, ), want, nil},
{"shebang", pkg.NewFile("shebang", wantShebang), base.Append(
"identifier",
identShebang,
), wantShebang, pkg.WNew, nil},
{"elf", pkg.NewFile("ELF", wantELF), base.Append(
"identifier",
identELF,
), wantELF, pkg.WNew, nil},
}) })
}, expectsFS{ }, expectsFS{
".": {Mode: fs.ModeDir | 0700}, ".": {Mode: fs.ModeDir | 0700},
"checksum": {Mode: fs.ModeDir | 0700}, "checksum": {Mode: fs.ModeDir | 0700},
"checksum/" + pkg.Encode(want.hash()): {Mode: 0400, Data: want}, "checksum/" + pkg.Encode(want.hash()): {Mode: 0400, Data: []byte{0}},
"checksum/" + pkg.Encode(wantShebang.hash()): {Mode: 0500, Data: wantShebang},
"checksum/" + pkg.Encode(wantELF.hash()): {Mode: 0500, Data: wantELF},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/" + ident: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + pkg.Encode(want.hash()))}, "identifier/3376ALA7hIUm2LbzH2fDvRezgzod1eTK_G6XjyOgbM2u-6swvkFaF0BOwSl_juBi": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")},
"identifier/" + identShebang: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + pkg.Encode(wantShebang.hash()))},
"identifier/" + identELF: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + pkg.Encode(wantELF.hash()))},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
@@ -1,11 +1,11 @@
package expected package expected
const ( const (
Offline = "IKmVhALejtYA3JhN1PCfjKF-Dio1XVcOeMWDHhfSHCbb4iBNhGjFy2UspZveZvb8" Offline = "q5ktDTq0miP-VvB2blxqXQeaRXCUWgP_KbC18KNtUDtyoaI_h5mHmGuPMArVEBDs"
OfflineS = "6ZubjSyCnz4-TKgQZi_BbY-JUwsStYCtD_f-BTDOOotfEbqkm47W20AazHRaLviz" OfflineS = "IY91PCtOpCYy21AaIK0c9f8-Z6fb2_2ewoHWkt4dxoLf0GOrWqS8yAGFLV84b1Dw"
OvlRoot = "Yox7WpG8B_OqIXWF4HWFZkUfsEl7DGzpFyZM0qtd9T8nQ-7SAkEUurlAe6_DJ6Iu" OvlRoot = "NacZGXwuRkTvcHaG08a22ujJ8qCWN0RSoFlRSR5FSt0ZcBbJ28FRvkYsHEtX7G8i"
Layers = "aAwfxDB-efVo379FNl178jK7HEKU3LEjHbZdaVVjk7GnXGZGEANNsXnB3SCUxM9A" Layers = "WBJDrATtX6rIE5yAu8ePX3WmDF0Tt9kFiue0m3cRnyRoVx1my8a67fh3CAW486oP"
Net = "IU3GwU2jTBd7HjotvFo2wsDWBQGVGpamwiSKdc2G1EnhM-ZaacxAuK6q0wbTVxra" Net = "CmYtj2sNB3LHtqiDuck_Lz3MjLLIiwyP8N4NDitQ1Icvv__LVP9p8tm-sHeQaKKp"
Promote = "rOjetGHzT_jPErrBR24IAZKDj8Q9dLLqxFQqloMgjsULss0VXDI--n6LxuMBHvmZ" Promote = "TX3eCloaQFkV-SZIH6Jg6E5WKH--rcXY1P0jnZKmLFKWrNqnOzd4G9eIBh6i5ywN"
Work = "7f9XDeREfvJIjCe0qesND0s0tasExAIcc-OH33-eq9tsrQPUXhJp4KEkpsYH4hCD" Work = "OuNiLSC68pZhAOr1YQ4WbV1tzASA0nxLEBcK7lO7MqxDY_j8dmP_C612RTuF23Lu"
) )
@@ -1,11 +1,11 @@
package expected package expected
const ( const (
Offline = "MTgCSzwPrzGigp9phvDPRwUTNGLm1KM2EsEtbz-l2cNieqjaGtnzm65CDXlSxXrl" Offline = "WapqyoPxbWSnq07dWHt71mHaJXq99pAjJfFlELlJljSiZMhTFqqlzU1_mN86shSj"
OfflineS = "p89bQg7_jR4nwaSbpEd08ne1jHOEw7Wlnlyv5FHq7ba1Kml3HSBZwEKRzu0gCqEb" OfflineS = "ibQZHcdXgNQ1OiMX1FrburBbGPVvKEHvPilbQCkm_0oV0BQCHomyyTbYNrFMGIwl"
OvlRoot = "9ifwO6MAaV9kTqRR2O5PJzm8XhfXbr50mFLWyEHQm8OHHcJtAEUgXZzJYoZt9YVV" OvlRoot = "V9anFOiRvjGfAeBhLl14AL8TKdWZyD0WTPYe4fS9mOBw8iW5Lmarvt6TG6MV8uWm"
Layers = "INuGActFPwJtS7mAR9Bkt7FD4yoHlXHSJ9CaSAQJrpkUBL_k815Cs_jvRfxy5-a7" Layers = "tKx7JNRoSBdK_7MdzI-nwTNV2wmiPzwYdcd17oLmXKL_iLmUzUiA79qTqdrTasrv"
Net = "kJvy24uk4ePvHoxSKf4vMOhR4NxLbmyWlA9bTS7d3z3fE9WF1Tvpao9g-QuYU4vm" Net = "aXyDLzBCJ9XltXZIfetEVsEkrqHfcXuD5XE_FcUnYbN3emwL55N6P8LlHzNfGnM5"
Promote = "lYqyYrfjPawIbld5Qz0NbKvd_bmI3nJC1eEnlIQWzsjWE9uA1HuKPAaClnDeyG6L" Promote = "3k4V16n96Lq04gjFSKmm4sFjyQ883FFBNXgTy9s_DjeTwxT3pg_iacEh8yMb_S4m"
Work = "V6k4FqOBMRCmHiqchs5-joVGZ_MhNWZyXm3gdbU0S7S5eMze7G4SwYMTf66ojKQL" Work = "6Q49MhFWRE3Ne6MycwAotgl1GtoU5WCHqJNWG2byYZCY-zX-IxPrWiKk7bKkNzhE"
) )
@@ -1,11 +1,11 @@
package expected package expected
const ( const (
Offline = "LlieSHVDe1lnKvfI8t4oGRg_2hPxwyy1drmaqc-EKCOENuS9C2YHrp2VY5pSyhbg" Offline = "Z6yXE5gOJScL3srmnVMWgCXccDiUNZ5snSrf6RkXuU1_U0rX_kGVwsfHUgNG_awd"
OfflineS = "qccjM8oYjUuEYPKFFzAxDFVzMerSUKUZc18NdVQoiPWf-70XOsNK8NaUQqjTgTAD" OfflineS = "zN16xv6LKRJRipUJwupyxg2rZcvf-qpsMn_qCxUmgxlTSuNwYI70ZEb7dHW5k0gO"
OvlRoot = "lpV2bb_KGYFHPiOn-jtGop8K_ew0y1p3BezRp0ebM93_a-T9J-vkqGD5uokda1aN" OvlRoot = "zYXJHFRLuxvUhuisZEXgGgVvdQd6piMfp5jmtT6jdVjvC2gICXquOq-UTwlrSD5I"
Layers = "sM3DJm210CHPGHkAquIbrXhspB81FpVC60ApQrvJGD5El2i7cSONj7LWf7EWqKpT" Layers = "_F8EDazHbcLeT0sVSQXRN_kn9IjduqJcDYgzXpsT-hpKU4EBcZ0PISN2zchpqMbm"
Net = "JihQUwsVETRYjb2-yIXLYZ6WVsaFebwl8btSYAsNQ2QapS5MZVaI-dYwrVoySycR" Net = "CA_FAaSIYJgapBEHV40doxpH23PdUEy_6s1TZc7wfSPN0XYqwGpMceXXDSabGveO"
Promote = "lGqwR4W2OjylSVitMQP1lc6wApOBkbMjsmIqkxQbfEZ3DHEQBbJr84ErW6Xb54zo" Promote = "_3LPrLp--4h9k4GsNNApu9hHtAafq-GUhfU6d4hJKBDKT3bz_szOsvkXxc5sK53d"
Work = "aZgtyv6aMtoHFkHAaJr6nLaA7N_SHjShgWPyQ5UdzaIqYSiVqvCrZqSokGTCcTii" Work = "FEgHeiCD_WT4wsfB-9kDH5n6cRWCEYtJmXdKZgmUUukAOoXumH_hLlosXREC-tqq"
) )
+1 -9
View File
@@ -38,15 +38,7 @@ func main() {
} }
environ := slices.DeleteFunc(slices.Clone(os.Environ()), func(s string) bool { environ := slices.DeleteFunc(slices.Clone(os.Environ()), func(s string) bool {
for _, t := range []string{ return s == "CURE_JOBS="+strconv.Itoa(runtime.NumCPU())
"CURE_JOBS=" + strconv.Itoa(runtime.NumCPU()),
"CURE_LOAD=" + strconv.Itoa(runtime.NumCPU()+2),
} {
if s == t {
return true
}
}
return false
}) })
var hostNet, layers, promote bool var hostNet, layers, promote bool
+26 -44
View File
@@ -90,7 +90,7 @@ type IContext struct {
// returns and must not be exposed directly. // returns and must not be exposed directly.
w io.Writer w io.Writer
// Optional [Artifact] to cureRes cache, replaces [IRKindIdent] with // Optional [Artifact] to cureRes cache, replaces [IRKindIdent] with
// checksum values if non-nil. The pathname field may not be populated. // checksum values if non-nil.
inputs map[Artifact]cureRes inputs map[Artifact]cureRes
} }
@@ -173,7 +173,7 @@ func (i *IContext) mustWrite(p []byte) {
// WriteIdent writes the identifier of [Artifact] to the IR. The behaviour of // WriteIdent writes the identifier of [Artifact] to the IR. The behaviour of
// WriteIdent is not defined for an [Artifact] not part of the slice returned by // WriteIdent is not defined for an [Artifact] not part of the slice returned by
// [Artifact.Inputs]. // [Artifact.Dependencies].
func (i *IContext) WriteIdent(a Artifact) { func (i *IContext) WriteIdent(a Artifact) {
buf := i.ic.getIdentBuf() buf := i.ic.getIdentBuf()
defer i.ic.putIdentBuf(buf) defer i.ic.putIdentBuf(buf)
@@ -235,14 +235,13 @@ func (ic *irCache) Encode(w io.Writer, a Artifact) (err error) {
} }
// encode implements Encode but replaces identifiers with their cured checksums // encode implements Encode but replaces identifiers with their cured checksums
// for a non-nil ident. The pathname field is unused. Caller must acquire // for a non-nil ident. Caller must acquire Cache.identMu.
// Cache.identMu.
func (ic *irCache) encode( func (ic *irCache) encode(
w io.Writer, w io.Writer,
a Artifact, a Artifact,
inputs map[Artifact]cureRes, inputs map[Artifact]cureRes,
) (err error) { ) (err error) {
deps := a.Inputs() deps := a.Dependencies()
idents := make([]*extIdent, len(deps)) idents := make([]*extIdent, len(deps))
if inputs == nil { if inputs == nil {
for i, d := range deps { for i, d := range deps {
@@ -278,11 +277,10 @@ func (ic *irCache) encode(
return *a == *b return *a == *b
}) })
// kind uint64 | rev uint64 | deps_sz uint64 // kind uint64 | deps_sz uint64
var buf [wordSize * 3]byte var buf [wordSize * 2]byte
binary.LittleEndian.PutUint64(buf[:], uint64(a.Kind())) binary.LittleEndian.PutUint64(buf[:], uint64(a.Kind()))
binary.LittleEndian.PutUint64(buf[wordSize:], GetRevision(a)) binary.LittleEndian.PutUint64(buf[wordSize:], uint64(len(idents)))
binary.LittleEndian.PutUint64(buf[wordSize*2:], uint64(len(idents)))
if _, err = w.Write(buf[:]); err != nil { if _, err = w.Write(buf[:]); err != nil {
return return
} }
@@ -332,7 +330,7 @@ func (ic *irCache) encodeAll(
return return
} }
for _, d := range a.Inputs() { for _, d := range a.Dependencies() {
if err = ic.encodeAll(w, d, encoded); err != nil { if err = ic.encodeAll(w, d, encoded); err != nil {
return return
} }
@@ -434,9 +432,9 @@ type (
// Common buffer for word-sized reads. // Common buffer for word-sized reads.
buf [wordSize]byte buf [wordSize]byte
// Inputs sent before params, sorted by identifier. Resliced on // Dependencies sent before params, sorted by identifier. Resliced on
// each call to Next and checked to be depleted during Finalise. // each call to Next and checked to be depleted during Finalise.
inputs []*extIdent deps []*extIdent
// Number of values already read, -1 denotes a finalised IRReader. // Number of values already read, -1 denotes a finalised IRReader.
count int count int
@@ -520,7 +518,7 @@ const (
irMaxValues = 1 << 12 irMaxValues = 1 << 12
// irMaxDeps is the arbitrary maximum number of direct dependencies allowed // irMaxDeps is the arbitrary maximum number of direct dependencies allowed
// to be returned by [Artifact.Inputs] and subsequently decoded by // to be returned by [Artifact.Dependencies] and subsequently decoded by
// [IRDecoder]. // [IRDecoder].
irMaxDeps = 1 << 10 irMaxDeps = 1 << 10
) )
@@ -605,17 +603,17 @@ func (ir *IRReader) mustReadHeader(k IRValueKind) {
// putAll returns all dependency buffers to the underlying [Cache]. // putAll returns all dependency buffers to the underlying [Cache].
func (ir *IRReader) putAll() { func (ir *IRReader) putAll() {
for _, buf := range ir.inputs { for _, buf := range ir.deps {
ir.d.c.putIdentBuf(buf) ir.d.c.putIdentBuf(buf)
} }
ir.inputs = nil ir.deps = nil
} }
// DiscardAll discards all unstructured dependencies. This is useful to // DiscardAll discards all unstructured dependencies. This is useful to
// implementations that encode dependencies as [IRKindIdent] which are read back // implementations that encode dependencies as [IRKindIdent] which are read back
// via ReadIdent. // via ReadIdent.
func (ir *IRReader) DiscardAll() { func (ir *IRReader) DiscardAll() {
if ir.inputs == nil { if ir.deps == nil {
panic("attempting to discard dependencies twice") panic("attempting to discard dependencies twice")
} }
ir.putAll() ir.putAll()
@@ -627,13 +625,13 @@ var ErrDependencyDepleted = errors.New("reading past end of dependencies")
// Next returns the next unstructured dependency. // Next returns the next unstructured dependency.
func (ir *IRReader) Next() Artifact { func (ir *IRReader) Next() Artifact {
if len(ir.inputs) == 0 { if len(ir.deps) == 0 {
panic(ErrDependencyDepleted) panic(ErrDependencyDepleted)
} }
id := unique.Make(ID(ir.inputs[0][wordSize:])) id := unique.Make(ID(ir.deps[0][wordSize:]))
ir.d.c.putIdentBuf(ir.inputs[0]) ir.d.c.putIdentBuf(ir.deps[0])
ir.inputs = ir.inputs[1:] ir.deps = ir.deps[1:]
if a, ok := ir.d.ident[id]; !ok { if a, ok := ir.d.ident[id]; !ok {
ir.putAll() ir.putAll()
@@ -686,8 +684,8 @@ func (ir *IRReader) Finalise() (checksum unique.Handle[Checksum], ok bool) {
} }
ir.r, ir.ibw = nil, nil ir.r, ir.ibw = nil, nil
if len(ir.inputs) != 0 { if len(ir.deps) != 0 {
panic(MissedDependencyError(len(ir.inputs))) panic(MissedDependencyError(len(ir.deps)))
} }
return return
@@ -741,15 +739,6 @@ func (ir *IRReader) ReadString() string {
return unsafe.String(unsafe.SliceData(p), len(p)) return unsafe.String(unsafe.SliceData(p), len(p))
} }
// A RevisionError describes the first [Artifact] in an IR stream claiming a
// revision not supported by the registered implementation.
type RevisionError [2]uint64
func (e RevisionError) Error() string {
return "claimed revision " + strconv.FormatUint(e[0], 10) +
" differs from implementation value " + strconv.FormatUint(e[1], 10)
}
// decode decodes the next [Artifact] in the IR stream and returns any buffer // decode decodes the next [Artifact] in the IR stream and returns any buffer
// originating from [Cache] before returning. decode returns [io.EOF] if and // originating from [Cache] before returning. decode returns [io.EOF] if and
// only if the underlying [io.Reader] is already read to EOF. // only if the underlying [io.Reader] is already read to EOF.
@@ -782,9 +771,6 @@ func (d *IRDecoder) decode() (a Artifact, err error) {
return return
} }
ir.mustRead(ir.buf[:])
rev := binary.LittleEndian.Uint64(ir.buf[:])
defer ir.putAll() defer ir.putAll()
ir.mustRead(ir.buf[:]) ir.mustRead(ir.buf[:])
sz := binary.LittleEndian.Uint64(ir.buf[:]) sz := binary.LittleEndian.Uint64(ir.buf[:])
@@ -792,11 +778,11 @@ func (d *IRDecoder) decode() (a Artifact, err error) {
err = ErrIRDepend err = ErrIRDepend
return return
} }
ir.inputs = make([]*extIdent, sz) ir.deps = make([]*extIdent, sz)
for i := range ir.inputs { for i := range ir.deps {
ir.inputs[i] = d.c.getIdentBuf() ir.deps[i] = d.c.getIdentBuf()
} }
for _, buf := range ir.inputs { for _, buf := range ir.deps {
ir.mustRead(buf[:]) ir.mustRead(buf[:])
} }
@@ -805,10 +791,6 @@ func (d *IRDecoder) decode() (a Artifact, err error) {
err = syscall.ENOTRECOVERABLE err = syscall.ENOTRECOVERABLE
return return
} }
if _rev := GetRevision(a); _rev != rev {
err = RevisionError{rev, _rev}
return
}
if ir.count != -1 { if ir.count != -1 {
err = ErrRemainingIR err = ErrRemainingIR
@@ -823,8 +805,8 @@ func (d *IRDecoder) decode() (a Artifact, err error) {
d.ident[id] = a d.ident[id] = a
} else { } else {
d.c.msg.Verbosef( d.c.msg.Verbosef(
"artifact %s%s%s appeared more than once in IR stream", "artifact %s appeared more than once in IR stream",
d.c.sgrIdent, Encode(id.Value()), d.c.sgrRes, Encode(id.Value()),
) )
} }
-33
View File
@@ -135,36 +135,3 @@ func TestIRRoundtrip(t *testing.T) {
} }
checkWithCache(t, testCasesCache) checkWithCache(t, testCasesCache)
} }
func TestRevision(t *testing.T) {
checkWithCache(t, []cacheTestCase{{"revision", 0, nil,
func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
a := pkg.NewFile("", nil)
var buf bytes.Buffer
if err := c.EncodeAll(&buf, a); err != nil {
t.Fatal(err)
}
if _, err := c.NewDecoder(
bytes.NewReader(buf.Bytes()),
).Decode(); err != nil {
t.Fatalf("Decode: error = %v", err)
}
p := buf.Bytes()
p[8] = 0xfd
wantErr := pkg.RevisionError{0xfd, 0}
if _, err := c.NewDecoder(
bytes.NewReader(p),
).Decode(); !reflect.DeepEqual(err, wantErr) {
t.Fatalf("Decode: error = %v, want %v", err, wantErr)
}
}, expectsFS{
".": {Mode: fs.ModeDir | 0700},
"checksum": {Mode: fs.ModeDir | 0700},
"identifier": {Mode: fs.ModeDir | 0700},
"substitute": {Mode: fs.ModeDir | 0700},
"work": {Mode: fs.ModeDir | 0700},
},
}})
}
+2 -5
View File
@@ -54,8 +54,8 @@ func init() {
}) })
} }
// Inputs returns a nil slice. // Dependencies returns a nil slice.
func (*httpArtifact) Inputs() []Artifact { return nil } func (*httpArtifact) Dependencies() []Artifact { return nil }
// IsExclusive returns false: Cure returns as soon as a response is received. // IsExclusive returns false: Cure returns as soon as a response is received.
func (*httpArtifact) IsExclusive() bool { return false } func (*httpArtifact) IsExclusive() bool { return false }
@@ -74,9 +74,6 @@ func (e ResponseStatusError) Error() string {
return "the requested URL returned non-OK status: " + http.StatusText(int(e)) return "the requested URL returned non-OK status: " + http.StatusText(int(e))
} }
// IsExecutable returns false.
func (*httpArtifact) IsExecutable() bool { return false }
// Cure sends the http request and returns the resulting response body reader // Cure sends the http request and returns the resulting response body reader
// wrapped to perform checksum validation. It is valid but not encouraged to // 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 the resulting [io.ReadCloser] before it is read to EOF, as that causes
+2 -2
View File
@@ -104,7 +104,7 @@ func TestHTTPGet(t *testing.T) {
) )
wantPathname := base.Append( wantPathname := base.Append(
"identifier", "identifier",
"COBRUwkxpIvZWJggpRxn7HVXiw6epXZOex8jGRJKpowpwukV7GRBY_LBvOJNQHue", "oM-2pUlk-mOxK1t3aMWZer69UdOQlAXiAgMrpZ1476VoOqpYVP1aGFS9_HYy-D8_",
) )
if pathname, checksum, err := c.Cure(f); err != nil { if pathname, checksum, err := c.Cure(f); err != nil {
t.Fatalf("Cure: error = %v", err) t.Fatalf("Cure: error = %v", err)
@@ -158,7 +158,7 @@ func TestHTTPGet(t *testing.T) {
"checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU": {Mode: 0400, Data: []byte("\x7f\xe1\x69\xa2\xdd\x63\x96\x26\x83\x79\x61\x8b\xf0\x3f\xd5\x16\x9a\x39\x3a\xdb\xcf\xb1\xbc\x8d\x33\xff\x75\xee\x62\x56\xa9\xf0\x27\xac\x13\x94\x69")}, "checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU": {Mode: 0400, Data: []byte("\x7f\xe1\x69\xa2\xdd\x63\x96\x26\x83\x79\x61\x8b\xf0\x3f\xd5\x16\x9a\x39\x3a\xdb\xcf\xb1\xbc\x8d\x33\xff\x75\xee\x62\x56\xa9\xf0\x27\xac\x13\x94\x69")},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/COBRUwkxpIvZWJggpRxn7HVXiw6epXZOex8jGRJKpowpwukV7GRBY_LBvOJNQHue": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU")}, "identifier/oM-2pUlk-mOxK1t3aMWZer69UdOQlAXiAgMrpZ1476VoOqpYVP1aGFS9_HYy-D8_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
+125 -430
View File
@@ -6,7 +6,6 @@ import (
"bytes" "bytes"
"cmp" "cmp"
"context" "context"
"crypto/rand"
"crypto/sha512" "crypto/sha512"
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
@@ -16,7 +15,6 @@ import (
"io" "io"
"io/fs" "io/fs"
"maps" "maps"
"math"
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
@@ -280,11 +278,7 @@ func (c *common) GetMessage() message.Msg { return c.cache.msg }
// GetJobs returns the preferred number of jobs to run, when applicable. Its // GetJobs returns the preferred number of jobs to run, when applicable. Its
// value must not affect cure outcome. // value must not affect cure outcome.
func (c *common) GetJobs() int { return c.cache.attr.Jobs } func (c *common) GetJobs() int { return c.cache.jobs }
// GetLoad returns the preferred load average target, when applicable. Its
// value must not affect cure outcome.
func (c *common) GetLoad() int { return c.cache.attr.Load }
// GetWorkDir returns a pathname to a directory which [Artifact] is expected to // GetWorkDir returns a pathname to a directory which [Artifact] is expected to
// write its output to. This is not the final resting place of the [Artifact] // write its output to. This is not the final resting place of the [Artifact]
@@ -309,7 +303,7 @@ func (c *common) Open(a Artifact) (r io.ReadCloser, err error) {
} }
var pathname *check.Absolute var pathname *check.Absolute
if pathname, _, _, err = c.cache.cure(a, true, false); err != nil { if pathname, _, err = c.cache.cure(a, true); err != nil {
return return
} }
@@ -336,8 +330,8 @@ func (c *common) Open(a Artifact) (r io.ReadCloser, err error) {
type FContext struct { type FContext struct {
TContext TContext
// Cured top-level inputs looked up by Pathname. // Cured top-level dependencies looked up by Pathname.
inputs map[Artifact]cureRes deps map[Artifact]cureRes
} }
// linkSubstitute links status for substitute if populated. // linkSubstitute links status for substitute if populated.
@@ -362,24 +356,24 @@ func (f *FContext) linkSubstitute(ids, substitutes string) (err error) {
return return
} }
// InvalidLookupError is the identifier of non-input [Artifact] looked up // InvalidLookupError is the identifier of non-dependency [Artifact] looked up
// via [FContext.GetArtifact] by a misbehaving [Artifact] implementation. // via [FContext.GetArtifact] by a misbehaving [Artifact] implementation.
type InvalidLookupError ID type InvalidLookupError ID
func (e InvalidLookupError) Error() string { func (e InvalidLookupError) Error() string {
return "attempting to look up non-input artifact " + Encode(e) return "attempting to look up non-dependency artifact " + Encode(e)
} }
var _ error = InvalidLookupError{} var _ error = InvalidLookupError{}
// GetArtifact returns the identifier pathname and checksum of an [Artifact]. // GetArtifact returns the identifier pathname and checksum of an [Artifact].
// Calling Pathname with an [Artifact] not part of the slice returned by // Calling Pathname with an [Artifact] not part of the slice returned by
// [Artifact.Inputs] panics. // [Artifact.Dependencies] panics.
func (f *FContext) GetArtifact(a Artifact) ( func (f *FContext) GetArtifact(a Artifact) (
pathname *check.Absolute, pathname *check.Absolute,
checksum unique.Handle[Checksum], checksum unique.Handle[Checksum],
) { ) {
if res, ok := f.inputs[a]; ok { if res, ok := f.deps[a]; ok {
return res.pathname, res.checksum return res.pathname, res.checksum
} }
panic(InvalidLookupError(f.cache.Ident(a).Value())) panic(InvalidLookupError(f.cache.Ident(a).Value()))
@@ -411,13 +405,13 @@ type Artifact interface {
// Result must remain identical across multiple invocations. // Result must remain identical across multiple invocations.
Params(ctx *IContext) Params(ctx *IContext)
// Inputs returns a slice of [Artifact] the current instance has access to // Dependencies returns a slice of [Artifact] that the current instance
// while producing its output. // depends on to produce its contents.
// //
// Callers must not modify the retuned slice. // Callers must not modify the retuned slice.
// //
// Result must remain identical across multiple invocations. // Result must remain identical across multiple invocations.
Inputs() []Artifact Dependencies() []Artifact
// IsExclusive returns whether the [Artifact] is exclusive. Exclusive // IsExclusive returns whether the [Artifact] is exclusive. Exclusive
// artifacts might not run in parallel with each other, and are still // artifacts might not run in parallel with each other, and are still
@@ -478,8 +472,6 @@ type KnownIdent interface {
// [Artifact]. This value must be known ahead of time and guaranteed to be // [Artifact]. This value must be known ahead of time and guaranteed to be
// unique without having obtained the full contents of the [Artifact]. // unique without having obtained the full contents of the [Artifact].
ID() ID ID() ID
Artifact
} }
// KnownChecksum is optionally implemented by [Artifact] for an artifact with // KnownChecksum is optionally implemented by [Artifact] for an artifact with
@@ -491,8 +483,6 @@ type KnownChecksum interface {
// //
// Result must remain identical across multiple invocations. // Result must remain identical across multiple invocations.
Checksum() Checksum Checksum() Checksum
Artifact
} }
// CuresExempt is optionally implemented for an artifact exempt to the // CuresExempt is optionally implemented for an artifact exempt to the
@@ -510,12 +500,6 @@ type CuresExempt interface {
// FileArtifact does not support fine-grained cancellation. Its context is // FileArtifact does not support fine-grained cancellation. Its context is
// inherited from the first [TrivialArtifact] or [FloodArtifact] that opens it. // inherited from the first [TrivialArtifact] or [FloodArtifact] that opens it.
type FileArtifact interface { type FileArtifact interface {
// IsExecutable returns whether the resulting filesystem entry should be made
// executable, if the [FileArtifact] is cured to the on-disk cache.
//
// Result must remain identical across multiple invocations.
IsExecutable() bool
// Cure returns [io.ReadCloser] of the full contents of [FileArtifact]. If // Cure returns [io.ReadCloser] of the full contents of [FileArtifact]. If
// [FileArtifact] implements [KnownChecksum], Cure is responsible for // [FileArtifact] implements [KnownChecksum], Cure is responsible for
// validating any data it produces and must return [ChecksumMismatchError] // validating any data it produces and must return [ChecksumMismatchError]
@@ -525,35 +509,12 @@ type FileArtifact interface {
// //
// Callers are responsible for closing the resulting [io.ReadCloser]. // Callers are responsible for closing the resulting [io.ReadCloser].
// //
// The resulting [io.ReadCloser] across multiple invocations must have // Result must remain identical across multiple invocations.
// identical behaviour.
Cure(r *RContext) (io.ReadCloser, error) Cure(r *RContext) (io.ReadCloser, error)
Artifact Artifact
} }
// RevisionArtifact is optionally implemented by an artifact that had undergone
// internal changes affecting its behaviour while retaining its IR structure.
type RevisionArtifact interface {
// Revision returns the revision number of [Artifact]. This value is always
// represented in the IR. An IR stream produced from the same [Kind] with
// differing revision is rejected.
//
// Result must remain identical across multiple invocations.
Revision() uint64
Artifact
}
// GetRevision returns the revision number of an [Artifact].
func GetRevision(a Artifact) (revision uint64) {
revision = math.MaxUint64
if r, ok := a.(RevisionArtifact); ok {
revision = r.Revision()
}
return
}
// reportName returns a string describing [Artifact] presented to the user. // reportName returns a string describing [Artifact] presented to the user.
func reportName(a Artifact, id unique.Handle[ID]) string { func reportName(a Artifact, id unique.Handle[ID]) string {
r := Encode(id.Value()) r := Encode(id.Value())
@@ -641,7 +602,7 @@ type cureRes struct {
checksum unique.Handle[Checksum] checksum unique.Handle[Checksum]
} }
// A pendingArtifactDep is an input [Artifact] pending concurrent curing, // A pendingArtifactDep is a dependency [Artifact] pending concurrent curing,
// subject to the cures limit. Values pointed to by result addresses are safe // subject to the cures limit. Values pointed to by result addresses are safe
// to access after the [sync.WaitGroup] associated with this pendingArtifactDep // to access after the [sync.WaitGroup] associated with this pendingArtifactDep
// is done. pendingArtifactDep must not be reused or modified after it is sent // is done. pendingArtifactDep must not be reused or modified after it is sent
@@ -715,15 +676,8 @@ const (
// suppressed regardless of [message.Msg] state. // suppressed regardless of [message.Msg] state.
CSuppressInit CSuppressInit
// CIgnoreSubstitutes disables content-based input substitution. // CIgnoreSubstitutes disables content-based dependency substitution.
CIgnoreSubstitutes CIgnoreSubstitutes
// CExternShallow arranges for only non-flood inputs to be fetched when
// curing an [Artifact] available via the external cache.
CExternShallow
// CColourOutput enables output colouring via ANSI control sequences.
CColourOutput
) )
// toplevel holds [context.WithCancel] over caller-supplied context, where all // toplevel holds [context.WithCancel] over caller-supplied context, where all
@@ -775,17 +729,17 @@ type Cache struct {
parent context.Context parent context.Context
// For deriving curing context, must not be accessed directly. // For deriving curing context, must not be accessed directly.
toplevel atomic.Pointer[toplevel] toplevel atomic.Pointer[toplevel]
// For waiting on input curing goroutines. // For waiting on dependency curing goroutines.
wg sync.WaitGroup wg sync.WaitGroup
// Reports new cures and passed to [Artifact]. // Reports new cures and passed to [Artifact].
msg message.Msg msg message.Msg
// Select graphics rendition sequences, populated by Open.
sgrRes, sgrIdent, sgrWarn, sgrErr string
// Directory where all [Cache] related files are placed. // Directory where all [Cache] related files are placed.
base *check.Absolute base *check.Absolute
// Immutable [CacheAttr] populated by [Open]. // Immutable cure options set by [Open].
attr CacheAttr flags int
// Immutable job count, when applicable.
jobs int
// Must not be exposed directly. // Must not be exposed directly.
irCache irCache
@@ -816,8 +770,6 @@ type Cache struct {
// Optional external cache implementation. // Optional external cache implementation.
extern External extern External
// Caches responses from extern.
externCache map[unique.Handle[ID]]unique.Handle[Checksum]
// Synchronises access to extern. // Synchronises access to extern.
externMu sync.RWMutex externMu sync.RWMutex
@@ -978,7 +930,6 @@ func readlinkChecksum(a *check.Absolute, buf *Checksum) error {
// SetExternal sets e as the [External] implementation of c. // SetExternal sets e as the [External] implementation of c.
func (c *Cache) SetExternal(e External) { func (c *Cache) SetExternal(e External) {
c.externMu.Lock() c.externMu.Lock()
c.externCache = make(map[unique.Handle[ID]]unique.Handle[Checksum])
c.extern = e c.extern = e
c.externMu.Unlock() c.externMu.Unlock()
} }
@@ -1137,10 +1088,7 @@ func (c *Cache) Scrub(checks int) error {
} else if !ce.check(ent, want) { } else if !ce.check(ent, want) {
wg.Go(func() { condemn(ent) }) wg.Go(func() { condemn(ent) })
} else { } else {
c.msg.Verbosef( c.msg.Verbosef("%s is consistent", ent.Name())
"%s%s%s is consistent",
c.sgrIdent, ent.Name(), c.sgrRes,
)
} }
p.Put(want) p.Put(want)
wg.Done() wg.Done()
@@ -1493,7 +1441,7 @@ func (c *Cache) openFile(
ctx context.Context, ctx context.Context,
f FileArtifact, f FileArtifact,
) (r io.ReadCloser, err error) { ) (r io.ReadCloser, err error) {
if kc, ok := f.(KnownChecksum); c.attr.Flags&CAssumeChecksum != 0 && ok { if kc, ok := f.(KnownChecksum); c.flags&CAssumeChecksum != 0 && ok {
c.checksumMu.RLock() c.checksumMu.RLock()
r, err = os.Open(c.base.Append( r, err = os.Open(c.base.Append(
dirChecksum, dirChecksum,
@@ -1516,10 +1464,10 @@ func (c *Cache) openFile(
id := c.Ident(f) id := c.Ident(f)
if c.msg.IsVerbose() { if c.msg.IsVerbose() {
rn := reportName(f, id) rn := reportName(f, id)
c.msg.Verbosef("curing %s%s%s in memory...", c.sgrIdent, rn, c.sgrRes) c.msg.Verbosef("curing %s in memory...", rn)
defer func() { defer func() {
if err == nil { if err == nil {
c.msg.Verbosef("opened %s%s%s for reading", c.sgrIdent, rn, c.sgrRes) c.msg.Verbosef("opened %s for reading", rn)
} }
}() }()
} }
@@ -1678,47 +1626,7 @@ func (c *Cache) Cure(a Artifact) (
return return
} }
pathname, checksum, _, err = c.cure(a, true, false) return c.cure(a, true)
return
}
// CureWhence is like Cure, but returns the whence value.
func (c *Cache) CureWhence(a Artifact) (
pathname *check.Absolute,
checksum unique.Handle[Checksum],
whence int,
err error,
) {
c.abortMu.RLock()
defer c.abortMu.RUnlock()
if err = c.toplevel.Load().ctx.Err(); err != nil {
return
}
return c.cure(a, true, false)
}
// CureNew is like Cure, but always enters the implementation.
func (c *Cache) CureNew(a Artifact) (
pathname *check.Absolute,
checksum unique.Handle[Checksum],
err error,
) {
c.abortMu.RLock()
defer c.abortMu.RUnlock()
if err = c.toplevel.Load().ctx.Err(); err != nil {
return
}
var whence int
retry:
pathname, checksum, whence, err = c.cure(a, true, true)
if err != nil || whence == WNew {
return
}
goto retry
} }
// CureError wraps a non-nil error returned attempting to cure an [Artifact]. // CureError wraps a non-nil error returned attempting to cure an [Artifact].
@@ -1826,10 +1734,6 @@ func (e *DependencyCureError) Error() string {
// enterCure must be called before entering an [Artifact] implementation. // enterCure must be called before entering an [Artifact] implementation.
func (c *Cache) enterCure(a Artifact, curesExempt bool) error { func (c *Cache) enterCure(a Artifact, curesExempt bool) error {
if c.attr.Notify != nil {
c.attr.Notify <- true
}
if a.IsExclusive() { if a.IsExclusive() {
c.exclMu.Lock() c.exclMu.Lock()
} }
@@ -1852,10 +1756,6 @@ func (c *Cache) enterCure(a Artifact, curesExempt bool) error {
// exitCure must be called after exiting an [Artifact] implementation. // exitCure must be called after exiting an [Artifact] implementation.
func (c *Cache) exitCure(a Artifact, curesExempt bool) { func (c *Cache) exitCure(a Artifact, curesExempt bool) {
if c.attr.Notify != nil {
c.attr.Notify <- false
}
if a.IsExclusive() { if a.IsExclusive() {
c.exclMu.Unlock() c.exclMu.Unlock()
} }
@@ -1906,10 +1806,7 @@ func (mr *measuredReader) Close() (err error) {
} }
if n > 0 { if n > 0 {
mr.c.msg.Verbosef( mr.c.msg.Verbosef("missed %d bytes on measured reader", n)
"%smissed %d bytes on measured reader%s",
mr.c.sgrWarn, n, mr.c.sgrRes,
)
} }
if err = mr.r.Close(); err != nil { if err = mr.r.Close(); err != nil {
@@ -1951,166 +1848,48 @@ func (r *RContext) NewMeasuredReader(
return r.cache.newMeasuredReader(rc, checksum) return r.cache.newMeasuredReader(rc, checksum)
} }
// tryChecksum dereferences a symlink to a cure outcome.
func (c *Cache) tryChecksum(pathname *check.Absolute) (
checksum unique.Handle[Checksum],
err error,
) {
_, err = os.Lstat(pathname.String())
if err == nil {
var name string
if name, err = os.Readlink(pathname.String()); err != nil {
return
}
buf := c.getIdentBuf()
err = Decode((*Checksum)(buf[:]), filepath.Base(name))
if err == nil {
checksum = unique.Make(Checksum(buf[:]))
}
c.putIdentBuf(buf)
}
return
}
// tryLocal attempts to obtain an [Artifact] outcome from the filesystem.
func (c *Cache) tryLocal(id unique.Handle[ID]) (unique.Handle[Checksum], error) {
return c.tryChecksum(c.base.Append(
dirIdentifier,
Encode(id.Value()),
))
}
// tryExtern attempts to obtain an [Artifact] outcome from extern. // tryExtern attempts to obtain an [Artifact] outcome from extern.
func (c *Cache) tryExtern(ctx context.Context, id unique.Handle[ID]) ( func (c *Cache) tryExtern(ctx context.Context, id unique.Handle[ID]) (
unique.Handle[Checksum], unique.Handle[Checksum],
io.ReadCloser,
error, error,
) { ) {
c.externMu.RLock() c.externMu.RLock()
defer c.externMu.RUnlock() defer c.externMu.RUnlock()
checksum, ok := c.externCache[id]
if !ok {
if c.extern == nil { if c.extern == nil {
return zeroChecksum, nil return zeroChecksum, nil, nil
} }
v, err := c.extern.Artifact(ctx, id) v, err := c.extern.Artifact(ctx, id)
if err != nil { if err != nil {
return zeroChecksum, err return zeroChecksum, nil, err
} }
if v == nil { if v == nil {
return zeroChecksum, nil return zeroChecksum, nil, nil
} }
checksum = unique.Make(*v) checksum := unique.Make(*v)
var got unique.Handle[Checksum] var got unique.Handle[Checksum]
if _, got, err = c.Cure(c.extern.Checksum(checksum)); err != nil { if _, got, err = c.Cure(c.extern.Checksum(checksum)); err != nil {
return checksum, err return checksum, nil, err
} else if got != checksum { } else if got != checksum {
return zeroChecksum, &ChecksumMismatchError{got.Value(), checksum.Value()} return zeroChecksum, nil, &ChecksumMismatchError{got.Value(), checksum.Value()}
}
}
return checksum, nil
}
// cureMany concurrently collects outcome of multiple [Artifact].
func (c *Cache) cureMany(
inputs []Artifact,
r map[Artifact]cureRes,
shallow bool,
) ([]bool, error) {
var wg sync.WaitGroup
wg.Add(len(inputs))
var mask []bool
res := make([]cureRes, len(inputs))
errs := make(DependencyCureError, 0, len(inputs))
var errsMu sync.Mutex
if shallow {
mask = make([]bool, len(inputs))
}
for i, d := range inputs {
if shallow {
if _, ok := d.(FloodArtifact); ok {
mask[i] = true
wg.Done()
continue
} }
if kc, ok := d.(KnownChecksum); ok { var status io.ReadCloser
res[i].checksum = unique.Make(kc.Checksum()) status, err = c.extern.Status(&RContext{common{ctx, c}}, id)
wg.Done() return checksum, status, err
continue
}
}
pending := pendingArtifactDep{d, &res[i], &errs, &errsMu, &wg}
go pending.cure(c)
}
wg.Wait()
if len(errs) > 0 {
return mask, &errs
}
for i, p := range res {
if shallow && mask[i] {
continue
}
r[inputs[i]] = p
}
return mask, nil
}
// HangingInputError describes an input of an [Artifact] on a sparse cache
// without an outcome available locally or via [External].
type HangingInputError unique.Handle[ID]
func (e HangingInputError) Error() string {
return Encode(unique.Handle[ID](e).Value()) + " is unavailable"
}
const (
// WNew indicates a cure entering the implementation.
WNew = iota
// WCache indicates an [Artifact] present in the cache.
WCache
// WSubstitute indicates an [Artifact] hitting a content-based input
// substitution.
WSubstitute
// WExternal indicates a cure entering the external cache.
WExternal
)
// WhenceString returns a printable string for a whence value.
func WhenceString(whence int) string {
switch whence {
case WNew:
return "new"
case WCache:
return "cache"
case WSubstitute:
return "substitute"
case WExternal:
return "external"
default:
return "invalid whence " + strconv.Itoa(whence)
}
} }
// cure implements Cure without acquiring a read lock on abortMu. cure must not // cure implements Cure without acquiring a read lock on abortMu. cure must not
// be entered during Abort. // be entered during Abort.
func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) ( func (c *Cache) cure(a Artifact, curesExempt bool) (
pathname *check.Absolute, pathname *check.Absolute,
checksum unique.Handle[Checksum], checksum unique.Handle[Checksum],
whence int,
err error, err error,
) { ) {
id := c.Ident(a) id := c.Ident(a)
if rebuild {
var v ID
_, _ = rand.Read(v[:])
id = unique.Make(v)
}
ids := Encode(id.Value()) ids := Encode(id.Value())
pathname = c.base.Append( pathname = c.base.Append(
dirIdentifier, dirIdentifier,
@@ -2133,15 +1912,26 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
) )
ctx, done, checksum, err = c.loadOrStoreIdent(id) ctx, done, checksum, err = c.loadOrStoreIdent(id)
if done == nil { if done == nil {
whence = WCache
return return
} else { } else {
defer func() { c.finaliseIdent(done, id, checksum, err) }() defer func() { c.finaliseIdent(done, id, checksum, err) }()
} }
checksum, err = c.tryChecksum(pathname) _, err = os.Lstat(pathname.String())
if err == nil || !errors.Is(err, os.ErrNotExist) { if err == nil {
whence = WCache var name string
if name, err = os.Readlink(pathname.String()); err != nil {
return
}
buf := c.getIdentBuf()
err = Decode((*Checksum)(buf[:]), filepath.Base(name))
if err == nil {
checksum = unique.Make(Checksum(buf[:]))
}
c.putIdentBuf(buf)
return
}
if !errors.Is(err, os.ErrNotExist) {
return return
} }
@@ -2170,9 +1960,8 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
) )
if errors.Is(err, os.ErrExist) { if errors.Is(err, os.ErrExist) {
c.msg.Verbosef( c.msg.Verbosef(
"creating alternative over %s%s%s for artifact %s%s%s", "creating alternative over %s for artifact %s",
c.sgrIdent, Encode(substitute.Value()), c.sgrRes, Encode(substitute.Value()), ids,
c.sgrIdent, ids, c.sgrRes,
) )
err = nil err = nil
} }
@@ -2197,7 +1986,7 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
checksums, checksums,
) )
if c.attr.Flags&CAssumeChecksum != 0 { if c.flags&CAssumeChecksum != 0 {
c.checksumMu.RLock() c.checksumMu.RLock()
checksumFi, err = os.Stat(checksumPathname.String()) checksumFi, err = os.Stat(checksumPathname.String())
c.checksumMu.RUnlock() c.checksumMu.RUnlock()
@@ -2212,21 +2001,17 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
} }
} }
whence = WNew
if c.msg.IsVerbose() { if c.msg.IsVerbose() {
rn := reportName(a, id) rn := reportName(a, id)
c.msg.Verbosef("curing %s%s%s...", c.sgrIdent, rn, c.sgrRes) c.msg.Verbosef("curing %s...", rn)
defer func() { defer func() {
if err != nil { if err != nil {
return return
} }
if checksums != "" { if checksums != "" {
c.msg.Verbosef( c.msg.Verbosef("cured %s checksum %s", rn, checksums)
"cured %s%s%s checksum %s%s%s",
c.sgrIdent, rn, c.sgrRes,
c.sgrIdent, checksums, c.sgrRes)
} else { } else {
c.msg.Verbosef("cured %s%s%s", c.sgrIdent, rn, c.sgrRes) c.msg.Verbosef("cured %s", rn)
} }
}() }()
} }
@@ -2234,7 +2019,6 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
// cure FileArtifact outside type switch to skip TContext initialisation // cure FileArtifact outside type switch to skip TContext initialisation
if f, ok := a.(FileArtifact); ok { if f, ok := a.(FileArtifact); ok {
if checksumFi != nil { if checksumFi != nil {
whence = WCache
if !checksumFi.Mode().IsRegular() { if !checksumFi.Mode().IsRegular() {
// unreachable // unreachable
err = InvalidFileModeError(checksumFi.Mode()) err = InvalidFileModeError(checksumFi.Mode())
@@ -2242,17 +2026,12 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
return return
} }
perm := os.FileMode(0400)
if f.IsExecutable() {
perm = 0500
}
work := c.base.Append(dirWork, ids) work := c.base.Append(dirWork, ids)
var w *os.File var w *os.File
if w, err = os.OpenFile( if w, err = os.OpenFile(
work.String(), work.String(),
os.O_CREATE|os.O_EXCL|os.O_WRONLY, os.O_CREATE|os.O_EXCL|os.O_WRONLY,
perm, 0400,
); err != nil { ); err != nil {
return return
} }
@@ -2274,7 +2053,7 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
} }
r, err = f.Cure(&RContext{common{ctx, c}}) r, err = f.Cure(&RContext{common{ctx, c}})
if err == nil { if err == nil {
if checksumPathname == nil || c.attr.Flags&CValidateKnown != 0 { if checksumPathname == nil || c.flags&CValidateKnown != 0 {
h := sha512.New384() h := sha512.New384()
hbw := c.getWriter(h) hbw := c.getWriter(h)
_, err = io.Copy(w, io.TeeReader(r, hbw)) _, err = io.Copy(w, io.TeeReader(r, hbw))
@@ -2291,7 +2070,7 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
if checksumPathname == nil { if checksumPathname == nil {
checksum = unique.Make(Checksum(buf[:])) checksum = unique.Make(Checksum(buf[:]))
checksums = Encode(Checksum(buf[:])) checksums = Encode(Checksum(buf[:]))
} else if c.attr.Flags&CValidateKnown != 0 { } else if c.flags&CValidateKnown != 0 {
if got := Checksum(buf[:]); got != checksum.Value() { if got := Checksum(buf[:]); got != checksum.Value() {
err = &ChecksumMismatchError{ err = &ChecksumMismatchError{
Got: got, Got: got,
@@ -2321,11 +2100,7 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
c.exitCure(a, curesExempt) c.exitCure(a, curesExempt)
if err != nil { if err != nil {
if c.msg.IsVerbose() { if c.msg.IsVerbose() {
c.msg.Verbosef( c.msg.Verbosef("cure file %s: %v", reportName(f, id), err)
"cure file %s%s%s: %s%v%s",
c.sgrIdent, reportName(f, id), c.sgrRes,
c.sgrErr, err, c.sgrRes,
)
} }
return return
} }
@@ -2348,7 +2123,6 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
} }
if checksumFi != nil { if checksumFi != nil {
whence = WCache
if !checksumFi.Mode().IsDir() { if !checksumFi.Mode().IsDir() {
// unreachable // unreachable
err = InvalidFileModeError(checksumFi.Mode()) err = InvalidFileModeError(checksumFi.Mode())
@@ -2372,79 +2146,37 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
c.exitCure(a, curesExempt) c.exitCure(a, curesExempt)
if err != nil { if err != nil {
if c.msg.IsVerbose() { if c.msg.IsVerbose() {
c.msg.Verbosef( c.msg.Verbosef("cure trivial %s: %v", reportName(ca, id), err)
"cure trivial %s%s%s: %s%v%s",
c.sgrIdent, reportName(ca, id), c.sgrRes,
c.sgrErr, err, c.sgrRes,
)
} }
return return
} }
break break
case FloodArtifact: case FloodArtifact:
var externChecksum unique.Handle[Checksum] deps := a.Dependencies()
if externChecksum, err = c.tryExtern(ctx, id); err != nil { f := FContext{t, make(map[Artifact]cureRes, len(deps))}
if c.msg.IsVerbose() {
c.msg.Verbosef( var wg sync.WaitGroup
"extern %s%s%s: %s%v%s", wg.Add(len(deps))
c.sgrIdent, reportName(ca, id), c.sgrRes, res := make([]cureRes, len(deps))
c.sgrErr, err, c.sgrRes, errs := make(DependencyCureError, 0, len(deps))
) var errsMu sync.Mutex
for i, d := range deps {
pending := pendingArtifactDep{d, &res[i], &errs, &errsMu, &wg}
go pending.cure(c)
} }
wg.Wait()
if len(errs) > 0 {
err = &errs
return return
} }
extern := externChecksum != zeroChecksum for i, p := range res {
shallow := extern && c.attr.Flags&CExternShallow != 0 f.deps[deps[i]] = p
inputs := a.Inputs()
f := FContext{t, make(map[Artifact]cureRes, len(inputs))}
var mask []bool
if mask, err = c.cureMany(inputs, f.inputs, shallow); err != nil {
return
}
if shallow {
for i, d := range inputs {
if !mask[i] {
continue
}
if kc, ok := d.(KnownChecksum); ok {
f.inputs[d] = cureRes{checksum: unique.Make(kc.Checksum())}
continue
}
var sum unique.Handle[Checksum]
did := c.Ident(d)
sum, err = c.tryLocal(did)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return
}
sum, err = c.tryExtern(ctx, did)
if err != nil {
return
}
if sum == zeroChecksum {
if c.msg.IsVerbose() {
c.msg.Verbosef(
"input %s%s%s not available",
c.sgrIdent, reportName(d, did), c.sgrRes,
)
}
err = HangingInputError(did)
return
}
}
f.inputs[d] = cureRes{checksum: sum}
}
} }
sh := sha512.New384() sh := sha512.New384()
err = c.encode(sh, a, f.inputs) err = c.encode(sh, a, f.deps)
if err != nil { if err != nil {
return return
} }
@@ -2459,14 +2191,13 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
substitutes, substitutes,
) )
if !rebuild && c.attr.Flags&CIgnoreSubstitutes == 0 { if c.flags&CIgnoreSubstitutes == 0 {
var substituteChecksum unique.Handle[Checksum] var substituteChecksum unique.Handle[Checksum]
substituteChecksum, err = c.loadSubstitute(substitute) substituteChecksum, err = c.loadSubstitute(substitute)
if err != nil { if err != nil {
return return
} }
if substituteChecksum != zeroChecksum { if substituteChecksum != zeroChecksum {
whence = WSubstitute
checksum = substituteChecksum checksum = substituteChecksum
checksums = Encode(checksum.Value()) checksums = Encode(checksum.Value())
checksumPathname = c.base.Append( checksumPathname = c.base.Append(
@@ -2490,25 +2221,25 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
defer f.destroy(&err) defer f.destroy(&err)
if extern { var (
whence = WExternal externChecksum unique.Handle[Checksum]
if checksum != zeroChecksum && externChecksum != checksum { externStatus io.ReadCloser
err = &ChecksumMismatchError{externChecksum.Value(), checksum.Value()}
if c.msg.IsVerbose() {
c.msg.Verbosef(
"extern %s%s%s: %s%v%s",
c.sgrIdent, reportName(ca, id), c.sgrRes,
c.sgrErr, err, c.sgrRes,
) )
if externChecksum, externStatus, err = c.tryExtern(ctx, id); err != nil {
if c.msg.IsVerbose() {
c.msg.Verbosef("extern %s: %v", reportName(ca, id), err)
} }
return return
} }
if externChecksum != zeroChecksum {
var externStatus io.ReadCloser if checksum != zeroChecksum && externChecksum != checksum {
c.externMu.RLock() if externStatus != nil {
externStatus, err = c.extern.Status(&RContext{common{ctx, c}}, id) _ = externStatus.Close()
c.externMu.RUnlock() }
if err != nil { err = &ChecksumMismatchError{externChecksum.Value(), checksum.Value()}
if c.msg.IsVerbose() {
c.msg.Verbosef("extern %s: %v", reportName(ca, id), err)
}
return return
} }
@@ -2528,12 +2259,10 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
return return
} else if err = externStatus.Close(); err != nil { } else if err = externStatus.Close(); err != nil {
return return
} else if !rebuild { } else if err = f.linkSubstitute(ids, substitutes); err != nil {
if err = f.linkSubstitute(ids, substitutes); err != nil {
return return
} }
} }
}
return return
} }
@@ -2543,16 +2272,12 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
err = ca.Cure(&f) err = ca.Cure(&f)
c.exitCure(a, curesExempt) c.exitCure(a, curesExempt)
if !rebuild && err == nil { if err == nil {
err = f.linkSubstitute(ids, substitutes) err = f.linkSubstitute(ids, substitutes)
} }
if err != nil { if err != nil {
if c.msg.IsVerbose() { if c.msg.IsVerbose() {
c.msg.Verbosef( c.msg.Verbosef("cure %s: %v", reportName(ca, id), err)
"cure %s%s%s: %s%v%s",
c.sgrIdent, reportName(ca, id), c.sgrRes,
c.sgrErr, err, c.sgrRes,
)
} }
return return
} }
@@ -2603,11 +2328,7 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
Want: checksum.Value(), Want: checksum.Value(),
} }
if c.msg.IsVerbose() { if c.msg.IsVerbose() {
c.msg.Verbosef( c.msg.Verbosef("validate %s: %v", reportName(a, id), err)
"validate %s%s%s: %s%v%s",
c.sgrIdent, reportName(a, id), c.sgrRes,
c.sgrErr, err, c.sgrRes,
)
} }
return return
} }
@@ -2645,7 +2366,7 @@ func (pending *pendingArtifactDep) cure(c *Cache) {
defer pending.Done() defer pending.Done()
var err error var err error
pending.resP.pathname, pending.resP.checksum, _, err = c.cure(pending.a, false, false) pending.resP.pathname, pending.resP.checksum, err = c.cure(pending.a, false)
if err == nil { if err == nil {
return return
} }
@@ -2749,10 +2470,6 @@ func (c *Cache) Close() {
c.wg.Wait() c.wg.Wait()
close(c.cures) close(c.cures)
c.unlock() c.unlock()
if c.attr.Notify != nil {
close(c.attr.Notify)
}
} }
// UnsupportedVariantError describes an on-disk cache with an extension variant // UnsupportedVariantError describes an on-disk cache with an extension variant
@@ -2769,24 +2486,6 @@ var (
ErrWouldPromote = errors.New("operation would promote unextended cache") ErrWouldPromote = errors.New("operation would promote unextended cache")
) )
// CacheAttr holds the attributes that will be applied to a new [Cache] opened
// by [Open].
type CacheAttr struct {
// Concurrent cures of a [FloodArtifact] dependency graph.
Cures int
// Options affecting [Cache] behaviour.
Flags int
// Preferred job count, when applicable.
Jobs int
// Preferred loadavg target, when applicable.
Load int
// Optional cure entry and exit notification.
Notify chan<- bool
// Omit the [lockedfile] lock.
skipLock bool
}
// Open returns the address of a newly opened instance of [Cache]. // Open returns the address of a newly opened instance of [Cache].
// //
// Concurrent cures of a [FloodArtifact] dependency graph is limited to the // Concurrent cures of a [FloodArtifact] dependency graph is limited to the
@@ -2803,8 +2502,20 @@ type CacheAttr struct {
func Open( func Open(
ctx context.Context, ctx context.Context,
msg message.Msg, msg message.Msg,
flags, cures, jobs int,
base *check.Absolute, base *check.Absolute,
attr *CacheAttr, ) (*Cache, error) {
return open(ctx, msg, flags, cures, jobs, base, true)
}
// open implements Open but allows omitting the [lockedfile] lock when called
// from a test. This is used to simulate invalid states in the test suite.
func open(
ctx context.Context,
msg message.Msg,
flags, cures, jobs int,
base *check.Absolute,
lock bool,
) (*Cache, error) { ) (*Cache, error) {
openMu.Lock() openMu.Lock()
defer openMu.Unlock() defer openMu.Unlock()
@@ -2814,19 +2525,11 @@ func Open(
panic("attempting to open cache with incomplete variant setup") panic("attempting to open cache with incomplete variant setup")
} }
var a CacheAttr if cures < 1 {
if attr != nil { cures = runtime.NumCPU()
a = *attr
} }
if jobs < 1 {
if a.Cures < 1 { jobs = runtime.NumCPU()
a.Cures = runtime.NumCPU()
}
if a.Jobs < 1 {
a.Jobs = runtime.NumCPU()
}
if a.Load < 1 {
a.Load = runtime.NumCPU() + 2
} }
for _, name := range []string{ for _, name := range []string{
@@ -2848,8 +2551,9 @@ func Open(
c := Cache{ c := Cache{
parent: ctx, parent: ctx,
cures: make(chan struct{}, a.Cures), cures: make(chan struct{}, cures),
attr: a, flags: flags,
jobs: jobs,
msg: msg, msg: msg,
base: base, base: base,
@@ -2866,7 +2570,7 @@ func Open(
} }
c.toplevel.Store(newToplevel(ctx)) c.toplevel.Store(newToplevel(ctx))
if !a.skipLock || !testing.Testing() { if lock || !testing.Testing() {
if unlock, err := lockedfile.MutexAt( if unlock, err := lockedfile.MutexAt(
base.Append(fileLock).String(), base.Append(fileLock).String(),
).Lock(); err != nil { ).Lock(); err != nil {
@@ -2927,7 +2631,7 @@ func Open(
} }
} else if s := string(p); s == "" { } else if s := string(p); s == "" {
if extension != "" { if extension != "" {
if a.Flags&CPromoteVariant == 0 { if flags&CPromoteVariant == 0 {
c.unlock() c.unlock()
return nil, ErrWouldPromote return nil, ErrWouldPromote
} }
@@ -2948,13 +2652,6 @@ func Open(
return nil, UnsupportedVariantError(s) return nil, UnsupportedVariantError(s)
} }
if a.Flags&CColourOutput != 0 {
c.sgrRes = "\x1b[0m"
c.sgrIdent = "\x1b[1m"
c.sgrWarn = "\x1b[35m"
c.sgrErr = "\x1b[1;31m"
}
return &c, nil return &c, nil
} }
@@ -2973,8 +2670,6 @@ func IsCollected(err error) bool { return errors.As(err, new(Collected)) }
// [pkg.Artifact]. It returns [Collected]. // [pkg.Artifact]. It returns [Collected].
type Collect []Artifact type Collect []Artifact
var _ Artifact = new(Collect)
// Cure returns [Collected]. // Cure returns [Collected].
func (*Collect) Cure(*FContext) error { return Collected{} } func (*Collect) Cure(*FContext) error { return Collected{} }
@@ -2984,8 +2679,8 @@ func (*Collect) Kind() Kind { return kindCollection }
// Params is a noop: dependencies are already represented in the header. // Params is a noop: dependencies are already represented in the header.
func (*Collect) Params(*IContext) {} func (*Collect) Params(*IContext) {}
// Inputs returns [Collect] as is. // Dependencies returns [Collect] as is.
func (c *Collect) Inputs() []Artifact { return *c } func (c *Collect) Dependencies() []Artifact { return *c }
// IsExclusive returns false: Cure is a noop. // IsExclusive returns false: Cure is a noop.
func (*Collect) IsExclusive() bool { return false } func (*Collect) IsExclusive() bool { return false }
+100 -249
View File
@@ -35,14 +35,14 @@ import (
"hakurei.app/message" "hakurei.app/message"
) )
var skipLock = func() pkg.CacheAttr { //go:linkname unsafeOpen hakurei.app/internal/pkg.open
var attr pkg.CacheAttr func unsafeOpen(
*(*bool)(unsafe.Pointer(reflect.ValueOf(&attr). ctx context.Context,
Elem(). msg message.Msg,
FieldByName("skipLock"). flags, cures, jobs int,
UnsafeAddr())) = true base *check.Absolute,
return attr lock bool,
}() ) (*pkg.Cache, error)
var ( var (
// extension is a string uniquely identifying a set of custom [Artifact] // extension is a string uniquely identifying a set of custom [Artifact]
@@ -142,7 +142,7 @@ type stubArtifact struct {
func (a *stubArtifact) Kind() pkg.Kind { return a.kind } func (a *stubArtifact) Kind() pkg.Kind { return a.kind }
func (a *stubArtifact) Params(ctx *pkg.IContext) { ctx.Write(a.params) } func (a *stubArtifact) Params(ctx *pkg.IContext) { ctx.Write(a.params) }
func (a *stubArtifact) Inputs() []pkg.Artifact { return a.deps } func (a *stubArtifact) Dependencies() []pkg.Artifact { return a.deps }
func (a *stubArtifact) Cure(t *pkg.TContext) error { return a.cure(t) } func (a *stubArtifact) Cure(t *pkg.TContext) error { return a.cure(t) }
func (*stubArtifact) IsExclusive() bool { return false } func (*stubArtifact) IsExclusive() bool { return false }
@@ -158,7 +158,7 @@ type stubArtifactF struct {
func (a *stubArtifactF) Kind() pkg.Kind { return a.kind } func (a *stubArtifactF) Kind() pkg.Kind { return a.kind }
func (a *stubArtifactF) Params(ctx *pkg.IContext) { ctx.Write(a.params) } func (a *stubArtifactF) Params(ctx *pkg.IContext) { ctx.Write(a.params) }
func (a *stubArtifactF) Inputs() []pkg.Artifact { return a.deps } func (a *stubArtifactF) Dependencies() []pkg.Artifact { return a.deps }
func (a *stubArtifactF) Cure(f *pkg.FContext) error { return a.cure(f) } func (a *stubArtifactF) Cure(f *pkg.FContext) error { return a.cure(f) }
func (a *stubArtifactF) IsExclusive() bool { return a.excl } func (a *stubArtifactF) IsExclusive() bool { return a.excl }
@@ -170,8 +170,6 @@ type stubFile struct {
stubArtifact stubArtifact
} }
func (*stubFile) IsExecutable() bool { return false }
func (a *stubFile) Cure(*pkg.RContext) (io.ReadCloser, error) { func (a *stubFile) Cure(*pkg.RContext) (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(a.data)), a.err return io.NopCloser(bytes.NewReader(a.data)), a.err
} }
@@ -332,7 +330,7 @@ func TestIdent(t *testing.T) {
}, },
nil, nil,
}, unique.Make[pkg.ID](pkg.MustDecode( }, unique.Make[pkg.ID](pkg.MustDecode(
"fkmge-H56Ph2LzNNbEUjTypes8LfOxrF_CEXg07kq6ho_nbTNvzQde9-bjB7gyB_", "97Y85QewssfPbNIN9cyNhzD4e6dLHcDTU8rb2c34k-aCrZfBNXFUc0duPiLFFcw_",
))}, ))},
} }
@@ -341,7 +339,7 @@ func TestIdent(t *testing.T) {
var cache *pkg.Cache var cache *pkg.Cache
if a, err := check.NewAbs(t.TempDir()); err != nil { if a, err := check.NewAbs(t.TempDir()); err != nil {
t.Fatal(err) t.Fatal(err)
} else if cache, err = pkg.Open(t.Context(), msg, a, nil); err != nil { } else if cache, err = pkg.Open(t.Context(), msg, 0, 0, 0, a); err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Cleanup(cache.Close) t.Cleanup(cache.Close)
@@ -521,10 +519,7 @@ func checkWithCache(t *testing.T, testCases []cacheTestCase) {
} }
var scrubFunc func() error // scrub after hashing var scrubFunc func() error // scrub after hashing
if c, err := pkg.Open(t.Context(), msg, base, &pkg.CacheAttr{ if c, err := pkg.Open(t.Context(), msg, flags, 1<<4, 0, base); err != nil {
Cures: 1 << 4,
Flags: flags,
}); err != nil {
t.Fatalf("Open: error = %v", err) t.Fatalf("Open: error = %v", err)
} else { } else {
t.Cleanup(c.Close) t.Cleanup(c.Close)
@@ -632,7 +627,6 @@ type cureStep struct {
pathname *check.Absolute pathname *check.Absolute
output expectsKnown output expectsKnown
whence int
err error err error
} }
@@ -652,7 +646,7 @@ func cureMany(t *testing.T, c *pkg.Cache, steps []cureStep) {
for _, step := range steps { for _, step := range steps {
t.Log("cure step:", step.name) t.Log("cure step:", step.name)
if pathname, checksum, whence, err := c.CureWhence(step.a); !reflect.DeepEqual(err, step.err) { if pathname, checksum, err := c.Cure(step.a); !reflect.DeepEqual(err, step.err) {
faults, _err := c.ReadFaults(step.a) faults, _err := c.ReadFaults(step.a)
if _err != nil { if _err != nil {
t.Errorf("ReadFaults: error = %v", _err) t.Errorf("ReadFaults: error = %v", _err)
@@ -669,8 +663,6 @@ func cureMany(t *testing.T, c *pkg.Cache, steps []cureStep) {
t.Logf("faulting cure terminated %s ago", time.Since(faults[0].Time())) t.Logf("faulting cure terminated %s ago", time.Since(faults[0].Time()))
} }
t.Fatalf("Cure: error = %v, want %v", err, step.err) t.Fatalf("Cure: error = %v, want %v", err, step.err)
} else if step.whence != whence {
t.Fatalf("Cure: whence = %s, want %s", pkg.WhenceString(whence), pkg.WhenceString(step.whence))
} else if step.pathname != ignorePathname && !pathname.Is(step.pathname) { } else if step.pathname != ignorePathname && !pathname.Is(step.pathname) {
t.Fatalf("Cure: pathname = %q, want %q", pathname, step.pathname) t.Fatalf("Cure: pathname = %q, want %q", pathname, step.pathname)
} else if step.output == nil || checksum != makeChecksumH(step.output.hash()) { } else if step.output == nil || checksum != makeChecksumH(step.output.hash()) {
@@ -782,28 +774,28 @@ func TestCache(t *testing.T) {
identifier, identifier,
new(testdata.hash()), new(testdata.hash()),
testdata, nil, testdata, nil,
), wantPathname, testdata, pkg.WNew, nil}, ), wantPathname, testdata, nil},
{"identical content", newStubFile( {"identical content", newStubFile(
pkg.KindHTTPGet, pkg.KindHTTPGet,
identifier0, identifier0,
new(testdata.hash()), new(testdata.hash()),
testdata, nil, testdata, nil,
), wantPathname0, testdata, pkg.WCache, nil}, ), wantPathname0, testdata, nil},
{"existing entry", newStubFile( {"existing entry", newStubFile(
pkg.KindHTTPGet, pkg.KindHTTPGet,
identifier, identifier,
new(testdata.hash()), new(testdata.hash()),
testdata, nil, testdata, nil,
), wantPathname, testdata, pkg.WCache, nil}, ), wantPathname, testdata, nil},
{"checksum mismatch", newStubFile( {"checksum mismatch", newStubFile(
pkg.KindHTTPGet, pkg.KindHTTPGet,
pkg.ID{0xff, 0}, pkg.ID{0xff, 0},
new(pkg.Checksum), new(pkg.Checksum),
testdata, nil, testdata, nil,
), nil, nil, pkg.WNew, &pkg.ChecksumMismatchError{ ), nil, nil, &pkg.ChecksumMismatchError{
Got: testdata.hash(), Got: testdata.hash(),
}}, }},
@@ -824,13 +816,13 @@ func TestCache(t *testing.T) {
0x36, 0x65, 0x59, 0x33, 0xec, 0x2b, 0x36, 0x65, 0x59, 0x33, 0xec, 0x2b,
0x41, 0x34, 0x65, 0x96, 0x68, 0x17, 0x41, 0x34, 0x65, 0x96, 0x68, 0x17,
0xa9, 0xc2, 0x08, 0xa1, 0x17, 0x17, 0xa9, 0xc2, 0x08, 0xa1, 0x17, 0x17,
}, pkg.WNew, nil}, }, nil},
{"incomplete implementation", struct{ pkg.Artifact }{&stubArtifact{ {"incomplete implementation", struct{ pkg.Artifact }{&stubArtifact{
kind: pkg.KindExec, kind: pkg.KindExec,
params: []byte("artifact overridden to be incomplete"), params: []byte("artifact overridden to be incomplete"),
}}, nil, nil, pkg.WNew, pkg.InvalidArtifactError(pkg.MustDecode( }}, nil, nil, pkg.InvalidArtifactError(pkg.MustDecode(
"poVrv3zNINbHd-WUJihwgWQwJpbHURFHfpY7Zux-ztto_bQhhS5PdpbNMjdkXv-V", "E__uZ1sLIvb84vzSm5Uezb03RogsiaeTt1nfIVv8TKnnf4LqwtSi-smdHhlkZrUJ",
))}, ))},
{"error passthrough", newStubFile( {"error passthrough", newStubFile(
@@ -838,18 +830,18 @@ func TestCache(t *testing.T) {
pkg.ID{0xff, 1}, pkg.ID{0xff, 1},
nil, nil,
nil, stub.UniqueError(0xcafe), nil, stub.UniqueError(0xcafe),
), nil, nil, pkg.WNew, stub.UniqueError(0xcafe)}, ), nil, nil, stub.UniqueError(0xcafe)},
{"error caching", newStubFile( {"error caching", newStubFile(
pkg.KindHTTPGet, pkg.KindHTTPGet,
pkg.ID{0xff, 1}, pkg.ID{0xff, 1},
nil, nil,
nil, nil, nil, nil,
), nil, nil, pkg.WCache, stub.UniqueError(0xcafe)}, ), nil, nil, stub.UniqueError(0xcafe)},
{"cache hit bad type", overrideChecksum{testdata.hash(), overrideIdent{pkg.ID{0xff, 2}, &stubArtifact{ {"cache hit bad type", overrideChecksum{testdata.hash(), overrideIdent{pkg.ID{0xff, 2}, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
}}}, nil, nil, pkg.WCache, pkg.InvalidFileModeError( }}}, nil, nil, pkg.InvalidFileModeError(
0400, 0400,
)}, )},
@@ -861,7 +853,7 @@ func TestCache(t *testing.T) {
cure: func(f *pkg.FContext) error { cure: func(f *pkg.FContext) error {
panic("attempting to cure impossible artifact") panic("attempting to cure impossible artifact")
}, },
}, nil, nil, pkg.WNew, &pkg.DependencyCureError{ }, nil, nil, &pkg.DependencyCureError{
{ {
A: failingFile, A: failingFile,
Err: struct { Err: struct {
@@ -872,10 +864,10 @@ func TestCache(t *testing.T) {
}}, }},
}) })
if c0, err := pkg.Open( if c0, err := unsafeOpen(
t.Context(), t.Context(),
message.New(nil), message.New(nil),
base, &skipLock, 0, 0, 0, base, false,
); err != nil { ); err != nil {
t.Fatalf("open: error = %v", err) t.Fatalf("open: error = %v", err)
} else { } else {
@@ -883,7 +875,7 @@ func TestCache(t *testing.T) {
cureMany(t, c0, []cureStep{ cureMany(t, c0, []cureStep{
{"cache hit ident", overrideIdent{ {"cache hit ident", overrideIdent{
id: identifier, id: identifier,
}, wantPathname, testdata, pkg.WCache, nil}, }, wantPathname, testdata, nil},
{"cache miss checksum match", newStubFile( {"cache miss checksum match", newStubFile(
pkg.KindHTTPGet, pkg.KindHTTPGet,
@@ -894,7 +886,7 @@ func TestCache(t *testing.T) {
), base.Append( ), base.Append(
"identifier", "identifier",
pkg.Encode(testdata.hash()), pkg.Encode(testdata.hash()),
), testdata, pkg.WNew, nil}, ), testdata, nil},
}) })
// cure after close // cure after close
@@ -1024,30 +1016,30 @@ func TestCache(t *testing.T) {
{"initial directory", overrideChecksum{want.hash(), overrideIdent{id, &stubArtifact{ {"initial directory", overrideChecksum{want.hash(), overrideIdent{id, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
cure: makeSample, cure: makeSample,
}}}, wantPathname, want, pkg.WNew, nil}, }}}, wantPathname, want, nil},
{"identical identifier", overrideChecksum{want.hash(), overrideIdent{id, &stubArtifact{ {"identical identifier", overrideChecksum{want.hash(), overrideIdent{id, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
}}}, wantPathname, want, pkg.WCache, nil}, }}}, wantPathname, want, nil},
{"identical checksum", overrideIdent{id0, &stubArtifact{ {"identical checksum", overrideIdent{id0, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
cure: makeSample, cure: makeSample,
}}, wantPathname0, want, pkg.WNew, nil}, }}, wantPathname0, want, nil},
{"cure fault", overrideIdent{pkg.ID{0xff, 0}, &stubArtifact{ {"cure fault", overrideIdent{pkg.ID{0xff, 0}, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
cure: func(t *pkg.TContext) error { cure: func(t *pkg.TContext) error {
return makeGarbage(t.GetWorkDir(), stub.UniqueError(0xcafe)) return makeGarbage(t.GetWorkDir(), stub.UniqueError(0xcafe))
}, },
}}, nil, nil, pkg.WNew, stub.UniqueError(0xcafe)}, }}, nil, nil, stub.UniqueError(0xcafe)},
{"checksum mismatch", overrideChecksum{pkg.Checksum{}, overrideIdent{pkg.ID{0xff, 1}, &stubArtifact{ {"checksum mismatch", overrideChecksum{pkg.Checksum{}, overrideIdent{pkg.ID{0xff, 1}, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
cure: func(t *pkg.TContext) error { cure: func(t *pkg.TContext) error {
return makeGarbage(t.GetWorkDir(), nil) return makeGarbage(t.GetWorkDir(), nil)
}, },
}}}, nil, nil, pkg.WNew, &pkg.ChecksumMismatchError{ }}}, nil, nil, &pkg.ChecksumMismatchError{
Got: pkg.MustDecode( Got: pkg.MustDecode(
"CUx-3hSbTWPsbMfDhgalG4Ni_GmR9TnVX8F99tY_P5GtkYvczg9RrF5zO0jX9XYT", "CUx-3hSbTWPsbMfDhgalG4Ni_GmR9TnVX8F99tY_P5GtkYvczg9RrF5zO0jX9XYT",
), ),
@@ -1058,7 +1050,7 @@ func TestCache(t *testing.T) {
pkg.ID{0xff, 2}, pkg.ID{0xff, 2},
new(want.hash()), new(want.hash()),
testdata, nil, testdata, nil,
), nil, nil, pkg.WCache, pkg.InvalidFileModeError( ), nil, nil, pkg.InvalidFileModeError(
fs.ModeDir | 0500, fs.ModeDir | 0500,
)}, )},
@@ -1072,7 +1064,7 @@ func TestCache(t *testing.T) {
_, err = io.ReadAll(r) _, err = io.ReadAll(r)
return err return err
}, },
}}, nil, nil, pkg.WNew, &os.PathError{ }}, nil, nil, &os.PathError{
Op: "read", Op: "read",
Path: base.Append( Path: base.Append(
"checksum", "checksum",
@@ -1086,14 +1078,14 @@ func TestCache(t *testing.T) {
cure: func(t *pkg.TContext) error { cure: func(t *pkg.TContext) error {
return nil return nil
}, },
}}, nil, nil, pkg.WNew, pkg.NoOutputError{}}, }}, nil, nil, pkg.NoOutputError{}},
{"file output", overrideIdent{pkg.ID{0xff, 5}, &stubArtifact{ {"file output", overrideIdent{pkg.ID{0xff, 5}, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
cure: func(t *pkg.TContext) error { cure: func(t *pkg.TContext) error {
return os.WriteFile(t.GetWorkDir().String(), []byte{0}, 0400) return os.WriteFile(t.GetWorkDir().String(), []byte{0}, 0400)
}, },
}}, nil, nil, pkg.WNew, errors.New("non-file artifact produced regular file")}, }}, nil, nil, errors.New("non-file artifact produced regular file")},
{"symlink output", overrideIdent{pkg.ID{0xff, 6}, &stubArtifact{ {"symlink output", overrideIdent{pkg.ID{0xff, 6}, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
@@ -1103,7 +1095,7 @@ func TestCache(t *testing.T) {
t.GetWorkDir().String(), t.GetWorkDir().String(),
) )
}, },
}}, nil, nil, pkg.WNew, pkg.InvalidFileModeError( }}, nil, nil, pkg.InvalidFileModeError(
fs.ModeSymlink | 0777, fs.ModeSymlink | 0777,
)}, )},
@@ -1123,8 +1115,8 @@ func TestCache(t *testing.T) {
}, },
}, base.Append( }, base.Append(
"identifier", "identifier",
"gIH5eti07nBAhVti3ZTn8EIGOBCZWWvNpxg5CmCZlDzIC4mBdomPmQLeEpf1IQg9", "xMDWovje7OfyIaDy_2VnjpKxRqSOQ_LoeD946t-3WsS2V2SeMJ7nDGrNfpa4Pbc-",
), want, pkg.WNew, nil}, ), want, nil},
{"substitutable", &stubArtifactF{ {"substitutable", &stubArtifactF{
kind: pkg.KindExec, kind: pkg.KindExec,
@@ -1142,14 +1134,14 @@ func TestCache(t *testing.T) {
}, },
}, base.Append( }, base.Append(
"identifier", "identifier",
"VGjpp7NVw3OYLpgOQumfM0KcJgY3suz6torykUczcg4bGT24ySkPn5hy-4dNqqkR", "k2ilgG5KQ9NXnMoT2oB6NdwOnSPRn_H24oXQc4l6qOYIxIG9XfuEczeyrR8UEv_f",
), want, pkg.WSubstitute, nil}, ), want, nil},
}) })
if c0, err := pkg.Open( if c0, err := unsafeOpen(
t.Context(), t.Context(),
message.New(nil), message.New(nil),
base, &skipLock, 0, 0, 0, base, false,
); err != nil { ); err != nil {
t.Fatalf("open: error = %v", err) t.Fatalf("open: error = %v", err)
} else { } else {
@@ -1171,8 +1163,8 @@ func TestCache(t *testing.T) {
}, },
}, base.Append( }, base.Append(
"identifier", "identifier",
"zeZ4JEZ0atAKl6Am67cBID6-KR18nZGgeYfU4qTuYaWhQC4eAn0dyTXdIBH9Bos8", "_EmV5nsYZ2UWHgRmLDMU8i-rJWDx-kv5_1pFrzQI7vMMCM5mAXivO8UZtVfOqMR_",
), want, pkg.WSubstitute, nil}, ), want, nil},
}) })
} }
@@ -1196,15 +1188,15 @@ func TestCache(t *testing.T) {
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")}, "identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
"identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")}, "identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
"identifier/zeZ4JEZ0atAKl6Am67cBID6-KR18nZGgeYfU4qTuYaWhQC4eAn0dyTXdIBH9Bos8": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")}, "identifier/_EmV5nsYZ2UWHgRmLDMU8i-rJWDx-kv5_1pFrzQI7vMMCM5mAXivO8UZtVfOqMR_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
"identifier/___9_QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO")}, "identifier/___9_QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO")},
"identifier/_wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO")}, "identifier/_wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO")},
"identifier/_woAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO")}, "identifier/_woAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO")},
"identifier/VGjpp7NVw3OYLpgOQumfM0KcJgY3suz6torykUczcg4bGT24ySkPn5hy-4dNqqkR": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")}, "identifier/k2ilgG5KQ9NXnMoT2oB6NdwOnSPRn_H24oXQc4l6qOYIxIG9XfuEczeyrR8UEv_f": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
"identifier/gIH5eti07nBAhVti3ZTn8EIGOBCZWWvNpxg5CmCZlDzIC4mBdomPmQLeEpf1IQg9": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")}, "identifier/xMDWovje7OfyIaDy_2VnjpKxRqSOQ_LoeD946t-3WsS2V2SeMJ7nDGrNfpa4Pbc-": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
"substitute/nWPg893oqn_YNN8kgfZrs_LVLwP90e0mKxqU8UwoazQh-yBPwhoviD7of6YALx-M": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")}, "substitute/OyBGorh72Z9kVw35JUa8FbqDbpR4DqT-MX1jic0uKN5PdYmUBiAF38BRsIRnBigf": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
"work": {Mode: fs.ModeDir | 0700}, "work": {Mode: fs.ModeDir | 0700},
}}, }},
@@ -1243,7 +1235,7 @@ func TestCache(t *testing.T) {
pkg.ID{0xff, 1}, pkg.ID{0xff, 1},
nil, nil,
nil, stub.UniqueError(0xbad), nil, stub.UniqueError(0xbad),
), nil, nil, pkg.WNew, stub.UniqueError(0xbad)}, ), nil, nil, stub.UniqueError(0xbad)},
{"file output", overrideIdent{pkg.ID{0xff, 2}, &stubArtifact{ {"file output", overrideIdent{pkg.ID{0xff, 2}, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
@@ -1254,7 +1246,7 @@ func TestCache(t *testing.T) {
0400, 0400,
) )
}, },
}}, nil, nil, pkg.WNew, errors.New( }}, nil, nil, errors.New(
"non-file artifact produced regular file", "non-file artifact produced regular file",
)}, )},
}) })
@@ -1371,14 +1363,14 @@ func TestCache(t *testing.T) {
}}}, base.Append( }}}, base.Append(
"identifier", "identifier",
pkg.Encode(pkg.ID{0xff, 0}), pkg.Encode(pkg.ID{0xff, 0}),
), want, pkg.WNew, nil}, ), want, nil},
{"reject", overrideChecksum{want.hash(), overrideIdent{pkg.ID{0xfe, 1}, &stubArtifact{ {"reject", overrideChecksum{want.hash(), overrideIdent{pkg.ID{0xfe, 1}, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
cure: func(t *pkg.TContext) error { cure: func(t *pkg.TContext) error {
return makeGarbage(t.GetWorkDir(), stub.UniqueError(0xbad)) return makeGarbage(t.GetWorkDir(), stub.UniqueError(0xbad))
}, },
}}}, nil, nil, pkg.WNew, stub.UniqueError(0xbad)}, }}}, nil, nil, stub.UniqueError(0xbad)},
{"match", overrideChecksum{want.hash(), overrideIdent{pkg.ID{0xff, 1}, &stubArtifact{ {"match", overrideChecksum{want.hash(), overrideIdent{pkg.ID{0xff, 1}, &stubArtifact{
kind: pkg.KindTar, kind: pkg.KindTar,
@@ -1388,7 +1380,7 @@ func TestCache(t *testing.T) {
}}}, base.Append( }}}, base.Append(
"identifier", "identifier",
pkg.Encode(pkg.ID{0xff, 1}), pkg.Encode(pkg.ID{0xff, 1}),
), want, pkg.WNew, nil}, ), want, nil},
}) })
}, expectsFS{ }, expectsFS{
".": {Mode: fs.ModeDir | 0700}, ".": {Mode: fs.ModeDir | 0700},
@@ -1416,7 +1408,7 @@ func TestCache(t *testing.T) {
), base.Append( ), base.Append(
"identifier", "identifier",
pkg.Encode(pkg.Checksum{0xfe, 0}), pkg.Encode(pkg.Checksum{0xfe, 0}),
), expectsChecksum{0xff, 0}, pkg.WNew, nil}, ), expectsChecksum{0xff, 0}, nil},
}) })
for _, p := range [][]string{ for _, p := range [][]string{
@@ -1493,7 +1485,7 @@ func TestCache(t *testing.T) {
), expectsFS{ ), expectsFS{
".": {Mode: fs.ModeDir | 0500}, ".": {Mode: fs.ModeDir | 0500},
"result": {Mode: 0444}, "result": {Mode: 0444},
}, pkg.WNew, nil}, }, nil},
{"substituted", substituted, base.Append( {"substituted", substituted, base.Append(
"identifier", "identifier",
@@ -1501,7 +1493,7 @@ func TestCache(t *testing.T) {
), expectsFS{ ), expectsFS{
".": {Mode: fs.ModeDir | 0500}, ".": {Mode: fs.ModeDir | 0500},
"result": {Mode: 0444}, "result": {Mode: 0444},
}, pkg.WSubstitute, nil}, }, nil},
}) })
}, expectsFS{ }, expectsFS{
".": {Mode: fs.ModeDir | 0700}, ".": {Mode: fs.ModeDir | 0700},
@@ -1512,18 +1504,18 @@ func TestCache(t *testing.T) {
"checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV": {Mode: 0400, Data: []byte("destroyed")}, "checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV": {Mode: 0400, Data: []byte("destroyed")},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/CLyyxJWn1yUGFs_0KnJ4ZSU2hH4-elvGFRB36x14LrNrKBJPxvORhL9O4Rc7oeYU": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV")}, "identifier/0Jmc-vGnNDlXTD3jxy-4DGxHW-2-2LtLZ9SXaJtDIqu4uyHfDwDbghNBQ2aYRpab": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
"identifier/arv8a6z4i0x6pUb5Z2T-3iE4ETvyZj-uReHGKsS7uiBgMgKNEOzTGG3LIZb8vp0L": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV")}, "identifier/L-A8SK4ZX631eyealbJVH08u5pAVEf2NMk8RmLlxl7BVADkU4hNjWD6pi5H7uL1F": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV")},
"identifier/heRgWhNJmhm898V68kw0ta76t4rP1o29tYW_TrdyBWXGfo37K3QWA7Z-JkLXAwcn": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")}, "identifier/VbVV2dFVosCriHnG9t5vwfW4lbHvzOkV2eYwpGpPJZOgNBc0g1wZAhbdKEweLqmW": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
"identifier/q-VoVBI3IjcWTqwIokpi3y05CP113CHgHslc-XWnhOvDULwzVc3q9Y5xOCm_YyLZ": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")}, "identifier/aSMwvPAwdsIF9e1spuLyRNEc8aTFA4HRVasoNqGjxdm1laSMs2h2teGsoSzLp_pR": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV")},
"status": {Mode: fs.ModeDir | 0700}, "status": {Mode: fs.ModeDir | 0700},
"status/q-VoVBI3IjcWTqwIokpi3y05CP113CHgHslc-XWnhOvDULwzVc3q9Y5xOCm_YyLZ": {Mode: fs.ModeSymlink | 0777, Data: []byte("dP-6_wIDRRouOaOF-nkBy-IaUbLdYHbOUTrptu7j_qfW01mnHSjYJ0oykUmvUd2x")}, "status/0Jmc-vGnNDlXTD3jxy-4DGxHW-2-2LtLZ9SXaJtDIqu4uyHfDwDbghNBQ2aYRpab": {Mode: fs.ModeSymlink | 0777, Data: []byte("dzO8FEY9lu4hwRT6BfRZOX-uYGsC_5XH4jEJ7sJyThcmG9J_w1ArOAaUCGfL8wAM")},
"status/heRgWhNJmhm898V68kw0ta76t4rP1o29tYW_TrdyBWXGfo37K3QWA7Z-JkLXAwcn": {Mode: 0400, Data: []byte(statusHeader + "destroyed")}, "status/VbVV2dFVosCriHnG9t5vwfW4lbHvzOkV2eYwpGpPJZOgNBc0g1wZAhbdKEweLqmW": {Mode: 0400, Data: []byte(statusHeader + "destroyed")},
"status/dP-6_wIDRRouOaOF-nkBy-IaUbLdYHbOUTrptu7j_qfW01mnHSjYJ0oykUmvUd2x": {Mode: 0400, Data: []byte(statusHeader + "destroyed")}, "status/dzO8FEY9lu4hwRT6BfRZOX-uYGsC_5XH4jEJ7sJyThcmG9J_w1ArOAaUCGfL8wAM": {Mode: 0400, Data: []byte(statusHeader + "destroyed")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
"substitute/dP-6_wIDRRouOaOF-nkBy-IaUbLdYHbOUTrptu7j_qfW01mnHSjYJ0oykUmvUd2x": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")}, "substitute/dzO8FEY9lu4hwRT6BfRZOX-uYGsC_5XH4jEJ7sJyThcmG9J_w1ArOAaUCGfL8wAM": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
"work": {Mode: fs.ModeDir | 0700}, "work": {Mode: fs.ModeDir | 0700},
}}, }},
@@ -1594,14 +1586,14 @@ func TestCache(t *testing.T) {
{"extern", a, base.Append( {"extern", a, base.Append(
"identifier", "identifier",
pkg.Encode(wantIdent.Value()), pkg.Encode(wantIdent.Value()),
), wantOutput, pkg.WExternal, nil}, ), wantOutput, nil},
{"substitute", _a, base.Append( {"substitute", _a, base.Append(
"identifier", "identifier",
pkg.Encode(_wantIdent.Value()), pkg.Encode(_wantIdent.Value()),
), _wantOutput, pkg.WExternal, nil}, ), _wantOutput, nil},
{"mismatch", kca, nil, nil, pkg.WExternal, &pkg.ChecksumMismatchError{ {"mismatch", kca, nil, nil, &pkg.ChecksumMismatchError{
Got: wantChecksum, Got: wantChecksum,
}}, }},
}) })
@@ -1615,163 +1607,17 @@ func TestCache(t *testing.T) {
"checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl/result": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent")}, "checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl/result": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent")},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/k8yyhlBV3JpYzzywAnrRWkfp2TmgIDNC6FqMFwrrR2x0C3unBLveDNKgigOjG9MM": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/4HqRo4uTwRQjfy3d2cujMoDC_pC4iv20h4a7NYlx0UdbVuky18o5iK78TFEfPX2U": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"identifier/Xys8hPpJsKirZmTTuT7SwQ6661TCp_ZRu8iUjPPD7pC5E3ll7xw4zhNlZ2PVcz_-": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")}, "identifier/7AZcJm58ghFyTVf_v2baSntgpsxkP5el7ti9dC77C29n8YTEqQW9jRW92KGNdYnz": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"identifier/8sInO_dhPW9kweit7jVuH_vtMkRxlZT7XE4_yEKPZmpGkgVxkmxz0OpXgbMsH6ET": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "identifier/c4aCI00C-ZVyo_FQDQLl1OYK4U_kjzxwrLdFDiXMHnbMcZXCkXo_nxUWauScZ_4V": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"identifier/ofaXbB5gIza_7W4Rzo5XDe_8FwlCPKWn3TBcmGgFAkkTWMl5GXGmOzX89Jd2Mk2A": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")}, "identifier/cNoG77frXGRCJa7fUi1INKUEQg7L4qrX5acsSv-wqZdGZT7dQwM93rD3at6kSFFF": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
"identifier/8VL-egZySw-RbnHZtxGWdf5Z2v9S9CihrdFwa54Bc5EYMQcEgWEem7QrdCTWlCb8": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")}, "identifier/gvCqzexZVqXjF8B5lKMcP5onmq3jJ6AKqzOW_WN0Fl2yTr9NKhPt9l_ClD2EOSlS": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"status": {Mode: fs.ModeDir | 0700}, "status": {Mode: fs.ModeDir | 0700},
"status/8VL-egZySw-RbnHZtxGWdf5Z2v9S9CihrdFwa54Bc5EYMQcEgWEem7QrdCTWlCb8": {Mode: 0400, Data: []byte("\x00")}, "status/gvCqzexZVqXjF8B5lKMcP5onmq3jJ6AKqzOW_WN0Fl2yTr9NKhPt9l_ClD2EOSlS": {Mode: 0400, Data: []byte("\x00")},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
"substitute/socGRnhdsJlJ4Z01ZwHFZdLzFH0rWvoaJrylcUKtvhBdGsE7VoYl-vdUuvfK8B4I": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "substitute/qOYrxy9ztKeOA96Os811_0Ox5sd8FBOxis6psJAnRJL5MLazFMaqmd4g7t7k1OHk": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"work": {Mode: fs.ModeDir | 0700},
}},
{"extern shallow", pkg.CExternShallow, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
a := &stubArtifactF{
kind: pkg.KindExec,
params: []byte("extern"),
}
wantIdent := c.Ident(a)
wantOutput := expectsFS{
".": {Mode: fs.ModeDir | 0500},
"result": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent")},
}
var wantChecksum pkg.Checksum
if err := pkg.SumFS(
&wantChecksum,
fstest.MapFS(wantOutput),
".",
); err != nil {
t.Fatal(err)
}
wantChecksumH := unique.Make(wantChecksum)
_a := &stubArtifactF{
kind: pkg.KindExec,
params: []byte("extern substitute"),
deps: []pkg.Artifact{pkg.NewFile("", nil)},
}
_wantIdent := c.Ident(_a)
_wantOutput := expectsFS{
".": {Mode: fs.ModeDir | 0500},
}
var _wantChecksum pkg.Checksum
if err := pkg.SumFS(
&_wantChecksum,
fstest.MapFS(_wantOutput),
".",
); err != nil {
t.Fatal(err)
}
_wantChecksumH := unique.Make(_wantChecksum)
kca := pkg.NewExec(
"", "",
new(pkg.Checksum), 0, false, false,
fhs.AbsRoot, nil, fhs.AbsRoot, nil,
)
kcIdent := c.Ident(kca)
fia := &stubArtifactF{
kind: pkg.KindExec,
params: []byte("flood input"),
deps: []pkg.Artifact{_a},
}
fiaIdent := c.Ident(fia)
flia := &stubArtifactF{
kind: pkg.KindExec,
params: []byte("flood input local"),
deps: []pkg.Artifact{_a},
}
fliaIdent := c.Ident(flia)
h := &stubArtifactF{
kind: pkg.KindExec,
params: []byte("hanging input"),
}
fhia := &stubArtifactF{
kind: pkg.KindExec,
params: []byte("flood input local"),
deps: []pkg.Artifact{h},
}
fhiaIdent := c.Ident(fhia)
c.SetExternal(stubExtern{
artifact: map[unique.Handle[pkg.ID]]pkg.Checksum{
wantIdent: wantChecksum,
_wantIdent: _wantChecksum,
kcIdent: wantChecksum,
fiaIdent: wantChecksum,
fliaIdent: wantChecksum,
fhiaIdent: wantChecksum,
},
checksum: map[unique.Handle[pkg.Checksum]]fstest.MapFS{
wantChecksumH: fstest.MapFS(wantOutput),
_wantChecksumH: fstest.MapFS(_wantOutput),
},
status: map[unique.Handle[pkg.ID]]string{
wantIdent: "\x00",
kcIdent: "unreachable",
},
})
cureMany(t, c, []cureStep{
{"extern", a, base.Append(
"identifier",
pkg.Encode(wantIdent.Value()),
), wantOutput, pkg.WExternal, nil},
{"flood shallow extern", fia, base.Append(
"identifier",
pkg.Encode(fiaIdent.Value()),
), wantOutput, pkg.WExternal, nil},
{"substitute", _a, base.Append(
"identifier",
pkg.Encode(_wantIdent.Value()),
), _wantOutput, pkg.WExternal, nil},
{"flood shallow local", flia, base.Append(
"identifier",
pkg.Encode(fliaIdent.Value()),
), wantOutput, pkg.WExternal, nil},
{"flood shallow hanging", fhia, nil, nil,
pkg.WNew, pkg.HangingInputError(c.Ident(h))},
{"mismatch", kca, nil, nil, pkg.WExternal, &pkg.ChecksumMismatchError{
Got: wantChecksum,
}},
})
}, expectsFS{
".": {Mode: fs.ModeDir | 0700},
"checksum": {Mode: fs.ModeDir | 0700},
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
"checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl": {Mode: fs.ModeDir | 0500},
"checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl/result": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent")},
"identifier": {Mode: fs.ModeDir | 0700},
"identifier/3fYJjSiWhklt7TK9MPVLGk5_8AIU8ostTcFPXULJf_qeAVG6pmC-Cqqu4ZhEXRxn": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"identifier/8VL-egZySw-RbnHZtxGWdf5Z2v9S9CihrdFwa54Bc5EYMQcEgWEem7QrdCTWlCb8": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"identifier/8sInO_dhPW9kweit7jVuH_vtMkRxlZT7XE4_yEKPZmpGkgVxkmxz0OpXgbMsH6ET": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"identifier/Xys8hPpJsKirZmTTuT7SwQ6661TCp_ZRu8iUjPPD7pC5E3ll7xw4zhNlZ2PVcz_-": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"identifier/k8yyhlBV3JpYzzywAnrRWkfp2TmgIDNC6FqMFwrrR2x0C3unBLveDNKgigOjG9MM": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"identifier/mXlNz_v77nW7jstn_rJn4RL7dCQlkakM0y-HjO6tkDubtzaj-wCmW1hIr2Gk64_O": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"status": {Mode: fs.ModeDir | 0700},
"status/8VL-egZySw-RbnHZtxGWdf5Z2v9S9CihrdFwa54Bc5EYMQcEgWEem7QrdCTWlCb8": {Mode: 0400, Data: []byte("\x00")},
"substitute": {Mode: fs.ModeDir | 0700},
"substitute/socGRnhdsJlJ4Z01ZwHFZdLzFH0rWvoaJrylcUKtvhBdGsE7VoYl-vdUuvfK8B4I": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
"substitute/MccYezQ4pshkeBV4w_aAEIwvZdKHL3OAgAkR3ouZ43EF94Ur1HBHaN7njPfJAj50": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"substitute/VV5W0_DiA1iylaqbKFThp1ZpV_y1ccVS1XyqDnDaXoHQ-EEyPI4XFrQHcHOF2hWG": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"work": {Mode: fs.ModeDir | 0700}, "work": {Mode: fs.ModeDir | 0700},
}}, }},
@@ -1789,7 +1635,7 @@ func TestErrors(t *testing.T) {
}{ }{
{"InvalidLookupError", pkg.InvalidLookupError{ {"InvalidLookupError", pkg.InvalidLookupError{
0xff, 0xf0, 0xff, 0xf0,
}, "attempting to look up non-input artifact __AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}, }, "attempting to look up non-dependency artifact __AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},
{"InvalidArtifactError", pkg.InvalidArtifactError{ {"InvalidArtifactError", pkg.InvalidArtifactError{
0xff, 0xfd, 0xff, 0xfd,
@@ -2037,7 +1883,7 @@ func (earlyFailureF) Kind() pkg.Kind { return pkg.KindExec }
func (earlyFailureF) Params(*pkg.IContext) {} func (earlyFailureF) Params(*pkg.IContext) {}
func (earlyFailureF) IsExclusive() bool { return false } func (earlyFailureF) IsExclusive() bool { return false }
func (a earlyFailureF) Inputs() []pkg.Artifact { func (a earlyFailureF) Dependencies() []pkg.Artifact {
deps := make([]pkg.Artifact, a) deps := make([]pkg.Artifact, a)
for i := range deps { for i := range deps {
deps[i] = a - 1 deps[i] = a - 1
@@ -2055,7 +1901,7 @@ func (a earlyFailureF) Cure(*pkg.FContext) error {
func BenchmarkEarlyDCE(b *testing.B) { func BenchmarkEarlyDCE(b *testing.B) {
msg := message.New(log.New(os.Stderr, "dce: ", 0)) msg := message.New(log.New(os.Stderr, "dce: ", 0))
msg.SwapVerbose(testing.Verbose()) msg.SwapVerbose(testing.Verbose())
c, err := pkg.Open(b.Context(), msg, check.MustAbs(b.TempDir()), nil) c, err := pkg.Open(b.Context(), msg, 0, 0, 0, check.MustAbs(b.TempDir()))
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@@ -2104,8 +1950,7 @@ func TestOpen(t *testing.T) {
if _, err := pkg.Open( if _, err := pkg.Open(
t.Context(), t.Context(),
message.New(nil), message.New(nil),
check.MustAbs(container.Nonexistent), 0, 0, 0, check.MustAbs(container.Nonexistent),
nil,
); !reflect.DeepEqual(err, wantErr) { ); !reflect.DeepEqual(err, wantErr) {
t.Errorf("Open: error = %#v, want %#v", err, wantErr) t.Errorf("Open: error = %#v, want %#v", err, wantErr)
} }
@@ -2133,8 +1978,7 @@ func TestOpen(t *testing.T) {
if _, err := pkg.Open( if _, err := pkg.Open(
t.Context(), t.Context(),
message.New(nil), message.New(nil),
tempDir.Append("cache"), 0, 0, 0, tempDir.Append("cache"),
nil,
); !reflect.DeepEqual(err, wantErr) { ); !reflect.DeepEqual(err, wantErr) {
t.Errorf("Open: error = %#v, want %#v", err, wantErr) t.Errorf("Open: error = %#v, want %#v", err, wantErr)
} }
@@ -2156,8 +2000,7 @@ func TestOpen(t *testing.T) {
if _, err := pkg.Open( if _, err := pkg.Open(
t.Context(), t.Context(),
message.New(nil), message.New(nil),
tempDir.Append("cache"), 0, 0, 0, tempDir.Append("cache"),
nil,
); !reflect.DeepEqual(err, wantErr) { ); !reflect.DeepEqual(err, wantErr) {
t.Errorf("Open: error = %#v, want %#v", err, wantErr) t.Errorf("Open: error = %#v, want %#v", err, wantErr)
} }
@@ -2178,7 +2021,7 @@ func TestOpen(t *testing.T) {
if _, err := pkg.Open( if _, err := pkg.Open(
t.Context(), t.Context(),
message.New(nil), message.New(nil),
tempDir.Append("cache"), nil, 0, 0, 0, tempDir.Append("cache"),
); !reflect.DeepEqual(err, wantErr) { ); !reflect.DeepEqual(err, wantErr) {
t.Errorf("Open: error = %#v, want %#v", err, wantErr) t.Errorf("Open: error = %#v, want %#v", err, wantErr)
} }
@@ -2231,8 +2074,8 @@ func TestExtensionRegister(t *testing.T) {
if _, err := pkg.Open( if _, err := pkg.Open(
t.Context(), t.Context(),
message.New(log.Default()), message.New(log.Default()),
0, 0, 0,
check.MustAbs(container.Nonexistent), check.MustAbs(container.Nonexistent),
nil,
); !errors.Is(err, os.ErrNotExist) { ); !errors.Is(err, os.ErrNotExist) {
t.Fatalf("Open: error = %v", err) t.Fatalf("Open: error = %v", err)
} }
@@ -2270,7 +2113,7 @@ func TestExtensionRegister(t *testing.T) {
pkg.Register(pkg.KindCustomOffset, nil) pkg.Register(pkg.KindCustomOffset, nil)
t.Cleanup(func() { opened = false }) t.Cleanup(func() { opened = false })
_, _ = pkg.Open(nil, nil, nil, nil) _, _ = pkg.Open(nil, nil, 0, 0, 0, nil)
panic("unreachable") panic("unreachable")
}) })
@@ -2281,7 +2124,11 @@ func TestExtensionRegister(t *testing.T) {
base := check.MustAbs(t.TempDir()) base := check.MustAbs(t.TempDir())
t.Cleanup(func() { opened = false }) t.Cleanup(func() { opened = false })
if c, err := pkg.Open(t.Context(), nil, base, nil); err != nil { if c, err := pkg.Open(
t.Context(), nil,
0, 0, 0,
base,
); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
c.Close() c.Close()
@@ -2309,7 +2156,8 @@ func TestExtensionRegister(t *testing.T) {
} }
if _, err := pkg.Open( if _, err := pkg.Open(
t.Context(), nil, t.Context(), nil,
base, nil, 0, 0, 0,
base,
); !reflect.DeepEqual(err, wantErr) { ); !reflect.DeepEqual(err, wantErr) {
t.Fatalf("Open: error = %v, want %v", err, wantErr) t.Fatalf("Open: error = %v, want %v", err, wantErr)
} }
@@ -2330,8 +2178,8 @@ func TestExtensionRegister(t *testing.T) {
if _, err := pkg.Open( if _, err := pkg.Open(
t.Context(), nil, t.Context(), nil,
0, 0, 0,
base, base,
nil,
); !reflect.DeepEqual(err, pkg.ErrWouldPromote) { ); !reflect.DeepEqual(err, pkg.ErrWouldPromote) {
t.Fatalf("Open: error = %v", err) t.Fatalf("Open: error = %v", err)
} }
@@ -2344,7 +2192,8 @@ func TestExtensionRegister(t *testing.T) {
if c, err := pkg.Open( if c, err := pkg.Open(
t.Context(), nil, t.Context(), nil,
base, &pkg.CacheAttr{Flags: pkg.CPromoteVariant}, pkg.CPromoteVariant, 0, 0,
base,
); err != nil { ); err != nil {
t.Fatalf("Open: error = %v", err) t.Fatalf("Open: error = %v", err)
} else { } else {
@@ -2369,7 +2218,8 @@ func TestExtensionRegister(t *testing.T) {
if _, err := pkg.Open( if _, err := pkg.Open(
t.Context(), nil, t.Context(), nil,
base, nil, 0, 0, 0,
base,
); !reflect.DeepEqual(err, pkg.ErrInvalidExtension) { ); !reflect.DeepEqual(err, pkg.ErrInvalidExtension) {
t.Fatalf("Open: error = %v", err) t.Fatalf("Open: error = %v", err)
} }
@@ -2386,7 +2236,8 @@ func TestExtensionRegister(t *testing.T) {
if _, err := pkg.Open( if _, err := pkg.Open(
t.Context(), nil, t.Context(), nil,
base, nil, 0, 0, 0,
base,
); !reflect.DeepEqual(err, pkg.UnsupportedVariantError("rosa")) { ); !reflect.DeepEqual(err, pkg.UnsupportedVariantError("rosa")) {
t.Fatalf("Open: error = %v", err) t.Fatalf("Open: error = %v", err)
} }
+2 -9
View File
@@ -58,8 +58,8 @@ func init() {
}) })
} }
// Inputs returns a slice containing the backing file. // Dependencies returns a slice containing the backing file.
func (a *tarArtifact) Inputs() []Artifact { func (a *tarArtifact) Dependencies() []Artifact {
return []Artifact{a.f} return []Artifact{a.f}
} }
@@ -172,13 +172,6 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
} }
break break
case tar.TypeChar:
t.GetMessage().Verbosef(
"%sskipping character device %d, %d %q%s",
t.cache.sgrWarn, header.Devmajor, header.Devminor, header.Name, t.cache.sgrRes,
)
continue
case tar.TypeXGlobalHeader: case tar.TypeXGlobalHeader:
continue // ignore continue // ignore
+9 -9
View File
@@ -80,8 +80,8 @@ func TestTar(t *testing.T) {
"checksum/" + wantEncode + "/work": {Mode: fs.ModeDir | 0500}, "checksum/" + wantEncode + "/work": {Mode: fs.ModeDir | 0500},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/7OZ5ErCxUJyRnaaFvUKhTryjsJAoOBUR2MZpUB1DD57jRa09Xxsh9BUs0dM9z5uH": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantEncode)}, "identifier/T68YqeEW5moiwVe4J0wPvjERgyMUd4k_cKKxTcQx6AXqZaQAuuryu-Iv-qxDV6-T": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantEncode)},
"identifier/paWdPW_BowV3345c4GP4xxSNgxVs_7F09m9m7OaH5XbyuI3dTGZ-kFPOXFbwgr5O": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantEncode)}, "identifier/WbWIIUGl6bqDBsjU-UB69JqOWLjG5waiUJBUBGeeRp3V7FBbwaBe3sG1qm7DlPSk": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantEncode)},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
@@ -104,8 +104,8 @@ func TestTar(t *testing.T) {
"checksum/" + wantExpandEncode + "/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")}, "checksum/" + wantExpandEncode + "/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
"identifier": {Mode: fs.ModeDir | 0700}, "identifier": {Mode: fs.ModeDir | 0700},
"identifier/1g2Tcg7pOJkZFxlKploBm4U0vIyMYbxDkcwwydwsF9BMs-w-knFJS41jnqOt16jT": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantExpandEncode)}, "identifier/WbWIIUGl6bqDBsjU-UB69JqOWLjG5waiUJBUBGeeRp3V7FBbwaBe3sG1qm7DlPSk": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantExpandEncode)},
"identifier/7OZ5ErCxUJyRnaaFvUKhTryjsJAoOBUR2MZpUB1DD57jRa09Xxsh9BUs0dM9z5uH": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantExpandEncode)}, "identifier/e3BECw_x_vDyTvj2P48MJVgwpgALeXQ90Ye2Y8gr6SwIPtMup6SR2LVcCTDRGpVi": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantExpandEncode)},
"substitute": {Mode: fs.ModeDir | 0700}, "substitute": {Mode: fs.ModeDir | 0700},
@@ -212,24 +212,24 @@ func checkTarHTTP(
&client, &client,
"file:///testdata", "file:///testdata",
testdataChecksum, testdataChecksum,
), pkg.Gzip)), ignorePathname, want, pkg.WNew, nil}, ), pkg.Gzip)), ignorePathname, want, nil},
{"directory", pkg.NewTar(pkg.NewDecompress( {"directory", pkg.NewTar(pkg.NewDecompress(
&tarDir, &tarDir,
pkg.Gzip, pkg.Gzip,
)), ignorePathname, want, pkg.WNew, nil}, )), ignorePathname, want, nil},
{"multiple entries", pkg.NewTar(pkg.NewDecompress( {"multiple entries", pkg.NewTar(pkg.NewDecompress(
&tarDirMulti, &tarDirMulti,
pkg.Gzip, pkg.Gzip,
)), nil, nil, pkg.WNew, errors.New( )), nil, nil, errors.New(
"input directory does not contain a single regular file", "input directory does not contain a single regular file",
)}, )},
{"bad type", pkg.NewTar(pkg.NewDecompress( {"bad type", pkg.NewTar(pkg.NewDecompress(
&tarDirType, &tarDirType,
pkg.Gzip, pkg.Gzip,
)), nil, nil, pkg.WNew, errors.New( )), nil, nil, errors.New(
"input directory does not contain a single regular file", "input directory does not contain a single regular file",
)}, )},
@@ -239,6 +239,6 @@ func checkTarHTTP(
cure: func(t *pkg.TContext) error { cure: func(t *pkg.TContext) error {
return stub.UniqueError(0xcafe) return stub.UniqueError(0xcafe)
}, },
}), nil, nil, pkg.WNew, stub.UniqueError(0xcafe)}, }), nil, nil, stub.UniqueError(0xcafe)},
}) })
} }
+1 -6
View File
@@ -18,11 +18,6 @@ const (
jobsLE = `"$(expr ` + jobsE + ` '*' 2)"` jobsLE = `"$(expr ` + jobsE + ` '*' 2)"`
// jobsLFlagE is expression for flag with double of preferred job count. // jobsLFlagE is expression for flag with double of preferred job count.
jobsLFlagE = `"-j$(expr ` + jobsE + ` '*' 2)"` jobsLFlagE = `"-j$(expr ` + jobsE + ` '*' 2)"`
// loadE is expression for preferred loadavg target set by [pkg].
loadE = `"$` + pkg.EnvLoad + `"`
// loadFlagE is expression for flag with preferred loadavg target.
loadFlagE = `"-l$` + pkg.EnvLoad + `"`
) )
// newTar wraps [pkg.NewHTTPGetTar] with a simpler function signature. // newTar wraps [pkg.NewHTTPGetTar] with a simpler function signature.
@@ -107,7 +102,7 @@ func skipGNUTests(tests ...int64) string {
buf.WriteString(strconv.Itoa(int(n - 1))) buf.WriteString(strconv.Itoa(int(n - 1)))
buf.WriteString(" ") buf.WriteString(" ")
} }
if i == len(tests)-1 || (tests[i+1] != n+1 && tests[i+1] != n+2) { if i == len(tests)-1 || tests[i+1] != n+1 {
buf.WriteString(strconv.Itoa(int(n + 1))) buf.WriteString(strconv.Itoa(int(n + 1)))
buf.WriteString("-") buf.WriteString("-")
} }
-1
View File
@@ -16,7 +16,6 @@ func TestSkipGNUTests(t *testing.T) {
}{ }{
{[]int64{764}, "1-763 765-"}, {[]int64{764}, "1-763 765-"},
{[]int64{764, 0xcafe, 37, 9}, "1-8 10-36 38-763 765-51965 51967-"}, {[]int64{764, 0xcafe, 37, 9}, "1-8 10-36 38-763 765-51965 51967-"},
{[]int64{168, 170, 623, 764}, "1-167 169 171-622 624-763 765-"},
{[]int64{1, 2, 0xbed}, "3-3052 3054-"}, {[]int64{1, 2, 0xbed}, "3-3052 3054-"},
{[]int64{3, 4}, "1-2 5-"}, {[]int64{3, 4}, "1-2 5-"},
} }
+124
View File
@@ -0,0 +1,124 @@
package rosa
import (
"fmt"
"io"
"net/http"
"os"
"time"
"hakurei.app/fhs"
"hakurei.app/internal/pkg"
)
// busyboxBin is a busybox binary distribution installed under bin/busybox.
type busyboxBin struct {
// Underlying busybox binary.
bin pkg.FileArtifact
}
// Kind returns the hardcoded [pkg.Kind] value.
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.FileArtifact].
func (a busyboxBin) Dependencies() []pkg.Artifact {
return []pkg.Artifact{a.bin}
}
func init() {
pkg.Register(kindBusyboxBin, func(r *pkg.IRReader) pkg.Artifact {
a := busyboxBin{r.Next().(pkg.FileArtifact)}
if _, ok := r.Finalise(); ok {
panic(pkg.ErrUnexpectedChecksum)
}
return a
})
}
// String returns the reporting name of the underlying file prefixed with expand.
func (a busyboxBin) String() string {
return "expand-" + a.bin.(fmt.Stringer).String()
}
// Cure installs the underlying busybox [pkg.File] to bin/busybox.
func (a busyboxBin) Cure(t *pkg.TContext) (err error) {
var r io.ReadCloser
if r, err = t.Open(a.bin); err != nil {
return
}
defer func() {
closeErr := r.Close()
if err == nil {
err = closeErr
}
}()
binDir := t.GetWorkDir().Append("bin")
if err = os.MkdirAll(binDir.String(), 0700); err != nil {
return
}
var w *os.File
if w, err = os.OpenFile(
binDir.Append("busybox").String(),
os.O_WRONLY|os.O_CREATE|os.O_EXCL,
0500,
); err != nil {
return
}
defer func() {
closeErr := w.Close()
if err == nil {
err = closeErr
}
}()
_, err = io.Copy(w, r)
return
}
// newBusyboxBin returns a [pkg.Artifact] containing a busybox installation from
// the https://busybox.net/downloads/binaries/ binary release.
func (s *S) newBusyboxBin() pkg.Artifact {
var version, url, checksum string
switch s.arch {
case "amd64":
version = "1.35.0"
url = "https://busybox.net/downloads/binaries/" +
version + "-" + s.linuxArch() + "-linux-musl/busybox"
checksum = "L7OBIsPu9enNHn7FqpBT1kOg_mCLNmetSeNMA3i4Y60Z5jTgnlX3qX3zcQtLx5AB"
case "arm64":
version = "1.31.0"
url = "https://busybox.net/downloads/binaries/" +
version + "-defconfig-multiarch-musl/busybox-armv8l"
checksum = "npJjBO7iwhjW6Kx2aXeSxf8kXhVgTCDChOZTTsI8ZfFfa3tbsklxRiidZQdrVERg"
default:
panic("unsupported target " + s.arch)
}
return pkg.NewExec(
"busybox-bin-"+version, s.arch, nil, pkg.ExecTimeoutMax, false, false,
fhs.AbsRoot, []string{
"PATH=/system/bin",
},
AbsSystem.Append("bin", "busybox"),
[]string{"hush", "-c", "" +
"busybox mkdir -p /work/system/bin/ && " +
"busybox cp /system/bin/busybox /work/system/bin/ && " +
"busybox --install -s /work/system/bin/"},
pkg.Path(AbsSystem, true, busyboxBin{pkg.NewHTTPGet(
&http.Client{Transport: &http.Transport{
// busybox website is really slow to respond
TLSHandshakeTimeout: 2 * time.Minute,
}}, url,
mustDecode(checksum),
)}),
)
}
+4 -5
View File
@@ -35,7 +35,7 @@ type CMakeHelper struct {
var _ Helper = new(CMakeHelper) var _ Helper = new(CMakeHelper)
// extra returns the cmake handle alongside either ninja or make. // extra returns the cmake handle alongside either ninja or make.
func (attr *CMakeHelper) extra() P { func (attr *CMakeHelper) extra(int) P {
if attr != nil && attr.Make { if attr != nil && attr.Make {
return P{_cmake, _make} return P{_cmake, _make}
} }
@@ -61,10 +61,10 @@ func (attr *CMakeHelper) script(t Toolchain, name string) string {
} }
generate := "Ninja" generate := "Ninja"
test := "ninja " + jobsFlagE + " " + loadFlagE + " test" test := "ninja " + jobsFlagE + " test"
if attr.Make { if attr.Make {
generate = "'Unix Makefiles'" generate = "'Unix Makefiles'"
test = "make " + jobsFlagE + " " + loadFlagE + " test" test = "make " + jobsFlagE + " test"
} }
if attr.Test != "" { if attr.Test != "" {
test = attr.Test test = attr.Test
@@ -84,7 +84,6 @@ func (attr *CMakeHelper) script(t Toolchain, name string) string {
return ` return `
cmake -G ` + generate + ` \ cmake -G ` + generate + ` \
-Wno-author -Wno-unused-cli \
-DCMAKE_C_COMPILER_TARGET="${ROSA_TRIPLE}" \ -DCMAKE_C_COMPILER_TARGET="${ROSA_TRIPLE}" \
-DCMAKE_CXX_COMPILER_TARGET="${ROSA_TRIPLE}" \ -DCMAKE_CXX_COMPILER_TARGET="${ROSA_TRIPLE}" \
-DCMAKE_ASM_COMPILER_TARGET="${ROSA_TRIPLE}" \ -DCMAKE_ASM_COMPILER_TARGET="${ROSA_TRIPLE}" \
@@ -98,7 +97,7 @@ cmake -G ` + generate + ` \
}), " \\\n\t") + ` \ }), " \\\n\t") + ` \
-DCMAKE_INSTALL_PREFIX=/system \ -DCMAKE_INSTALL_PREFIX=/system \
'/usr/src/` + name + `/` + filepath.Join(attr.Append...) + `' '/usr/src/` + name + `/` + filepath.Join(attr.Append...) + `'
cmake --build . --parallel=` + jobsE + ` -- ` + loadFlagE + ` cmake --build . --parallel=` + jobsE + `
DESTDIR=/work cmake --install . DESTDIR=/work cmake --install .
` + script ` + script
} }
+2 -4
View File
@@ -107,8 +107,6 @@ func init() {
a := cureEtc{} a := cureEtc{}
if r.ReadUint32() == 0 { if r.ReadUint32() == 0 {
a.minimal = true a.minimal = true
} else {
r.Next()
} }
if _, ok := r.Finalise(); ok { if _, ok := r.Finalise(); ok {
panic(pkg.ErrUnexpectedChecksum) panic(pkg.ErrUnexpectedChecksum)
@@ -120,8 +118,8 @@ func init() {
// IsExclusive returns false: Cure performs a few trivial filesystem writes. // IsExclusive returns false: Cure performs a few trivial filesystem writes.
func (cureEtc) IsExclusive() bool { return false } func (cureEtc) IsExclusive() bool { return false }
// Inputs returns a slice containing the backing iana-etc release. // Dependencies returns a slice containing the backing iana-etc release.
func (a cureEtc) Inputs() []pkg.Artifact { func (a cureEtc) Dependencies() []pkg.Artifact {
if !a.minimal { if !a.minimal {
return []pkg.Artifact{ianaEtc} return []pkg.Artifact{ianaEtc}
} }
-104
View File
@@ -1,104 +0,0 @@
package rosa
import (
"errors"
"io/fs"
"os"
"path/filepath"
"hakurei.app/internal/pkg"
)
// gentooOverlay are symlinks on top of a Gentoo LLVM stage3 tarball for
// compatibility with the Rosa OS stage0 distribution.
type gentooOverlay struct{ stage3 pkg.Artifact }
// Kind returns the hardcoded [pkg.Kind] value.
func (a gentooOverlay) Kind() pkg.Kind { return kindGentooOverlay }
// Params is a noop.
func (a gentooOverlay) Params(*pkg.IContext) {}
// IsExclusive returns false: Cure performs a trivial filesystem write.
func (gentooOverlay) IsExclusive() bool { return false }
// Inputs returns the underlying Gentoo stage3.
func (a gentooOverlay) Inputs() []pkg.Artifact {
return []pkg.Artifact{a.stage3}
}
func init() {
pkg.Register(kindGentooOverlay, func(r *pkg.IRReader) pkg.Artifact {
a := gentooOverlay{r.Next()}
if _, ok := r.Finalise(); ok {
panic(pkg.ErrUnexpectedChecksum)
}
return a
})
}
// String returns a hardcoded name.
func (a gentooOverlay) String() string { return "gentoo-overlay" }
// Revision returns a hardcoded value incremented on layout changes.
func (a gentooOverlay) Revision() uint64 { return 0 }
// Cure installs the overlay.
func (a gentooOverlay) Cure(f *pkg.FContext) (err error) {
pathname, _ := f.GetArtifact(a.stage3)
var stage3 *os.Root
if stage3, err = os.OpenRoot(pathname.String()); err != nil {
return err
}
defer func() {
closeErr := stage3.Close()
if err == nil {
err = closeErr
}
}()
fsys := stage3.FS()
system := f.GetWorkDir().Append("system")
bin := system.Append("bin")
if err = os.MkdirAll(bin.String(), 0700); err != nil {
return
}
linknames := []string{
"../../bin/sh",
}
var dents []os.DirEntry
const llvm = "usr/lib/llvm"
dents, err = fs.ReadDir(fsys, llvm)
if err != nil {
return
}
if len(dents) != 1 {
return errors.New("stage3 /" + llvm + " contains more than one entry")
}
llvmBin := filepath.Join(llvm, dents[0].Name(), "bin")
dents, err = fs.ReadDir(fsys, llvmBin)
if err != nil {
return
}
for _, dent := range dents {
linknames = append(linknames, filepath.Join(
"../..",
llvmBin,
dent.Name(),
))
}
for _, linkname := range linknames {
if err = os.Symlink(
linkname,
bin.Append(filepath.Base(linkname)).String(),
); err != nil {
return
}
}
return
}
+7 -14
View File
@@ -20,26 +20,19 @@ func (t Toolchain) NewViaGit(
return t.New(strings.TrimSuffix( return t.New(strings.TrimSuffix(
path.Base(url), path.Base(url),
".git", ".git",
)+"-src", path.Base(rev), nil, &PackageAttr{ )+"-src-"+path.Base(rev), THostNet, t.Append(nil,
KnownChecksum: &checksum, _nssCACert,
_git,
HostNet: true, ), &checksum, nil, `
Paths: []pkg.ExecPath{resolvconf()},
}, &GenericHelper{
Build: `
git \ git \
-c advice.detachedHead=false \ -c advice.detachedHead=false \
clone \ clone \
--depth=1 \ --depth=1 \
--revision=` + rev + ` \ --revision=`+rev+` \
--shallow-submodules \ --shallow-submodules \
--recurse-submodules \ --recurse-submodules \
` + url + ` \ `+url+` \
/work /work
rm -rf /work/.git rm -rf /work/.git
`, `, resolvconf())
},
_nssCACert,
_git,
)
} }
+28 -10
View File
@@ -13,7 +13,7 @@ func (t Toolchain) newGo(
script string, script string,
boot ...pkg.Artifact, boot ...pkg.Artifact,
) pkg.Artifact { ) pkg.Artifact {
return t.New("go", version, newTar( return t.NewPackage("go", version, newTar(
"https://go.dev/dl/go"+version+".src.tar.gz", "https://go.dev/dl/go"+version+".src.tar.gz",
checksum, checksum,
pkg.Gzip, pkg.Gzip,
@@ -56,15 +56,16 @@ ln -s \
func init() { func init() {
const ( const (
version = "1.25.12" version = "1.26.4"
checksum = "XNrGD_boJl2-El_a_T3BuTcMin_WPoU-FGddcNG8dcXA8n2QuxZB7kaUaCWu60QX" checksum = "fpqJlxa41wKRtbnviYNLk9VxgcL-5oIEDxsriF8svU6kNwQW70oRRA9gTz_ImVoB"
) )
meta := Metadata{ meta := Metadata{
Name: "go-bootstrap", Name: "go",
Description: "the Go programming language toolchain (bootstrap)", Description: "the Go programming language toolchain",
Website: "https://go.dev", Website: "https://go.dev",
Version: version, Version: version,
Exclude: true,
ID: 1227,
} }
native.MustRegister(meta.Name, func(t Toolchain) (*Metadata, pkg.Artifact) { native.MustRegister(meta.Name, func(t Toolchain) (*Metadata, pkg.Artifact) {
var ( var (
@@ -75,7 +76,7 @@ func init() {
) )
switch t.arch { switch t.arch {
case "amd64": case "amd64":
bootstrapEarly = []pkg.Artifact{t.New("go", "1.4-bootstrap", newTar( bootstrapEarly = []pkg.Artifact{t.NewPackage("go", "1.4-bootstrap", newTar(
"https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz", "https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz",
"8o9JL_ToiQKadCTb04nvBDkp8O1xiWOolAxVEqaTGodieNe4lOFEjlOxN3bwwe23", "8o9JL_ToiQKadCTb04nvBDkp8O1xiWOolAxVEqaTGodieNe4lOFEjlOxN3bwwe23",
pkg.Gzip, pkg.Gzip,
@@ -151,8 +152,8 @@ sed -i \
) )
go125 := t.newGo( go125 := t.newGo(
version, "1.25.11",
checksum, "cnqAQ8wpVZXcM6nHAfSqFkIW4L3H73ALB9rBfKUBbfgg4_pOF3I9zOZARgYQ6MR2",
[]string{"CGO_ENABLED=0"}, ` []string{"CGO_ENABLED=0"}, `
sed -i \ sed -i \
's,/lib/ld-musl-`+t.linuxArch()+`.so.1,/system/bin/linker,' \ 's,/lib/ld-musl-`+t.linuxArch()+`.so.1,/system/bin/linker,' \
@@ -164,6 +165,23 @@ rm \
`, go123, `, go123,
) )
return &meta, go125 return &meta, t.newGo(
version,
checksum,
finalEnv, `
sed -i \
's,/lib/ld-musl-`+t.linuxArch()+`.so.1,/system/bin/linker,' \
cmd/link/internal/`+t.arch+`/obj.go
sed -i \
's/cpu.X86.HasAVX512VBMI/& \&\& cpu.X86.HasPOPCNT/' \
internal/runtime/gc/scan/scan_amd64.go
rm \
os/root_unix_test.go \
cmd/cgo/internal/testsanitizers/tsan_test.go \
cmd/cgo/internal/testsanitizers/cshared_test.go
`, go125,
)
}) })
} }
+178 -29
View File
@@ -1,9 +1,11 @@
package rosa package rosa
import ( import (
"io/fs"
"regexp" "regexp"
"slices" "slices"
"strings" "strings"
"unsafe"
"hakurei.app/internal/pkg" "hakurei.app/internal/pkg"
) )
@@ -11,12 +13,12 @@ import (
var ( var (
_llvmSource = H("llvm-project") _llvmSource = H("llvm-project")
_muslHeaders = H("musl-headers") _muslHeaders = H("musl-headers")
_earlyCompilerRT = H("early-compiler-rt")
_earlyRuntimes = H("early-runtimes") _earlyRuntimes = H("early-runtimes")
_musl = H("musl") _musl = H("musl")
_bash = H("bash") _bash = H("bash")
_awk = H("awk") _gawk = H("gawk")
_pythonEarly = H("python-early")
_coreutils = H("coreutils") _coreutils = H("coreutils")
_diffutils = H("diffutils") _diffutils = H("diffutils")
_findutils = H("findutils") _findutils = H("findutils")
@@ -48,6 +50,165 @@ func litArgs(verbose bool, skipChecks ...string) string {
} }
func init() { func init() {
var llvmPatches []KV
for _, name := range [...]string{
"increase-stack-size-unconditional",
"add-rosa-vendor",
"xfail-broken-tests",
"path-system-include",
"path-system-libraries",
} {
p, err := fs.ReadFile(nativeB, "package/llvm/"+name+".patch")
if err != nil {
panic(err)
}
llvmPatches = append(llvmPatches, KV{
name,
unsafe.String(unsafe.SliceData(p), len(p)),
})
}
native.MustRegister("early-compiler-rt", func(t Toolchain) (*Metadata, pkg.Artifact) {
meta := Metadata{
Name: "early-compiler-rt",
Description: "early LLVM runtime: compiler-rt",
Dependencies: P{_musl},
}
_meta, source := t.MustLoad(_llvmSource)
meta.Version = _meta.Version
major, _, _ := strings.Cut(meta.Version, ".")
return &meta, t.NewPackage("early-compiler-rt", meta.Version, source, &PackageAttr{
Flag: TExclusive,
}, &CMakeHelper{
Append: []string{"compiler-rt"},
Cache: []KV{
{"ENABLE_LINKER_BUILD_ID", "ON"},
// libc++ not yet available
{"CMAKE_CXX_COMPILER_TARGET", ""},
{"LLVM_HOST_TRIPLE", `"${ROSA_TRIPLE}"`},
{"LLVM_DEFAULT_TARGET_TRIPLE", `"${ROSA_TRIPLE}"`},
{"LLVM_ENABLE_RTTI", "ON"},
{"LLVM_BUILD_LLVM_DYLIB", "ON"},
{"LLVM_LINK_LLVM_DYLIB", "ON"},
{"LLVM_ENABLE_PER_TARGET_RUNTIME_DIR", "ON"},
{"COMPILER_RT_BUILD_BUILTINS", "ON"},
{"COMPILER_RT_DEFAULT_TARGET_ONLY", "ON"},
{"COMPILER_RT_USE_BUILTINS_LIBRARY", "ON"},
{"COMPILER_RT_SANITIZERS_TO_BUILD", "asan"},
{"COMPILER_RT_BUILD_GWP_ASAN", "OFF"},
// does not work without libunwind
{"COMPILER_RT_BUILD_CTX_PROFILE", "OFF"},
{"COMPILER_RT_BUILD_LIBFUZZER", "OFF"},
{"COMPILER_RT_BUILD_MEMPROF", "OFF"},
{"COMPILER_RT_BUILD_PROFILE", "OFF"},
{"COMPILER_RT_BUILD_XRAY", "OFF"},
},
SkipTest: true,
Script: `
mkdir -p "/work/system/lib/clang/` + major + `/lib/"
ln -s \
"../../../${ROSA_TRIPLE}" \
"/work/system/lib/clang/` + major + `/lib/"
ln -s \
"clang_rt.crtbegin-` + t.linuxArch() + `.o" \
"/work/system/lib/${ROSA_TRIPLE}/crtbeginS.o"
ln -s \
"clang_rt.crtend-` + t.linuxArch() + `.o" \
"/work/system/lib/${ROSA_TRIPLE}/crtendS.o"
`,
},
_python,
_muslHeaders,
_kernelHeaders,
)
})
native.MustRegister("early-runtimes", func(t Toolchain) (*Metadata, pkg.Artifact) {
meta := Metadata{
Name: "early-runtimes",
Description: "early LLVM runtimes: libunwind, libcxx, libcxxabi",
Dependencies: P{
_earlyCompilerRT,
},
}
_meta, source := t.MustLoad(_llvmSource)
meta.Version = _meta.Version
return &meta, t.NewPackage("early-runtimes", meta.Version, source, &PackageAttr{
Flag: TExclusive,
}, &CMakeHelper{
Append: []string{"runtimes"},
Cache: []KV{
{"ENABLE_LINKER_BUILD_ID", "ON"},
// libc++ not yet available
{"CMAKE_CXX_COMPILER_WORKS", "ON"},
{"LLVM_HOST_TRIPLE", `"${ROSA_TRIPLE}"`},
{"LLVM_DEFAULT_TARGET_TRIPLE", `"${ROSA_TRIPLE}"`},
{"LLVM_ENABLE_RTTI", "ON"},
{"LLVM_BUILD_LLVM_DYLIB", "ON"},
{"LLVM_LINK_LLVM_DYLIB", "ON"},
{"LLVM_ENABLE_RUNTIMES", "'libunwind;libcxx;libcxxabi'"},
{"LIBUNWIND_ENABLE_ASSERTIONS", "OFF"},
{"LIBUNWIND_USE_COMPILER_RT", "ON"},
{"LIBCXX_HAS_MUSL_LIBC", "ON"},
{"LIBCXX_USE_COMPILER_RT", "ON"},
{"LIBCXX_CXX_ABI", "libcxxabi"},
{"LIBCXX_ENABLE_STATIC_ABI_LIBRARY", "OFF"},
{"LIBCXX_HARDENING_MODE", "fast"},
{"LIBCXX_HAS_ATOMIC_LIB", "OFF"},
{"LIBCXXABI_USE_COMPILER_RT", "ON"},
{"LIBCXXABI_USE_LLVM_UNWINDER", "ON"},
{"LIBCXXABI_ENABLE_STATIC_UNWINDER", "OFF"},
{"LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL", "OFF"},
{"LLVM_ENABLE_ZLIB", "FORCE_ON"},
{"LLVM_ENABLE_ZSTD", "FORCE_ON"},
{"LLVM_ENABLE_LIBXML2", "OFF"},
},
SkipTest: true,
},
_python,
_zlib,
_zstd,
_earlyCompilerRT,
_kernelHeaders,
)
})
const (
version = "22.1.8"
checksum = "_QzoDE0W6cv0vfIWeLPDvqG_vKhz6IGWl1nqUUvhlKWiRzf5dMGqeC3tyS2XgI6j"
)
native.MustRegister("llvm-project", func(t Toolchain) (*Metadata, pkg.Artifact) {
meta := Metadata{
Name: "llvm-project",
Description: "LLVM monorepo with Rosa OS patches",
Version: version,
ID: 1830,
}
return &meta, t.NewPatchedSource("llvm-"+version, newFromGitHub(
"llvm/llvm-project",
"llvmorg-"+version,
checksum,
), true, llvmPatches...)
})
native.MustRegister("llvm", func(t Toolchain) (*Metadata, pkg.Artifact) { native.MustRegister("llvm", func(t Toolchain) (*Metadata, pkg.Artifact) {
meta := Metadata{ meta := Metadata{
Name: "llvm", Name: "llvm",
@@ -83,11 +244,9 @@ func init() {
{"LIBCXX_ENABLE_STATIC_ABI_LIBRARY", "OFF"}, {"LIBCXX_ENABLE_STATIC_ABI_LIBRARY", "OFF"},
{"LIBCXX_HAS_MUSL_LIBC", "ON"}, {"LIBCXX_HAS_MUSL_LIBC", "ON"},
{"LIBCXX_HARDENING_MODE", "fast"}, {"LIBCXX_HARDENING_MODE", "fast"},
{"LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY", "ON"},
{"LIBCXXABI_USE_LLVM_UNWINDER", "ON"}, {"LIBCXXABI_USE_LLVM_UNWINDER", "ON"},
{"LIBCXXABI_ENABLE_STATIC_UNWINDER", "ON"}, {"LIBCXXABI_ENABLE_STATIC_UNWINDER", "OFF"},
{"LIBCXXABI_USE_COMPILER_RT", "ON"}, {"LIBCXXABI_USE_COMPILER_RT", "ON"},
{"LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY", "ON"},
{"LLVM_INSTALL_BINUTILS_SYMLINKS", "ON"}, {"LLVM_INSTALL_BINUTILS_SYMLINKS", "ON"},
{"LLVM_INSTALL_UTILS", "ON"}, {"LLVM_INSTALL_UTILS", "ON"},
{"LLVM_BUILD_LLVM_DYLIB", "ON"}, {"LLVM_BUILD_LLVM_DYLIB", "ON"},
@@ -140,10 +299,6 @@ func init() {
"CodeGen/PowerPC/ppc-tmmintrin.c", "CodeGen/PowerPC/ppc-tmmintrin.c",
"CodeGen/PowerPC/ppc-smmintrin.c", "CodeGen/PowerPC/ppc-smmintrin.c",
"CodeGenCUDA/amdgpu-alias-undef-symbols.cu", "CodeGenCUDA/amdgpu-alias-undef-symbols.cu",
// clang: deadlocks with LIBCXXABI_ENABLE_STATIC_UNWINDER
"global-value-regex",
"global-hex-value-regex",
"check-globals",
// cxx: fails on musl // cxx: fails on musl
"close.dont-get-rid-of-buffer", "close.dont-get-rid-of-buffer",
"re/re.traits", "re/re.traits",
@@ -184,19 +339,8 @@ func init() {
}...) }...)
} }
return &meta, t.New("llvm", meta.Version, source, &PackageAttr{ return &meta, t.NewPackage("llvm", meta.Version, source, &PackageAttr{
Exclusive: true, Flag: TExclusive,
Bin: []string{
"chmod",
"mkdir",
"rm",
"tr",
"awk",
},
ScriptEarly: `
rm -f /system/bin/clang++.cfg
`,
}, &CMakeHelper{ }, &CMakeHelper{
Append: []string{"llvm"}, Append: []string{"llvm"},
@@ -206,10 +350,6 @@ ln -s ld.lld /work/system/bin/ld
ln -s clang /work/system/bin/cc ln -s clang /work/system/bin/cc
ln -s clang /work/system/bin/cpp ln -s clang /work/system/bin/cpp
ln -s clang++ /work/system/bin/c++ ln -s clang++ /work/system/bin/c++
cat << EOF > /work/system/bin/clang++.cfg
-static-libstdc++
-Wl,--exclude-libs,libunwind.a:libc++.a:libc++experimental.a:libc++abi.a
EOF
`, `,
// LLVM_LINK_LLVM_DYLIB causes llvm test suite to leak system // LLVM_LINK_LLVM_DYLIB causes llvm test suite to leak system
@@ -218,13 +358,22 @@ EOF
// on 3-stage determinism to test later stages. // on 3-stage determinism to test later stages.
SkipTest: t.stage.isStage0(), SkipTest: t.stage.isStage0(),
Test: "ninja " + jobsFlagE + " " + loadFlagE + " check-all", Test: `
chmod +w /bin && ln -s \
../system/bin/chmod \
../system/bin/mkdir \
../system/bin/rm \
../system/bin/tr \
../system/bin/awk \
/bin
ninja ` + jobsFlagE + ` check-all
`,
}, },
_pythonEarly, _python,
_perl, _perl,
_diffutils, _diffutils,
_bash, _bash,
_awk, _gawk,
_coreutils, _coreutils,
_findutils, _findutils,
+1 -1
View File
@@ -8,7 +8,7 @@ import (
) )
func TestLLVMInputs(t *testing.T) { func TestLLVMInputs(t *testing.T) {
const wantInputCount = 470 const wantInputCount = 553
_, llvm := rosa.Native().Std().MustLoad(rosa.H("llvm")) _, llvm := rosa.Native().Std().MustLoad(rosa.H("llvm"))
var n int var n int
+9 -13
View File
@@ -52,16 +52,15 @@ type MakeHelper struct {
var _ Helper = new(MakeHelper) var _ Helper = new(MakeHelper)
// extra returns make and other optional dependencies. // extra returns make and other optional dependencies.
func (attr *MakeHelper) extra() P { func (attr *MakeHelper) extra(flag int) P {
if attr == nil || !attr.OmitDefaults { extra := P{_make}
return P{ if (attr == nil || !attr.OmitDefaults) && flag&TEarly == 0 {
_make, extra = append(extra,
_gawk,
_awk,
_coreutils, _coreutils,
)
} }
} return extra
return P{_make}
} }
// wantsChmod returns whether the build system needs to be generated. // wantsChmod returns whether the build system needs to be generated.
@@ -90,7 +89,7 @@ func (attr *MakeHelper) wantsDir() (string, bool) {
if attr != nil && attr.InPlace { if attr != nil && attr.InPlace {
return helperInPlace, false return helperInPlace, false
} }
return "/cure/", true return `"$(mktemp -d)"`, false
} }
// script generates the cure script. // script generates the cure script.
@@ -161,8 +160,7 @@ func (attr *MakeHelper) script(t Toolchain, name string) string {
scriptMake := ` scriptMake := `
make \ make \
` + jobsFlagE + ` \ ` + jobsFlagE
` + loadFlagE
if len(attr.Make) > 0 { if len(attr.Make) > 0 {
scriptMake += " \\\n\t" + strings.Join(attr.Make, " \\\n\t") scriptMake += " \\\n\t" + strings.Join(attr.Make, " \\\n\t")
} }
@@ -172,8 +170,6 @@ make \
(!attr.SkipCheckEarly || !t.stage.isStage0()) { (!attr.SkipCheckEarly || !t.stage.isStage0()) {
scriptMake += attr.ScriptCheckEarly + `make \ scriptMake += attr.ScriptCheckEarly + `make \
` + jobsFlagE + ` \ ` + jobsFlagE + ` \
` + loadFlagE + ` \
AM_COLOR_TESTS=always \
` `
if len(attr.Check) > 0 { if len(attr.Check) > 0 {
scriptMake += strings.Join(attr.Check, " \\\n\t") scriptMake += strings.Join(attr.Check, " \\\n\t")
+15 -45
View File
@@ -5,16 +5,10 @@ import (
"strings" "strings"
) )
var ( var _meson = H("meson")
_muon = H("muon")
_meson = H("meson")
)
// MesonHelper builds and tests a meson project. // MesonHelper builds and tests a meson project.
type MesonHelper struct { type MesonHelper struct {
// Use muon instead of meson.
Muon bool
// Runs after setup. // Runs after setup.
ScriptCompileEarly string ScriptCompileEarly string
// Runs after compile. // Runs after compile.
@@ -35,12 +29,7 @@ type MesonHelper struct {
var _ Helper = new(MesonHelper) var _ Helper = new(MesonHelper)
// extra returns hardcoded meson runtime dependencies. // extra returns hardcoded meson runtime dependencies.
func (attr *MesonHelper) extra() P { func (*MesonHelper) extra(int) P { return P{_meson} }
if !attr.isPython() {
return P{_muon}
}
return P{_meson}
}
// wantsChmod returns false. // wantsChmod returns false.
func (*MesonHelper) wantsChmod() bool { return false } func (*MesonHelper) wantsChmod() bool { return false }
@@ -54,12 +43,6 @@ func (*MesonHelper) scriptEarly() string { return "" }
// wantsDir requests a new directory in TMPDIR. // wantsDir requests a new directory in TMPDIR.
func (*MesonHelper) wantsDir() (string, bool) { return `"$(mktemp -d)"`, false } func (*MesonHelper) wantsDir() (string, bool) { return `"$(mktemp -d)"`, false }
// isPython returns whether the legacy python implementation is used.
func (attr *MesonHelper) isPython() bool {
return attr == nil ||
!attr.Muon || len(attr.SkipTests) != 0 || attr.InteractiveTest
}
// script generates the cure script. // script generates the cure script.
func (attr *MesonHelper) script(t Toolchain, name string) string { func (attr *MesonHelper) script(t Toolchain, name string) string {
if attr == nil { if attr == nil {
@@ -71,28 +54,11 @@ func (attr *MesonHelper) script(t Toolchain, name string) string {
scriptCompiled = "\n" + scriptCompiled scriptCompiled = "\n" + scriptCompiled
} }
python := attr.isPython()
muon := "muon"
compile := " samu " + jobsFlagE
destdir := "-d"
if python {
muon = "meson"
compile = " compile " + jobsFlagE + " " + loadFlagE
destdir = "--destdir"
}
var scriptTest string var scriptTest string
if !attr.SkipTest && t.opts&OptSkipCheck == 0 { if !attr.SkipTest && t.opts&OptSkipCheck == 0 {
scriptTest = ` scriptTest = `
` + muon + ` test \ meson test \
` + jobsFlagE + ` \
-t 10`
if python {
scriptTest += ` \
--print-errorlogs` --print-errorlogs`
} else {
scriptTest += " -Rv"
}
if attr.InteractiveTest { if attr.InteractiveTest {
scriptTest += ` \ scriptTest += ` \
--interactive` --interactive`
@@ -103,25 +69,29 @@ func (attr *MesonHelper) script(t Toolchain, name string) string {
} }
return ` return `
` + muon + ` setup \ cd "$(mktemp -d)"
meson setup \
` + strings.Join(slices.Collect(func(yield func(string) bool) { ` + strings.Join(slices.Collect(func(yield func(string) bool) {
for _, v := range append([]KV{ for _, v := range append([]KV{
{"wrap_mode", "nofallback"}, {"wrap-mode", "nofallback"},
{"prefix", "/system"}, {"prefix", "/system"},
{"buildtype", "release"}, {"buildtype", "release"},
}, attr.Setup...) { }, attr.Setup...) {
s := v[0] s := "-" + v[0]
if len(v[0]) > 0 && v[0][0] != 'D' {
s = "-" + s
}
if v[1] != "" { if v[1] != "" {
s = "-D" + s + "=" + v[1] s += "=" + v[1]
} }
if !yield(s) { if !yield(s) {
return return
} }
} }
}), " \\\n\t") + ` \ }), " \\\n\t") + ` \
'/usr/src/` + name + `' . '/usr/src/` + name + `'
` + muon + compile + scriptCompiled + scriptTest + ` meson compile` + scriptCompiled + scriptTest + `
` + muon + ` install \ meson install \
` + destdir + ` /work --destdir=/work
` + attr.Script ` + attr.Script
} }
+113 -256
View File
@@ -14,7 +14,6 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"syscall"
"unique" "unique"
"hakurei.app/internal/pkg" "hakurei.app/internal/pkg"
@@ -31,8 +30,8 @@ type Remote struct {
c *http.Client c *http.Client
} }
// NewRemote returns a populated [Remote]. // NewRemote returns a populated [Remote]
func NewRemote(c *http.Client, base string, pub ed25519.PublicKey) (Remote, error) { func NewRemote(base string, pub ed25519.PublicKey, c *http.Client) (Remote, error) {
u, err := url.Parse(base) u, err := url.Parse(base)
return Remote{u, pub, c}, err return Remote{u, pub, c}, err
} }
@@ -202,261 +201,14 @@ func (r Remote) Status(
), nil ), nil
} }
// Mirror services requests made by [Remote]. // NewMirror returns an [http.Handler] for servicing mirror requests.
type Mirror struct {
// Cache on-filesystem format.
fsys fs.FS
// Signs payloads.
key ed25519.PrivateKey
// Verbose output.
msg message.Msg
}
// Populate fills an empty root with a static mirror snapshot.
func (m Mirror) Populate(root *os.Root) error {
if dents, err := fs.ReadDir(root.FS(), "."); err != nil {
return err
} else if len(dents) > 0 {
return syscall.ENOTEMPTY
}
for _, name := range []string{
dirArtifact,
dirOutcome,
dirStatus,
} {
if err := root.Mkdir(name, 0755); err != nil {
return err
}
}
var w *os.File
if dents, err := fs.ReadDir(m.fsys, "identifier"); err != nil {
return err
} else {
for _, dent := range dents {
name := dent.Name()
var buf [2 * len(pkg.Checksum{})]byte
if err = pkg.Decode(
(*pkg.Checksum)(buf[:len(pkg.Checksum{})]),
name,
); err != nil {
return err
}
if w, err = root.OpenFile(
filepath.Join(dirArtifact, name),
syscall.O_WRONLY|syscall.O_CREAT|syscall.O_EXCL,
0444,
); err != nil {
return err
}
err = m.writeArtifact(&buf, w, nil)
if err = errors.Join(err, w.Close()); err != nil {
return err
}
}
}
if dents, err := fs.ReadDir(m.fsys, "checksum"); err != nil {
return err
} else {
for _, dent := range dents {
name := dent.Name()
var buf pkg.Checksum
if err = pkg.Decode(&buf, name); err != nil {
return err
}
var fsys fs.FS
fsys, err = fs.Sub(m.fsys, filepath.Join("checksum", name))
if err != nil {
return err
}
if w, err = root.OpenFile(
filepath.Join(dirOutcome, name),
syscall.O_WRONLY|syscall.O_CREAT|syscall.O_EXCL,
0444,
); err != nil {
return err
}
m.msg.Verbosef("exporting outcome %s", name)
err = pkg.Write(fsys, ".", w)
if err = errors.Join(err, w.Close()); err != nil {
return err
}
}
}
if dents, err := fs.ReadDir(m.fsys, "status"); err != nil {
return err
} else {
for _, dent := range dents {
name := dent.Name()
var buf [2 * len(pkg.Checksum{})]byte
if err = pkg.Decode(
(*pkg.Checksum)(buf[:len(pkg.Checksum{})]),
name,
); err != nil {
return err
}
if w, err = root.OpenFile(
filepath.Join(dirStatus, name),
syscall.O_WRONLY|syscall.O_CREAT|syscall.O_EXCL,
0444,
); err != nil {
return err
}
err = m.writeStatus(&buf, w, nil)
if err = errors.Join(err, w.Close()); err != nil {
return err
}
}
}
return nil
}
// NewMirror returns a populated [Mirror] for servicing mirror requests.
func NewMirror( func NewMirror(
msg message.Msg, msg message.Msg,
fsys fs.FS, fsys fs.FS,
key ed25519.PrivateKey, key ed25519.PrivateKey,
) Mirror { ) http.Handler {
return Mirror{fsys, key, msg}
}
// writeArtifact writes a signed dirArtifact response.
func (m Mirror) writeArtifact(
buf *[2 * len(pkg.Checksum{})]byte,
w io.Writer,
header http.Header,
) error {
ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
if linkname, err := fs.ReadLink(m.fsys, filepath.Join(
"identifier",
ids,
)); err != nil {
return err
} else if err = pkg.Decode(
(*pkg.Checksum)(buf[len(pkg.Checksum{}):]),
filepath.Base(linkname),
); err != nil {
return err
}
m.msg.Verbosef("serving artifact %s", ids)
if header != nil {
header.Set(
"Content-Length",
strconv.Itoa(ed25519.SignatureSize+len(buf)),
)
}
_, err := w.Write(append(ed25519.Sign(m.key, buf[:]), buf[:]...))
return err
}
// writeOutcome writes a dirOutcome response.
func (m Mirror) writeOutcome(
buf *pkg.Checksum,
w io.Writer,
header http.Header,
) error {
checksums := pkg.Encode(*buf)
rel := filepath.Join("checksum", checksums)
if _, err := fs.Lstat(m.fsys, rel); err != nil {
return err
}
m.msg.Verbosef("serving outcome %s", checksums)
_fsys, err := fs.Sub(m.fsys, rel)
if err != nil {
return err
}
var gw *gzip.Writer
if gw, err = gzip.NewWriterLevel(w, gzip.BestCompression); err != nil {
return err
}
if header != nil {
header.Set("Content-Encoding", "gzip")
}
if err = pkg.Write(_fsys, ".", gw); err != nil {
_ = gw.Close()
return err
}
return gw.Close()
}
// writeStatus writes a signed dirStatus response.
func (m Mirror) writeStatus(
buf *[2 * len(pkg.Checksum{})]byte,
w io.Writer,
_ http.Header,
) error {
ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
f, err := m.fsys.Open(filepath.Join(
"status",
ids,
))
if err != nil {
return err
}
s, ok := f.(io.Seeker)
if !ok {
return &fs.PathError{Op: "seek", Path: ids, Err: fs.ErrInvalid}
}
m.msg.Verbosef("serving status %s", ids)
h := sha512.New384()
if _, err = io.Copy(h, f); err != nil {
_ = f.Close()
return err
}
h.Sum(buf[len(pkg.Checksum{}):len(pkg.Checksum{})])
if _, err = w.Write(append(ed25519.Sign(m.key, buf[:]), buf[:]...)); err != nil {
_ = f.Close()
return err
} else if _, err = s.Seek(0, io.SeekStart); err != nil {
_ = f.Close()
return err
} else if _, err = io.Copy(w, f); err != nil {
_ = f.Close()
return err
}
return f.Close()
}
// writeF is a helper for writing a mirror response with error handling.
func writeF[B any](
m Mirror,
w http.ResponseWriter,
buf B,
f func(
buf B,
w io.Writer,
header http.Header,
) error,
) {
err := f(buf, w, w.Header())
if err == nil {
return
}
if errors.Is(err, os.ErrNotExist) {
w.WriteHeader(http.StatusNotFound)
return
}
m.msg.GetLogger().Println(err)
w.WriteHeader(http.StatusInternalServerError)
}
// Register configures an [http.ServeMux] for servicing mirror requests.
func (m Mirror) Register(mux *http.ServeMux) {
const identName = "ident" const identName = "ident"
var mux http.ServeMux
mux.HandleFunc("/"+dirArtifact+"/{"+identName+"}", func( mux.HandleFunc("/"+dirArtifact+"/{"+identName+"}", func(
w http.ResponseWriter, w http.ResponseWriter,
@@ -470,7 +222,38 @@ func (m Mirror) Register(mux *http.ServeMux) {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
writeF(m, w, &buf, m.writeArtifact)
ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
if linkname, err := fs.ReadLink(fsys, filepath.Join(
"identifier",
ids,
)); err != nil {
if errors.Is(err, os.ErrNotExist) {
w.WriteHeader(http.StatusNotFound)
return
}
msg.GetLogger().Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
} else if err = pkg.Decode(
(*pkg.Checksum)(buf[len(pkg.Checksum{}):]),
filepath.Base(linkname),
); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
msg.Verbosef("serving artifact %s", ids)
w.Header().Set(
"Content-Length",
strconv.Itoa(ed25519.SignatureSize+len(buf)),
)
if _, err := w.Write(append(
ed25519.Sign(key, buf[:]),
buf[:]...,
)); err != nil {
msg.Verbose(err)
}
}) })
mux.HandleFunc("/"+dirOutcome+"/{"+identName+"}", func( mux.HandleFunc("/"+dirOutcome+"/{"+identName+"}", func(
@@ -490,7 +273,40 @@ func (m Mirror) Register(mux *http.ServeMux) {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
writeF(m, w, &buf, m.writeOutcome)
checksums := pkg.Encode(buf)
rel := filepath.Join("checksum", checksums)
if _, err := fs.Lstat(fsys, rel); err != nil {
if errors.Is(err, os.ErrNotExist) {
w.WriteHeader(http.StatusNotFound)
return
}
msg.GetLogger().Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
msg.Verbosef("serving outcome %s", pkg.Encode(buf))
_fsys, err := fs.Sub(fsys, rel)
if err != nil {
msg.GetLogger().Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
var gw *gzip.Writer
if gw, err = gzip.NewWriterLevel(w, gzip.BestCompression); err != nil {
msg.GetLogger().Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Encoding", "gzip")
if err = pkg.Write(_fsys, ".", gw); err != nil {
msg.Verbose(err)
}
if err = gw.Close(); err != nil {
msg.GetLogger().Println(err)
}
}) })
mux.HandleFunc("/"+dirStatus+"/{"+identName+"}", func( mux.HandleFunc("/"+dirStatus+"/{"+identName+"}", func(
@@ -505,6 +321,47 @@ func (m Mirror) Register(mux *http.ServeMux) {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
writeF(m, w, &buf, m.writeStatus)
ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
f, err := fsys.Open(filepath.Join(
"status",
ids,
))
if err != nil {
if errors.Is(err, os.ErrNotExist) {
w.WriteHeader(http.StatusNotFound)
return
}
msg.GetLogger().Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
s, ok := f.(io.Seeker)
if !ok {
msg.GetLogger().Println("backing filesystem does not support seek")
w.WriteHeader(http.StatusInternalServerError)
return
}
msg.Verbosef("serving status %s", ids)
h := sha512.New384()
if _, err = io.Copy(h, f); err != nil {
_ = f.Close()
msg.Verbose(err)
w.WriteHeader(http.StatusInternalServerError)
}
h.Sum(buf[len(pkg.Checksum{}):len(pkg.Checksum{})])
if _, err = w.Write(append(ed25519.Sign(key, buf[:]), buf[:]...)); err != nil {
msg.Verbose(err)
return
} else if _, err = s.Seek(0, io.SeekStart); err != nil {
msg.GetLogger().Println(err)
return
} else if _, err = io.Copy(w, f); err != nil {
msg.Verbose(err)
return
}
}) })
return &mux
} }
+4 -7
View File
@@ -4,7 +4,6 @@ import (
"crypto/ed25519" "crypto/ed25519"
"io/fs" "io/fs"
"log" "log"
"net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"testing" "testing"
@@ -31,7 +30,7 @@ func TestMirror(t *testing.T) {
msg.SwapVerbose(testing.Verbose()) msg.SwapVerbose(testing.Verbose())
var c *pkg.Cache var c *pkg.Cache
c, err = pkg.Open(t.Context(), msg, base, nil) c, err = pkg.Open(t.Context(), msg, 0, 0, 0, base)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -53,16 +52,14 @@ func TestMirror(t *testing.T) {
} }
wantChecksums := pkg.Encode(wantChecksum) wantChecksums := pkg.Encode(wantChecksum)
var mux http.ServeMux server := httptest.NewServer(rosa.NewMirror(msg, fstest.MapFS{
rosa.NewMirror(msg, fstest.MapFS{
"identifier/" + ids: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantChecksums)}, "identifier/" + ids: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantChecksums)},
"checksum/" + wantChecksums: {Mode: os.ModeDir | 0500}, "checksum/" + wantChecksums: {Mode: os.ModeDir | 0500},
}, priv).Register(&mux) }, priv))
server := httptest.NewServer(&mux)
t.Cleanup(server.Close) t.Cleanup(server.Close)
var extern pkg.External var extern pkg.External
extern, err = rosa.NewRemote(server.Client(), "http://example.com:80", pub) extern, err = rosa.NewRemote("http://example.com:80", pub, server.Client())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+4 -2
View File
@@ -13,8 +13,10 @@ package attr {
patches = [ "musl-errno.patch" ]; patches = [ "musl-errno.patch" ];
bin = [ "perl" ]; early = `
populateUsrBin = true; ln -s ../../system/bin/perl /usr/bin
`;
exec = make {}; exec = make {};
inputs = [ inputs = [
-31
View File
@@ -1,31 +0,0 @@
package awk {
description = "one true awk";
website = "https://github.com/onetrueawk/awk";
anitya = 16171;
version# = "20260426";
source = remoteGitHub {
suffix = "onetrueawk/awk";
tag = version;
checksum = "ewAvWLEHajhm7cA7v4DsKVk1c11niCLKIE5Q-8W_sxv1XXsbZlcOuWn1KOBYMxjo";
};
enterSource = true;
writable = true;
chmod = true;
exec = make {
omitDefaults = true;
inPlace = true;
skipConfigure = true;
make = [ "YACC='yacc -d -b awkgram'" ];
preCheck = "install -vD a.out /system/bin/awk\n";
install = "install -vD a.out /work/system/bin/awk";
};
inputs = [
m4,
byacc,
diffutils,
];
}
+6 -6
View File
@@ -5,19 +5,15 @@ package bison {
version# = "3.8.2"; version# = "3.8.2";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/bison/bison-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/bison/bison-"+version+".tar.gz";
checksum = "BhRM6K7URj1LNOkIDCFDctSErLS-Xo5d9ba9seg10o6ACrgC1uNhED7CQPgIY29Y"; checksum = "BhRM6K7URj1LNOkIDCFDctSErLS-Xo5d9ba9seg10o6ACrgC1uNhED7CQPgIY29Y";
compress = gzip; compress = gzip;
}; };
toyboxEarly = true;
exec = make { exec = make {
omitDefaults = true;
check = [ check = [
"TESTSUITEFLAGS=" + jobsFlagE + "' " + skipGNUTests { "TESTSUITEFLAGS=" + jobsFlagE + "' " + skipGNUTests {
tests = [ tests = [
// GNU-isms not implemented in toybox
168, 170, 623,
// clang miscompiles (SIGILL) // clang miscompiles (SIGILL)
764, 764,
]; ];
@@ -26,5 +22,9 @@ package bison {
]; ];
}; };
inputs = [ m4 ]; inputs = [
m4,
diffutils,
sed,
];
} }
-24
View File
@@ -1,24 +0,0 @@
package byacc {
description = "Berkeley Yacc";
website = "https://invisible-island.net/byacc";
anitya = 233;
version# = "20260126";
source = remoteTar {
url = "https://invisible-island.net/archives/byacc/"+
"byacc-"+version+".tgz";
compress = gzip;
checksum = "3IXzewZmFUW3hkFKBuweR0hUSftlI2zE0lBZf16I8oK3mFEyj51FzVv1wvA12uoC";
};
// awk dependency loop
toyboxEarly = true;
// test suite assumes writable source
writable = true;
chmod = true;
exec = make {
omitDefaults = true;
};
}
+10 -10
View File
@@ -13,16 +13,16 @@ package cairo {
exec = meson { exec = meson {
setup = { setup = {
"dwrite": "enabled"; "Ddwrite": "enabled";
"fontconfig": "enabled"; "Dfontconfig": "enabled";
"freetype": "enabled"; "Dfreetype": "enabled";
"png": "enabled"; "Dpng": "enabled";
"tee": "enabled"; "Dtee": "enabled";
"xcb": "enabled"; "Dxcb": "enabled";
"xlib": "disabled"; "Dxlib": "disabled";
"xlib-xcb": "disabled"; "Dxlib-xcb": "disabled";
"zlib": "enabled"; "Dzlib": "enabled";
}; };
// runs the entire test suite in a single meson test // runs the entire test suite in a single meson test
interactiveTest = true; interactiveTest = true;
@@ -31,7 +31,7 @@ package cairo {
}; };
inputs = [ inputs = [
awk, gawk,
zlib, zlib,
libpng, libpng,
@@ -0,0 +1,13 @@
diff --git a/Tests/BootstrapTest.cmake b/Tests/BootstrapTest.cmake
index 137de78bc1..b4da52e664 100644
--- a/Tests/BootstrapTest.cmake
+++ b/Tests/BootstrapTest.cmake
@@ -9,7 +9,7 @@ if(NOT nproc EQUAL 0)
endif()
message(STATUS "running bootstrap: ${bootstrap} ${ninja_arg} ${parallel_arg}")
execute_process(
- COMMAND ${bootstrap} ${ninja_arg} ${parallel_arg}
+ COMMAND ${bootstrap} ${ninja_arg} ${parallel_arg} -- -DCMAKE_USE_OPENSSL=OFF
WORKING_DIRECTORY "${bin_dir}"
RESULT_VARIABLE result
)
+6 -4
View File
@@ -3,15 +3,18 @@ package cmake {
website = "https://cmake.org"; website = "https://cmake.org";
anitya = 306; anitya = 306;
version# = "4.4.2"; version# = "4.3.4";
source = remoteGitHubRelease { source = remoteGitHubRelease {
suffix = "Kitware/CMake"; suffix = "Kitware/CMake";
tag = "v"+version; tag = "v"+version;
name = "cmake-"+version+".tar.gz"; name = "cmake-"+version+".tar.gz";
checksum = "IWlqJqJCZ6f-z-7Ft1U4ycvCX4riuihKale5NKWusiHemBgC3m5gCecrzikXoL7S"; checksum = "6A50pqarKDXKOCv7ffKMvWJWmNTVp_ep8KoaWwNdg-RYQRddgKbxayHveTo6jZ_7";
compress = gzip; compress = gzip;
}; };
patches = [ "disable-broken-tests-musl.patch" ]; patches = [
"bootstrap-test-no-openssl.patch",
"disable-broken-tests-musl.patch",
];
// test suite expects writable source tree // test suite expects writable source tree
writable = true; writable = true;
@@ -29,7 +32,6 @@ package cmake {
"--"; "--";
"-DCMAKE_USE_OPENSSL": "OFF"; "-DCMAKE_USE_OPENSSL": "OFF";
"-DCMake_TEST_NO_NETWORK": "ON"; "-DCMake_TEST_NO_NETWORK": "ON";
"-DCMake_TEST_BOOTSTRAP": "OFF";
}; };
check = [ check = [
-43
View File
@@ -1,43 +0,0 @@
package cryptsetup {
description = "open-source disk encryption";
website = "https://gitlab.com/cryptsetup/cryptsetup";
anitya = 13709;
version# = "2.8.7";
source = remoteGitLab {
domain = "gitlab.com";
suffix = "cryptsetup/cryptsetup";
ref = "v"+version;
checksum = "eDi_UaMtwhIzqpsdcA91aVb56zCYDrsDzeMWfWXAnPbuvn2bEgeH5dFs7mouHz8V";
};
bin = [ "bash" ];
exec = meson {
setup = {
"asciidoc": "disabled";
"ssh-token": "false";
"blkid": "false";
};
};
inputs = [
bash,
gettext,
coreutils,
popt,
lvm2,
json-c,
e2fsprogs,
argp-standalone,
kernel-headers,
];
runtime = [
popt,
lvm2,
json-c,
e2fsprogs,
openssl,
];
}
+4 -4
View File
@@ -19,9 +19,9 @@ package dbus {
exec = meson { exec = meson {
setup = { setup = {
"epoll": "enabled"; "Depoll": "enabled";
"inotify": "enabled"; "Dinotify": "enabled";
"x11_autolaunch": "disabled"; "Dx11_autolaunch": "disabled";
}; };
}; };
@@ -50,7 +50,7 @@ package xdg-dbus-proxy {
exec = meson { exec = meson {
setup = { setup = {
"man": "disabled"; "Dman": "disabled";
}; };
}; };
+1 -1
View File
@@ -18,7 +18,7 @@ package dtc {
exec = meson { exec = meson {
setup = { setup = {
"yaml": "disabled"; "Dyaml": "disabled";
}; };
}; };
-28
View File
@@ -1,28 +0,0 @@
package e2fsprogs {
description = "ext2/3/4 filesystem userspace utilities";
website = "http://e2fsprogs.sourceforge.net";
anitya = 646;
version# = "1.47.4";
source = remoteTar {
url = "https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git"+
"/snapshot/e2fsprogs-"+version+".tar.gz";
compress = gzip;
checksum = "gdnAEVjncIciqG-ZhFfYVv4wbBbCHBmPPYwRxltIfOTqdYH5L3ri8wk75YIVlgN0";
};
exec = make {
configure = {
"enable-elf-shlibs";
};
};
inputs = [
perl,
gzip,
bzip2,
diffutils,
kernel-headers,
];
}
+2 -3
View File
@@ -5,9 +5,8 @@ package fcft {
version# = "3.3.3"; version# = "3.3.3";
source = remoteTar { source = remoteTar {
url = "https://codeberg.org/dnkl/fcft/releases/download/"+version+ url = "https://codeberg.org/dnkl/fcft/archive/"+version+".tar.gz";
"/fcft-"+version+".tar.gz"; checksum = "bMj8OmqtoANRKZWbjHk4WfP09AxJYRbWGqUsm1gRJ_dOFg6gooNbgkDu597PLfz2";
checksum = "pryAJA-d1OpY8KXbGFEHum9Wm6ME9XkPx9EM_OQ_ZEpOUpQnqlkzIq-cwS1ziMPk";
compress = gzip; compress = gzip;
}; };
+1 -1
View File
@@ -34,7 +34,7 @@ package firmware {
// requires pre-commit // requires pre-commit
skipCheck = true; skipCheck = true;
install = "make " + jobsFlagE + " " + loadFlagE + " DESTDIR=/work/system dedup"; install = "make " + jobsFlagE + " DESTDIR=/work/system dedup";
}; };
inputs = [ inputs = [
+2 -2
View File
@@ -3,12 +3,12 @@ package fontconfig {
website = "https://www.freedesktop.org/wiki/Software/fontconfig"; website = "https://www.freedesktop.org/wiki/Software/fontconfig";
anitya = 827; anitya = 827;
version# = "2.18.3"; version# = "2.18.1";
source = remoteGitLab { source = remoteGitLab {
domain = "gitlab.freedesktop.org"; domain = "gitlab.freedesktop.org";
suffix = "fontconfig/fontconfig"; suffix = "fontconfig/fontconfig";
ref = version; ref = version;
checksum = "ZZzmt8fYh-3jKJ6vHXUDbeAWM2ilRusWxdynLt3Z4E_CQQ8cCwzpuAWACXmr9qHI"; checksum = "S3FJfh-PeZ2_YgNrUqL0H2G4Wf_ilj5HpIGV2oURnkK672AQHpWk526bNtRHwyJt";
}; };
exec = make { exec = make {
+2 -3
View File
@@ -5,9 +5,8 @@ package foot {
version# = "1.27.0"; version# = "1.27.0";
source = remoteTar { source = remoteTar {
url = "https://codeberg.org/dnkl/foot/releases/download/"+version+ url = "https://codeberg.org/dnkl/foot/archive/"+version+".tar.gz";
"/foot-"+version+".tar.gz"; checksum = "E2XIPRD8t-WBBJUebIwChFT8K_85diwdalssUNwnuZBIynPa7rlXro5WCe9v3UlK";
checksum = "22rG3rMuQI8Ae2XTcZgOJ-6AbF7Taex8XpgvD6Fyg1Mn5Va67FfUcK3tRaq61TFg";
compress = gzip; compress = gzip;
}; };
+1 -1
View File
@@ -12,7 +12,7 @@ package fribidi {
exec = meson { exec = meson {
setup = { setup = {
"docs": "false"; "Ddocs": "false";
}; };
}; };
} }
+4 -4
View File
@@ -14,10 +14,10 @@ package fuse {
exec = meson { exec = meson {
setup = { setup = {
"default_library": "both"; "Ddefault_library": "both";
"tests": "true"; "Dtests": "true";
"useroot": "false"; "Duseroot": "false";
"initscriptdir": "/system/etc"; "Dinitscriptdir": "/system/etc";
}; };
postCompile = "python3 -m pytest test/"; postCompile = "python3 -m pytest test/";
@@ -1,35 +0,0 @@
diff --git a/test/Makefile.am b/test/Makefile.am
index 88e15ef1f..bfc279fcb 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1726,7 +1726,7 @@ LOCALE_CHARSET_TESTS = \
SHLIB_TESTS = \
apiterm filefuncs fnmatch fork fork2 fts functab4 functab5 \
getfile indirectbuiltin2 inplace1 inplace2 inplace2bcomp inplace3 \
- inplace3bcomp ordchr ordchr2 readall readdir readdir_retest \
+ inplace3bcomp ordchr ordchr2 readall readdir_retest \
readdir_test readfile readfile2 revout revtwoway rwarray \
testext time
diff --git a/test/Makefile.in b/test/Makefile.in
index 2d4eeaa5a..7930af8d5 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -1986,7 +1986,7 @@ LOCALE_CHARSET_TESTS = \
SHLIB_TESTS = \
apiterm filefuncs fnmatch fork fork2 fts functab4 functab5 \
getfile indirectbuiltin2 inplace1 inplace2 inplace2bcomp inplace3 \
- inplace3bcomp ordchr ordchr2 readall readdir readdir_retest \
+ inplace3bcomp ordchr ordchr2 readall readdir_retest \
readdir_test readfile readfile2 revout revtwoway rwarray \
testext time
diff --git a/test/clos1way6.ok b/test/clos1way6.ok
index 5768617d3..be4d73e79 100644
--- a/test/clos1way6.ok
+++ b/test/clos1way6.ok
@@ -1,3 +1,3 @@
gawk: clos1way6.awk:5: warning: fflush: cannot flush: two-way pipe `cat - 1>&2; sleep 2' has closed write end
-test1
Bad file descriptor
+test1
@@ -1,39 +0,0 @@
diff --git a/test/testext-mpfr.ok b/test/testext-mpfr.ok
index 6a5f98147..adf8685e2 100644
--- a/test/testext-mpfr.ok
+++ b/test/testext-mpfr.ok
@@ -21,7 +21,7 @@ var_test: sym_update of ARGC failed - correctly
var_test: sym_update("testvar") succeeded
var_test() returned 1, test_var = 42
-test_errno() returned 1, ERRNO = No child processes
+test_errno() returned 1, ERRNO = No child process
fubar = 9
rumpus = -5
diff --git a/test/testext.ok b/test/testext.ok
index 3e0c1328c..11d150fe3 100644
--- a/test/testext.ok
+++ b/test/testext.ok
@@ -21,7 +21,7 @@ var_test: sym_update of ARGC failed - correctly
var_test: sym_update("testvar") succeeded
var_test() returned 1, test_var = 42
-test_errno() returned 1, ERRNO = No child processes
+test_errno() returned 1, ERRNO = No child process
fubar = 9
rumpus = -5
diff --git a/vms/vmstest.com b/vms/vmstest.com
index a042764b1..c47984e38 100644
--- a/vms/vmstest.com
+++ b/vms/vmstest.com
@@ -917,7 +917,7 @@ $ set noOn
$ AWKLIBPATH_dir
$ gawk -f _'test'.awk >_'test'.tmp 2>&1
$ if .not. $status then call exit_code '$status' _'test'.tmp
-$ gawk "{gsub(""no children"",""No child processes"")}1" -
+$ gawk "{gsub(""no children"",""No child process"")}1" -
_'test'.tmp >_'test'.tmp1
$ rm sys$disk:[]_'test'.tmp;*
$ mv sys$disk:[]_'test'.tmp1 sys$disk:[]_'test'.tmp
-26
View File
@@ -1,26 +0,0 @@
package gawk {
description = "an implementation of awk with GNU extensions";
website = "https://www.gnu.org/software/gawk";
anitya = 868;
block = "regressions introduced in 5.4.1, "+
"does not even pass its own test suite";
version# = "5.4.0";
source = remoteTar {
url = "https://mirrors.kernel.org/gnu/gawk/gawk-"+version+".tar.gz";
checksum = "m0RkIolC-PI7EY5q8pcx5Y-0twlIW0Yp3wXXmV-QaHorSdf8BhZ7kW9F8iWomz0C";
compress = gzip;
};
patches = [
"musl-errno.patch",
"broken-tests.patch",
];
exec = make {};
inputs = [
diffutils,
util-linux,
];
}
+5 -6
View File
@@ -3,10 +3,10 @@ package binutils {
website = "https://www.gnu.org/software/binutils"; website = "https://www.gnu.org/software/binutils";
anitya = 7981; anitya = 7981;
version# = "2.47"; version# = "2.46.1";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/binutils/binutils-"+version+".tar.bz2"; url = "https://ftpmirror.gnu.org/gnu/binutils/binutils-"+version+".tar.bz2";
checksum = "VjbMg5KtZ6VSeNc77Z5p8RKg9eZ56g29ViKbbsF3tcFhZu0Yurg0nZt7UwB577LM"; checksum = "ihCVohBxX-URAR-YFDdVrL3inLYsEVX8KbbQmcbHaShHgqqmtsvUue1h3LXSjW-d";
compress = bzip2; compress = bzip2;
}; };
@@ -20,11 +20,11 @@ package gcc {
website = "https://www.gnu.org/software/gcc"; website = "https://www.gnu.org/software/gcc";
anitya = 6502; anitya = 6502;
version# = "16.2.0"; version# = "16.1.0";
source = remoteTar { source = remoteTar {
url = "https://ftp.tsukuba.wide.ad.jp/software/gcc/releases/"+ url = "https://ftp.tsukuba.wide.ad.jp/software/gcc/releases/"+
"gcc-"+version+"/gcc-"+version+".tar.gz"; "gcc-"+version+"/gcc-"+version+".tar.gz";
checksum = "yWDNSrcKa6yA5ucD4G-a5nCN99yPA8cRlxKgQlaaT-jM4pWYR6e1EvI2qbjUqXWZ"; checksum = "4ASoWbxaA2FW7PAB0zzHDPC5XnNhyaAyjtDPpGzceSLeYnEIXsNYZR3PA_Zu5P0K";
compress = gzip; compress = gzip;
}; };
patches = [ patches = [
@@ -82,7 +82,6 @@ ln -s system/lib /work/
mpc, mpc,
zlib, zlib,
zstd,
libucontext, libucontext,
]; ];
} }
+2 -5
View File
@@ -12,6 +12,8 @@ package git {
}; };
early = ` early = `
ln -s ../../system/bin/perl /usr/bin/ || true
# test suite assumes apache # test suite assumes apache
rm -f /system/bin/httpd rm -f /system/bin/httpd
`; `;
@@ -20,8 +22,6 @@ rm -f /system/bin/httpd
enterSource = true; enterSource = true;
env = [ "NO_RUST=YesPlease" ]; env = [ "NO_RUST=YesPlease" ];
bin = [ "perl" ];
populateUsrBin = true;
exec = make { exec = make {
inPlace = true; inPlace = true;
generate = "make configure"; generate = "make configure";
@@ -60,8 +60,6 @@ disable_test t1451-fsck-buffer
disable_test t4104-apply-boundary disable_test t4104-apply-boundary
disable_test t4200-rerere disable_test t4200-rerere
disable_test t5515-fetch-merge-logic disable_test t5515-fetch-merge-logic
disable_test t4124-apply-ws-rule
disable_test t3404-rebase-interactive
`; `;
check = [ check = [
@@ -72,7 +70,6 @@ disable_test t3404-rebase-interactive
install = `make \ install = `make \
` + jobsFlagE + ` \ ` + jobsFlagE + ` \
` + loadFlagE + ` \
DESTDIR=/work \ DESTDIR=/work \
NO_INSTALL_HARDLINKS=1 \ NO_INSTALL_HARDLINKS=1 \
install`; install`;
+3 -3
View File
@@ -3,11 +3,11 @@ package glib {
website = "https://developer.gnome.org/glib"; website = "https://developer.gnome.org/glib";
anitya = 10024; anitya = 10024;
version# = "2.89.3"; version# = "2.89.1";
source = remoteGit { source = remoteGit {
url = "https://gitlab.gnome.org/GNOME/glib.git"; url = "https://gitlab.gnome.org/GNOME/glib.git";
tag = version; tag = version;
checksum = "eM9KrXwhwKiZferwrILKmDeq-IGnSjr0EKqV90EEVl0Ee82QVcyVSf2LDMROHcOF"; checksum = "9_6Eew2KIwa1AHopjU7CqC13_nur5FPJMu-iGUd7sD_1gAM1pa_HVUuAtqExJoYU";
}; };
files = { files = {
@@ -17,7 +17,7 @@ package glib {
exec = meson { exec = meson {
setup = { setup = {
"default_library": "both"; "Ddefault_library": "both";
}; };
// fails with ipv6 disabled // fails with ipv6 disabled
skipTests = [ "gio" ]; skipTests = [ "gio" ];
+4 -4
View File
@@ -1,13 +1,13 @@
package glslang { package glslang {
description = "reference front end for GLSL/ESSL"; description = "reference front end for GLSL/ESSL";
website = "https://github.com/KhronosGroup/glslang"; website = "https://github.com/KhronosGroup/glslang";
anitya = 334917; anitya = 205796;
version# = "1.4.357.0"; version# = "16.3.0";
source = remoteGitHub { source = remoteGitHub {
suffix = "KhronosGroup/glslang"; suffix = "KhronosGroup/glslang";
tag = "vulkan-sdk-"+version; tag = version;
checksum = "7Zq12xtSd5z_aEEeYCnsWR5WvcEGVlTsRaV6MqkmY1a3iNIrRbX_rrg-taI3wMe6"; checksum = "xyqDf8k3-D0_BXHGi0uLgMglnJ05Rf3j73QgbDs3sGtKNdBIQhY8JfqX1NcNoJQN";
}; };
// test suite writes to source // test suite writes to source
+55 -55
View File
@@ -7,7 +7,7 @@ package m4 {
version# = "1.4.21"; version# = "1.4.21";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/m4/m4-"+version+".tar.bz2"; url = "https://ftpmirror.gnu.org/gnu/m4/m4-"+version+".tar.bz2";
checksum = "pPa6YOo722Jw80l1OsH1tnUaklnPFjFT-bxGw5iAVrZTm1P8FQaWao_NXop46-pm"; checksum = "pPa6YOo722Jw80l1OsH1tnUaklnPFjFT-bxGw5iAVrZTm1P8FQaWao_NXop46-pm";
compress = bzip2; compress = bzip2;
}; };
@@ -17,10 +17,7 @@ package m4 {
chmod +w tests/test-c32ispunct.sh && echo '#!/bin/sh' > tests/test-c32ispunct.sh chmod +w tests/test-c32ispunct.sh && echo '#!/bin/sh' > tests/test-c32ispunct.sh
`; `;
toyboxEarly = true; exec = make {};
exec = make {
omitDefaults = true;
};
inputs = [ inputs = [
diffutils, diffutils,
@@ -36,7 +33,7 @@ package autoconf {
version# = "2.73"; version# = "2.73";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/autoconf/autoconf-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/autoconf/autoconf-"+version+".tar.gz";
checksum = "yGabDTeOfaCUB0JX-h3REYLYzMzvpDwFmFFzHNR7QilChCUNE4hR6q7nma4viDYg"; checksum = "yGabDTeOfaCUB0JX-h3REYLYzMzvpDwFmFFzHNR7QilChCUNE4hR6q7nma4viDYg";
compress = gzip; compress = gzip;
}; };
@@ -71,7 +68,7 @@ package automake {
version# = "1.18.1"; version# = "1.18.1";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/automake/automake-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/automake/automake-"+version+".tar.gz";
checksum = "FjvLG_GdQP7cThTZJLDMxYpRcKdpAVG-YDs1Fj1yaHlSdh_Kx6nRGN14E0r_BjcG"; checksum = "FjvLG_GdQP7cThTZJLDMxYpRcKdpAVG-YDs1Fj1yaHlSdh_Kx6nRGN14E0r_BjcG";
compress = gzip; compress = gzip;
}; };
@@ -105,10 +102,10 @@ package libtool {
website = "https://www.gnu.org/software/libtool"; website = "https://www.gnu.org/software/libtool";
anitya = 1741; anitya = 1741;
version# = "2.6.2"; version# = "2.5.4";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/libtool/libtool-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/libtool/libtool-"+version+".tar.gz";
checksum = "roFqdjyoGjpK7CWWLJJrTS56LuOQcIdq6Tgr4yplacSwirFHoaiDe6CojIWYinWe"; checksum = "pa6LSrQggh8mSJHQfwGjysAApmZlGJt8wif2cCLzqAAa2jpsTY0jZ-6stS3BWZ2Q";
compress = gzip; compress = gzip;
}; };
@@ -135,7 +132,7 @@ package gzip {
version# = "1.14"; version# = "1.14";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/gzip/gzip-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/gzip/gzip-"+version+".tar.gz";
checksum = "NWhjUavnNfTDFkZJyAUonL9aCOak8GVajWX2OMlzpFnuI0ErpBFyj88mz2xSjz0q"; checksum = "NWhjUavnNfTDFkZJyAUonL9aCOak8GVajWX2OMlzpFnuI0ErpBFyj88mz2xSjz0q";
compress = gzip; compress = gzip;
}; };
@@ -153,18 +150,11 @@ package sed {
version# = "4.10"; version# = "4.10";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/sed/sed-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/sed/sed-"+version+".tar.gz";
checksum = "TXTRFQJCyflb-bpBRI2S5Y1DpplwvT7-KfXtpqN4AdZgZ5OtI6yStn1-bkhDKx51"; checksum = "TXTRFQJCyflb-bpBRI2S5Y1DpplwvT7-KfXtpqN4AdZgZ5OtI6yStn1-bkhDKx51";
compress = gzip; compress = gzip;
}; };
writable = true;
early = `
test_disable() { chmod +w "$2" && echo "$1" > "$2"; }
test_disable '#!/bin/sh' testsuite/read-error-stale-errno.sh
`;
exec = make {}; exec = make {};
inputs = [ inputs = [
@@ -181,7 +171,7 @@ package diffutils {
version# = "3.12"; version# = "3.12";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/diffutils/diffutils-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/diffutils/diffutils-"+version+".tar.gz";
checksum = "9J5VAq5oA7eqwzS1Yvw-l3G5o-TccUrNQR3PvyB_lgdryOFAfxtvQfKfhdpquE44"; checksum = "9J5VAq5oA7eqwzS1Yvw-l3G5o-TccUrNQR3PvyB_lgdryOFAfxtvQfKfhdpquE44";
compress = gzip; compress = gzip;
}; };
@@ -196,9 +186,7 @@ test_disable '#!/bin/sh' tests/cmp
`; `;
toyboxEarly = true; toyboxEarly = true;
exec = make { exec = make {};
omitDefaults = true;
};
} }
package patch { package patch {
@@ -208,7 +196,7 @@ package patch {
version# = "2.8"; version# = "2.8";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/patch/patch-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/patch/patch-"+version+".tar.gz";
checksum = "MA0BQc662i8QYBD-DdGgyyfTwaeALZ1K0yusV9rAmNiIsQdX-69YC4t9JEGXZkeR"; checksum = "MA0BQc662i8QYBD-DdGgyyfTwaeALZ1K0yusV9rAmNiIsQdX-69YC4t9JEGXZkeR";
compress = gzip; compress = gzip;
}; };
@@ -221,9 +209,26 @@ test_disable '#!/bin/sh' tests/ed-style
test_disable '#!/bin/sh' tests/need-filename test_disable '#!/bin/sh' tests/need-filename
`; `;
toyboxEarly = true;
exec = make {};
}
package gawk {
description = "an implementation of awk with GNU extensions";
website = "https://www.gnu.org/software/gawk";
anitya = 868;
version# = "5.4.0";
source = remoteTar {
url = "https://ftpmirror.gnu.org/gnu/gawk/gawk-"+version+".tar.gz";
checksum = "m0RkIolC-PI7EY5q8pcx5Y-0twlIW0Yp3wXXmV-QaHorSdf8BhZ7kW9F8iWomz0C";
compress = gzip;
};
toyboxEarly = true; toyboxEarly = true;
exec = make { exec = make {
omitDefaults = true; // dependency loop
skipCheck = true;
}; };
} }
@@ -234,7 +239,7 @@ package grep {
version# = "3.12"; version# = "3.12";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/grep/grep-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/grep/grep-"+version+".tar.gz";
checksum = "qMB4RjaPNRRYsxix6YOrjE8gyAT1zVSTy4nW4wKW9fqa0CHYAuWgPwDTirENzm_1"; checksum = "qMB4RjaPNRRYsxix6YOrjE8gyAT1zVSTy4nW4wKW9fqa0CHYAuWgPwDTirENzm_1";
compress = gzip; compress = gzip;
}; };
@@ -259,7 +264,7 @@ package gettext {
version# = "1.0"; version# = "1.0";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/gettext/gettext-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/gettext/gettext-"+version+".tar.gz";
checksum = "3MasKeEdPeFEgWgzsBKk7JqWqql1wEMbgPmzAfs-mluyokoW0N8oQVxPQoOnSdgC"; checksum = "3MasKeEdPeFEgWgzsBKk7JqWqql1wEMbgPmzAfs-mluyokoW0N8oQVxPQoOnSdgC";
compress = gzip; compress = gzip;
}; };
@@ -297,18 +302,14 @@ package findutils {
website = "https://www.gnu.org/software/findutils"; website = "https://www.gnu.org/software/findutils";
anitya = 812; anitya = 812;
version# = "4.11.0"; version# = "4.10.0";
source = remoteTar { source = remoteFile {
url = "https://mirrors.kernel.org/gnu/findutils/findutils-"+version+".tar.xz"; url = "https://ftpmirror.gnu.org/gnu/findutils/findutils-"+version+".tar.xz";
compress = xz; checksum = "ZXABdNBQXL7QjTygynRRTdXYWxQKZ0Wn5eMd3NUnxR0xaS0u0VfcKoTlbo50zxv6";
checksum = "X-qsv9aUoBL583q-4iZcBI3_7Ufd3fSIqFmNLXWdEpUihlRfMpGGyAUt8SAvDKae";
}; };
writable = true;
chmod = true;
early = ` early = `
echo '#!/bin/sh' > gnulib-tests/test-c32ispunct.sh echo '#!/bin/sh' > gnulib-tests/test-c32ispunct.sh
echo 'int main(){return 0;}' > gnulib-tests/test-fstatat.c
echo 'int main(){return 0;}' > tests/xargs/test-sigusr.c echo 'int main(){return 0;}' > tests/xargs/test-sigusr.c
`; `;
@@ -316,6 +317,7 @@ echo 'int main(){return 0;}' > tests/xargs/test-sigusr.c
inputs = [ inputs = [
diffutils, diffutils,
xz,
sed, sed,
]; ];
} }
@@ -327,14 +329,13 @@ package bash {
version# = "5.3"; version# = "5.3";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/bash/bash-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/bash/bash-"+version+".tar.gz";
checksum = "4LQ_GRoB_ko-Ih8QPf_xRKA02xAm_TOxQgcJLmFDT6udUPxTAWrsj-ZNeuTusyDq"; checksum = "4LQ_GRoB_ko-Ih8QPf_xRKA02xAm_TOxQgcJLmFDT6udUPxTAWrsj-ZNeuTusyDq";
compress = gzip; compress = gzip;
}; };
toyboxEarly = true; toyboxEarly = true;
exec = make { exec = make {
omitDefaults = true;
configure = { configure = {
"without-bash-malloc"; "without-bash-malloc";
}; };
@@ -349,7 +350,7 @@ package coreutils {
version# = "9.11"; version# = "9.11";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/coreutils/coreutils-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/coreutils/coreutils-"+version+".tar.gz";
checksum = "t8UMed5wpFEoC56aa42_yidfOAaRGzOfj7MRtQkkqgGbpXiskNA8bd-EmVSQkZie"; checksum = "t8UMed5wpFEoC56aa42_yidfOAaRGzOfj7MRtQkkqgGbpXiskNA8bd-EmVSQkZie";
compress = gzip; compress = gzip;
}; };
@@ -362,16 +363,12 @@ test_disable() { chmod +w "$2" && echo "$1" > "$2"; }
test_disable '#!/bin/sh' gnulib-tests/test-c32ispunct.sh test_disable '#!/bin/sh' gnulib-tests/test-c32ispunct.sh
test_disable '#!/bin/sh' tests/ls/hyperlink.sh test_disable '#!/bin/sh' tests/ls/hyperlink.sh
test_disable '#!/bin/sh' tests/misc/user.sh test_disable '#!/bin/sh' tests/misc/user.sh
test_disable '#!/bin/sh' tests/split/line-bytes.sh
test_disable '#!/bin/sh' tests/ls/stat-vs-dirent.sh
test_disable '#!/bin/sh' tests/dd/sparse.sh
test_disable 'int main(){return 0;}' gnulib-tests/test-chown.c test_disable 'int main(){return 0;}' gnulib-tests/test-chown.c
test_disable 'int main(){return 0;}' gnulib-tests/test-fchownat.c test_disable 'int main(){return 0;}' gnulib-tests/test-fchownat.c
test_disable 'int main(){return 0;}' gnulib-tests/test-lchown.c test_disable 'int main(){return 0;}' gnulib-tests/test-lchown.c
`; `;
exec = make { exec = make {
omitDefaults = true;
configure = { configure = {
"enable-single-binary": "symlinks"; "enable-single-binary": "symlinks";
}; };
@@ -392,7 +389,7 @@ package tar {
version# = "1.35"; version# = "1.35";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/tar/tar-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/tar/tar-"+version+".tar.gz";
checksum = "zSaoSlVUDW0dSfm4sbL4FrXLFR8U40Fh3zY5DWhR5NCIJ6GjU6Kc4VZo2-ZqpBRA"; checksum = "zSaoSlVUDW0dSfm4sbL4FrXLFR8U40Fh3zY5DWhR5NCIJ6GjU6Kc4VZo2-ZqpBRA";
compress = gzip; compress = gzip;
}; };
@@ -429,7 +426,7 @@ package texinfo {
version# = "7.3"; version# = "7.3";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/texinfo/texinfo-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/texinfo/texinfo-"+version+".tar.gz";
checksum = "RRmC8Xwdof7JuZJeWGAQ_GeASIHAuJFQMbNONXBz5InooKIQGmqmWRjGNGEr5n4-"; checksum = "RRmC8Xwdof7JuZJeWGAQ_GeASIHAuJFQMbNONXBz5InooKIQGmqmWRjGNGEr5n4-";
compress = gzip; compress = gzip;
}; };
@@ -443,7 +440,7 @@ package texinfo {
runtime = [ runtime = [
perl, perl,
awk, gawk,
]; ];
} }
@@ -454,7 +451,7 @@ package gperf {
version# = "3.3"; version# = "3.3";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/gperf/gperf-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gperf/gperf-"+version+".tar.gz";
checksum = "RtIy9pPb_Bb8-31J2Nw-rRGso2JlS-lDlVhuNYhqR7Nt4xM_nObznxAlBMnarJv7"; checksum = "RtIy9pPb_Bb8-31J2Nw-rRGso2JlS-lDlVhuNYhqR7Nt4xM_nObznxAlBMnarJv7";
compress = gzip; compress = gzip;
}; };
@@ -471,7 +468,7 @@ package bc {
version# = "1.08.2"; version# = "1.08.2";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/bc/bc-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/bc/bc-"+version+".tar.gz";
checksum = "8h6f3hjV80XiFs6v9HOPF2KEyg1kuOgn5eeFdVspV05ODBVQss-ey5glc8AmneLy"; checksum = "8h6f3hjV80XiFs6v9HOPF2KEyg1kuOgn5eeFdVspV05ODBVQss-ey5glc8AmneLy";
compress = gzip; compress = gzip;
}; };
@@ -494,7 +491,7 @@ package libiconv {
version# = "1.19"; version# = "1.19";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/libiconv/libiconv-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/libiconv/libiconv-"+version+".tar.gz";
checksum = "UibB6E23y4MksNqYmCCrA3zTFO6vJugD1DEDqqWYFZNuBsUWMVMcncb_5pPAr88x"; checksum = "UibB6E23y4MksNqYmCCrA3zTFO6vJugD1DEDqqWYFZNuBsUWMVMcncb_5pPAr88x";
compress = gzip; compress = gzip;
}; };
@@ -507,14 +504,17 @@ package parallel {
website = "https://www.gnu.org/software/parallel"; website = "https://www.gnu.org/software/parallel";
anitya = 5448; anitya = 5448;
version# = "20260722"; version# = "20260622";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/parallel/parallel-"+version+".tar.bz2"; url = "https://ftpmirror.gnu.org/gnu/parallel/parallel-"+version+".tar.bz2";
checksum = "liG9pBSRqUPqDuRXmnJPoVh-G53lDKkkO8GGabSxQnSho4b9BD0EaJDdScgVhnwj"; checksum = "pEkZ_J18AvKCMoPfxEprC0Smw7zP6qXASmC1uFf_HVD_jc95H06LvzGi40-mtCUo";
compress = bzip2; compress = bzip2;
}; };
bin = [ "bash" ]; early = `
ln -s ../system/bin/bash /bin/
`;
exec = make {}; exec = make {};
inputs = [ inputs = [
@@ -532,7 +532,7 @@ package libunistring {
version# = "1.4.2"; version# = "1.4.2";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/libunistring/libunistring-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/libunistring/libunistring-"+version+".tar.gz";
checksum = "iW9BbfLoVlXjWoLTZ4AekQSu4cFBnLcZ4W8OHWbv0AhJNgD3j65_zqaLMzFKylg2"; checksum = "iW9BbfLoVlXjWoLTZ4AekQSu4cFBnLcZ4W8OHWbv0AhJNgD3j65_zqaLMzFKylg2";
compress = gzip; compress = gzip;
}; };
@@ -559,7 +559,7 @@ package libtasn1 {
version# = "4.21.0"; version# = "4.21.0";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/libtasn1/libtasn1-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/libtasn1/libtasn1-"+version+".tar.gz";
checksum = "9DYI3UYbfYLy8JsKUcY6f0irskbfL0fHZA91Q-JEOA3kiUwpodyjemRsYRjUpjuq"; checksum = "9DYI3UYbfYLy8JsKUcY6f0irskbfL0fHZA91Q-JEOA3kiUwpodyjemRsYRjUpjuq";
compress = gzip; compress = gzip;
}; };
@@ -574,7 +574,7 @@ package readline {
version# = "8.3"; version# = "8.3";
source = remoteTar { source = remoteTar {
url = "https://mirrors.kernel.org/gnu/readline/readline-"+version+".tar.gz"; url = "https://ftpmirror.gnu.org/gnu/readline/readline-"+version+".tar.gz";
checksum = "r-lcGRJq_MvvBpOq47Z2Y1OI2iqrmtcqhTLVXR0xWo37ZpC2uT_md7gKq5o_qTMV"; checksum = "r-lcGRJq_MvvBpOq47Z2Y1OI2iqrmtcqhTLVXR0xWo37ZpC2uT_md7gKq5o_qTMV";
compress = gzip; compress = gzip;
}; };
+1 -2
View File
@@ -34,8 +34,7 @@ echo 'int main(){return 0;}' > tests/mini-dtls-fragments.c
"with-zstd": "link"; "with-zstd": "link";
"": arch { "": arch {
// miscompilation on arm64 arm64 = "disable-hardware-acceleration";
arm64 = "--disable-hardware-acceleration";
}; };
}; };
}; };
-29
View File
@@ -1,29 +0,0 @@
From 45929774c3d5c084cc478afba63c77cf67a68bd7 Mon Sep 17 00:00:00 2001
From: khr@golang.org <khr@golang.org>
Date: Wed, 18 Feb 2026 09:09:57 -0800
Subject: [PATCH] internal/runtime/gc/scan: require popcnt for x86
The GOAMD64=v1 test disables popcnt instructions, which is a feature
this package's amd64 assembly uses.
Fixes #77674
Change-Id: I7be9bb665838f5da50275f96ef3df398412bb44a
Reviewed-on: https://go-review.googlesource.com/c/go/+/746640
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Keith Randall <khr@google.com>
Auto-Submit: Keith Randall <khr@golang.org>
---
diff --git a/src/internal/runtime/gc/scan/scan_amd64.go b/src/internal/runtime/gc/scan/scan_amd64.go
index c03021c7d4..544b237770 100644
--- a/src/internal/runtime/gc/scan/scan_amd64.go
+++ b/src/internal/runtime/gc/scan/scan_amd64.go
@@ -39,4 +39,5 @@ var avx512ScanPackedReqsMet = cpu.X86.HasAVX512VL &&
cpu.X86.HasGFNI &&
cpu.X86.HasAVX512BITALG &&
cpu.X86.HasAVX512DQ && // for kmovb, see #79871
- cpu.X86.HasAVX512VBMI
+ cpu.X86.HasAVX512VBMI &&
+ cpu.X86.HasPOPCNT
-135
View File
@@ -1,135 +0,0 @@
From 06f90ebfa6f3e0a744584513e2786988b26569da Mon Sep 17 00:00:00 2001
From: Damien Neil <dneil@google.com>
Date: Wed, 08 Jul 2026 11:11:48 -0700
Subject: [PATCH] os: properly handle trailing / in Root.MkdirAll
MkdirAll("dir/") should create "dir", not return an error.
Fixes #80308
Change-Id: I75458c30b6cb94be3a3d368f6024caef6a6a6964
---
diff --git a/src/os/root_noopenat.go b/src/os/root_noopenat.go
index 228b580..a84e952 100644
--- a/src/os/root_noopenat.go
+++ b/src/os/root_noopenat.go
@@ -158,6 +158,9 @@
if err := checkPathEscapes(r, name); err == errPathEscapes {
return &PathError{Op: "mkdirat", Path: name, Err: err}
}
+ if name == "" {
+ return &PathError{Op: "mkdirat", Path: name, Err: syscall.ENOENT}
+ }
prefix := r.root.name + string(PathSeparator)
if err := MkdirAll(prefix+name, perm); err != nil {
if pe, ok := err.(*PathError); ok {
diff --git a/src/os/root_openat.go b/src/os/root_openat.go
index d7c9334..92865e4 100644
--- a/src/os/root_openat.go
+++ b/src/os/root_openat.go
@@ -167,6 +167,12 @@
fi, e := r.Stat(fullname)
if e == nil && fi.Mode().IsDir() {
err = nil
+ } else if e == nil {
+ err = syscall.ENOTDIR
+ } else if !IsNotExist(e) {
+ // EPERM, ELOOP, etc.,
+ // probably more useful than EEXIST.
+ err = e
}
}
}
@@ -177,7 +183,12 @@
}
return struct{}{}, &PathError{Op: "mkdirat", Err: err}
}
- _, err := doInRoot(r, fullname, 0, openDirFunc, openLastComponentFunc)
+ flags := uint(doInRootCreatingDirectory)
+ switch runtime.GOOS {
+ case "linux", "windows":
+ flags = doInRootNoHandleTerminalSlash // see rootMkdir
+ }
+ _, err := doInRoot(r, fullname, flags, openDirFunc, openLastComponentFunc)
if err != nil {
if _, ok := err.(*PathError); !ok {
err = &PathError{Op: "mkdirat", Path: fullname, Err: err}
diff --git a/src/os/root_test.go b/src/os/root_test.go
index 5e0acf9..33a2f33 100644
--- a/src/os/root_test.go
+++ b/src/os/root_test.go
@@ -585,6 +585,11 @@
func TestRootMkdirAll(t *testing.T) {
for _, test := range rootTestCases {
+ if test.name == "directory does not exist" {
+ // Test expects error, mkdirall creates the missing directory.
+ // TestRootMultiMkdirAll covers this case better anyway, just skip.
+ continue
+ }
test.run(t, func(t *testing.T, target string, root *os.Root) {
wantError := test.wantError
if test.ltarget != "" {
@@ -593,7 +598,7 @@
wantError = true
}
- err := root.Mkdir(test.open, 0o777)
+ err := root.MkdirAll(test.open, 0o777)
if errEndsTest(t, err, wantError, "root.MkdirAll(%q)", test.open) {
return
}
@@ -3133,6 +3138,52 @@
})
}
+func TestRootMultiMkdirAllShallow(t *testing.T) {
+ runRootMultiTest(t, func(t *testing.T, test *rootMultiTest) (string, error) {
+ return testRootMultiMkdirAll(t, test, test.targetPath)
+ })
+}
+
+func TestRootMultiMkdirAllDeep(t *testing.T) {
+ runRootMultiTest(t, func(t *testing.T, test *rootMultiTest) (string, error) {
+ targetPath := test.targetPath
+ if len(targetPath) > 0 && os.IsPathSeparator(targetPath[len(targetPath)-1]) {
+ targetPath += "a/b/"
+ } else {
+ targetPath += "/a/b"
+ }
+ return testRootMultiMkdirAll(t, test, targetPath)
+ })
+}
+
+func testRootMultiMkdirAll(t *testing.T, test *rootMultiTest, targetPath string) (string, error) {
+ var mkdirAll = os.MkdirAll
+ if test.root != nil {
+ mkdirAll = test.root.MkdirAll
+ }
+
+ test.setOp("MkdirAll(%q, 0o777)", targetPath)
+ gotErr := mkdirAll(targetPath, 0o777)
+
+ switch {
+ case test.root != nil && test.target.lescapes():
+ // "mkdir ../target", or equivalent escaping path.
+ test.wantError(t, gotErr, os.ErrPathEscapes)
+ case test.root != nil && test.target.escapes():
+ // "mkdir ../target", or equivalent escaping path.
+ test.wantError(t, gotErr, errAny)
+ return "", errSkipRootConsistencyCheck
+ case test.root != nil && test.target.kind == testFileSymlink && test.target.target.kind == testFileAbsent && targetPath != test.targetPath:
+ // A minor inconsistency between Root.MkdirAll and os.MkdirAll:
+ // When an intermediate component of the tree being constructed is a
+ // dangling symlink, Root.MkdirAll will follow the symlink and create
+ // its target directory, while os.MkdirAll will fail with an error.
+ return "", errSkipRootConsistencyCheck
+ default:
+ }
+ return "", gotErr
+}
+
func TestRootMultiRename(t *testing.T) {
if runtime.GOOS == "wasip1" {
switch os.Getenv("GOWASIRUNTIME") {
-64
View File
@@ -1,64 +0,0 @@
package go {
description = "the Go programming language toolchain";
website = "https://go.dev";
anitya = 1227;
version# = "1.26.5";
source = remoteTar {
url = "https://go.dev/dl/go"+version+".src.tar.gz";
checksum = "13yt1u_NGOd62LO-WZ8W6CUXodOrEF5CdkiTTiH9E635IHn1luvlXYjsHpBXNFSw";
compress = gzip;
};
patches = [
// https://go.dev/issue/77674
"746640.patch",
// https://go.dev/issue/80308
"799064.patch",
];
env = [
"CC=cc",
"GOCACHE=/tmp/gocache",
"GOROOT_BOOTSTRAP=/system/go",
"TMPDIR=/dev/shm/go",
];
enterSource = true;
exec = generic {
inPlace = true;
build = `
mkdir /work/system/ "${TMPDIR}"
cp -r . /work/system/go
cd /work/system/go/src/
chmod -R +w ..
sed -i \
's,/lib/ld-musl-`+linuxArch+`.so.1,/system/bin/linker,' \
cmd/link/internal/`+arch+`/obj.go
rm \
os/root_unix_test.go \
runtime/crash_cgo_test.go \
cmd/cgo/internal/testsanitizers/tsan_test.go \
cmd/cgo/internal/testsanitizers/cshared_test.go
set +u
. ./make.bash "$@" --no-banner
set -u
`;
check = "bash run.bash --no-rebuild\n";
install = `
../bin/go tool dist banner # print build info
mkdir /work/system/bin
ln -s \
../go/bin/go \
../go/bin/gofmt \
/work/system/bin
`;
};
inputs = [ bash, go-bootstrap ];
}
+6 -6
View File
@@ -3,19 +3,19 @@ package harfbuzz {
website = "https://harfbuzz.github.io"; website = "https://harfbuzz.github.io";
anitya = 1299; anitya = 1299;
version# = "14.3.0"; version# = "14.2.1";
source = remoteGitHub { source = remoteGitHub {
suffix = "harfbuzz/harfbuzz"; suffix = "harfbuzz/harfbuzz";
tag = version; tag = version;
checksum = "C03eSJbyFvrOPXWhlHgwxfqUKyR9xQO_FKzbwQzT7hW9DTUAQIiwvUIf_6cqaKPm"; checksum = "gWl1fS1nXSH9Z9GpJSrh6w7Iv6AOjZbDL_4JZAZTiTYSyLCfxIFuCRLjYmqT2nbt";
}; };
exec = meson { exec = meson {
setup = { setup = {
"cairo": "enabled"; "Dcairo": "enabled";
"png": "enabled"; "Dpng": "enabled";
"zlib": "enabled"; "Dzlib": "enabled";
"freetype": "enabled"; "Dfreetype": "enabled";
}; };
}; };
+2 -2
View File
@@ -3,11 +3,11 @@ package hwdata {
website = "https://github.com/vcrhonek/hwdata"; website = "https://github.com/vcrhonek/hwdata";
anitya = 5387; anitya = 5387;
version# = "0.410"; version# = "0.408";
source = remoteGitHub { source = remoteGitHub {
suffix = "vcrhonek/hwdata"; suffix = "vcrhonek/hwdata";
tag = "v"+version; tag = "v"+version;
checksum = "w1fbTz8NR7C6SYOupqTyNZGJHa_cDRAsGs4guZ3LddqlmawgEP_CWFw9-OYFKJ1q"; checksum = "1Rm52ovBb8sZRlA578EKgKSIea9QGgDVSBDETU872BisZTVosQy-OTKkWpzU_ddB";
}; };
writable = true; writable = true;
-40
View File
@@ -1,40 +0,0 @@
package iana-enterprise-numbers {
description = "IANA Enterprise Numbers";
website = "https://www.iana.org/assignments/enterprise-numbers.txt";
exclude = true;
version# = "20250113140800";
output = remoteFile {
url = "https://web.archive.org/web/"+version+"id_/"+
"https://www.iana.org/assignments/enterprise-numbers.txt";
checksum = "IplVyf7P4PDhhDovWCzL6jPGmF0T75InOCv2IOTMoR7khHUcRfki1pY93kobuXHF";
};
}
package ipmitool {
description = "an open-source tool for controlling IPMI-enabled systems";
website = "https://codeberg.org/IPMITool/ipmitool";
anitya = 1390;
version# = "1_8_19";
source = remoteTar {
url = "https://codeberg.org/IPMITool/ipmitool/archive/"+
"IPMITOOL_"+version+".tar.gz";
checksum = "oxY5S_C2Dl4NhVWtE0BMJtAAxCUgjAwvZm7gzqEzDf0Ina6auYNKztVdM3tclfgT";
compress = gzip;
};
extra = [ iana-enterprise-numbers ];
early = "cp ../extra/iana-enterprise-numbers enterprise-numbers\n";
exec = make {
generate = "./bootstrap";
};
inputs = [
automake,
libtool,
readline,
];
runtime = [ readline ];
}
+2 -2
View File
@@ -28,7 +28,7 @@ package iproute2 {
"prefix": "/system"; "prefix": "/system";
"color": "auto"; "color": "auto";
}; };
make = [ "CC=cc", "YACC=yacc" ]; make = [ "CC=cc" ];
// wants root // wants root
skipCheck = true; skipCheck = true;
}; };
@@ -36,7 +36,7 @@ package iproute2 {
inputs = [ inputs = [
pkg-config, pkg-config,
m4, m4,
byacc, bison,
flex, flex,
libbpf, libbpf,
@@ -1,6 +1,6 @@
# #
# Automatically generated file; DO NOT EDIT. # Automatically generated file; DO NOT EDIT.
# Linux/x86 6.18.43 Kernel Configuration # Linux/x86 6.18.37 Kernel Configuration
# #
CONFIG_CC_VERSION_TEXT="clang version 22.1.8" CONFIG_CC_VERSION_TEXT="clang version 22.1.8"
CONFIG_GCC_VERSION=0 CONFIG_GCC_VERSION=0
@@ -424,12 +424,12 @@ CONFIG_X86_DEBUGCTLMSR=y
CONFIG_IA32_FEAT_CTL=y CONFIG_IA32_FEAT_CTL=y
CONFIG_X86_VMX_FEATURE_NAMES=y CONFIG_X86_VMX_FEATURE_NAMES=y
# CONFIG_PROCESSOR_SELECT is not set # CONFIG_PROCESSOR_SELECT is not set
CONFIG_BROADCAST_TLB_FLUSH=y
CONFIG_CPU_SUP_INTEL=y CONFIG_CPU_SUP_INTEL=y
CONFIG_CPU_SUP_AMD=y CONFIG_CPU_SUP_AMD=y
CONFIG_CPU_SUP_HYGON=y CONFIG_CPU_SUP_HYGON=y
CONFIG_CPU_SUP_CENTAUR=y CONFIG_CPU_SUP_CENTAUR=y
CONFIG_CPU_SUP_ZHAOXIN=y CONFIG_CPU_SUP_ZHAOXIN=y
CONFIG_BROADCAST_TLB_FLUSH=y
CONFIG_HPET_TIMER=y CONFIG_HPET_TIMER=y
CONFIG_HPET_EMULATE_RTC=y CONFIG_HPET_EMULATE_RTC=y
CONFIG_DMI=y CONFIG_DMI=y
@@ -7311,7 +7311,6 @@ CONFIG_AGP_INTEL=m
CONFIG_AGP_SIS=m CONFIG_AGP_SIS=m
CONFIG_AGP_VIA=m CONFIG_AGP_VIA=m
CONFIG_INTEL_GTT=m CONFIG_INTEL_GTT=m
CONFIG_GPU_BUDDY=y
CONFIG_DRM=m CONFIG_DRM=m
# #

Some files were not shown because too many files have changed in this diff Show More