diff --git a/cmd/dep/status.go b/cmd/dep/status.go index eb57abc9f4..e85dd19a4e 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -214,8 +214,23 @@ func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error { } } - if err := runStatusAll(ctx.Loggers, out, p, sm); err != nil { - return err + switch { + case cmd.unused: + if err := runStatusUnused(ctx.Loggers, p); err != nil { + return err + } + case cmd.missing: + if err := runStatusMissing(ctx.Loggers, p); err != nil { + return err + } + case cmd.old: + if err := runStatusOld(ctx.Loggers, p, sm); err != nil { + return err + } + default: + if err := runStatusAll(ctx.Loggers, out, p, sm); err != nil { + return err + } } ctx.Loggers.Out.Print(buf.String()) @@ -422,6 +437,135 @@ outer: return nil } +// runStatusUnused analyses the project for unused dependencies that are present +// in manifest but not imported in the project. +func runStatusUnused(loggers *dep.Loggers, p *dep.Project) error { + external, err := getExternalPackages(p) + if err != nil { + return err + } + + var unusedDeps []string + for pr, _ := range p.Manifest.Dependencies { + if !contains(external, string(pr)) { + unusedDeps = append(unusedDeps, string(pr)) + } + } + + if unusedDeps != nil { + loggers.Err.Println("Unused dependencies present in manifest:\n") + + for _, d := range unusedDeps { + loggers.Err.Printf(" %v\n", d) + } + } else { + loggers.Err.Println("No unused dependencies found.") + } + + return nil +} + +// runStatusMissing analyses the project for missing dependencies in lock file. +func runStatusMissing(loggers *dep.Loggers, p *dep.Project) error { + external, err := getExternalPackages(p) + if err != nil { + return err + } + + var missingDeps []string + +missingOuter: + for _, d := range external { + for _, lp := range p.Lock.Projects() { + if string(lp.Ident().ProjectRoot) == d { + continue missingOuter + } + } + + missingDeps = append(missingDeps, d) + } + + if missingDeps != nil { + loggers.Err.Println("Missing dependencies (not present in lock):\n") + for _, d := range missingDeps { + loggers.Err.Printf(" %v\n", d) + } + } else { + loggers.Err.Println("No missing dependencies found.") + } + + return nil +} + +// runStatusOld analyses the project for old dependencies that have a newer +// version available. +func runStatusOld(loggers *dep.Loggers, p *dep.Project, sm gps.SourceManager) error { + var projects []BasicStatus + + for _, proj := range p.Lock.Projects() { + bs := BasicStatus{ + ProjectRoot: string(proj.Ident().ProjectRoot), + } + + switch tv := proj.Version().(type) { + case gps.UnpairedVersion: + bs.Version = tv + case gps.Revision: + bs.Revision = tv + case gps.PairedVersion: + bs.Version = tv.Unpair() + bs.Revision = tv.Underlying() + } + + if bs.Version != nil && bs.Version.Type() != gps.IsVersion { + c, has := p.Manifest.Dependencies[proj.Ident().ProjectRoot] + if !has { + c.Constraint = gps.Any() + } + bs.Constraint = c.Constraint + + vl, err := sm.ListVersions(proj.Ident()) + if err == nil { + gps.SortPairedForUpgrade(vl) + + for _, v := range vl { + if c.Constraint.Matches(v) { + bs.Latest = v.Underlying() + break + } + } + } + } + + if bs.Latest != bs.Revision { + projects = append(projects, bs) + } + } + + if projects != nil { + loggers.Err.Println("Following are the out-of-date dependencies:\n") + for _, project := range projects { + loggers.Err.Printf("%v is on %v, latest available is %v\n", project.ProjectRoot, project.Revision, project.Latest) + } + } else { + loggers.Err.Println("All the dependencies are up-to-date.") + } + + return nil +} + +// getExternalPackages parses the project and returns a string slice of +// projects' ImportRoot +func getExternalPackages(p *dep.Project) ([]string, error) { + ptree, err := pkgtree.ListPackages(p.AbsRoot, string(p.ImportRoot)) + if err != nil { + return nil, errors.Errorf("analysis of local packages failed: %v", err) + } + + rm, _ := ptree.ToReachMap(true, true, false, nil) + return rm.FlattenOmitStdLib(), nil +} + func formatVersion(v gps.Version) string { if v == nil { return ""