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  }