aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-05-19 01:26:21 +0900
committerOphestra <cat@gensokyo.uk>2026-05-19 01:26:21 +0900
commit4d60fa5632246d5e1c355573dc0fd0e85ca3dcd3 (patch)
tree7cfd915a7fb6e568c6a0664843cba7f76eaa585f
parent8807cbc7305e424725b2be8eed7ecd9234f7772e (diff)
internal/rosa: evaluate packages late
This also enables concurrent evaluation. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--cmd/mbf/main.go12
-rw-r--r--internal/rosa/rosa.go12
-rw-r--r--internal/rosa/state.go135
3 files changed, 92 insertions, 67 deletions
diff --git a/cmd/mbf/main.go b/cmd/mbf/main.go
index 1910a100..86a3aa1e 100644
--- a/cmd/mbf/main.go
+++ b/cmd/mbf/main.go
@@ -84,15 +84,15 @@ func main() {
flagArch string
flagCheck bool
flagLTO bool
- flagET bool
+ flagPT bool
flagCrossOverride int
addr net.UnixAddr
)
c := command.New(os.Stderr, log.Printf, "mbf", func([]string) error {
- if flagET {
- log.Println("evaluated in", rosa.EvalTime())
+ if flagPT {
+ log.Println("parsed in", rosa.ParseTime())
}
msg.SwapVerbose(!flagQuiet)
@@ -189,9 +189,9 @@ func main() {
"socket", command.StringFlag("$MBF_DAEMON_SOCKET"),
"Pathname of socket to bind to",
).Flag(
- &flagET,
- "eval-time", command.BoolFlag(false),
- "Print duration of the initial azalea evaluation",
+ &flagPT,
+ "parse-time", command.BoolFlag(false),
+ "Print duration of the initial azalea parse",
)
c.NewCommand(
diff --git a/internal/rosa/rosa.go b/internal/rosa/rosa.go
index dde85e00..209b8942 100644
--- a/internal/rosa/rosa.go
+++ b/internal/rosa/rosa.go
@@ -614,11 +614,11 @@ var native S
// Native returns the global [S].
func Native() *S { return &native }
-// evalTime is the duration of the initial built-in evaluation.
-var evalTime time.Duration
+// parseTime is the duration of early parsing of built-in azalea expressions.
+var parseTime time.Duration
-// EvalTime returns the duration of the initial built-in evaluation.
-func EvalTime() time.Duration { return evalTime }
+// ParseTime returns the time taken by early parsing of built-in azalea expressions.
+func ParseTime() time.Duration { return parseTime }
// nativeB is the backing directory of built-in azalea-based [Artifact]
// implementations.
@@ -632,9 +632,9 @@ func init() {
panic(err)
}
t := time.Now()
- if err = native.EvaluateFS(sub); err != nil {
+ if err = native.RegisterFS(sub); err != nil {
println(err.Error())
os.Exit(1)
}
- evalTime = time.Since(t)
+ parseTime = time.Since(t)
}
diff --git a/internal/rosa/state.go b/internal/rosa/state.go
index c2f0650b..7b858ed9 100644
--- a/internal/rosa/state.go
+++ b/internal/rosa/state.go
@@ -189,9 +189,11 @@ type S struct {
archOnce sync.Once
// Built-in functions.
- s []azalea.Frame
- // For initialising s.
- sOnce sync.Once
+ frame azalea.Frame
+ // For initialising frame.
+ frameOnce sync.Once
+ // Must only be accessed after a call to getFrame.
+ spool sync.Pool
// Options for [pkg.Artifact] created against [S].
opts int
@@ -402,10 +404,15 @@ type deferredGit struct {
checksum string
}
-// getS must be called before accessing s. This value is not currently safe for
-// concurrent use, but the underlying frame is immutable.
-func (s *S) getS() []azalea.Frame {
- s.sOnce.Do(func() {
+// getFrame must be called before accessing s.
+func (s *S) getFrame() azalea.Frame {
+ s.frameOnce.Do(func() {
+ s.spool.New = func() any {
+ v := make([]azalea.Frame, 1, 1<<4)
+ v[0] = s.getFrame()
+ return v
+ }
+
s.wantsArch()
k := func(name string) unique.Handle[azalea.Ident] {
return unique.Make(azalea.Ident(name))
@@ -416,14 +423,13 @@ func (s *S) getS() []azalea.Frame {
identArch = k(s.arch)
)
- s.s = make([]azalea.Frame, 1, 1<<4)
- s.s[0].Val = map[unique.Handle[azalea.Ident]]any{
+ s.frame.Val = map[unique.Handle[azalea.Ident]]any{
k("jobsE"): jobsE,
k("jobsFlagE"): jobsFlagE,
k("jobsLE"): jobsLE,
k("jobsLFlagE"): jobsLFlagE,
}
- s.s[0].Func = map[unique.Handle[azalea.Ident]]azalea.F{
+ s.frame.Func = map[unique.Handle[azalea.Ident]]azalea.F{
// intenral/pkg built-ins
@@ -551,9 +557,18 @@ func (s *S) getS() []azalea.Frame {
}},
}
})
- return s.s
+ return s.frame
}
+// getStack returns a new stack for azalea evaluation.
+func (s *S) getStack() []azalea.Frame {
+ s.getFrame()
+ return s.spool.Get().([]azalea.Frame)
+}
+
+// putStack returns a stack to spool.
+func (s *S) putStack(v []azalea.Frame) { s.spool.Put(v) }
+
// toHandles makes handles out of an [azalea.Array] of identifiers.
func toHandles(idents azalea.Array) (P, error) {
handles := make(P, len(idents))
@@ -576,11 +591,18 @@ func toHandles(idents azalea.Array) (P, error) {
return handles, nil
}
-// evalContext holds per-reader context.
-type evalContext struct{ b fs.FS }
+// evalContext holds per-artifact context.
+type evalContext struct {
+ // Backing filesystem.
+ b fs.FS
+ // Pending azalea function.
+ expr *azalea.Func
+ // Current toolchain.
+ t Toolchain
+}
-// f implements [azalea.PF].
-func (ctx *evalContext) f(
+// pf implements [azalea.PF].
+func (ctx *evalContext) pf(
name azalea.Ident,
args azalea.FArgs,
) (v any, set bool, err error) {
@@ -649,31 +671,29 @@ func (ctx *evalContext) f(
}
meta.ID = int(anitya)
- v = Artifact(func(t Toolchain) (*Metadata, pkg.Artifact) {
- var source pkg.Artifact
- switch p := sourceA.(type) {
- case pkg.Artifact:
- source = p
+ var source pkg.Artifact
+ switch p := sourceA.(type) {
+ case pkg.Artifact:
+ source = p
- case deferredGit:
- source = t.newTagRemote(p.url, p.tag, p.checksum)
+ case deferredGit:
+ source = ctx.t.newTagRemote(p.url, p.tag, p.checksum)
- default:
- panic(azalea.TypeError{
- Concrete: reflect.TypeOf(sourceA),
- Asserted: reflect.TypeFor[pkg.Artifact](),
- })
- }
+ default:
+ panic(azalea.TypeError{
+ Concrete: reflect.TypeOf(sourceA),
+ Asserted: reflect.TypeFor[pkg.Artifact](),
+ })
+ }
- return &meta, t.NewPackage(
- meta.Name,
- meta.Version,
- source,
- &attr,
- helper,
- inputsH...,
- )
- })
+ v = cachedArtifact{&meta, ctx.t.NewPackage(
+ meta.Name,
+ meta.Version,
+ source,
+ &attr,
+ helper,
+ inputsH...,
+ )}
set = true
return
}
@@ -684,9 +704,9 @@ var (
ErrToplevel = errors.New("top level must only contain package declarations")
)
-// Evaluate defines all package declarations from r. The backing filesystem is
-// directly exposed to azalea pathnames.
-func (s *S) Evaluate(r io.Reader, b fs.FS) error {
+// RegisterAzalea registers all package declarations from r. The backing
+// filesystem is directly exposed to azalea pathnames.
+func (s *S) RegisterAzalea(r io.Reader, b fs.FS) error {
var pending []*azalea.Func
if expressions, err := azalea.Parse(r); err != nil {
return err
@@ -701,26 +721,31 @@ func (s *S) Evaluate(r io.Reader, b fs.FS) error {
}
}
- ctx := evalContext{b}
for _, f := range pending {
- lf, set, err := azalea.Evaluate[Artifact](ctx.f, s.getS(), *f)
- if err != nil {
- return err
- } else if !set {
- return errors.New("unexpected unset")
- }
- if !s.Register(string(f.Ident), lf) {
+ if !s.Register(string(f.Ident), func(t Toolchain) (*Metadata, pkg.Artifact) {
+ v, set, err := azalea.Evaluate[cachedArtifact](
+ (&evalContext{b, f, t}).pf,
+ s.getStack(),
+ *f,
+ )
+ if err != nil {
+ panic(err)
+ } else if !set {
+ panic(errors.New("unexpected unset"))
+ }
+ return v.meta, v.a
+ }) {
return RegisterError(H(string(f.Ident)))
}
}
return nil
}
-// EvaluateFS evaluates azalea files discovered in fsys. A file is evaluated if
-// it is at the top level and its name has the suffix ".az", or it is in a
-// top-level directory with the exact file name "package.az". The backing
-// filesystem is directly exposed to azalea pathnames.
-func (s *S) EvaluateFS(fsys fs.FS) error {
+// RegisterFS registers from azalea files discovered in fsys. A file is
+// evaluated if it is at the top level and its name has the suffix ".az", or it
+// is in a top-level directory with the exact file name "package.az". The
+// backing filesystem is directly exposed to azalea pathnames.
+func (s *S) RegisterFS(fsys fs.FS) error {
dents, err := fs.ReadDir(fsys, ".")
if err != nil {
return err
@@ -743,7 +768,7 @@ func (s *S) EvaluateFS(fsys fs.FS) error {
return err
}
- err = s.Evaluate(r, sub)
+ err = s.RegisterAzalea(r, sub)
if _err := r.Close(); err == nil {
err = _err
}
@@ -763,7 +788,7 @@ func (s *S) EvaluateFS(fsys fs.FS) error {
return err
}
- err = s.Evaluate(r, fsys)
+ err = s.RegisterAzalea(r, fsys)
if _err := r.Close(); err == nil {
err = _err
}