blob: 871dc6c82bfcf798230fcbe91171222c9b4bf907 [file] [log] [blame] [edit]
package cmd
import (
"os"
"strings"
"github.com/Masterminds/cookoo"
"github.com/Masterminds/glide/yaml"
)
// GuessDeps tries to get the dependencies for the current directory.
//
// Params
// - dirname (string): Directory to use as the base. Default: "."
func GuessDeps(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
buildContext, err := GetBuildContext()
if err != nil {
return nil, err
}
base := p.Get("dirname", ".").(string)
deps := make(map[string]bool)
err = findDeps(buildContext, deps, base, "")
deps = compactDeps(deps)
delete(deps, base)
if err != nil {
return nil, err
}
config := new(yaml.Config)
// Get the name of the top level package
config.Name = guessPackageName(buildContext, base)
config.Imports = make([]*yaml.Dependency, len(deps))
i := 0
for pa := range deps {
Info("Found reference to %s\n", pa)
d := &yaml.Dependency{
Name: pa,
}
config.Imports[i] = d
i++
}
return config, nil
}
// findDeps finds all of the dependenices.
// https://golang.org/src/cmd/go/pkg.go#485
//
// As of Go 1.5 the go command knows about the vendor directory but the go/build
// package does not. It only knows about the GOPATH and GOROOT. In order to look
// for packages in the vendor/ directory we need to fake it for now.
func findDeps(b *BuildCtxt, soFar map[string]bool, name, vpath string) error {
cwd, err := os.Getwd()
if err != nil {
return err
}
// Skip cgo pseudo-package.
if name == "C" {
return nil
}
pkg, err := b.Import(name, cwd, 0)
if err != nil {
return err
}
if pkg.Goroot {
return nil
}
if vpath == "" {
vpath = pkg.ImportPath
}
// When the vendor/ directory is present make sure we strip it out before
// registering it as a guess.
realName := strings.TrimPrefix(pkg.ImportPath, vpath+"/vendor/")
// Before adding a name to the list make sure it's not the name of the
// top level package.
lookupName, _ := NormalizeName(realName)
if vpath != lookupName {
soFar[realName] = true
}
for _, imp := range pkg.Imports {
if !soFar[imp] {
// Try looking for a dependency as a vendor. If it's not there then
// fall back to a way where it will be found in the GOPATH or GOROOT.
if err := findDeps(b, soFar, vpath+"/vendor/"+imp, vpath); err != nil {
if err := findDeps(b, soFar, imp, vpath); err != nil {
return err
}
}
}
}
return nil
}
// compactDeps registers only top level packages.
//
// Minimize the package imports. For example, importing github.com/Masterminds/cookoo
// and github.com/Masterminds/cookoo/io should not import two packages. Only one
// package needs to be referenced.
func compactDeps(soFar map[string]bool) map[string]bool {
basePackages := make(map[string]bool, len(soFar))
for k := range soFar {
base, _ := NormalizeName(k)
basePackages[base] = true
}
return basePackages
}
// Attempt to guess at the package name at the top level. When unable to detect
// a name goes to default of "main".
func guessPackageName(b *BuildCtxt, base string) string {
cwd, err := os.Getwd()
if err != nil {
return "main"
}
pkg, err := b.Import(base, cwd, 0)
if err != nil {
return "main"
}
return pkg.ImportPath
}