Design 47 *A module's registration*, "No part fixes a name" (b229f5c4 lines 482-485), and *What ADR 0656 leaves to this design*, "The indexer reads it [acts] into the catalog graph" (line 1583): ToolchainModules, ToolchainStreams, BuildGoModule and BuildNodeModule go. The build module of a language is the module whose manifest claims the seat whose acting rule is step build on it (index.BuildModulesOf), its stream the one of its manifest carrying toolchain.reported, its bucket <holder>_toolchains as before. Found again at start, at every change of the seat list and at every catalog revision; the watch, the gate and the hearing follow. A language with no build module found reads as no toolchain reported. Rows: TestAgreedToolchainChangesAGoModulesReasonWithNoMerge/the build module of a language is the holder... (fails without: no BuildModulesOf, and go-builds' report refused); TestToolchainWatch's later build module. Left: six integration tests of cmd/mesh-delivery-indexer hold no build seat with acts nor a holder manifest and fail (TestIndexerWhole, TestIndexerWritesAGoModuleAgain..., TestIndexerGivesACandidateItsFingerprint..., TestIndexerJudgesAnUnreadableVerdictAgainAtStart, TestIndexerCountsOnlyThePlacedBuildNodesToolchains, TestAMeshSDKMergeMovesOnlyTheModulesImportingAChangedPackage): their fixtures are to be given both. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ Builds: design 47 G §364, §428, §535
140 lines
5.4 KiB
Go
140 lines
5.4 KiB
Go
package index
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"maps"
|
|
"slices"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"git.novox.be/novox/mesh-sdk/go/seatdef"
|
|
)
|
|
|
|
// The build module of a language is no name in code (design 47 *A module's registration*, "No part fixes a name": every
|
|
// module name a part needs is read from the registrations and the catalog graph; *What ADR 0656 leaves to this design*: a
|
|
// seat's acting rule `acts`, which "the indexer reads into the catalog graph"). It is the holder of the seat whose acting
|
|
// rule builds that language (`acts` {step: build, on: <language>}), found by the claims of the manifests the indexer
|
|
// holds; its toolchains are its state toolchains (ToolchainsBucket), and its toolchain reports ride the stream of its
|
|
// manifest's `streams` that carries ToolchainReportedEvent.
|
|
|
|
// ToolchainReportedEvent is the event a build module says each time it wrote its toolchains key, on a stream of its own.
|
|
const ToolchainReportedEvent = "toolchain.reported"
|
|
|
|
// BuildModule is the build module of one language, as the seat list and the manifests say it.
|
|
type BuildModule struct {
|
|
Language string // the language the seat's acting rule builds
|
|
Seat string // the seat whose acting rule it is
|
|
Module string // the module whose manifest claims the seat
|
|
Stream string // the stream carrying ToolchainReportedEvent, as the bus names it; empty when its manifest declares none
|
|
}
|
|
|
|
// BuildModulesOf are the build modules by language: for each seat whose acting rule is step build on a language, the
|
|
// module among manifests that claims it. A language two seats build, a seat two modules claim, a seat nobody claims and a
|
|
// holder declaring no stream for its reports are said in the problems; the first two give no build module of that
|
|
// language, the last one a build module with no stream.
|
|
func BuildModulesOf(defs map[string]seatdef.Definition, manifests []catalogue.Manifest) (map[string]BuildModule, []string) {
|
|
var problems []string
|
|
seats := map[string][]string{} // language -> the seats building it
|
|
for _, name := range slices.Sorted(maps.Keys(defs)) {
|
|
for _, r := range defs[name].Acts {
|
|
if r.Step != seatdef.StepBuild || r.Acting() == seatdef.OnEvery {
|
|
continue
|
|
}
|
|
seats[r.Acting()] = append(seats[r.Acting()], name)
|
|
}
|
|
}
|
|
out := map[string]BuildModule{}
|
|
for _, language := range slices.Sorted(maps.Keys(seats)) {
|
|
if len(seats[language]) > 1 {
|
|
problems = append(problems, fmt.Sprintf("the seats %v each build %s: no build module of %s is taken", seats[language], language, language))
|
|
continue
|
|
}
|
|
seat := seats[language][0]
|
|
var holders []catalogue.Manifest
|
|
for _, m := range manifests {
|
|
if slices.ContainsFunc(m.Claims, func(c catalogue.Claim) bool { return c.Name == seat }) {
|
|
holders = append(holders, m)
|
|
}
|
|
}
|
|
switch len(holders) {
|
|
case 0:
|
|
problems = append(problems, fmt.Sprintf("no module the indexer holds claims %s, which builds %s", seat, language))
|
|
continue
|
|
case 1:
|
|
default:
|
|
var names []string
|
|
for _, h := range holders {
|
|
names = append(names, h.Module)
|
|
}
|
|
slices.Sort(names)
|
|
problems = append(problems, fmt.Sprintf("the modules %v each claim %s, which builds %s: no build module of %s is taken", names, seat, language, language))
|
|
continue
|
|
}
|
|
b := BuildModule{Language: language, Seat: seat, Module: holders[0].Module}
|
|
for _, s := range holders[0].Streams {
|
|
if s.Carries(ToolchainReportedEvent) {
|
|
b.Stream = catalogue.StreamBusName(s.Name)
|
|
break
|
|
}
|
|
}
|
|
if b.Stream == "" {
|
|
problems = append(problems, fmt.Sprintf("%s, which builds %s, declares no stream carrying %s: its reports are not heard", b.Module, language, ToolchainReportedEvent))
|
|
}
|
|
out[language] = b
|
|
}
|
|
return out, problems
|
|
}
|
|
|
|
// UseBuildModules is what BuildModulesNow found: the build modules whose toolchains the indexer takes in from now on.
|
|
func (ix *Indexer) UseBuildModules(by map[string]BuildModule) {
|
|
ix.mu.Lock()
|
|
defer ix.mu.Unlock()
|
|
ix.buildModules = maps.Clone(by)
|
|
}
|
|
|
|
// BuildModules are the build modules by language the indexer takes toolchains of (UseBuildModules).
|
|
func (ix *Indexer) BuildModules() map[string]BuildModule {
|
|
ix.mu.Lock()
|
|
defer ix.mu.Unlock()
|
|
return maps.Clone(ix.buildModules)
|
|
}
|
|
|
|
// BuildModulesNow finds the build modules from the seat definitions defs and the manifests of the modules the indexer
|
|
// holds (BuildModulesOf), says each problem in the log, uses them (UseBuildModules) and answers them.
|
|
func (ix *Indexer) BuildModulesNow(ctx context.Context, defs map[string]seatdef.Definition) (map[string]BuildModule, error) {
|
|
held, err := ix.heldModules(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var manifests []catalogue.Manifest
|
|
for _, key := range slices.Sorted(maps.Keys(held)) {
|
|
if held[key].Manifest == "" || ix.Manifests == nil {
|
|
continue
|
|
}
|
|
_, m, err := ix.manifestRaw(ctx, held[key].Manifest)
|
|
if err != nil {
|
|
ix.logf("modules.%s: its manifest %s cannot be read, so it holds no build seat here: %v", key, held[key].Manifest, err)
|
|
continue
|
|
}
|
|
manifests = append(manifests, m)
|
|
}
|
|
by, problems := BuildModulesOf(defs, manifests)
|
|
for _, p := range problems {
|
|
ix.logf("%s", p)
|
|
}
|
|
ix.UseBuildModules(by)
|
|
return by, nil
|
|
}
|
|
|
|
// languageBuiltBy is the language module is the build module of, or "".
|
|
func (ix *Indexer) languageBuiltBy(module string) string {
|
|
ix.mu.Lock()
|
|
defer ix.mu.Unlock()
|
|
for language, b := range ix.buildModules {
|
|
if b.Module == module {
|
|
return language
|
|
}
|
|
}
|
|
return ""
|
|
}
|