aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-04-16 15:43:06 +0900
committerOphestra <cat@gensokyo.uk>2026-04-16 15:59:34 +0900
commit136bc0917be7c02b818bfc8d4d54ec8ffbc8d21b (patch)
tree08fec4ac2d6beb05c4c536497cd88678feb419e1 /cmd
parentd6b082dd0b162197c06b8ac06279024c64ceb0e3 (diff)
cmd/mbf: optionally open cache
Some commands do not require the cache. This change also makes acquisition of locked cache cancelable. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/mbf/cache.go90
-rw-r--r--cmd/mbf/cache_test.go38
-rw-r--r--cmd/mbf/main.go167
3 files changed, 219 insertions, 76 deletions
diff --git a/cmd/mbf/cache.go b/cmd/mbf/cache.go
new file mode 100644
index 00000000..16e77cbc
--- /dev/null
+++ b/cmd/mbf/cache.go
@@ -0,0 +1,90 @@
+package main
+
+import (
+ "context"
+ "os"
+ "path/filepath"
+
+ "hakurei.app/check"
+ "hakurei.app/internal/pkg"
+ "hakurei.app/message"
+)
+
+// cache refers to an instance of [pkg.Cache] that might be open.
+type cache struct {
+ ctx context.Context
+ msg message.Msg
+
+ // Should generally not be used directly.
+ c *pkg.Cache
+
+ cures, jobs int
+ hostAbstract, idle bool
+
+ base string
+}
+
+// open opens the underlying [pkg.Cache].
+func (cache *cache) open() (err error) {
+ if cache.c != nil {
+ return os.ErrInvalid
+ }
+
+ if cache.base == "" {
+ cache.base = "cache"
+ }
+ var base *check.Absolute
+ if cache.base, err = filepath.Abs(cache.base); err != nil {
+ return
+ } else if base, err = check.NewAbs(cache.base); err != nil {
+ return
+ }
+
+ var flags int
+ if cache.idle {
+ flags |= pkg.CSchedIdle
+ }
+ if cache.hostAbstract {
+ flags |= pkg.CHostAbstract
+ }
+
+ done := make(chan struct{})
+ defer close(done)
+ go func() {
+ select {
+ case <-cache.ctx.Done():
+ os.Exit(2)
+
+ case <-done:
+ return
+ }
+ }()
+
+ cache.msg.Verbosef("opening cache at %s", base)
+ cache.c, err = pkg.Open(
+ cache.ctx,
+ cache.msg,
+ flags,
+ cache.cures,
+ cache.jobs,
+ base,
+ )
+ return
+}
+
+// Close closes the underlying [pkg.Cache] if it is open.
+func (cache *cache) Close() {
+ if cache.c != nil {
+ cache.c.Close()
+ }
+}
+
+// Do calls f on the underlying cache and returns its error value.
+func (cache *cache) Do(f func(cache *pkg.Cache) error) error {
+ if cache.c == nil {
+ if err := cache.open(); err != nil {
+ return err
+ }
+ }
+ return f(cache.c)
+}
diff --git a/cmd/mbf/cache_test.go b/cmd/mbf/cache_test.go
new file mode 100644
index 00000000..0f036542
--- /dev/null
+++ b/cmd/mbf/cache_test.go
@@ -0,0 +1,38 @@
+package main
+
+import (
+ "context"
+ "log"
+ "os"
+ "testing"
+
+ "hakurei.app/internal/pkg"
+ "hakurei.app/message"
+)
+
+func TestCache(t *testing.T) {
+ t.Parallel()
+
+ cm := cache{
+ ctx: context.Background(),
+ msg: message.New(log.New(os.Stderr, "check: ", 0)),
+ base: t.TempDir(),
+
+ hostAbstract: true, idle: true,
+ }
+ defer cm.Close()
+ cm.Close()
+
+ if err := cm.open(); err != nil {
+ t.Fatalf("open: error = %v", err)
+ }
+ if err := cm.open(); err != os.ErrInvalid {
+ t.Errorf("(duplicate) open: error = %v", err)
+ }
+
+ if err := cm.Do(func(cache *pkg.Cache) error {
+ return cache.Scrub(0)
+ }); err != nil {
+ t.Errorf("Scrub: error = %v", err)
+ }
+}
diff --git a/cmd/mbf/main.go b/cmd/mbf/main.go
index a9d9a0c8..bfa2aa07 100644
--- a/cmd/mbf/main.go
+++ b/cmd/mbf/main.go
@@ -54,14 +54,13 @@ func main() {
log.Fatal("this program must not run as root")
}
- var cache *pkg.Cache
ctx, stop := signal.NotifyContext(context.Background(),
syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
defer stop()
+
+ var cm cache
defer func() {
- if cache != nil {
- cache.Close()
- }
+ cm.Close()
if r := recover(); r != nil {
fmt.Println(r)
@@ -71,60 +70,34 @@ func main() {
var (
flagQuiet bool
- flagCures int
- flagJobs int
- flagBase string
- flagIdle bool
-
- flagHostAbstract bool
)
- c := command.New(os.Stderr, log.Printf, "mbf", func([]string) (err error) {
+ c := command.New(os.Stderr, log.Printf, "mbf", func([]string) error {
msg.SwapVerbose(!flagQuiet)
-
- flagBase = os.ExpandEnv(flagBase)
- if flagBase == "" {
- flagBase = "cache"
- }
-
- var base *check.Absolute
- if flagBase, err = filepath.Abs(flagBase); err != nil {
- return
- } else if base, err = check.NewAbs(flagBase); err != nil {
- return
- }
-
- var flags int
- if flagIdle {
- flags |= pkg.CSchedIdle
- }
- if flagHostAbstract {
- flags |= pkg.CHostAbstract
- }
- cache, err = pkg.Open(ctx, msg, flags, flagCures, flagJobs, base)
-
- return
+ cm.ctx, cm.msg = ctx, msg
+ cm.base = os.ExpandEnv(cm.base)
+ return nil
}).Flag(
&flagQuiet,
"q", command.BoolFlag(false),
"Do not print cure messages",
).Flag(
- &flagCures,
+ &cm.cures,
"cures", command.IntFlag(0),
"Maximum number of dependencies to cure at any given time",
).Flag(
- &flagJobs,
+ &cm.jobs,
"jobs", command.IntFlag(0),
"Preferred number of jobs to run, when applicable",
).Flag(
- &flagBase,
+ &cm.base,
"d", command.StringFlag("$MBF_CACHE_DIR"),
"Directory to store cured artifacts",
).Flag(
- &flagIdle,
+ &cm.idle,
"sched-idle", command.BoolFlag(false),
"Set SCHED_IDLE scheduling policy",
).Flag(
- &flagHostAbstract,
+ &cm.hostAbstract,
"host-abstract", command.BoolFlag(
os.Getenv("MBF_HOST_ABSTRACT") != "",
),
@@ -156,7 +129,9 @@ func main() {
if flagShifts < 0 || flagShifts > 31 {
flagShifts = 12
}
- return cache.Scrub(runtime.NumCPU() << flagShifts)
+ return cm.Do(func(cache *pkg.Cache) error {
+ return cache.Scrub(runtime.NumCPU() << flagShifts)
+ })
},
).Flag(
&flagShifts,
@@ -223,7 +198,10 @@ func main() {
if flagStatus {
if r == nil {
var f io.ReadSeekCloser
- f, err = cache.OpenStatus(rosa.Std.Load(p))
+ err = cm.Do(func(cache *pkg.Cache) (err error) {
+ f, err = cache.OpenStatus(rosa.Std.Load(p))
+ return
+ })
if err != nil {
if errors.Is(err, os.ErrNotExist) {
fmt.Println(
@@ -239,7 +217,7 @@ func main() {
return
}
}
- } else {
+ } else if err = cm.Do(func(cache *pkg.Cache) (err error) {
status, n := r.ArtifactOf(cache.Ident(rosa.Std.Load(p)))
if status == nil {
fmt.Println(
@@ -252,6 +230,9 @@ func main() {
return
}
}
+ return
+ }); err != nil {
+ return
}
}
@@ -306,7 +287,9 @@ func main() {
if ext.Isatty(int(w.Fd())) {
return errors.New("output appears to be a terminal")
}
- return rosa.WriteReport(msg, w, cache)
+ return cm.Do(func(cache *pkg.Cache) error {
+ return rosa.WriteReport(msg, w, cache)
+ })
},
)
@@ -406,23 +389,32 @@ func main() {
checksum [2]unique.Handle[pkg.Checksum]
)
- if pathname, _, err = cache.Cure(
- (t - 2).Load(rosa.Clang),
- ); err != nil {
- return err
+ if err = cm.Do(func(cache *pkg.Cache) (err error) {
+ pathname, _, err = cache.Cure(
+ (t - 2).Load(rosa.Clang),
+ )
+ return
+ }); err != nil {
+ return
}
log.Println("stage1:", pathname)
- if pathname, checksum[0], err = cache.Cure(
- (t - 1).Load(rosa.Clang),
- ); err != nil {
- return err
+ if err = cm.Do(func(cache *pkg.Cache) (err error) {
+ pathname, checksum[0], err = cache.Cure(
+ (t - 1).Load(rosa.Clang),
+ )
+ return
+ }); err != nil {
+ return
}
log.Println("stage2:", pathname)
- if pathname, checksum[1], err = cache.Cure(
- t.Load(rosa.Clang),
- ); err != nil {
- return err
+ if err = cm.Do(func(cache *pkg.Cache) (err error) {
+ pathname, checksum[1], err = cache.Cure(
+ t.Load(rosa.Clang),
+ )
+ return
+ }); err != nil {
+ return
}
log.Println("stage3:", pathname)
@@ -439,10 +431,13 @@ func main() {
}
if flagStage0 {
- if pathname, _, err = cache.Cure(
- t.Load(rosa.Stage0),
- ); err != nil {
- return err
+ if err = cm.Do(func(cache *pkg.Cache) (err error) {
+ pathname, _, err = cache.Cure(
+ t.Load(rosa.Stage0),
+ )
+ return
+ }); err != nil {
+ return
}
log.Println(pathname)
}
@@ -487,7 +482,11 @@ func main() {
switch {
default:
- pathname, _, err := cache.Cure(rosa.Std.Load(p))
+ var pathname *check.Absolute
+ err := cm.Do(func(cache *pkg.Cache) (err error) {
+ pathname, _, err = cache.Cure(rosa.Std.Load(p))
+ return
+ })
if err != nil {
return err
}
@@ -527,7 +526,9 @@ func main() {
return err
}
- if err = cache.EncodeAll(f, rosa.Std.Load(p)); err != nil {
+ if err = cm.Do(func(cache *pkg.Cache) error {
+ return cache.EncodeAll(f, rosa.Std.Load(p))
+ }); err != nil {
_ = f.Close()
return err
}
@@ -535,13 +536,15 @@ func main() {
return f.Close()
case flagEnter:
- return cache.EnterExec(
- ctx,
- rosa.Std.Load(p),
- true, os.Stdin, os.Stdout, os.Stderr,
- rosa.AbsSystem.Append("bin", "mksh"),
- "sh",
- )
+ return cm.Do(func(cache *pkg.Cache) error {
+ return cache.EnterExec(
+ ctx,
+ rosa.Std.Load(p),
+ true, os.Stdin, os.Stdout, os.Stderr,
+ rosa.AbsSystem.Append("bin", "mksh"),
+ "sh",
+ )
+ })
}
},
).
@@ -595,7 +598,10 @@ func main() {
root := make(pkg.Collect, 0, 6+len(args))
root = rosa.Std.AppendPresets(root, presets...)
- if _, _, err := cache.Cure(&root); err == nil {
+ if err := cm.Do(func(cache *pkg.Cache) error {
+ _, _, err := cache.Cure(&root)
+ return err
+ }); err == nil {
return errors.New("unreachable")
} else if !pkg.IsCollected(err) {
return err
@@ -607,11 +613,22 @@ func main() {
}
cured := make(map[pkg.Artifact]cureRes)
for _, a := range root {
- pathname, checksum, err := cache.Cure(a)
- if err != nil {
+ if err := cm.Do(func(cache *pkg.Cache) error {
+ pathname, checksum, err := cache.Cure(a)
+ if err == nil {
+ cured[a] = cureRes{pathname, checksum}
+ }
+ return err
+ }); err != nil {
+ return err
+ }
+ }
+
+ // explicitly open for direct error-free use from this point
+ if cm.c == nil {
+ if err := cm.open(); err != nil {
return err
}
- cured[a] = cureRes{pathname, checksum}
}
layers := pkg.PromoteLayers(root, func(a pkg.Artifact) (
@@ -621,7 +638,7 @@ func main() {
res := cured[a]
return res.pathname, res.checksum
}, func(i int, d pkg.Artifact) {
- r := pkg.Encode(cache.Ident(d).Value())
+ r := pkg.Encode(cm.c.Ident(d).Value())
if s, ok := d.(fmt.Stringer); ok {
if name := s.String(); name != "" {
r += "-" + name
@@ -711,9 +728,7 @@ func main() {
)
c.MustParse(os.Args[1:], func(err error) {
- if cache != nil {
- cache.Close()
- }
+ cm.Close()
if w, ok := err.(interface{ Unwrap() []error }); !ok {
log.Fatal(err)
} else {