blob: a4d18b7f5a033a04c51ad31317baee18b68da912 [file] [log] [blame]
/**
* 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
}