forked from rosa/hakurei
internal/rosa: create metadata alongside artifact
This enables deferring evaluation of azalea-based packages and fixes the longstanding quirk of version being disjoint from other metadata. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -70,11 +70,9 @@ type (
|
||||
// P represents multiple [ArtifactH].
|
||||
type P []ArtifactH
|
||||
|
||||
// Artifact is stage-agnostic immutable data with a deterministic resulting
|
||||
// [pkg.Artifact]. It can be created natively or through evaluation.
|
||||
type Artifact struct {
|
||||
f func(t Toolchain) (a pkg.Artifact, version string)
|
||||
|
||||
// Metadata is stage-agnostic immutable data around [Artifact] not directly
|
||||
// representable in the resulting [pkg.Artifact].
|
||||
type Metadata struct {
|
||||
// Unique package name.
|
||||
Name string `json:"name"`
|
||||
// Short user-facing description.
|
||||
@@ -85,6 +83,8 @@ type Artifact struct {
|
||||
// Runtime dependencies.
|
||||
Dependencies P `json:"dependencies"`
|
||||
|
||||
// Package version.
|
||||
Version string `json:"version"`
|
||||
// Project identifier on [Anitya].
|
||||
//
|
||||
// [Anitya]: https://release-monitoring.org/
|
||||
@@ -98,7 +98,7 @@ type Artifact struct {
|
||||
}
|
||||
|
||||
// GetLatest returns the latest version described by v.
|
||||
func (meta *Artifact) GetLatest(v *Versions) string {
|
||||
func (meta *Metadata) GetLatest(v *Versions) string {
|
||||
if meta.latest != nil {
|
||||
return meta.latest(v)
|
||||
}
|
||||
@@ -134,7 +134,7 @@ func (v *Versions) getStable() string {
|
||||
}
|
||||
|
||||
// GetVersions returns versions fetched from Anitya.
|
||||
func (meta *Artifact) GetVersions(ctx context.Context) (*Versions, error) {
|
||||
func (meta *Metadata) GetVersions(ctx context.Context) (*Versions, error) {
|
||||
if meta.ID == 0 {
|
||||
return nil, UnpopulatedIDError{}
|
||||
}
|
||||
@@ -160,10 +160,13 @@ func (meta *Artifact) GetVersions(ctx context.Context) (*Versions, error) {
|
||||
return &v, errors.Join(err, resp.Body.Close())
|
||||
}
|
||||
|
||||
// A cachedArtifact holds [pkg.Artifact] and its corresponding version string.
|
||||
// Artifact is a lazily initialised [pkg.Artifact] with associated [Metadata].
|
||||
type Artifact func(t Toolchain) (meta *Metadata, a pkg.Artifact)
|
||||
|
||||
// A cachedArtifact caches satisfied [Artifact].
|
||||
type cachedArtifact struct {
|
||||
a pkg.Artifact
|
||||
v string
|
||||
meta *Metadata
|
||||
a pkg.Artifact
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -242,20 +245,20 @@ func (s *S) DropCaches(targetArch string, flags int) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the address of the named [Artifact].
|
||||
func (s *S) Get(handle ArtifactH) (meta *Artifact) {
|
||||
// get returns the named [Artifact].
|
||||
func (s *S) get(handle ArtifactH) (f Artifact) {
|
||||
s.wantsArch()
|
||||
v, ok := s.artifacts.Load(handle)
|
||||
if ok {
|
||||
meta = v.(*Artifact)
|
||||
f = v.(Artifact)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MustGet is like Get, but panics if the named [Artifact] is not registered.
|
||||
func (s *S) MustGet(handle ArtifactH) (meta *Artifact) {
|
||||
meta = s.Get(handle)
|
||||
if meta == nil {
|
||||
// mustGet is like get, but panics if the named [Artifact] is not registered.
|
||||
func (s *S) mustGet(handle ArtifactH) (f Artifact) {
|
||||
f = s.get(handle)
|
||||
if f == nil {
|
||||
panic(HandleError(handle))
|
||||
}
|
||||
return
|
||||
@@ -282,8 +285,8 @@ func (e LoadError) Error() string {
|
||||
return "cannot load " + strconv.Quote(e.Handle.String()) + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
// Load returns the resulting [pkg.Artifact] of [ArtifactH].
|
||||
func (t Toolchain) Load(handle ArtifactH) (pkg.Artifact, string) {
|
||||
// Load satisfies an [Artifact] referred to by an [ArtifactH].
|
||||
func (t Toolchain) Load(handle ArtifactH) (*Metadata, pkg.Artifact) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
@@ -305,36 +308,36 @@ func (t Toolchain) Load(handle ArtifactH) (pkg.Artifact, string) {
|
||||
e, ok := t.c[t.stage].Load(handle)
|
||||
if ok {
|
||||
r := e.(cachedArtifact)
|
||||
return r.a, r.v
|
||||
return r.meta, r.a
|
||||
}
|
||||
|
||||
meta := t.Get(handle)
|
||||
if meta == nil {
|
||||
return nil, ""
|
||||
f := t.get(handle)
|
||||
if f == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var r cachedArtifact
|
||||
r.a, r.v = meta.f(t)
|
||||
r.meta, r.a = f(t)
|
||||
t.c[t.stage].Store(handle, r)
|
||||
return r.a, r.v
|
||||
return r.meta, r.a
|
||||
}
|
||||
|
||||
// MustLoad is like Load, but panics if the named [Artifact] is not registered.
|
||||
func (t Toolchain) MustLoad(handle ArtifactH) (pkg.Artifact, string) {
|
||||
a, version := t.Load(handle)
|
||||
if a == nil {
|
||||
func (t Toolchain) MustLoad(handle ArtifactH) (*Metadata, pkg.Artifact) {
|
||||
meta, a := t.Load(handle)
|
||||
if meta == nil {
|
||||
panic(HandleError(handle))
|
||||
}
|
||||
return a, version
|
||||
return meta, a
|
||||
}
|
||||
|
||||
// Register arranges for a new [Artifact] to be cured under s. It returns false
|
||||
// if another [Artifact] is already registered under the same name.
|
||||
func (s *S) Register(meta *Artifact) bool {
|
||||
if meta.Name == "" {
|
||||
func (s *S) Register(name string, f Artifact) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
p := ArtifactH(unique.Make(meta.Name))
|
||||
_, ok := s.artifacts.LoadOrStore(p, meta)
|
||||
p := ArtifactH(unique.Make(name))
|
||||
_, ok := s.artifacts.LoadOrStore(p, f)
|
||||
if !ok {
|
||||
s.artifactCount.Add(1)
|
||||
}
|
||||
@@ -346,16 +349,34 @@ func (s *S) Register(meta *Artifact) bool {
|
||||
type RegisterError ArtifactH
|
||||
|
||||
func (e RegisterError) Error() string {
|
||||
if ArtifactH(e).String() == "" {
|
||||
return "attempting to register invalid name"
|
||||
}
|
||||
return "attempting to register " + strconv.Quote(ArtifactH(e).String()) + " twice"
|
||||
}
|
||||
|
||||
// MustRegister is like Register, but panics if registration fails.
|
||||
func (s *S) MustRegister(meta *Artifact) {
|
||||
if !s.Register(meta) {
|
||||
panic(RegisterError(H(meta.Name)))
|
||||
func (s *S) MustRegister(name string, f Artifact) {
|
||||
if !s.Register(name, f) {
|
||||
panic(RegisterError(H(name)))
|
||||
}
|
||||
}
|
||||
|
||||
// mustRegister registers an [Artifact] with the old function signature.
|
||||
//
|
||||
// Deprecated: Artifacts should be migrated to Register.
|
||||
func (s *S) mustRegister(
|
||||
f func(t Toolchain) (pkg.Artifact, string),
|
||||
meta *Metadata,
|
||||
) {
|
||||
s.MustRegister(meta.Name, func(t Toolchain) (*Metadata, pkg.Artifact) {
|
||||
v := *meta
|
||||
a, version := f(t)
|
||||
v.Version = version
|
||||
return &v, a
|
||||
})
|
||||
}
|
||||
|
||||
// Count returns the number of [Artifact] registered to s.
|
||||
func (s *S) Count() int {
|
||||
return int(s.artifactCount.Load())
|
||||
@@ -567,7 +588,7 @@ func (ctx *evalContext) f(
|
||||
return unique.Make(azalea.Ident(name))
|
||||
}
|
||||
|
||||
meta := Artifact{Name: string(name)}
|
||||
meta := Metadata{Name: string(name)}
|
||||
var (
|
||||
attr PackageAttr
|
||||
patches []string
|
||||
@@ -575,7 +596,6 @@ func (ctx *evalContext) f(
|
||||
early bool
|
||||
|
||||
anitya int64
|
||||
version string
|
||||
sourceA any
|
||||
helper Helper
|
||||
|
||||
@@ -584,11 +604,9 @@ func (ctx *evalContext) f(
|
||||
if err = args.Apply(map[unique.Handle[azalea.Ident]]any{
|
||||
k("description"): &meta.Description,
|
||||
k("website"): &meta.Website,
|
||||
k("version"): &meta.Version,
|
||||
k("anitya"): &anitya,
|
||||
|
||||
k("version"): &version,
|
||||
k("source"): &sourceA,
|
||||
|
||||
k("writable"): &attr.Writable,
|
||||
k("chmod"): &attr.Chmod,
|
||||
k("enterSource"): &attr.EnterSource,
|
||||
@@ -598,6 +616,7 @@ func (ctx *evalContext) f(
|
||||
k("exclusive"): &excl,
|
||||
k("toyboxEarly"): &early,
|
||||
|
||||
k("source"): &sourceA,
|
||||
k("exec"): &helper,
|
||||
k("inputs"): &inputs,
|
||||
k("runtime"): &runtimes,
|
||||
@@ -630,7 +649,7 @@ func (ctx *evalContext) f(
|
||||
}
|
||||
|
||||
meta.ID = int(anitya)
|
||||
meta.f = func(t Toolchain) (pkg.Artifact, string) {
|
||||
v = Artifact(func(t Toolchain) (*Metadata, pkg.Artifact) {
|
||||
var source pkg.Artifact
|
||||
switch p := sourceA.(type) {
|
||||
case pkg.Artifact:
|
||||
@@ -646,17 +665,15 @@ func (ctx *evalContext) f(
|
||||
})
|
||||
}
|
||||
|
||||
return t.NewPackage(
|
||||
return &meta, t.NewPackage(
|
||||
meta.Name,
|
||||
version,
|
||||
meta.Version,
|
||||
source,
|
||||
&attr,
|
||||
helper,
|
||||
inputsH...,
|
||||
), version
|
||||
}
|
||||
|
||||
v = meta
|
||||
)
|
||||
})
|
||||
set = true
|
||||
return
|
||||
}
|
||||
@@ -686,13 +703,13 @@ func (s *S) Evaluate(r io.Reader, b fs.FS) error {
|
||||
|
||||
ctx := evalContext{b}
|
||||
for _, f := range pending {
|
||||
meta, set, err := azalea.Evaluate[Artifact](ctx.f, s.getS(), *f)
|
||||
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(&meta) {
|
||||
if !s.Register(string(f.Ident), lf) {
|
||||
return RegisterError(H(string(f.Ident)))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user