newt/builder/depgraph.go (196 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package builder import ( "bytes" "fmt" "sort" "strings" "mynewt.apache.org/newt/newt/parse" "mynewt.apache.org/newt/newt/resolve" ) type DepEntry struct { PkgName string // Expressions that enable the dependency. DepExprs parse.ExprSet // Required APIs and their enabling expressions. ReqApiExprs parse.ExprMap // Satisfied APIs and their enabling expressions. ApiExprs parse.ExprMap } // Key=parent, Value=slice of children // For normal dependency graph: parent=depender, children=dependees. // For reverse dependency graph: parent=dependee, children=dependers. type DepGraph map[string][]DepEntry type depEntrySorter struct { entries []DepEntry } func (s depEntrySorter) Len() int { return len(s.entries) } func (s depEntrySorter) Swap(i, j int) { s.entries[i], s.entries[j] = s.entries[j], s.entries[i] } func (s depEntrySorter) Less(i, j int) bool { return s.entries[i].PkgName < s.entries[j].PkgName } func SortDepEntries(entries []DepEntry) { sorter := depEntrySorter{entries} sort.Sort(sorter) } func depGraph(rs *resolve.ResolveSet) (DepGraph, error) { graph := DepGraph{} for _, parent := range rs.Rpkgs { pname := parent.Lpkg.FullName() graph[pname] = make([]DepEntry, 0, len(parent.Deps)) for _, dep := range parent.Deps { child := dep.Rpkg graph[pname] = append(graph[pname], DepEntry{ PkgName: child.Lpkg.FullName(), DepExprs: dep.Exprs, ReqApiExprs: dep.ApiExprMap, ApiExprs: child.Apis, }) } SortDepEntries(graph[pname]) } return graph, nil } func revdepGraph(rs *resolve.ResolveSet) (DepGraph, error) { graph, err := depGraph(rs) if err != nil { return nil, err } rgraph := DepGraph{} for parent, entries := range graph { // Ensure parent is present in graph even if no one depends on it. if rgraph[parent] == nil { rgraph[parent] = []DepEntry{} } // Reverse the dependency relationship for each child and add to the // graph. for _, entry := range entries { rgraph[entry.PkgName] = append(rgraph[entry.PkgName], DepEntry{ PkgName: parent, DepExprs: entry.DepExprs, ReqApiExprs: entry.ReqApiExprs, ApiExprs: entry.ApiExprs, }) } } for _, entries := range rgraph { SortDepEntries(entries) } return rgraph, nil } func depString(entry DepEntry) string { s := fmt.Sprintf("%s", entry.PkgName) type ApiPair struct { api string exprs []*parse.Node } if len(entry.ReqApiExprs) > 0 { apis := make([]string, 0, len(entry.ReqApiExprs)) for api, _ := range entry.ReqApiExprs { apis = append(apis, api) } sort.Strings(apis) for _, api := range apis { reqes := entry.ReqApiExprs[api] reqdis := reqes.Disjunction().String() apies := entry.ApiExprs[api] apidis := apies.Disjunction().String() s += "(api:" + api if reqdis != "" || apidis != "" { s += ",syscfg:" + reqdis if apidis != "" { s += ";" + apidis } } s += ")" } } else { dis := entry.DepExprs.Disjunction().String() if dis != "" { s += "(syscfg:" + dis + ")" } } return s } func DepGraphText(graph DepGraph) string { parents := make([]string, 0, len(graph)) for pname, _ := range graph { parents = append(parents, pname) } sort.Strings(parents) buffer := bytes.NewBufferString("") fmt.Fprintf(buffer, "Dependency graph (depender --> [dependees]):") for _, pname := range parents { fmt.Fprintf(buffer, "\n * %s --> [", pname) for i, child := range graph[pname] { if i != 0 { fmt.Fprintf(buffer, " ") } fmt.Fprintf(buffer, "%s", depString(child)) } fmt.Fprintf(buffer, "]") } return buffer.String() } func DepGraphViz(graph DepGraph) string { parents := make([]string, 0, len(graph)) for pname, _ := range graph { parents = append(parents, pname) } sort.Strings(parents) buffer := bytes.NewBufferString("") fmt.Fprintf(buffer, "digraph deps {\n") for _, pname := range parents { for _, child := range graph[pname] { depStr := strings.TrimPrefix(depString(child), child.PkgName) fmt.Fprintf(buffer, " \"%s\" -> \"%s\" [label=\"%s\"];\n", pname, child.PkgName, depStr) } } fmt.Fprintf(buffer, "}\n") return buffer.String() } func RevdepGraphText(graph DepGraph) string { parents := make([]string, 0, len(graph)) for pname, _ := range graph { parents = append(parents, pname) } sort.Strings(parents) buffer := bytes.NewBufferString("") fmt.Fprintf(buffer, "Reverse dependency graph (dependee <-- [dependers]):") for _, pname := range parents { fmt.Fprintf(buffer, "\n * %s <-- [", pname) for i, child := range graph[pname] { if i != 0 { fmt.Fprintf(buffer, " ") } fmt.Fprintf(buffer, "%s", depString(child)) } fmt.Fprintf(buffer, "]") } return buffer.String() } func RevdepGraphViz(graph DepGraph) string { parents := make([]string, 0, len(graph)) for pname, _ := range graph { parents = append(parents, pname) } sort.Strings(parents) buffer := bytes.NewBufferString("") fmt.Fprintf(buffer, "digraph revdeps {\n") for _, pname := range parents { for _, child := range graph[pname] { depStr := strings.TrimPrefix(depString(child), child.PkgName) fmt.Fprintf(buffer, " \"%s\" -> \"%s\" [label=\"%s\"];\n", child.PkgName, pname, depStr) } } fmt.Fprintf(buffer, "}\n") return buffer.String() } // Extracts a new dependency graph containing only the specified parents. // // @param dg The source graph to filter. // @param parents The parent nodes to keep. // // @return DepGraph Filtered dependency graph. // []*ResolvePackage Specified packages that were not parents in // original graph. func FilterDepGraph(dg DepGraph, parents []*resolve.ResolvePackage) ( DepGraph, []*resolve.ResolvePackage) { newDg := DepGraph{} var missing []*resolve.ResolvePackage for _, p := range parents { pname := p.Lpkg.FullName() if dg[pname] == nil { missing = append(missing, p) } else { newDg[pname] = dg[pname] } } return newDg, missing }