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 }