Schedule underlay groups as dependency forests

This commit is contained in:
2026-10-19 19:53:01 +00:00
parent 263dd2f1bd
commit 014b99504b
7 changed files with 437 additions and 34 deletions

View File

@@ -89,6 +89,15 @@ func (graph importInclusionGraph) clone() importInclusionGraph {
return cloned
}
func mergeImportInclusionGraph(target *importInclusionGraph, source importInclusionGraph) {
for _, node := range source.nodes {
target.addNode(node)
}
for _, edge := range source.edges {
target.addEdge(edge.importer, edge.imported, edge.location)
}
}
func (graph importInclusionGraph) scheduleCycle(closingEdge int, location Location) (Diagnostic, bool) {
keys := make([]dependencyKey, len(graph.nodes))
for index, node := range graph.nodes {
@@ -149,21 +158,41 @@ func (graph importInclusionGraph) scheduleCycle(closingEdge int, location Locati
}
func scheduleAcquisitionScope(scope acquisitionScope, graph importInclusionGraph) acquisitionScope {
positions := make(map[importNodeKey]int, len(scope.contributions))
slots := make([]int, 0, len(scope.contributions))
keys := make([]dependencyKey, 0, len(scope.contributions))
for index, contribution := range scope.contributions {
if contribution.scopeOwner != scope.owner {
continue
}
positions[contribution.identity] = len(keys)
slots = append(slots, index)
keys = append(keys, contribution.identity.dependencyKey())
}
if len(keys) < 2 {
return scope
}
scheduled := scope
scheduled.contributions = scheduleContributionSlots(scope.contributions, slots, graph)
return scheduled
}
func scheduleAcquisitionGroup(scopes []acquisitionScope, graph importInclusionGraph) acquisitionScope {
var contributions []acquiredContribution
for _, scope := range scopes {
contributions = append(contributions, scope.contributions...)
}
slots := make([]int, len(contributions))
for index := range slots {
slots[index] = index
}
return acquisitionScope{contributions: scheduleContributionSlots(contributions, slots, graph)}
}
func scheduleContributionSlots(contributions []acquiredContribution, slots []int, graph importInclusionGraph) []acquiredContribution {
if len(slots) < 2 {
return append([]acquiredContribution(nil), contributions...)
}
positions := make(map[importNodeKey]int, len(slots))
keys := make([]dependencyKey, len(slots))
for position, slot := range slots {
contribution := contributions[slot]
positions[contribution.identity] = position
keys[position] = contribution.identity.dependencyKey()
}
edges := make([]dependencyEdge, 0, len(graph.edges))
for _, edge := range graph.edges {
importer, importerOK := positions[edge.importer]
@@ -174,16 +203,86 @@ func scheduleAcquisitionScope(scope acquisitionScope, graph importInclusionGraph
}
order, _ := scheduleDependencies(keys, edges)
if len(order) != len(keys) {
return scope
return append([]acquiredContribution(nil), contributions...)
}
scheduled := scope
scheduled.contributions = append([]acquiredContribution(nil), scope.contributions...)
scheduled := append([]acquiredContribution(nil), contributions...)
for position, scheduledIndex := range order {
scheduled.contributions[slots[position]] = scope.contributions[slots[scheduledIndex]]
scheduled[slots[position]] = contributions[slots[scheduledIndex]]
}
return scheduled
}
type importNamespaceAnchors struct {
kind string
host string
native string
input string
}
func explicitNamespaceAnchor(group, input int) string {
return fmt.Sprintf("%020d/%020d", group, input)
}
func reanchorImportKey(key importNodeKey, anchors importNamespaceAnchors) importNodeKey {
if anchors.kind != "" {
key.namespaceKind = anchors.kind
}
switch {
case key.resourceKind == "synthetic":
key.namespaceAnchor = anchors.input
case key.namespaceKind == "native":
key.namespaceAnchor = anchors.native
default:
key.namespaceAnchor = anchors.host
}
return key
}
func reanchorImportGraph(graph importInclusionGraph, anchors importNamespaceAnchors) (importInclusionGraph, map[importNodeKey]importNodeKey) {
reanchored := newImportInclusionGraph()
keys := make(map[importNodeKey]importNodeKey, len(graph.nodes))
for _, node := range graph.nodes {
old := node.key
node.key = reanchorImportKey(old, anchors)
keys[old] = node.key
reanchored.addNode(node)
}
for _, edge := range graph.edges {
reanchored.addEdge(keys[edge.importer], keys[edge.imported], edge.location)
}
return reanchored, keys
}
func reanchorAcquisitionScope(scope *acquisitionScope, keys map[importNodeKey]importNodeKey, anchors importNamespaceAnchors) {
if scope == nil {
return
}
if key, exists := keys[scope.owner]; exists {
scope.owner = key
} else {
scope.owner = reanchorImportKey(scope.owner, anchors)
}
for index := range scope.contributions {
contribution := &scope.contributions[index]
if key, exists := keys[contribution.identity]; exists {
contribution.identity = key
} else {
contribution.identity = reanchorImportKey(contribution.identity, anchors)
}
if key, exists := keys[contribution.scopeOwner]; exists {
contribution.scopeOwner = key
} else {
contribution.scopeOwner = reanchorImportKey(contribution.scopeOwner, anchors)
}
reanchorAcquisitionScope(contribution.pendingItem, keys, anchors)
if contribution.nested != nil {
for nestedIndex := range contribution.nested.acquisition {
reanchorAcquisitionScope(&contribution.nested.acquisition[nestedIndex], keys, anchors)
}
}
}
}
func importKeyFor(access *filesystemAccess, canonical string, synthetic bool) importNodeKey {
kind := "host"
resourceKind := "file"