internal/rosa: evaluate packages late
All checks were successful
Test / Create distribution (push) Successful in 1m5s
Test / Sandbox (push) Successful in 2m52s
Test / ShareFS (push) Successful in 3m43s
Test / Sandbox (race detector) (push) Successful in 5m26s
Test / Hakurei (race detector) (push) Successful in 6m31s
Test / Hakurei (push) Successful in 2m47s
Test / Flake checks (push) Successful in 1m37s
All checks were successful
Test / Create distribution (push) Successful in 1m5s
Test / Sandbox (push) Successful in 2m52s
Test / ShareFS (push) Successful in 3m43s
Test / Sandbox (race detector) (push) Successful in 5m26s
Test / Hakurei (race detector) (push) Successful in 6m31s
Test / Hakurei (push) Successful in 2m47s
Test / Flake checks (push) Successful in 1m37s
This also enables concurrent evaluation. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,14 +671,13 @@ 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
|
||||
|
||||
case deferredGit:
|
||||
source = t.newTagRemote(p.url, p.tag, p.checksum)
|
||||
source = ctx.t.newTagRemote(p.url, p.tag, p.checksum)
|
||||
|
||||
default:
|
||||
panic(azalea.TypeError{
|
||||
@@ -665,15 +686,14 @@ func (ctx *evalContext) f(
|
||||
})
|
||||
}
|
||||
|
||||
return &meta, t.NewPackage(
|
||||
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 !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 {
|
||||
return err
|
||||
panic(err)
|
||||
} else if !set {
|
||||
return errors.New("unexpected unset")
|
||||
panic(errors.New("unexpected unset"))
|
||||
}
|
||||
if !s.Register(string(f.Ident), lf) {
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user