| package imports |
| |
| import ( |
| "sync" |
| ) |
| |
| // ModuleResolver implements Resolver for modules using the go command as little |
| // as feasible. |
| // |
| // To find packages to import, the resolver needs to know about all of the |
| // the packages that could be imported. This includes packages that are |
| // already in modules that are in (1) the current module, (2) replace targets, |
| // and (3) packages in the module cache. Packages in (1) and (2) may change over |
| // time, as the client may edit the current module and locally replaced modules. |
| // The module cache (which includes all of the packages in (3)) can only |
| // ever be added to. |
| // |
| // The resolver can thus save state about packages in the module cache |
| // and guarantee that this will not change over time. To obtain information |
| // about new modules added to the module cache, the module cache should be |
| // rescanned. |
| // |
| // It is OK to serve information about modules that have been deleted, |
| // as they do still exist. |
| // TODO(suzmue): can we share information with the caller about |
| // what module needs to be downloaded to import this package? |
| |
| type directoryPackageStatus int |
| |
| const ( |
| _ directoryPackageStatus = iota |
| directoryScanned |
| ) |
| |
| type directoryPackageInfo struct { |
| // status indicates the extent to which this struct has been filled in. |
| status directoryPackageStatus |
| // err is non-nil when there was an error trying to reach status. |
| err error |
| |
| // Set when status > directoryScanned. |
| |
| // dir is the absolute directory of this package. |
| dir string |
| // nonCanonicalImportPath is the expected import path for this package. |
| // This may not be an import path that can be used to import this package. |
| nonCanonicalImportPath string |
| // needsReplace is true if the nonCanonicalImportPath does not match the |
| // the modules declared path, making it impossible to import without a |
| // replace directive. |
| needsReplace bool |
| } |
| |
| // reachedStatus returns true when info has a status at least target and any error associated with |
| // an attempt to reach target. |
| func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (bool, error) { |
| if info.err == nil { |
| return info.status >= target, nil |
| } |
| if info.status == target { |
| return true, info.err |
| } |
| return true, nil |
| } |
| |
| // moduleCacheInfo is a concurrency safe map for storing information about |
| // the directories in the module cache. |
| // |
| // The information in this cache is built incrementally. Entries are initialized in scan. |
| // No new keys should be added in any other functions, as all directories containing |
| // packages are identified in scan. |
| // |
| // Other functions, including loadExports and findPackage, may update entries in this cache |
| // as they discover new things about the directory. |
| // |
| // We do not need to protect the data in the cache for multiple writes, because it only stores |
| // module cache directories, which do not change. If two competing stores take place, there will be |
| // one store that wins. Although this could result in a loss of information it will not be incorrect |
| // and may just result in recomputing the same result later. |
| // |
| // TODO(suzmue): consider other concurrency strategies and data structures (RWLocks, sync.Map, etc) |
| type moduleCacheInfo struct { |
| mu sync.Mutex |
| // modCacheDirInfo stores information about packages in |
| // module cache directories. Keyed by absolute directory. |
| modCacheDirInfo map[string]*directoryPackageInfo |
| } |
| |
| // Store stores the package info for dir. |
| func (d *moduleCacheInfo) Store(dir string, info directoryPackageInfo) { |
| d.mu.Lock() |
| defer d.mu.Unlock() |
| d.modCacheDirInfo[dir] = &directoryPackageInfo{ |
| status: info.status, |
| err: info.err, |
| dir: info.dir, |
| nonCanonicalImportPath: info.nonCanonicalImportPath, |
| needsReplace: info.needsReplace, |
| } |
| } |
| |
| // Load returns a copy of the directoryPackageInfo for absolute directory dir. |
| func (d *moduleCacheInfo) Load(dir string) (directoryPackageInfo, bool) { |
| d.mu.Lock() |
| defer d.mu.Unlock() |
| info, ok := d.modCacheDirInfo[dir] |
| if !ok { |
| return directoryPackageInfo{}, false |
| } |
| return *info, true |
| } |
| |
| // Keys returns the keys currently present in d. |
| func (d *moduleCacheInfo) Keys() (keys []string) { |
| d.mu.Lock() |
| defer d.mu.Unlock() |
| for key := range d.modCacheDirInfo { |
| keys = append(keys, key) |
| } |
| return keys |
| } |