You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2018/11/14 17:02:25 UTC
[mynewt-newt] 02/02: resolve: Prune orphan packages during dep
resolutn
This is an automated email from the ASF dual-hosted git repository.
ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git
commit 88ad5de6f0826c71f961adba6c907e27e18755b4
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 6 17:18:02 2018 -0800
resolve: Prune orphan packages during dep resolutn
This fixes #233.
Detect orphan packages as follows: starting from each seed package,
recursively traverse the package's dependency list, keeping track of
which packages were visited. After the traversal is complete, any
non-visited package in the resolver are orphans and can be removed.
---
newt/resolve/resolve.go | 132 ++++++++++++++++++++++++++++++++++++------------
1 file changed, 101 insertions(+), 31 deletions(-)
diff --git a/newt/resolve/resolve.go b/newt/resolve/resolve.go
index 02d69f9..ef13e6a 100644
--- a/newt/resolve/resolve.go
+++ b/newt/resolve/resolve.go
@@ -58,6 +58,7 @@ type resolveReqApi struct {
type Resolver struct {
apis map[string]resolveApi
pkgMap map[*pkg.LocalPackage]*ResolvePackage
+ seedPkgs []*pkg.LocalPackage
injectedSettings map[string]string
flashMap flash.FlashMap
cfg syscfg.Cfg
@@ -134,6 +135,7 @@ func newResolver(
r := &Resolver{
apis: map[string]resolveApi{},
pkgMap: map[*pkg.LocalPackage]*ResolvePackage{},
+ seedPkgs: seedPkgs,
injectedSettings: injectedSettings,
flashMap: flashMap,
cfg: syscfg.NewCfg(),
@@ -340,19 +342,25 @@ func (r *Resolver) deletePkg(rpkg *ResolvePackage) error {
// Delete the package from syscfg.
r.cfg.DeletePkg(rpkg.Lpkg)
- // If the deleted package is the only depender for any other packages
- // (i.e., if any of its dependencies have only one reverse dependency),
+ // Remove all dependencies on the deleted package.
+ for revdep, _ := range rpkg.revDeps {
+ delete(revdep.Deps, rpkg)
+ }
+
+ // Remove all reverse dependencies pointing to the deleted package. If the
+ // deleted package is the only depender for any other packages (i.e., if
+ // any of its dependencies have only one reverse dependency),
// delete them as well.
- for rdep, _ := range rpkg.Deps {
- if len(rdep.revDeps) == 0 {
+ for dep, _ := range rpkg.Deps {
+ if len(dep.revDeps) == 0 {
return util.FmtNewtError(
"package %s unexpectedly has 0 reverse dependencies",
- rdep.Lpkg.FullName())
+ dep.Lpkg.FullName())
}
- delete(rdep.revDeps, rpkg)
- if len(rdep.revDeps) == 0 {
- if err := r.deletePkg(rdep); err != nil {
- return nil
+ delete(dep.revDeps, rpkg)
+ if len(dep.revDeps) == 0 {
+ if err := r.deletePkg(dep); err != nil {
+ return err
}
}
}
@@ -478,31 +486,94 @@ func (r *Resolver) reloadCfg() (bool, error) {
return false, nil
}
-func (r *Resolver) resolveDepsOnce() (bool, error) {
- // Circularly resolve dependencies, APIs, and required APIs until no new
- // ones exist.
- newDeps := false
- for {
- reprocess := false
- for _, rpkg := range r.pkgMap {
- newDeps, err := r.resolvePkg(rpkg)
- if err != nil {
+// @return bool True if any packages were pruned, false
+// otherwise.
+// @return err Error
+func (r *Resolver) pruneOrphans() (bool, error) {
+ seenMap := map[*ResolvePackage]struct{}{}
+
+ // This function traverses the specified package's dependency list,
+ // recording each visited packges in `seenMap`.
+ var visit func(rpkg *ResolvePackage)
+ visit = func(rpkg *ResolvePackage) {
+ if _, ok := seenMap[rpkg]; ok {
+ return
+ }
+
+ seenMap[rpkg] = struct{}{}
+ for dep, _ := range rpkg.Deps {
+ visit(dep)
+ }
+ }
+
+ // Starting from each seed package, recursively traverse the package's
+ // dependency list, keeping track of which packages were visited.
+ for _, lpkg := range r.seedPkgs {
+ rpkg := r.pkgMap[lpkg]
+ if rpkg == nil {
+ panic("Resolver lacks mapping for seed package " + lpkg.FullName())
+ }
+
+ visit(rpkg)
+ }
+
+ // Any non-visited packages in the resolver are orphans and can be removed.
+ anyPruned := false
+ for _, rpkg := range r.pkgMap {
+ if _, ok := seenMap[rpkg]; !ok {
+ anyPruned = true
+ if err := r.deletePkg(rpkg); err != nil {
return false, err
}
+ }
+ }
- if newDeps {
- // The new dependencies need to be processed. Iterate again
- // after this iteration completes.
- reprocess = true
- }
+ return anyPruned, nil
+}
+
+func (r *Resolver) resolveHardDepsOnce() (bool, error) {
+ // Circularly resolve dependencies, APIs, and required APIs until no new
+ // ones exist.
+ reprocess := false
+ for _, rpkg := range r.pkgMap {
+ newDeps, err := r.resolvePkg(rpkg)
+ if err != nil {
+ return false, err
}
- if !reprocess {
- break
+ if newDeps {
+ // The new dependencies need to be processed. Iterate again
+ // after this iteration completes.
+ reprocess = true
}
}
- return newDeps, nil
+ if reprocess {
+ return true, nil
+ }
+
+ // Now prune orphan packages.
+ anyPruned, err := r.pruneOrphans()
+ if err != nil {
+ return false, err
+ }
+
+ return anyPruned, nil
+}
+
+// Fully resolves all hard dependencies (i.e., packages listed in `pkg.deps`;
+// not API dependencies).
+func (r *Resolver) resolveHardDeps() error {
+ for {
+ reprocess, err := r.resolveHardDepsOnce()
+ if err != nil {
+ return err
+ }
+
+ if !reprocess {
+ return nil
+ }
+ }
}
// Given a fully calculated syscfg and API map, resolves package dependencies
@@ -512,7 +583,7 @@ func (r *Resolver) resolveDepsOnce() (bool, error) {
// resolved separately, after the master syscfg and API map have been
// calculated.
func (r *Resolver) resolveDeps() ([]*ResolvePackage, error) {
- if _, err := r.resolveDepsOnce(); err != nil {
+ if err := r.resolveHardDeps(); err != nil {
return nil, err
}
@@ -537,7 +608,7 @@ func (r *Resolver) resolveDeps() ([]*ResolvePackage, error) {
// 2. Determines which packages satisfy which API requirements.
// 3. Resolves package dependencies by populating the resolver's package map.
func (r *Resolver) resolveDepsAndCfg() error {
- if _, err := r.resolveDepsOnce(); err != nil {
+ if err := r.resolveHardDeps(); err != nil {
return err
}
@@ -556,12 +627,11 @@ func (r *Resolver) resolveDepsAndCfg() error {
}
}
- newDeps, err := r.resolveDepsOnce()
- if err != nil {
+ if err := r.resolveHardDeps(); err != nil {
return err
}
- if !newDeps && !cfgChanged {
+ if !cfgChanged {
break
}
}