github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/cmd/go/internal/modfetch/repo.go (about) 1 // Copyright 2018 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 modfetch 6 7 import ( 8 "fmt" 9 "io" 10 "os" 11 "sort" 12 "strconv" 13 "time" 14 15 "cmd/go/internal/cfg" 16 "cmd/go/internal/get" 17 "cmd/go/internal/modfetch/codehost" 18 "cmd/go/internal/par" 19 "cmd/go/internal/semver" 20 web "cmd/go/internal/web" 21 ) 22 23 const traceRepo = false // trace all repo actions, for debugging 24 25 // A Repo represents a repository storing all versions of a single module. 26 // It must be safe for simultaneous use by multiple goroutines. 27 type Repo interface { 28 // ModulePath returns the module path. 29 ModulePath() string 30 31 // Versions lists all known versions with the given prefix. 32 // Pseudo-versions are not included. 33 // Versions should be returned sorted in semver order 34 // (implementations can use SortVersions). 35 Versions(prefix string) (tags []string, err error) 36 37 // Stat returns information about the revision rev. 38 // A revision can be any identifier known to the underlying service: 39 // commit hash, branch, tag, and so on. 40 Stat(rev string) (*RevInfo, error) 41 42 // Latest returns the latest revision on the default branch, 43 // whatever that means in the underlying source code repository. 44 // It is only used when there are no tagged versions. 45 Latest() (*RevInfo, error) 46 47 // GoMod returns the go.mod file for the given version. 48 GoMod(version string) (data []byte, err error) 49 50 // Zip writes a zip file for the given version to dst. 51 Zip(dst io.Writer, version string) error 52 } 53 54 // A Rev describes a single revision in a module repository. 55 type RevInfo struct { 56 Version string // version string 57 Time time.Time // commit time 58 59 // These fields are used for Stat of arbitrary rev, 60 // but they are not recorded when talking about module versions. 61 Name string `json:"-"` // complete ID in underlying repository 62 Short string `json:"-"` // shortened ID, for use in pseudo-version 63 } 64 65 // Re: module paths, import paths, repository roots, and lookups 66 // 67 // A module is a collection of Go packages stored in a file tree 68 // with a go.mod file at the root of the tree. 69 // The go.mod defines the module path, which is the import path 70 // corresponding to the root of the file tree. 71 // The import path of a directory within that file tree is the module path 72 // joined with the name of the subdirectory relative to the root. 73 // 74 // For example, the module with path rsc.io/qr corresponds to the 75 // file tree in the repository https://github.com/rsc/qr. 76 // That file tree has a go.mod that says "module rsc.io/qr". 77 // The package in the root directory has import path "rsc.io/qr". 78 // The package in the gf256 subdirectory has import path "rsc.io/qr/gf256". 79 // In this example, "rsc.io/qr" is both a module path and an import path. 80 // But "rsc.io/qr/gf256" is only an import path, not a module path: 81 // it names an importable package, but not a module. 82 // 83 // As a special case to incorporate code written before modules were 84 // introduced, if a path p resolves using the pre-module "go get" lookup 85 // to the root of a source code repository without a go.mod file, 86 // that repository is treated as if it had a go.mod in its root directory 87 // declaring module path p. (The go.mod is further considered to 88 // contain requirements corresponding to any legacy version 89 // tracking format such as Gopkg.lock, vendor/vendor.conf, and so on.) 90 // 91 // The presentation so far ignores the fact that a source code repository 92 // has many different versions of a file tree, and those versions may 93 // differ in whether a particular go.mod exists and what it contains. 94 // In fact there is a well-defined mapping only from a module path, version 95 // pair - often written path@version - to a particular file tree. 96 // For example rsc.io/qr@v0.1.0 depends on the "implicit go.mod at root of 97 // repository" rule, while rsc.io/qr@v0.2.0 has an explicit go.mod. 98 // Because the "go get" import paths rsc.io/qr and github.com/rsc/qr 99 // both redirect to the Git repository https://github.com/rsc/qr, 100 // github.com/rsc/qr@v0.1.0 is the same file tree as rsc.io/qr@v0.1.0 101 // but a different module (a different name). In contrast, since v0.2.0 102 // of that repository has an explicit go.mod that declares path rsc.io/qr, 103 // github.com/rsc/qr@v0.2.0 is an invalid module path, version pair. 104 // Before modules, import comments would have had the same effect. 105 // 106 // The set of import paths associated with a given module path is 107 // clearly not fixed: at the least, new directories with new import paths 108 // can always be added. But another potential operation is to split a 109 // subtree out of a module into its own module. If done carefully, 110 // this operation can be done while preserving compatibility for clients. 111 // For example, suppose that we want to split rsc.io/qr/gf256 into its 112 // own module, so that there would be two modules rsc.io/qr and rsc.io/qr/gf256. 113 // Then we can simultaneously issue rsc.io/qr v0.3.0 (dropping the gf256 subdirectory) 114 // and rsc.io/qr/gf256 v0.1.0, including in their respective go.mod 115 // cyclic requirements pointing at each other: rsc.io/qr v0.3.0 requires 116 // rsc.io/qr/gf256 v0.1.0 and vice versa. Then a build can be 117 // using an older rsc.io/qr module that includes the gf256 package, but if 118 // it adds a requirement on either the newer rsc.io/qr or the newer 119 // rsc.io/qr/gf256 module, it will automatically add the requirement 120 // on the complementary half, ensuring both that rsc.io/qr/gf256 is 121 // available for importing by the build and also that it is only defined 122 // by a single module. The gf256 package could move back into the 123 // original by another simultaneous release of rsc.io/qr v0.4.0 including 124 // the gf256 subdirectory and an rsc.io/qr/gf256 v0.2.0 with no code 125 // in its root directory, along with a new requirement cycle. 126 // The ability to shift module boundaries in this way is expected to be 127 // important in large-scale program refactorings, similar to the ones 128 // described in https://talks.golang.org/2016/refactor.article. 129 // 130 // The possibility of shifting module boundaries reemphasizes 131 // that you must know both the module path and its version 132 // to determine the set of packages provided directly by that module. 133 // 134 // On top of all this, it is possible for a single code repository 135 // to contain multiple modules, either in branches or subdirectories, 136 // as a limited kind of monorepo. For example rsc.io/qr/v2, 137 // the v2.x.x continuation of rsc.io/qr, is expected to be found 138 // in v2-tagged commits in https://github.com/rsc/qr, either 139 // in the root or in a v2 subdirectory, disambiguated by go.mod. 140 // Again the precise file tree corresponding to a module 141 // depends on which version we are considering. 142 // 143 // It is also possible for the underlying repository to change over time, 144 // without changing the module path. If I copy the github repo over 145 // to https://bitbucket.org/rsc/qr and update https://rsc.io/qr?go-get=1, 146 // then clients of all versions should start fetching from bitbucket 147 // instead of github. That is, in contrast to the exact file tree, 148 // the location of the source code repository associated with a module path 149 // does not depend on the module version. (This is by design, as the whole 150 // point of these redirects is to allow package authors to establish a stable 151 // name that can be updated as code moves from one service to another.) 152 // 153 // All of this is important background for the lookup APIs defined in this 154 // file. 155 // 156 // The Lookup function takes a module path and returns a Repo representing 157 // that module path. Lookup can do only a little with the path alone. 158 // It can check that the path is well-formed (see semver.CheckPath) 159 // and it can check that the path can be resolved to a target repository. 160 // To avoid version control access except when absolutely necessary, 161 // Lookup does not attempt to connect to the repository itself. 162 // 163 // The Import function takes an import path found in source code and 164 // determines which module to add to the requirement list to satisfy 165 // that import. It checks successive truncations of the import path 166 // to determine possible modules and stops when it finds a module 167 // in which the latest version satisfies the import path. 168 // 169 // The ImportRepoRev function is a variant of Import which is limited 170 // to code in a source code repository at a particular revision identifier 171 // (usually a commit hash or source code repository tag, not necessarily 172 // a module version). 173 // ImportRepoRev is used when converting legacy dependency requirements 174 // from older systems into go.mod files. Those older systems worked 175 // at either package or repository granularity, and most of the time they 176 // recorded commit hashes, not tagged versions. 177 178 var lookupCache par.Cache 179 180 // Lookup returns the module with the given module path. 181 // A successful return does not guarantee that the module 182 // has any defined versions. 183 func Lookup(path string) (Repo, error) { 184 if traceRepo { 185 defer logCall("Lookup(%q)", path)() 186 } 187 188 type cached struct { 189 r Repo 190 err error 191 } 192 c := lookupCache.Do(path, func() interface{} { 193 r, err := lookup(path) 194 if err == nil { 195 if traceRepo { 196 r = newLoggingRepo(r) 197 } 198 r = newCachingRepo(r) 199 } 200 return cached{r, err} 201 }).(cached) 202 203 return c.r, c.err 204 } 205 206 // lookup returns the module with the given module path. 207 func lookup(path string) (r Repo, err error) { 208 if cfg.BuildMod == "vendor" { 209 return nil, fmt.Errorf("module lookup disabled by -mod=%s", cfg.BuildMod) 210 } 211 if proxyURL == "off" { 212 return nil, fmt.Errorf("module lookup disabled by GOPROXY=%s", proxyURL) 213 } 214 if proxyURL != "" && proxyURL != "direct" { 215 return lookupProxy(path) 216 } 217 218 security := web.Secure 219 if get.Insecure { 220 security = web.Insecure 221 } 222 rr, err := get.RepoRootForImportPath(path, get.PreferMod, security) 223 if err != nil { 224 // We don't know where to find code for a module with this path. 225 return nil, err 226 } 227 228 if rr.VCS == "mod" { 229 // Fetch module from proxy with base URL rr.Repo. 230 return newProxyRepo(rr.Repo, path) 231 } 232 233 code, err := lookupCodeRepo(rr) 234 if err != nil { 235 return nil, err 236 } 237 return newCodeRepo(code, rr.Root, path) 238 } 239 240 func lookupCodeRepo(rr *get.RepoRoot) (codehost.Repo, error) { 241 code, err := codehost.NewRepo(rr.VCS, rr.Repo) 242 if err != nil { 243 if _, ok := err.(*codehost.VCSError); ok { 244 return nil, err 245 } 246 return nil, fmt.Errorf("lookup %s: %v", rr.Root, err) 247 } 248 return code, nil 249 } 250 251 // ImportRepoRev returns the module and version to use to access 252 // the given import path loaded from the source code repository that 253 // the original "go get" would have used, at the specific repository revision 254 // (typically a commit hash, but possibly also a source control tag). 255 func ImportRepoRev(path, rev string) (Repo, *RevInfo, error) { 256 if cfg.BuildMod == "vendor" || cfg.BuildMod == "readonly" { 257 return nil, nil, fmt.Errorf("repo version lookup disabled by -mod=%s", cfg.BuildMod) 258 } 259 260 // Note: Because we are converting a code reference from a legacy 261 // version control system, we ignore meta tags about modules 262 // and use only direct source control entries (get.IgnoreMod). 263 security := web.Secure 264 if get.Insecure { 265 security = web.Insecure 266 } 267 rr, err := get.RepoRootForImportPath(path, get.IgnoreMod, security) 268 if err != nil { 269 return nil, nil, err 270 } 271 272 code, err := lookupCodeRepo(rr) 273 if err != nil { 274 return nil, nil, err 275 } 276 277 revInfo, err := code.Stat(rev) 278 if err != nil { 279 return nil, nil, err 280 } 281 282 // TODO: Look in repo to find path, check for go.mod files. 283 // For now we're just assuming rr.Root is the module path, 284 // which is true in the absence of go.mod files. 285 286 repo, err := newCodeRepo(code, rr.Root, rr.Root) 287 if err != nil { 288 return nil, nil, err 289 } 290 291 info, err := repo.(*codeRepo).convert(revInfo, "") 292 if err != nil { 293 return nil, nil, err 294 } 295 return repo, info, nil 296 } 297 298 func SortVersions(list []string) { 299 sort.Slice(list, func(i, j int) bool { 300 cmp := semver.Compare(list[i], list[j]) 301 if cmp != 0 { 302 return cmp < 0 303 } 304 return list[i] < list[j] 305 }) 306 } 307 308 // A loggingRepo is a wrapper around an underlying Repo 309 // that prints a log message at the start and end of each call. 310 // It can be inserted when debugging. 311 type loggingRepo struct { 312 r Repo 313 } 314 315 func newLoggingRepo(r Repo) *loggingRepo { 316 return &loggingRepo{r} 317 } 318 319 // logCall prints a log message using format and args and then 320 // also returns a function that will print the same message again, 321 // along with the elapsed time. 322 // Typical usage is: 323 // 324 // defer logCall("hello %s", arg)() 325 // 326 // Note the final (). 327 func logCall(format string, args ...interface{}) func() { 328 start := time.Now() 329 fmt.Fprintf(os.Stderr, "+++ %s\n", fmt.Sprintf(format, args...)) 330 return func() { 331 fmt.Fprintf(os.Stderr, "%.3fs %s\n", time.Since(start).Seconds(), fmt.Sprintf(format, args...)) 332 } 333 } 334 335 func (l *loggingRepo) ModulePath() string { 336 return l.r.ModulePath() 337 } 338 339 func (l *loggingRepo) Versions(prefix string) (tags []string, err error) { 340 defer logCall("Repo[%s]: Versions(%q)", l.r.ModulePath(), prefix)() 341 return l.r.Versions(prefix) 342 } 343 344 func (l *loggingRepo) Stat(rev string) (*RevInfo, error) { 345 defer logCall("Repo[%s]: Stat(%q)", l.r.ModulePath(), rev)() 346 return l.r.Stat(rev) 347 } 348 349 func (l *loggingRepo) Latest() (*RevInfo, error) { 350 defer logCall("Repo[%s]: Latest()", l.r.ModulePath())() 351 return l.r.Latest() 352 } 353 354 func (l *loggingRepo) GoMod(version string) ([]byte, error) { 355 defer logCall("Repo[%s]: GoMod(%q)", l.r.ModulePath(), version)() 356 return l.r.GoMod(version) 357 } 358 359 func (l *loggingRepo) Zip(dst io.Writer, version string) error { 360 dstName := "_" 361 if dst, ok := dst.(interface{ Name() string }); ok { 362 dstName = strconv.Quote(dst.Name()) 363 } 364 defer logCall("Repo[%s]: Zip(%s, %q)", l.r.ModulePath(), dstName, version)() 365 return l.r.Zip(dst, version) 366 }