github.com/golang/dep@v0.5.4/gps/source_cache.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gps
     6  
     7  import (
     8  	"fmt"
     9  	"path"
    10  	"sort"
    11  	"strings"
    12  	"sync"
    13  
    14  	"github.com/golang/dep/gps/pkgtree"
    15  )
    16  
    17  // sourceCache is an interface for creating singleSourceCaches, and safely
    18  // releasing backing resources via close.
    19  type sourceCache interface {
    20  	// newSingleSourceCache creates a new singleSourceCache for id, which
    21  	// remains valid until close is called.
    22  	newSingleSourceCache(id ProjectIdentifier) singleSourceCache
    23  	// close releases background resources.
    24  	close() error
    25  }
    26  
    27  // singleSourceCache provides a method set for storing and retrieving data about
    28  // a single source.
    29  type singleSourceCache interface {
    30  	// Store the manifest and lock information for a given revision, as defined by
    31  	// a particular ProjectAnalyzer.
    32  	setManifestAndLock(Revision, ProjectAnalyzerInfo, Manifest, Lock)
    33  
    34  	// Get the manifest and lock information for a given revision, as defined by
    35  	// a particular ProjectAnalyzer.
    36  	getManifestAndLock(Revision, ProjectAnalyzerInfo) (Manifest, Lock, bool)
    37  
    38  	// Store a PackageTree for a given revision.
    39  	setPackageTree(Revision, pkgtree.PackageTree)
    40  
    41  	// Get the PackageTree for a given revision.
    42  	getPackageTree(Revision, ProjectRoot) (pkgtree.PackageTree, bool)
    43  
    44  	// Indicate to the cache that an individual revision is known to exist.
    45  	markRevisionExists(r Revision)
    46  
    47  	// Store the mappings between a set of PairedVersions' surface versions
    48  	// their corresponding revisions.
    49  	//
    50  	// The existing list of versions will be purged before writing. Revisions
    51  	// will have their pairings purged, but record of the revision existing will
    52  	// be kept, on the assumption that revisions are immutable and permanent.
    53  	setVersionMap(versionList []PairedVersion)
    54  
    55  	// Get the list of unpaired versions corresponding to the given revision.
    56  	getVersionsFor(Revision) ([]UnpairedVersion, bool)
    57  
    58  	// Gets all the version pairs currently known to the cache.
    59  	getAllVersions() ([]PairedVersion, bool)
    60  
    61  	// Get the revision corresponding to the given unpaired version.
    62  	getRevisionFor(UnpairedVersion) (Revision, bool)
    63  
    64  	// Attempt to convert the given Version to a Revision, given information
    65  	// currently present in the cache, and in the Version itself.
    66  	toRevision(v Version) (Revision, bool)
    67  
    68  	// Attempt to convert the given Version to an UnpairedVersion, given
    69  	// information currently present in the cache, or in the Version itself.
    70  	//
    71  	// If the input is a revision and multiple UnpairedVersions are associated
    72  	// with it, whatever happens to be the first is returned.
    73  	toUnpaired(v Version) (UnpairedVersion, bool)
    74  }
    75  
    76  // memoryCache is a sourceCache which creates singleSourceCacheMemory instances.
    77  type memoryCache struct{}
    78  
    79  func (memoryCache) newSingleSourceCache(ProjectIdentifier) singleSourceCache {
    80  	return newMemoryCache()
    81  }
    82  
    83  func (memoryCache) close() error { return nil }
    84  
    85  type singleSourceCacheMemory struct {
    86  	// Protects all fields.
    87  	mut   sync.RWMutex
    88  	infos map[ProjectAnalyzerInfo]map[Revision]projectInfo
    89  	// Replaced, never modified. Imports are *relative* (ImportRoot prefix trimmed).
    90  	ptrees map[Revision]map[string]pkgtree.PackageOrErr
    91  	// Replaced, never modified.
    92  	vList []PairedVersion
    93  	vMap  map[UnpairedVersion]Revision
    94  	rMap  map[Revision][]UnpairedVersion
    95  }
    96  
    97  func newMemoryCache() singleSourceCache {
    98  	return &singleSourceCacheMemory{
    99  		infos:  make(map[ProjectAnalyzerInfo]map[Revision]projectInfo),
   100  		ptrees: make(map[Revision]map[string]pkgtree.PackageOrErr),
   101  		vMap:   make(map[UnpairedVersion]Revision),
   102  		rMap:   make(map[Revision][]UnpairedVersion),
   103  	}
   104  }
   105  
   106  type projectInfo struct {
   107  	Manifest
   108  	Lock
   109  }
   110  
   111  func (c *singleSourceCacheMemory) setManifestAndLock(r Revision, pai ProjectAnalyzerInfo, m Manifest, l Lock) {
   112  	c.mut.Lock()
   113  	inner, has := c.infos[pai]
   114  	if !has {
   115  		inner = make(map[Revision]projectInfo)
   116  		c.infos[pai] = inner
   117  	}
   118  	inner[r] = projectInfo{Manifest: m, Lock: l}
   119  
   120  	// Ensure there's at least an entry in the rMap so that the rMap always has
   121  	// a complete picture of the revisions we know to exist
   122  	if _, has = c.rMap[r]; !has {
   123  		c.rMap[r] = nil
   124  	}
   125  	c.mut.Unlock()
   126  }
   127  
   128  func (c *singleSourceCacheMemory) getManifestAndLock(r Revision, pai ProjectAnalyzerInfo) (Manifest, Lock, bool) {
   129  	c.mut.Lock()
   130  	defer c.mut.Unlock()
   131  
   132  	inner, has := c.infos[pai]
   133  	if !has {
   134  		return nil, nil, false
   135  	}
   136  
   137  	pi, has := inner[r]
   138  	if has {
   139  		return pi.Manifest, pi.Lock, true
   140  	}
   141  	return nil, nil, false
   142  }
   143  
   144  func (c *singleSourceCacheMemory) setPackageTree(r Revision, ptree pkgtree.PackageTree) {
   145  	// Make a copy, with relative import paths.
   146  	pkgs := pkgtree.CopyPackages(ptree.Packages, func(ip string, poe pkgtree.PackageOrErr) (string, pkgtree.PackageOrErr) {
   147  		poe.P.ImportPath = "" // Don't store this
   148  		return strings.TrimPrefix(ip, ptree.ImportRoot), poe
   149  	})
   150  
   151  	c.mut.Lock()
   152  	c.ptrees[r] = pkgs
   153  
   154  	// Ensure there's at least an entry in the rMap so that the rMap always has
   155  	// a complete picture of the revisions we know to exist
   156  	if _, has := c.rMap[r]; !has {
   157  		c.rMap[r] = nil
   158  	}
   159  	c.mut.Unlock()
   160  }
   161  
   162  func (c *singleSourceCacheMemory) getPackageTree(r Revision, pr ProjectRoot) (pkgtree.PackageTree, bool) {
   163  	c.mut.Lock()
   164  	rptree, has := c.ptrees[r]
   165  	c.mut.Unlock()
   166  
   167  	if !has {
   168  		return pkgtree.PackageTree{}, false
   169  	}
   170  
   171  	// Return a copy, with full import paths.
   172  	pkgs := pkgtree.CopyPackages(rptree, func(rpath string, poe pkgtree.PackageOrErr) (string, pkgtree.PackageOrErr) {
   173  		ip := path.Join(string(pr), rpath)
   174  		if poe.Err == nil {
   175  			poe.P.ImportPath = ip
   176  		}
   177  		return ip, poe
   178  	})
   179  
   180  	return pkgtree.PackageTree{
   181  		ImportRoot: string(pr),
   182  		Packages:   pkgs,
   183  	}, true
   184  }
   185  
   186  func (c *singleSourceCacheMemory) setVersionMap(versionList []PairedVersion) {
   187  	c.mut.Lock()
   188  	c.vList = versionList
   189  	// TODO(sdboyer) how do we handle cache consistency here - revs that may
   190  	// be out of date vis-a-vis the ptrees or infos maps?
   191  	for r := range c.rMap {
   192  		c.rMap[r] = nil
   193  	}
   194  
   195  	c.vMap = make(map[UnpairedVersion]Revision, len(versionList))
   196  
   197  	for _, pv := range versionList {
   198  		u, r := pv.Unpair(), pv.Revision()
   199  		c.vMap[u] = r
   200  		c.rMap[r] = append(c.rMap[r], u)
   201  	}
   202  	c.mut.Unlock()
   203  }
   204  
   205  func (c *singleSourceCacheMemory) markRevisionExists(r Revision) {
   206  	c.mut.Lock()
   207  	if _, has := c.rMap[r]; !has {
   208  		c.rMap[r] = nil
   209  	}
   210  	c.mut.Unlock()
   211  }
   212  
   213  func (c *singleSourceCacheMemory) getVersionsFor(r Revision) ([]UnpairedVersion, bool) {
   214  	c.mut.Lock()
   215  	versionList, has := c.rMap[r]
   216  	c.mut.Unlock()
   217  	return versionList, has
   218  }
   219  
   220  func (c *singleSourceCacheMemory) getAllVersions() ([]PairedVersion, bool) {
   221  	c.mut.Lock()
   222  	vList := c.vList
   223  	c.mut.Unlock()
   224  
   225  	if vList == nil {
   226  		return nil, false
   227  	}
   228  	cp := make([]PairedVersion, len(vList))
   229  	copy(cp, vList)
   230  	return cp, true
   231  }
   232  
   233  func (c *singleSourceCacheMemory) getRevisionFor(uv UnpairedVersion) (Revision, bool) {
   234  	c.mut.Lock()
   235  	r, has := c.vMap[uv]
   236  	c.mut.Unlock()
   237  	return r, has
   238  }
   239  
   240  func (c *singleSourceCacheMemory) toRevision(v Version) (Revision, bool) {
   241  	switch t := v.(type) {
   242  	case Revision:
   243  		return t, true
   244  	case PairedVersion:
   245  		return t.Revision(), true
   246  	case UnpairedVersion:
   247  		c.mut.Lock()
   248  		r, has := c.vMap[t]
   249  		c.mut.Unlock()
   250  		return r, has
   251  	default:
   252  		panic(fmt.Sprintf("Unknown version type %T", v))
   253  	}
   254  }
   255  
   256  func (c *singleSourceCacheMemory) toUnpaired(v Version) (UnpairedVersion, bool) {
   257  	switch t := v.(type) {
   258  	case UnpairedVersion:
   259  		return t, true
   260  	case PairedVersion:
   261  		return t.Unpair(), true
   262  	case Revision:
   263  		c.mut.Lock()
   264  		upv, has := c.rMap[t]
   265  		c.mut.Unlock()
   266  
   267  		if has && len(upv) > 0 {
   268  			return upv[0], true
   269  		}
   270  		return nil, false
   271  	default:
   272  		panic(fmt.Sprintf("unknown version type %T", v))
   273  	}
   274  }
   275  
   276  // TODO(sdboyer) remove once source caching can be moved into separate package
   277  func locksAreEq(l1, l2 Lock) bool {
   278  	ii1, ii2 := l1.InputImports(), l2.InputImports()
   279  	if len(ii1) != len(ii2) {
   280  		return false
   281  	}
   282  
   283  	ilen := len(ii1)
   284  	if ilen > 0 {
   285  		sort.Strings(ii1)
   286  		sort.Strings(ii2)
   287  		for i := 0; i < ilen; i++ {
   288  			if ii1[i] != ii2[i] {
   289  				return false
   290  			}
   291  		}
   292  	}
   293  
   294  	p1, p2 := l1.Projects(), l2.Projects()
   295  	if len(p1) != len(p2) {
   296  		return false
   297  	}
   298  
   299  	p1, p2 = sortLockedProjects(p1), sortLockedProjects(p2)
   300  
   301  	for k, lp := range p1 {
   302  		if !lp.Eq(p2[k]) {
   303  			return false
   304  		}
   305  	}
   306  	return true
   307  }