topo sorted map
This commit is contained in:
3 files changed
+233
-46
No files matched your search
+54
-3
@@ -52,10 +52,61 @@ func installLocalPKGBUILD(
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(graph)
|
||||
|
||||
topoSorted := graph.TopoSortedLayers()
|
||||
topoSorted := graph.TopoSortedLayerMap()
|
||||
fmt.Println(topoSorted, len(topoSorted))
|
||||
|
||||
installer := &Installer{dbExecutor: dbExecutor}
|
||||
|
||||
installer.Install(ctx, topoSorted)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Installer struct {
|
||||
dbExecutor db.Executor
|
||||
}
|
||||
|
||||
func (installer *Installer) Install(ctx context.Context, targets []map[string]*dep.InstallInfo) error {
|
||||
// Reorganize targets into layers of dependencies
|
||||
for i := len(targets) - 1; i >= 0; i-- {
|
||||
err := installer.handleLayer(ctx, targets[i])
|
||||
if err != nil {
|
||||
// rollback
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type MapBySourceAndType map[dep.Source]map[dep.Reason][]string
|
||||
|
||||
func (m *MapBySourceAndType) String() string {
|
||||
var s string
|
||||
for source, reasons := range *m {
|
||||
s += fmt.Sprintf("%s: [", source)
|
||||
for reason, names := range reasons {
|
||||
s += fmt.Sprintf(" %d: [%v] ", reason, names)
|
||||
}
|
||||
|
||||
s += "], "
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (installer *Installer) handleLayer(ctx context.Context, layer map[string]*dep.InstallInfo) error {
|
||||
// Install layer
|
||||
depByTypeAndReason := make(MapBySourceAndType)
|
||||
for name, info := range layer {
|
||||
if _, ok := depByTypeAndReason[info.Source]; !ok {
|
||||
depByTypeAndReason[info.Source] = make(map[dep.Reason][]string)
|
||||
}
|
||||
|
||||
depByTypeAndReason[info.Source][info.Reason] = append(depByTypeAndReason[info.Source][info.Reason], name)
|
||||
}
|
||||
|
||||
fmt.Printf("%v\n", depByTypeAndReason)
|
||||
|
||||
return nil
|
||||
}
|
||||
+115
-24
@@ -16,33 +16,66 @@ import (
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
type InstallInfo struct {
|
||||
Source Source
|
||||
Reason Reason
|
||||
}
|
||||
|
||||
func (i *InstallInfo) String() string {
|
||||
return fmt.Sprintf("InstallInfo{Source: %v, Reason: %v}", i.Source, i.Reason)
|
||||
}
|
||||
|
||||
type (
|
||||
packageType int
|
||||
sourceType int
|
||||
Reason int
|
||||
Source int
|
||||
)
|
||||
|
||||
const (
|
||||
Explicit packageType = iota
|
||||
Dep
|
||||
MakeDep
|
||||
CheckDep
|
||||
)
|
||||
func (r Reason) String() string {
|
||||
return reasonNames[r]
|
||||
}
|
||||
|
||||
func (s Source) String() string {
|
||||
return sourceNames[s]
|
||||
}
|
||||
|
||||
const (
|
||||
AUR sourceType = iota
|
||||
Explicit Reason = iota // 0
|
||||
Dep // 1
|
||||
MakeDep // 2
|
||||
CheckDep // 3
|
||||
)
|
||||
|
||||
var reasonNames = map[Reason]string{
|
||||
Explicit: gotext.Get("explicit"),
|
||||
Dep: gotext.Get("dep"),
|
||||
MakeDep: gotext.Get("makedep"),
|
||||
CheckDep: gotext.Get("checkdep"),
|
||||
}
|
||||
|
||||
const (
|
||||
AUR Source = iota
|
||||
Sync
|
||||
Local
|
||||
SrcInfo
|
||||
Missing
|
||||
)
|
||||
|
||||
var bgColorMap = map[sourceType]string{
|
||||
var sourceNames = map[Source]string{
|
||||
AUR: gotext.Get("aur"),
|
||||
Sync: gotext.Get("sync"),
|
||||
Local: gotext.Get("local"),
|
||||
SrcInfo: gotext.Get("srcinfo"),
|
||||
Missing: gotext.Get("missing"),
|
||||
}
|
||||
|
||||
var bgColorMap = map[Source]string{
|
||||
AUR: "lightblue",
|
||||
Sync: "lemonchiffon",
|
||||
Local: "darkolivegreen1",
|
||||
Missing: "tomato",
|
||||
}
|
||||
|
||||
var colorMap = map[packageType]string{
|
||||
var colorMap = map[Reason]string{
|
||||
Explicit: "black",
|
||||
Dep: "deeppink",
|
||||
MakeDep: "navyblue",
|
||||
@@ -67,8 +100,8 @@ func NewGrapher(dbExecutor db.Executor, aurCache *metadata.AURCache, fullGraph,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Grapher) GraphFromSrcInfo(pkgbuild *gosrc.Srcinfo) (*topo.Graph[string, int], error) {
|
||||
graph := topo.New[string, int]()
|
||||
func (g *Grapher) GraphFromSrcInfo(pkgbuild *gosrc.Srcinfo) (*topo.Graph[string, *InstallInfo], error) {
|
||||
graph := topo.New[string, *InstallInfo]()
|
||||
|
||||
aurPkgs, err := makeAURPKGFromSrcinfo(g.dbExecutor, pkgbuild)
|
||||
if err != nil {
|
||||
@@ -77,13 +110,27 @@ func (g *Grapher) GraphFromSrcInfo(pkgbuild *gosrc.Srcinfo) (*topo.Graph[string,
|
||||
|
||||
for _, pkg := range aurPkgs {
|
||||
pkg := pkg
|
||||
|
||||
if err := graph.Alias(pkg.PackageBase, pkg.Name); err != nil {
|
||||
text.Warnln("aur target alias warn:", pkg.PackageBase, pkg.Name, err)
|
||||
}
|
||||
|
||||
g.ValidateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[Explicit],
|
||||
Background: bgColorMap[AUR],
|
||||
Value: &InstallInfo{
|
||||
Source: SrcInfo,
|
||||
Reason: Explicit,
|
||||
},
|
||||
})
|
||||
|
||||
g.addDepNodes(&pkg, graph)
|
||||
}
|
||||
|
||||
return graph, nil
|
||||
}
|
||||
|
||||
func (g *Grapher) addDepNodes(pkg *aur.Pkg, graph *topo.Graph[string, int]) {
|
||||
func (g *Grapher) addDepNodes(pkg *aur.Pkg, graph *topo.Graph[string, *InstallInfo]) {
|
||||
if len(pkg.MakeDepends) > 0 {
|
||||
g.addNodes(graph, pkg.Name, pkg.MakeDepends, MakeDep)
|
||||
}
|
||||
@@ -97,8 +144,8 @@ func (g *Grapher) addDepNodes(pkg *aur.Pkg, graph *topo.Graph[string, int]) {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Grapher) GraphFromAURCache(targets []string) (*topo.Graph[string, int], error) {
|
||||
graph := topo.New[string, int]()
|
||||
func (g *Grapher) GraphFromAURCache(targets []string) (*topo.Graph[string, *InstallInfo], error) {
|
||||
graph := topo.New[string, *InstallInfo]()
|
||||
|
||||
for _, target := range targets {
|
||||
aurPkgs, _ := g.aurCache.FindPackage(target)
|
||||
@@ -108,25 +155,50 @@ func (g *Grapher) GraphFromAURCache(targets []string) (*topo.Graph[string, int],
|
||||
text.Warnln("aur target alias warn:", pkg.PackageBase, pkg.Name, err)
|
||||
}
|
||||
|
||||
graph.SetNodeInfo(pkg.Name, &topo.NodeInfo[int]{Color: colorMap[Explicit], Background: bgColorMap[AUR]})
|
||||
g.ValidateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[Explicit],
|
||||
Background: bgColorMap[AUR],
|
||||
Value: &InstallInfo{
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
},
|
||||
})
|
||||
|
||||
g.addDepNodes(pkg, graph)
|
||||
}
|
||||
|
||||
return graph, nil
|
||||
}
|
||||
|
||||
func (g *Grapher) ValidateAndSetNodeInfo(graph *topo.Graph[string, *InstallInfo],
|
||||
node string, nodeInfo *topo.NodeInfo[*InstallInfo],
|
||||
) {
|
||||
info := graph.GetNodeInfo(node)
|
||||
if info != nil {
|
||||
if info.Value.Reason < nodeInfo.Value.Reason {
|
||||
return // refuse to downgrade reason from explicit to dep
|
||||
}
|
||||
}
|
||||
|
||||
graph.SetNodeInfo(node, nodeInfo)
|
||||
}
|
||||
|
||||
func (g *Grapher) addNodes(
|
||||
graph *topo.Graph[string, int],
|
||||
graph *topo.Graph[string, *InstallInfo],
|
||||
parentPkgName string,
|
||||
deps []string,
|
||||
depType packageType,
|
||||
depType Reason,
|
||||
) {
|
||||
for _, depString := range deps {
|
||||
depName, _, _ := splitDep(depString)
|
||||
|
||||
if g.dbExecutor.LocalSatisfierExists(depString) {
|
||||
if g.fullGraph {
|
||||
graph.SetNodeInfo(depName, &topo.NodeInfo[int]{Color: colorMap[depType], Background: bgColorMap[Local]})
|
||||
g.ValidateAndSetNodeInfo(
|
||||
graph,
|
||||
depName,
|
||||
&topo.NodeInfo[*InstallInfo]{Color: colorMap[depType], Background: bgColorMap[Local]})
|
||||
|
||||
if err := graph.DependOn(depName, parentPkgName); err != nil {
|
||||
text.Warnln(depName, parentPkgName, err)
|
||||
}
|
||||
@@ -149,7 +221,17 @@ func (g *Grapher) addNodes(
|
||||
text.Warnln("repo dep warn:", depName, parentPkgName, err)
|
||||
}
|
||||
|
||||
graph.SetNodeInfo(alpmPkg.Name(), &topo.NodeInfo[int]{Color: colorMap[depType], Background: bgColorMap[Sync]})
|
||||
g.ValidateAndSetNodeInfo(
|
||||
graph,
|
||||
alpmPkg.Name(),
|
||||
&topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[depType],
|
||||
Background: bgColorMap[Sync],
|
||||
Value: &InstallInfo{
|
||||
Source: Sync,
|
||||
Reason: depType,
|
||||
},
|
||||
})
|
||||
|
||||
if newDeps := alpmPkg.Depends().Slice(); len(newDeps) != 0 && g.fullGraph {
|
||||
newDepsSlice := make([]string, 0, len(newDeps))
|
||||
@@ -166,7 +248,7 @@ func (g *Grapher) addNodes(
|
||||
if aurPkgs, _ := g.aurCache.FindPackage(depName); len(aurPkgs) != 0 { // Check AUR
|
||||
pkg := aurPkgs[0]
|
||||
if len(aurPkgs) > 1 {
|
||||
pkg := provideMenu(g.w, depName, aurPkgs, g.noConfirm)
|
||||
pkg = provideMenu(g.w, depName, aurPkgs, g.noConfirm)
|
||||
g.aurCache.SetProvideCache(depName, []*aur.Pkg{pkg})
|
||||
}
|
||||
|
||||
@@ -178,14 +260,23 @@ func (g *Grapher) addNodes(
|
||||
text.Warnln("aur dep warn:", pkg.PackageBase, parentPkgName, err)
|
||||
}
|
||||
|
||||
graph.SetNodeInfo(pkg.PackageBase, &topo.NodeInfo[int]{Color: colorMap[depType], Background: bgColorMap[AUR]})
|
||||
graph.SetNodeInfo(
|
||||
pkg.PackageBase,
|
||||
&topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[depType],
|
||||
Background: bgColorMap[AUR],
|
||||
Value: &InstallInfo{
|
||||
Source: AUR,
|
||||
Reason: depType,
|
||||
},
|
||||
})
|
||||
g.addDepNodes(pkg, graph)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// no dep found. add as missing
|
||||
graph.SetNodeInfo(depString, &topo.NodeInfo[int]{Color: colorMap[depType], Background: bgColorMap[Missing]})
|
||||
graph.SetNodeInfo(depString, &topo.NodeInfo[*InstallInfo]{Color: colorMap[depType], Background: bgColorMap[Missing]})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+64
-19
@@ -5,10 +5,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Mapable interface {
|
||||
Key() string
|
||||
}
|
||||
|
||||
type (
|
||||
AliasMap[T comparable] map[T]T
|
||||
NodeSet[T comparable] map[T]bool
|
||||
@@ -22,8 +18,9 @@ type NodeInfo[V any] struct {
|
||||
}
|
||||
|
||||
type Graph[T comparable, V any] struct {
|
||||
alias AliasMap[T]
|
||||
nodes NodeSet[T]
|
||||
alias AliasMap[T] // alias -> aliased
|
||||
aliases DepMap[T] // aliased -> alias
|
||||
nodes NodeSet[T]
|
||||
|
||||
// node info map
|
||||
nodeInfo map[T]*NodeInfo[V]
|
||||
@@ -41,6 +38,7 @@ func New[T comparable, V any]() *Graph[T, V] {
|
||||
dependencies: make(DepMap[T]),
|
||||
dependents: make(DepMap[T]),
|
||||
alias: make(AliasMap[T]),
|
||||
aliases: make(DepMap[T]),
|
||||
nodeInfo: make(map[T]*NodeInfo[V]),
|
||||
}
|
||||
}
|
||||
@@ -70,7 +68,9 @@ func (g *Graph[T, V]) Alias(node, alias T) error {
|
||||
if _, ok := g.alias[alias]; ok {
|
||||
return ErrConflictingAlias
|
||||
}
|
||||
|
||||
g.alias[alias] = node
|
||||
g.aliases.addNodeToNodeset(node, alias)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -89,15 +89,23 @@ func (g *Graph[T, V]) getAlias(node T) T {
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) SetNodeInfo(node T, nodeInfo *NodeInfo[V]) {
|
||||
node = g.getAlias(node)
|
||||
|
||||
g.nodeInfo[node] = nodeInfo
|
||||
g.nodeInfo[g.getAlias(node)] = nodeInfo
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) GetNodeInfo(node T) *NodeInfo[V] {
|
||||
node = g.getAlias(node)
|
||||
return g.nodeInfo[g.getAlias(node)]
|
||||
}
|
||||
|
||||
return g.nodeInfo[node]
|
||||
// Retrieve aliases of a node.
|
||||
func (g *Graph[T, V]) GetAliases(node T) []T {
|
||||
size := len(g.aliases[node])
|
||||
aliases := make([]T, 0, size)
|
||||
|
||||
for alias := range g.aliases[node] {
|
||||
aliases = append(aliases, alias)
|
||||
}
|
||||
|
||||
return aliases
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) DependOn(child, parent T) error {
|
||||
@@ -150,7 +158,9 @@ func (g *Graph[T, V]) String() string {
|
||||
sb.WriteString(fmt.Sprintf("\t\"%v\" -> \"%v\";\n", parent, child))
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString("}")
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
@@ -180,13 +190,25 @@ func (g *Graph[T, V]) Leaves() []T {
|
||||
return leaves
|
||||
}
|
||||
|
||||
// TopoSortedLayers returns a slice of all of the graph nodes in topological sort order. That is,
|
||||
// if `B` depends on `A`, then `A` is guaranteed to come before `B` in the sorted output.
|
||||
// The graph is guaranteed to be cycle-free because cycles are detected while building the
|
||||
// graph. Additionally, the output is grouped into "layers", which are guaranteed to not have
|
||||
// any dependencies within each layer. This is useful, e.g. when building an execution plan for
|
||||
// some DAG, in which case each element within each layer could be executed in parallel. If you
|
||||
// do not need this layered property, use `Graph.TopoSorted()`, which flattens all elements.
|
||||
// LeavesMap returns a map of leaves with the node as key and the node info value as value.
|
||||
func (g *Graph[T, V]) LeavesMap() map[T]V {
|
||||
leaves := make(map[T]V, 0)
|
||||
|
||||
for node := range g.nodes {
|
||||
if _, ok := g.dependencies[node]; !ok {
|
||||
nodeInfo := g.GetNodeInfo(node)
|
||||
if nodeInfo == nil {
|
||||
nodeInfo = &NodeInfo[V]{}
|
||||
}
|
||||
|
||||
leaves[node] = nodeInfo.Value
|
||||
}
|
||||
}
|
||||
|
||||
return leaves
|
||||
}
|
||||
|
||||
// TopoSortedLayers returns a slice of all of the graph nodes in topological sort order.
|
||||
func (g *Graph[T, V]) TopoSortedLayers() [][]T {
|
||||
layers := [][]T{}
|
||||
|
||||
@@ -209,6 +231,29 @@ func (g *Graph[T, V]) TopoSortedLayers() [][]T {
|
||||
return layers
|
||||
}
|
||||
|
||||
// TopoSortedLayerMap returns a slice of all of the graph nodes in topological sort order with their node info
|
||||
func (g *Graph[T, V]) TopoSortedLayerMap() []map[T]V {
|
||||
layers := []map[T]V{}
|
||||
|
||||
// Copy the graph
|
||||
shrinkingGraph := g.clone()
|
||||
|
||||
for {
|
||||
leaves := shrinkingGraph.LeavesMap()
|
||||
if len(leaves) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
layers = append(layers, leaves)
|
||||
|
||||
for leafNode := range leaves {
|
||||
shrinkingGraph.remove(leafNode)
|
||||
}
|
||||
}
|
||||
|
||||
return layers
|
||||
}
|
||||
|
||||
func (dm DepMap[T]) removeFromDepmap(key, node T) {
|
||||
if nodes := dm[key]; len(nodes) == 1 {
|
||||
// The only element in the nodeset must be `node`, so we
|
||||
@@ -240,7 +285,6 @@ func (g *Graph[T, V]) remove(node T) {
|
||||
}
|
||||
|
||||
// TopoSorted returns all the nodes in the graph is topological sort order.
|
||||
// See also `Graph.TopoSortedLayers()`.
|
||||
func (g *Graph[T, V]) TopoSorted() []T {
|
||||
nodeCount := 0
|
||||
layers := g.TopoSortedLayers()
|
||||
@@ -279,6 +323,7 @@ func (g *Graph[T, V]) clone() *Graph[T, V] {
|
||||
dependencies: g.dependencies.copy(),
|
||||
dependents: g.dependents.copy(),
|
||||
nodes: g.nodes.copy(),
|
||||
nodeInfo: g.nodeInfo, // not copied, as it is not modified
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user