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