diff --git a/internal/rosa/all.go b/internal/rosa/all.go index d1116e8..49f0ada 100644 --- a/internal/rosa/all.go +++ b/internal/rosa/all.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "net/http" "strconv" "sync" @@ -167,6 +168,36 @@ const ( PresetEnd ) +// P represents multiple [PArtifact] and is stable through JSON. +type P []PArtifact + +// MarshalJSON represents [PArtifact] by their [Metadata.Name]. +func (s P) MarshalJSON() ([]byte, error) { + names := make([]string, len(s)) + for i, p := range s { + names[i] = GetMetadata(p).Name + } + return json.Marshal(names) +} + +// UnmarshalJSON resolves the value created by MarshalJSON back to [P]. +func (s *P) UnmarshalJSON(data []byte) error { + var names []string + if err := json.Unmarshal(data, &names); err != nil { + return err + } + + *s = make(P, len(names)) + for i, name := range names { + if p, ok := ResolveName(name); !ok { + return fmt.Errorf("unknown artifact %q", name) + } else { + (*s)[i] = p + } + } + return nil +} + // Metadata is stage-agnostic information of a [PArtifact] not directly // representable in the resulting [pkg.Artifact]. type Metadata struct { @@ -179,6 +210,9 @@ type Metadata struct { // Project home page. Website string `json:"website,omitempty"` + // Runtime dependencies. + Dependencies P `json:"dependencies"` + // Project identifier on [Anitya]. // // [Anitya]: https://release-monitoring.org/ diff --git a/internal/rosa/rosa.go b/internal/rosa/rosa.go index fbdba17..f22e600 100644 --- a/internal/rosa/rosa.go +++ b/internal/rosa/rosa.go @@ -8,6 +8,7 @@ import ( "slices" "strconv" "strings" + "sync" "hakurei.app/container/fhs" "hakurei.app/internal/pkg" @@ -454,6 +455,41 @@ type PackageAttr struct { Flag int } +// pa holds whether a [PArtifact] is present. +type pa = [PresetEnd]bool + +// paPool holds addresses of pa. +var paPool = sync.Pool{New: func() any { return new(pa) }} + +// appendPreset recursively appends a [PArtifact] and its runtime dependencies. +func (t Toolchain) appendPreset( + a []pkg.Artifact, + pv *pa, p PArtifact, +) []pkg.Artifact { + if pv[p] { + return a + } + + for _, d := range GetMetadata(p).Dependencies { + a = t.appendPreset(a, pv, d) + } + return append(a, t.Load(p)) +} + +// AppendPresets recursively appends multiple [PArtifact] and their runtime +// dependencies. +func (t Toolchain) AppendPresets( + a []pkg.Artifact, + presets ...PArtifact, +) []pkg.Artifact { + pv := paPool.Get().(*pa) + for _, p := range presets { + a = t.appendPreset(a, pv, p) + } + paPool.Put(pv) + return a +} + // NewPackage constructs a [pkg.Artifact] via a build system helper. func (t Toolchain) NewPackage( name, version string, @@ -486,12 +522,14 @@ func (t Toolchain) NewPackage( extraRes := make([]pkg.Artifact, 0, dc) extraRes = append(extraRes, attr.NonStage0...) if !t.isStage0() { + pv := paPool.Get().(*pa) for _, p := range helper.extra(attr.Flag) { - extraRes = append(extraRes, t.Load(p)) + extraRes = t.appendPreset(extraRes, pv, p) } for _, p := range extra { - extraRes = append(extraRes, t.Load(p)) + extraRes = t.appendPreset(extraRes, pv, p) } + paPool.Put(pv) } var scriptEarly string