in newt/sysinit/sysinit.go [124:234]
func ResolveStageFuncsOrder(sfs []stage.StageFunc) ([]stage.StageFunc, error) {
var nodes []*stage.StageFunc
var nodesQ []*stage.StageFunc
nodesByStage := make(map[int][]*stage.StageFunc)
nodesByName := make(map[string]*stage.StageFunc)
for idx, _ := range sfs {
sfRef := &sfs[idx]
nodesByName[sfRef.Name] = sfRef
if len(sfRef.Stage.Befores) == 0 && len(sfRef.Stage.Afters) == 0 {
stage, _ := sfRef.Stage.IntVal()
nodesByStage[stage] = append(nodesByStage[stage], sfRef)
} else {
nodesQ = append(nodesQ, sfRef)
}
}
var stages []int
for stage := range nodesByStage {
stages = append(stages, stage)
}
sort.Ints(stages)
// Lexicographically sort nodes in each stage, then build the nodes stack.
// This helps ensure that sysinit order is reproducable and deterministic
for stageIndex, _ := range stages {
lsfs := nodesByStage[stages[stageIndex]]
sort.Slice(lsfs, func(i int, j int) bool {
a := lsfs[i]
b := lsfs[j]
if strings.Compare(a.Name, b.Name) == -1 {
return false
}
return true
})
nodes = append(nodes, nodesByStage[stages[stageIndex]]...)
}
sort.Slice(nodesQ, func(i int, j int) bool {
a := nodesQ[i]
b := nodesQ[j]
if strings.Compare(a.Name, b.Name) == -1 {
return false
}
return true
})
// Put nodes without stages first, so they are resolved and put to
// stack first - we do not want them to precede all nodes with stages.
// While technically correct, it's better not to start sysinit with
// nodes that do not have any stage since this will likely happen
// before os init packages.
nodes = append(nodesQ, nodes...)
// Add implicit dependencies for nodes with stages. We need to add
// direct dependencies between each node of stage X to each node of
// stage Y to make sure they can be resolved properly and reordered
// if needed due to other dependencies.
sfsPrev := nodesByStage[stages[0]]
stages = stages[1:]
for _, stage := range stages {
sfsCurr := nodesByStage[stage]
for _, sfsP := range sfsPrev {
for _, sfsC := range sfsCurr {
sfsP.Deps = append(sfsP.Deps, sfsC)
// Keep separate list of implicit dependencies
// This is only used for Graphviz output
sfsP.DepsI = append(sfsP.DepsI, sfsC)
}
}
sfsPrev = sfsCurr
}
// Now add other dependencies, i.e. $after and $before
for _, sf := range nodesQ {
for _, depStr := range sf.Stage.Afters {
depSf := nodesByName[depStr]
if depSf == nil {
return []stage.StageFunc{},
util.FmtNewtError("Unknown depdendency (\"%s\") for \"%s (%s)\".",
depStr, sf.Name, sf.Pkg.FullName())
}
depSf.Deps = append(depSf.Deps, sf)
}
for _, depStr := range sf.Stage.Befores {
depSf := nodesByName[depStr]
if depSf == nil {
return []stage.StageFunc{},
util.FmtNewtError("Unknown depdendency (\"%s\") for \"%s (%s)\".",
depStr, sf.Name, sf.Pkg.FullName())
}
sf.Deps = append(sf.Deps, depSf)
}
}
// Now we can resolve order of functions by sorting dependency graph
// topologically. This will also detect circular dependencies.
sfs = []stage.StageFunc{}
for _, sfRef := range nodes {
err := stageFuncResolve(sfRef, &sfs)
if err != nil {
return []stage.StageFunc{}, err
}
}
return sfs, nil
}