Add SourceManager.ListPackages(), and friends

This commit is contained in:
Sam Boyer 2016-06-14 23:57:48 -04:00
Родитель 3c76704b86
Коммит 4dd7ce0831
5 изменённых файлов: 128 добавлений и 2 удалений

Просмотреть файл

@ -25,10 +25,12 @@ type sourceBridge interface {
deduceRemoteRepo(path string) (*remoteRepo, error)
}
func newBridge(sm SourceManager, downgrade bool) sourceBridge {
func newBridge(name ProjectName, root string, sm SourceManager, downgrade bool) sourceBridge {
return &bridge{
sm: sm,
sortdown: downgrade,
name: name,
root: root,
vlists: make(map[ProjectName][]Version),
}
}
@ -54,6 +56,14 @@ type bridge struct {
// true for downgrades.
sortdown bool
// The name of the root project we're operating on. Used to redirect some
// calls that would ordinarily go to the SourceManager to a root-specific
// logical path, instead.
name ProjectName
// The path to the base directory of the root project.
root string
// Map of project root name to their available version list. This cache is
// layered on top of the proper SourceManager's cache; the only difference
// is that this keeps the versions sorted in the direction required by the
@ -358,6 +368,18 @@ func (b *bridge) computeRootReach(path string) ([]string, error) {
return listExternalDeps(path, path, true)
}
// listPackages lists all the packages contained within the given project at a
// particular version.
//
// Special handling is done for the root project.
func (b *bridge) listPackages(id ProjectIdentifier, v Version) (map[string]string, error) {
if id.LocalName != b.name {
return b.sm.ListPackages(b.key(id), v)
}
return listPackages(b.root, string(b.name), true)
}
// verifyRoot ensures that the provided path to the project root is in good
// working condition. This check is made only once, at the beginning of a solve
// run.

Просмотреть файл

@ -258,6 +258,65 @@ func listExternalDeps(basedir, projname string, main bool) ([]string, error) {
return ex, nil
}
// listPackages lists all packages, optionally including main packages,
// contained at or below the provided path.
//
// Directories without any valid Go files are excluded. Directories with
// multiple packages are excluded. (TODO - maybe accommodate that?)
//
// A map of import path to package name is returned.
func listPackages(basedir, prefix string, main bool) (map[string]string, error) {
ctx := build.Default
ctx.UseAllFiles = true // optimistic, but we do it for the first try
exm := make(map[string]string)
err := filepath.Walk(basedir, func(path string, fi os.FileInfo, err error) error {
if err != nil && err != filepath.SkipDir {
return err
}
if !fi.IsDir() {
return nil
}
// Skip a few types of dirs
if !localSrcDir(fi) {
return filepath.SkipDir
}
// Scan for dependencies, and anything that's not part of the local
// package gets added to the scan list.
p, err := ctx.ImportDir(path, 0)
var imps []string
if err != nil {
switch err.(type) {
case *build.NoGoError:
return nil
case *build.MultiplePackageError:
// Multiple package names declared in the dir, which causes
// ImportDir() to choke; use our custom iterative scanner.
imps, err = IterativeScan(path)
if err != nil {
return err
}
default:
return err
}
} else {
if prefix == "" {
exm[path] = path
} else {
exm[path] = prefix + os.PathSeparator + path
}
}
})
if err != nil {
return nil, err
}
return exm, nil
}
func localSrcDir(fi os.FileInfo) bool {
// Ignore _foo and .foo
if strings.HasPrefix(fi.Name(), "_") || strings.HasPrefix(fi.Name(), ".") {

Просмотреть файл

@ -22,6 +22,7 @@ type ProjectManager interface {
ExportVersionTo(Version, string) error
ExternalReach(Version) (map[string][]string, error)
ListExternal(Version) ([]string, error)
ListPackages(Version) (map[string]string, error)
}
type ProjectAnalyzer interface {
@ -207,6 +208,40 @@ func (pm *projectManager) ListExternal(v Version) ([]string, error) {
return ex, err
}
func (pm *projectManager) ListPackages(v Version) (map[string]string, error) {
var err error
if err = pm.ensureCacheExistence(); err != nil {
return nil, err
}
pm.crepo.mut.Lock()
// Check out the desired version for analysis
if pv, ok := v.(PairedVersion); ok {
// Always prefer a rev, if it's available
err = pm.crepo.r.UpdateVersion(pv.Underlying().String())
} else {
// If we don't have a rev, ensure the repo is up to date, otherwise we
// could have a desync issue
if !pm.crepo.synced {
err = pm.crepo.r.Update()
if err != nil {
return nil, fmt.Errorf("Could not fetch latest updates into repository")
}
pm.crepo.synced = true
}
err = pm.crepo.r.UpdateVersion(v.String())
}
// Nothing within the SourceManager is responsible for computing deps of a
// root package; it's assumed we're always operating on libraries.
// Consequently, we never want to include main packages, so we hardcode
// false for the third param.
ex, err := listPackages(filepath.Join(pm.ctx.GOPATH, "src", string(pm.n)), string(pm.n), true)
pm.crepo.mut.Unlock()
return ex, err
}
func (pm *projectManager) ensureCacheExistence() error {
// Technically, methods could could attempt to return straight from the
// metadata cache even if the repo cache doesn't exist on disk. But that

Просмотреть файл

@ -165,7 +165,7 @@ func prepareSolver(opts SolveOpts, sm SourceManager) (*solver, error) {
s := &solver{
o: opts,
b: newBridge(sm, opts.Downgrade),
b: newBridge(o.N, o.Root, sm, opts.Downgrade),
tl: opts.TraceLogger,
}

Просмотреть файл

@ -17,6 +17,7 @@ type SourceManager interface {
VendorCodeExists(ProjectName) (bool, error)
ExternalReach(ProjectName, Version) (map[string][]string, error)
ListExternal(ProjectName, Version) ([]string, error)
ListPackages(ProjectName, Version) (map[string]string, error)
ExportProject(ProjectName, Version, string) error
Release()
// Flush()
@ -119,6 +120,15 @@ func (sm *sourceManager) ListExternal(n ProjectName, v Version) ([]string, error
return pmc.pm.ListExternal(v)
}
func (sm *sourceManager) ListPackages(n ProjectName, v Version) ([]string, error) {
pmc, err := sm.getProjectManager(n)
if err != nil {
return nil, err
}
return pmc.pm.ListPackages(v)
}
func (sm *sourceManager) ListVersions(n ProjectName) ([]Version, error) {
pmc, err := sm.getProjectManager(n)
if err != nil {