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