forked from rosa/hakurei
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>
This commit is contained in:
90
cmd/mbf/cache.go
Normal file
90
cmd/mbf/cache.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
38
cmd/mbf/cache_test.go
Normal file
38
cmd/mbf/cache_test.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
167
cmd/mbf/main.go
167
cmd/mbf/main.go
@@ -54,14 +54,13 @@ func main() {
|
|||||||
log.Fatal("this program must not run as root")
|
log.Fatal("this program must not run as root")
|
||||||
}
|
}
|
||||||
|
|
||||||
var cache *pkg.Cache
|
|
||||||
ctx, stop := signal.NotifyContext(context.Background(),
|
ctx, stop := signal.NotifyContext(context.Background(),
|
||||||
syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
|
syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
|
||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
|
var cm cache
|
||||||
defer func() {
|
defer func() {
|
||||||
if cache != nil {
|
cm.Close()
|
||||||
cache.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
fmt.Println(r)
|
fmt.Println(r)
|
||||||
@@ -71,60 +70,34 @@ func main() {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
flagQuiet bool
|
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)
|
msg.SwapVerbose(!flagQuiet)
|
||||||
|
cm.ctx, cm.msg = ctx, msg
|
||||||
flagBase = os.ExpandEnv(flagBase)
|
cm.base = os.ExpandEnv(cm.base)
|
||||||
if flagBase == "" {
|
return nil
|
||||||
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
|
|
||||||
}).Flag(
|
}).Flag(
|
||||||
&flagQuiet,
|
&flagQuiet,
|
||||||
"q", command.BoolFlag(false),
|
"q", command.BoolFlag(false),
|
||||||
"Do not print cure messages",
|
"Do not print cure messages",
|
||||||
).Flag(
|
).Flag(
|
||||||
&flagCures,
|
&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(
|
||||||
&flagJobs,
|
&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(
|
).Flag(
|
||||||
&flagBase,
|
&cm.base,
|
||||||
"d", command.StringFlag("$MBF_CACHE_DIR"),
|
"d", command.StringFlag("$MBF_CACHE_DIR"),
|
||||||
"Directory to store cured artifacts",
|
"Directory to store cured artifacts",
|
||||||
).Flag(
|
).Flag(
|
||||||
&flagIdle,
|
&cm.idle,
|
||||||
"sched-idle", command.BoolFlag(false),
|
"sched-idle", command.BoolFlag(false),
|
||||||
"Set SCHED_IDLE scheduling policy",
|
"Set SCHED_IDLE scheduling policy",
|
||||||
).Flag(
|
).Flag(
|
||||||
&flagHostAbstract,
|
&cm.hostAbstract,
|
||||||
"host-abstract", command.BoolFlag(
|
"host-abstract", command.BoolFlag(
|
||||||
os.Getenv("MBF_HOST_ABSTRACT") != "",
|
os.Getenv("MBF_HOST_ABSTRACT") != "",
|
||||||
),
|
),
|
||||||
@@ -156,7 +129,9 @@ func main() {
|
|||||||
if flagShifts < 0 || flagShifts > 31 {
|
if flagShifts < 0 || flagShifts > 31 {
|
||||||
flagShifts = 12
|
flagShifts = 12
|
||||||
}
|
}
|
||||||
return cache.Scrub(runtime.NumCPU() << flagShifts)
|
return cm.Do(func(cache *pkg.Cache) error {
|
||||||
|
return cache.Scrub(runtime.NumCPU() << flagShifts)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
).Flag(
|
).Flag(
|
||||||
&flagShifts,
|
&flagShifts,
|
||||||
@@ -223,7 +198,10 @@ func main() {
|
|||||||
if flagStatus {
|
if flagStatus {
|
||||||
if r == nil {
|
if r == nil {
|
||||||
var f io.ReadSeekCloser
|
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 err != nil {
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
fmt.Println(
|
fmt.Println(
|
||||||
@@ -239,7 +217,7 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if err = cm.Do(func(cache *pkg.Cache) (err error) {
|
||||||
status, n := r.ArtifactOf(cache.Ident(rosa.Std.Load(p)))
|
status, n := r.ArtifactOf(cache.Ident(rosa.Std.Load(p)))
|
||||||
if status == nil {
|
if status == nil {
|
||||||
fmt.Println(
|
fmt.Println(
|
||||||
@@ -252,6 +230,9 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +287,9 @@ func main() {
|
|||||||
if ext.Isatty(int(w.Fd())) {
|
if ext.Isatty(int(w.Fd())) {
|
||||||
return errors.New("output appears to be a terminal")
|
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]
|
checksum [2]unique.Handle[pkg.Checksum]
|
||||||
)
|
)
|
||||||
|
|
||||||
if pathname, _, err = cache.Cure(
|
if err = cm.Do(func(cache *pkg.Cache) (err error) {
|
||||||
(t - 2).Load(rosa.Clang),
|
pathname, _, err = cache.Cure(
|
||||||
); err != nil {
|
(t - 2).Load(rosa.Clang),
|
||||||
return err
|
)
|
||||||
|
return
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
log.Println("stage1:", pathname)
|
log.Println("stage1:", pathname)
|
||||||
|
|
||||||
if pathname, checksum[0], err = cache.Cure(
|
if err = cm.Do(func(cache *pkg.Cache) (err error) {
|
||||||
(t - 1).Load(rosa.Clang),
|
pathname, checksum[0], err = cache.Cure(
|
||||||
); err != nil {
|
(t - 1).Load(rosa.Clang),
|
||||||
return err
|
)
|
||||||
|
return
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
log.Println("stage2:", pathname)
|
log.Println("stage2:", pathname)
|
||||||
if pathname, checksum[1], err = cache.Cure(
|
if err = cm.Do(func(cache *pkg.Cache) (err error) {
|
||||||
t.Load(rosa.Clang),
|
pathname, checksum[1], err = cache.Cure(
|
||||||
); err != nil {
|
t.Load(rosa.Clang),
|
||||||
return err
|
)
|
||||||
|
return
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
log.Println("stage3:", pathname)
|
log.Println("stage3:", pathname)
|
||||||
|
|
||||||
@@ -439,10 +431,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if flagStage0 {
|
if flagStage0 {
|
||||||
if pathname, _, err = cache.Cure(
|
if err = cm.Do(func(cache *pkg.Cache) (err error) {
|
||||||
t.Load(rosa.Stage0),
|
pathname, _, err = cache.Cure(
|
||||||
); err != nil {
|
t.Load(rosa.Stage0),
|
||||||
return err
|
)
|
||||||
|
return
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
log.Println(pathname)
|
log.Println(pathname)
|
||||||
}
|
}
|
||||||
@@ -487,7 +482,11 @@ func main() {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
default:
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -527,7 +526,9 @@ func main() {
|
|||||||
return err
|
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()
|
_ = f.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -535,13 +536,15 @@ func main() {
|
|||||||
return f.Close()
|
return f.Close()
|
||||||
|
|
||||||
case flagEnter:
|
case flagEnter:
|
||||||
return cache.EnterExec(
|
return cm.Do(func(cache *pkg.Cache) error {
|
||||||
ctx,
|
return cache.EnterExec(
|
||||||
rosa.Std.Load(p),
|
ctx,
|
||||||
true, os.Stdin, os.Stdout, os.Stderr,
|
rosa.Std.Load(p),
|
||||||
rosa.AbsSystem.Append("bin", "mksh"),
|
true, os.Stdin, os.Stdout, os.Stderr,
|
||||||
"sh",
|
rosa.AbsSystem.Append("bin", "mksh"),
|
||||||
)
|
"sh",
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
).
|
).
|
||||||
@@ -595,7 +598,10 @@ func main() {
|
|||||||
root := make(pkg.Collect, 0, 6+len(args))
|
root := make(pkg.Collect, 0, 6+len(args))
|
||||||
root = rosa.Std.AppendPresets(root, presets...)
|
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")
|
return errors.New("unreachable")
|
||||||
} else if !pkg.IsCollected(err) {
|
} else if !pkg.IsCollected(err) {
|
||||||
return err
|
return err
|
||||||
@@ -607,11 +613,22 @@ func main() {
|
|||||||
}
|
}
|
||||||
cured := make(map[pkg.Artifact]cureRes)
|
cured := make(map[pkg.Artifact]cureRes)
|
||||||
for _, a := range root {
|
for _, a := range root {
|
||||||
pathname, checksum, err := cache.Cure(a)
|
if err := cm.Do(func(cache *pkg.Cache) error {
|
||||||
if err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
cured[a] = cureRes{pathname, checksum}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layers := pkg.PromoteLayers(root, func(a pkg.Artifact) (
|
layers := pkg.PromoteLayers(root, func(a pkg.Artifact) (
|
||||||
@@ -621,7 +638,7 @@ func main() {
|
|||||||
res := cured[a]
|
res := cured[a]
|
||||||
return res.pathname, res.checksum
|
return res.pathname, res.checksum
|
||||||
}, func(i int, d pkg.Artifact) {
|
}, 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 s, ok := d.(fmt.Stringer); ok {
|
||||||
if name := s.String(); name != "" {
|
if name := s.String(); name != "" {
|
||||||
r += "-" + name
|
r += "-" + name
|
||||||
@@ -711,9 +728,7 @@ func main() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
c.MustParse(os.Args[1:], func(err error) {
|
c.MustParse(os.Args[1:], func(err error) {
|
||||||
if cache != nil {
|
cm.Close()
|
||||||
cache.Close()
|
|
||||||
}
|
|
||||||
if w, ok := err.(interface{ Unwrap() []error }); !ok {
|
if w, ok := err.(interface{ Unwrap() []error }); !ok {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user