newt/deprepo/graph.go (127 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.
*/
// deprepo: Package for resolving repo dependencies.
//
// Vocabulary:
// * Dependee: A repo that is depended on.
// * Dependent: A repo that depends on others.
package deprepo
import (
"fmt"
"sort"
"strings"
"mynewt.apache.org/newt/newt/newtutil"
"mynewt.apache.org/newt/util"
)
const rootDependencyName = ""
const rootRepoName = "project.yml"
// Represents a top-level repo dependency (i.e., a repo specified in
// `project.yml`).
var rootDependent = RVPair{Name: rootDependencyName}
// Represents a repo-name,version pair.
type RVPair struct {
Name string
Ver newtutil.RepoVersion
}
// A repo dependency graph.
// Key: A repo with dependencies.
// Value: The corresponding list of dependencies.
type DepGraph map[RVPair][]RVPair
// A single node in a repo reverse dependency graph.
type RevdepGraphNode struct {
// The name of the dependent repo.
Name string
// The version of the dependent repo.
DependentVer newtutil.RepoVersion
// The version of the dependee repo that is required.
DependeeVer newtutil.RepoVersion
}
// A repo reverse dependency graph.
// Key: A depended-on repo.
// Value: The corresponding list of dependencies.
type RevdepGraph map[string][]RevdepGraphNode
func repoNameString(repoName string) string {
if repoName == rootDependencyName {
return rootRepoName
} else {
return repoName
}
}
func repoNameVerString(repoName string, ver newtutil.RepoVersion) string {
if repoName == rootDependencyName || repoName == rootRepoName {
return rootRepoName
} else {
return fmt.Sprintf("%s/%s", repoName, ver.String())
}
}
func (rvp *RVPair) String() string {
return repoNameVerString(rvp.Name, rvp.Ver)
}
func CompareRVPairs(a RVPair, b RVPair) int {
x := strings.Compare(a.Name, b.Name)
if x != 0 {
return x
}
x = newtutil.CompareRepoVersions(a.Ver, b.Ver)
if x != 0 {
return x
}
return 0
}
func (dg DepGraph) String() string {
lines := make([]string, 0, len(dg))
for dependent, nodes := range dg {
line := fmt.Sprintf("%s:", dependent.String())
for _, node := range nodes {
line += fmt.Sprintf(" (%s)", node.String())
}
lines = append(lines, line)
}
sort.Strings(lines)
return strings.Join(lines, "\n")
}
func (rgn *RevdepGraphNode) String() string {
return fmt.Sprintf("%s,%s", repoNameVerString(rgn.Name, rgn.DependentVer),
rgn.DependeeVer.String())
}
func (rg RevdepGraph) String() string {
lines := make([]string, 0, len(rg))
for repoName, nodes := range rg {
line := fmt.Sprintf("%s:", repoName)
for _, node := range nodes {
line += fmt.Sprintf(" (%s)", node.String())
}
lines = append(lines, line)
}
sort.Strings(lines)
return strings.Join(lines, "\n")
}
// Adds all dependencies expressed by a single version of a repo.
//
// @param repoName The name of the dependent repo.
// @param repoVer The version of the dependent repo.
// @param reqMap The dependency requirements of the specified
// repo version.
func (dg DepGraph) AddRepoVer(repoName string, repoVer newtutil.RepoVersion,
reqMap RequirementMap) error {
dep := RVPair{
Name: repoName,
Ver: repoVer,
}
if _, ok := dg[dep]; ok {
return util.FmtNewtError(
"Duplicate repo-version-pair in repo dependency graph: %s,%s",
repoName, repoVer.String())
}
for depName, depReq := range reqMap {
dg[dep] = append(dg[dep], RVPair{
Name: depName,
Ver: depReq,
})
}
return nil
}
// Adds a root dependency (i.e., required repo specified in `project.yml`).
func (dg DepGraph) AddRootDep(repoName string, ver newtutil.RepoVersion) error {
rootDeps := dg[rootDependent]
for _, d := range rootDeps {
if d.Name == repoName {
return util.FmtNewtError(
"Duplicate root dependency repo dependency graph: %s",
repoName)
}
}
dg[rootDependent] = append(dg[rootDependent], RVPair{
Name: repoName,
Ver: ver,
})
return nil
}
// Reverses a dependency graph, forming a reverse dependency graph.
//
// A normal dependency graph expresses the following relationship:
// [dependent] => depended-on
//
// A reverse dependency graph expresses the following relationship:
// [depended-on] => dependent
func (dg DepGraph) Reverse() RevdepGraph {
rg := RevdepGraph{}
for dependent, nodes := range dg {
for _, node := range nodes {
// Nothing depends on project.yml (""), so exclude it from the
// result.
if node.Name != "" {
rg[node.Name] = append(rg[node.Name], RevdepGraphNode{
Name: dependent.Name,
DependentVer: dependent.Ver,
DependeeVer: node.Ver,
})
}
}
}
return rg
}