Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/go/internal/get/get.go

Documentation: cmd/go/internal/get

     1  // Copyright 2011 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 get implements the ``go get'' command.
     6  package get
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"runtime"
    14  	"strings"
    15  
    16  	"cmd/go/internal/base"
    17  	"cmd/go/internal/cfg"
    18  	"cmd/go/internal/load"
    19  	"cmd/go/internal/search"
    20  	"cmd/go/internal/str"
    21  	"cmd/go/internal/vcs"
    22  	"cmd/go/internal/web"
    23  	"cmd/go/internal/work"
    24  
    25  	"golang.org/x/mod/module"
    26  )
    27  
    28  var CmdGet = &base.Command{
    29  	UsageLine: "go get [-d] [-f] [-t] [-u] [-v] [-fix] [build flags] [packages]",
    30  	Short:     "download and install packages and dependencies",
    31  	Long: `
    32  Get downloads the packages named by the import paths, along with their
    33  dependencies. It then installs the named packages, like 'go install'.
    34  
    35  The -d flag instructs get to stop after downloading the packages; that is,
    36  it instructs get not to install the packages.
    37  
    38  The -f flag, valid only when -u is set, forces get -u not to verify that
    39  each package has been checked out from the source control repository
    40  implied by its import path. This can be useful if the source is a local fork
    41  of the original.
    42  
    43  The -fix flag instructs get to run the fix tool on the downloaded packages
    44  before resolving dependencies or building the code.
    45  
    46  The -t flag instructs get to also download the packages required to build
    47  the tests for the specified packages.
    48  
    49  The -u flag instructs get to use the network to update the named packages
    50  and their dependencies. By default, get uses the network to check out
    51  missing packages but does not use it to look for updates to existing packages.
    52  
    53  The -v flag enables verbose progress and debug output.
    54  
    55  Get also accepts build flags to control the installation. See 'go help build'.
    56  
    57  When checking out a new package, get creates the target directory
    58  GOPATH/src/<import-path>. If the GOPATH contains multiple entries,
    59  get uses the first one. For more details see: 'go help gopath'.
    60  
    61  When checking out or updating a package, get looks for a branch or tag
    62  that matches the locally installed version of Go. The most important
    63  rule is that if the local installation is running version "go1", get
    64  searches for a branch or tag named "go1". If no such version exists
    65  it retrieves the default branch of the package.
    66  
    67  When go get checks out or updates a Git repository,
    68  it also updates any git submodules referenced by the repository.
    69  
    70  Get never checks out or updates code stored in vendor directories.
    71  
    72  For more about specifying packages, see 'go help packages'.
    73  
    74  For more about how 'go get' finds source code to
    75  download, see 'go help importpath'.
    76  
    77  This text describes the behavior of get when using GOPATH
    78  to manage source code and dependencies.
    79  If instead the go command is running in module-aware mode,
    80  the details of get's flags and effects change, as does 'go help get'.
    81  See 'go help modules' and 'go help module-get'.
    82  
    83  See also: go build, go install, go clean.
    84  	`,
    85  }
    86  
    87  var HelpGopathGet = &base.Command{
    88  	UsageLine: "gopath-get",
    89  	Short:     "legacy GOPATH go get",
    90  	Long: `
    91  The 'go get' command changes behavior depending on whether the
    92  go command is running in module-aware mode or legacy GOPATH mode.
    93  This help text, accessible as 'go help gopath-get' even in module-aware mode,
    94  describes 'go get' as it operates in legacy GOPATH mode.
    95  
    96  Usage: ` + CmdGet.UsageLine + `
    97  ` + CmdGet.Long,
    98  }
    99  
   100  var (
   101  	getD        = CmdGet.Flag.Bool("d", false, "")
   102  	getF        = CmdGet.Flag.Bool("f", false, "")
   103  	getT        = CmdGet.Flag.Bool("t", false, "")
   104  	getU        = CmdGet.Flag.Bool("u", false, "")
   105  	getFix      = CmdGet.Flag.Bool("fix", false, "")
   106  	getInsecure = CmdGet.Flag.Bool("insecure", false, "")
   107  )
   108  
   109  func init() {
   110  	work.AddBuildFlags(CmdGet, work.OmitModFlag|work.OmitModCommonFlags)
   111  	CmdGet.Run = runGet // break init loop
   112  }
   113  
   114  func runGet(ctx context.Context, cmd *base.Command, args []string) {
   115  	if cfg.ModulesEnabled {
   116  		// Should not happen: main.go should install the separate module-enabled get code.
   117  		base.Fatalf("go get: modules not implemented")
   118  	}
   119  
   120  	work.BuildInit()
   121  
   122  	if *getF && !*getU {
   123  		base.Fatalf("go get: cannot use -f flag without -u")
   124  	}
   125  	if *getInsecure {
   126  		base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead")
   127  	}
   128  
   129  	// Disable any prompting for passwords by Git itself.
   130  	// Only has an effect for 2.3.0 or later, but avoiding
   131  	// the prompt in earlier versions is just too hard.
   132  	// If user has explicitly set GIT_TERMINAL_PROMPT=1, keep
   133  	// prompting.
   134  	// See golang.org/issue/9341 and golang.org/issue/12706.
   135  	if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
   136  		os.Setenv("GIT_TERMINAL_PROMPT", "0")
   137  	}
   138  
   139  	// Also disable prompting for passwords by the 'ssh' subprocess spawned by
   140  	// Git, because apparently GIT_TERMINAL_PROMPT isn't sufficient to do that.
   141  	// Adding '-o BatchMode=yes' should do the trick.
   142  	//
   143  	// If a Git subprocess forks a child into the background to cache a new connection,
   144  	// that child keeps stdout/stderr open. After the Git subprocess exits,
   145  	// os /exec expects to be able to read from the stdout/stderr pipe
   146  	// until EOF to get all the data that the Git subprocess wrote before exiting.
   147  	// The EOF doesn't come until the child exits too, because the child
   148  	// is holding the write end of the pipe.
   149  	// This is unfortunate, but it has come up at least twice
   150  	// (see golang.org/issue/13453 and golang.org/issue/16104)
   151  	// and confuses users when it does.
   152  	// If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND,
   153  	// assume they know what they are doing and don't step on it.
   154  	// But default to turning off ControlMaster.
   155  	if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
   156  		os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no -o BatchMode=yes")
   157  	}
   158  
   159  	// And one more source of Git prompts: the Git Credential Manager Core for Windows.
   160  	//
   161  	// See https://github.com/microsoft/Git-Credential-Manager-Core/blob/master/docs/environment.md#gcm_interactive.
   162  	if os.Getenv("GCM_INTERACTIVE") == "" {
   163  		os.Setenv("GCM_INTERACTIVE", "never")
   164  	}
   165  
   166  	// Phase 1. Download/update.
   167  	var stk load.ImportStack
   168  	mode := 0
   169  	if *getT {
   170  		mode |= load.GetTestDeps
   171  	}
   172  	for _, pkg := range downloadPaths(args) {
   173  		download(pkg, nil, &stk, mode)
   174  	}
   175  	base.ExitIfErrors()
   176  
   177  	// Phase 2. Rescan packages and re-evaluate args list.
   178  
   179  	// Code we downloaded and all code that depends on it
   180  	// needs to be evicted from the package cache so that
   181  	// the information will be recomputed. Instead of keeping
   182  	// track of the reverse dependency information, evict
   183  	// everything.
   184  	load.ClearPackageCache()
   185  
   186  	pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args)
   187  	load.CheckPackageErrors(pkgs)
   188  
   189  	// Phase 3. Install.
   190  	if *getD {
   191  		// Download only.
   192  		// Check delayed until now so that downloadPaths
   193  		// and CheckPackageErrors have a chance to print errors.
   194  		return
   195  	}
   196  
   197  	work.InstallPackages(ctx, args, pkgs)
   198  }
   199  
   200  // downloadPaths prepares the list of paths to pass to download.
   201  // It expands ... patterns that can be expanded. If there is no match
   202  // for a particular pattern, downloadPaths leaves it in the result list,
   203  // in the hope that we can figure out the repository from the
   204  // initial ...-free prefix.
   205  func downloadPaths(patterns []string) []string {
   206  	for _, arg := range patterns {
   207  		if strings.Contains(arg, "@") {
   208  			base.Fatalf("go: can only use path@version syntax with 'go get' and 'go install' in module-aware mode")
   209  			continue
   210  		}
   211  
   212  		// Guard against 'go get x.go', a common mistake.
   213  		// Note that package and module paths may end with '.go', so only print an error
   214  		// if the argument has no slash or refers to an existing file.
   215  		if strings.HasSuffix(arg, ".go") {
   216  			if !strings.Contains(arg, "/") {
   217  				base.Errorf("go get %s: arguments must be package or module paths", arg)
   218  				continue
   219  			}
   220  			if fi, err := os.Stat(arg); err == nil && !fi.IsDir() {
   221  				base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", arg)
   222  			}
   223  		}
   224  	}
   225  	base.ExitIfErrors()
   226  
   227  	var pkgs []string
   228  	for _, m := range search.ImportPathsQuiet(patterns) {
   229  		if len(m.Pkgs) == 0 && strings.Contains(m.Pattern(), "...") {
   230  			pkgs = append(pkgs, m.Pattern())
   231  		} else {
   232  			pkgs = append(pkgs, m.Pkgs...)
   233  		}
   234  	}
   235  	return pkgs
   236  }
   237  
   238  // downloadCache records the import paths we have already
   239  // considered during the download, to avoid duplicate work when
   240  // there is more than one dependency sequence leading to
   241  // a particular package.
   242  var downloadCache = map[string]bool{}
   243  
   244  // downloadRootCache records the version control repository
   245  // root directories we have already considered during the download.
   246  // For example, all the packages in the github.com/google/codesearch repo
   247  // share the same root (the directory for that path), and we only need
   248  // to run the hg commands to consider each repository once.
   249  var downloadRootCache = map[string]bool{}
   250  
   251  // download runs the download half of the get command
   252  // for the package or pattern named by the argument.
   253  func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) {
   254  	if mode&load.ResolveImport != 0 {
   255  		// Caller is responsible for expanding vendor paths.
   256  		panic("internal error: download mode has useVendor set")
   257  	}
   258  	load1 := func(path string, mode int) *load.Package {
   259  		if parent == nil {
   260  			mode := 0 // don't do module or vendor resolution
   261  			return load.LoadImport(context.TODO(), load.PackageOpts{}, path, base.Cwd(), nil, stk, nil, mode)
   262  		}
   263  		return load.LoadImport(context.TODO(), load.PackageOpts{}, path, parent.Dir, parent, stk, nil, mode|load.ResolveModule)
   264  	}
   265  
   266  	p := load1(arg, mode)
   267  	if p.Error != nil && p.Error.Hard {
   268  		base.Errorf("%s", p.Error)
   269  		return
   270  	}
   271  
   272  	// loadPackage inferred the canonical ImportPath from arg.
   273  	// Use that in the following to prevent hysteresis effects
   274  	// in e.g. downloadCache and packageCache.
   275  	// This allows invocations such as:
   276  	//   mkdir -p $GOPATH/src/github.com/user
   277  	//   cd $GOPATH/src/github.com/user
   278  	//   go get ./foo
   279  	// see: golang.org/issue/9767
   280  	arg = p.ImportPath
   281  
   282  	// There's nothing to do if this is a package in the standard library.
   283  	if p.Standard {
   284  		return
   285  	}
   286  
   287  	// Only process each package once.
   288  	// (Unless we're fetching test dependencies for this package,
   289  	// in which case we want to process it again.)
   290  	if downloadCache[arg] && mode&load.GetTestDeps == 0 {
   291  		return
   292  	}
   293  	downloadCache[arg] = true
   294  
   295  	pkgs := []*load.Package{p}
   296  	wildcardOkay := len(*stk) == 0
   297  	isWildcard := false
   298  
   299  	// Download if the package is missing, or update if we're using -u.
   300  	if p.Dir == "" || *getU {
   301  		// The actual download.
   302  		stk.Push(arg)
   303  		err := downloadPackage(p)
   304  		if err != nil {
   305  			base.Errorf("%s", &load.PackageError{ImportStack: stk.Copy(), Err: err})
   306  			stk.Pop()
   307  			return
   308  		}
   309  		stk.Pop()
   310  
   311  		args := []string{arg}
   312  		// If the argument has a wildcard in it, re-evaluate the wildcard.
   313  		// We delay this until after reloadPackage so that the old entry
   314  		// for p has been replaced in the package cache.
   315  		if wildcardOkay && strings.Contains(arg, "...") {
   316  			match := search.NewMatch(arg)
   317  			if match.IsLocal() {
   318  				match.MatchDirs()
   319  				args = match.Dirs
   320  			} else {
   321  				match.MatchPackages()
   322  				args = match.Pkgs
   323  			}
   324  			for _, err := range match.Errs {
   325  				base.Errorf("%s", err)
   326  			}
   327  			isWildcard = true
   328  		}
   329  
   330  		// Clear all relevant package cache entries before
   331  		// doing any new loads.
   332  		load.ClearPackageCachePartial(args)
   333  
   334  		pkgs = pkgs[:0]
   335  		for _, arg := range args {
   336  			// Note: load calls loadPackage or loadImport,
   337  			// which push arg onto stk already.
   338  			// Do not push here too, or else stk will say arg imports arg.
   339  			p := load1(arg, mode)
   340  			if p.Error != nil {
   341  				base.Errorf("%s", p.Error)
   342  				continue
   343  			}
   344  			pkgs = append(pkgs, p)
   345  		}
   346  	}
   347  
   348  	// Process package, which might now be multiple packages
   349  	// due to wildcard expansion.
   350  	for _, p := range pkgs {
   351  		if *getFix {
   352  			files := base.RelPaths(p.InternalAllGoFiles())
   353  			base.Run(cfg.BuildToolexec, str.StringList(base.Tool("fix"), files))
   354  
   355  			// The imports might have changed, so reload again.
   356  			p = load.ReloadPackageNoFlags(arg, stk)
   357  			if p.Error != nil {
   358  				base.Errorf("%s", p.Error)
   359  				return
   360  			}
   361  		}
   362  
   363  		if isWildcard {
   364  			// Report both the real package and the
   365  			// wildcard in any error message.
   366  			stk.Push(p.ImportPath)
   367  		}
   368  
   369  		// Process dependencies, now that we know what they are.
   370  		imports := p.Imports
   371  		if mode&load.GetTestDeps != 0 {
   372  			// Process test dependencies when -t is specified.
   373  			// (But don't get test dependencies for test dependencies:
   374  			// we always pass mode 0 to the recursive calls below.)
   375  			imports = str.StringList(imports, p.TestImports, p.XTestImports)
   376  		}
   377  		for i, path := range imports {
   378  			if path == "C" {
   379  				continue
   380  			}
   381  			// Fail fast on import naming full vendor path.
   382  			// Otherwise expand path as needed for test imports.
   383  			// Note that p.Imports can have additional entries beyond p.Internal.Build.Imports.
   384  			orig := path
   385  			if i < len(p.Internal.Build.Imports) {
   386  				orig = p.Internal.Build.Imports[i]
   387  			}
   388  			if j, ok := load.FindVendor(orig); ok {
   389  				stk.Push(path)
   390  				err := &load.PackageError{
   391  					ImportStack: stk.Copy(),
   392  					Err:         load.ImportErrorf(path, "%s must be imported as %s", path, path[j+len("vendor/"):]),
   393  				}
   394  				stk.Pop()
   395  				base.Errorf("%s", err)
   396  				continue
   397  			}
   398  			// If this is a test import, apply module and vendor lookup now.
   399  			// We cannot pass ResolveImport to download, because
   400  			// download does caching based on the value of path,
   401  			// so it must be the fully qualified path already.
   402  			if i >= len(p.Imports) {
   403  				path = load.ResolveImportPath(p, path)
   404  			}
   405  			download(path, p, stk, 0)
   406  		}
   407  
   408  		if isWildcard {
   409  			stk.Pop()
   410  		}
   411  	}
   412  }
   413  
   414  // downloadPackage runs the create or download command
   415  // to make the first copy of or update a copy of the given package.
   416  func downloadPackage(p *load.Package) error {
   417  	var (
   418  		vcsCmd         *vcs.Cmd
   419  		repo, rootPath string
   420  		err            error
   421  		blindRepo      bool // set if the repo has unusual configuration
   422  	)
   423  
   424  	// p can be either a real package, or a pseudo-package whose “import path” is
   425  	// actually a wildcard pattern.
   426  	// Trim the path at the element containing the first wildcard,
   427  	// and hope that it applies to the wildcarded parts too.
   428  	// This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH.
   429  	importPrefix := p.ImportPath
   430  	if i := strings.Index(importPrefix, "..."); i >= 0 {
   431  		slash := strings.LastIndexByte(importPrefix[:i], '/')
   432  		if slash < 0 {
   433  			return fmt.Errorf("cannot expand ... in %q", p.ImportPath)
   434  		}
   435  		importPrefix = importPrefix[:slash]
   436  	}
   437  	if err := checkImportPath(importPrefix); err != nil {
   438  		return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err)
   439  	}
   440  	security := web.SecureOnly
   441  	if module.MatchPrefixPatterns(cfg.GOINSECURE, importPrefix) {
   442  		security = web.Insecure
   443  	}
   444  
   445  	if p.Internal.Build.SrcRoot != "" {
   446  		// Directory exists. Look for checkout along path to src.
   447  		vcsCmd, rootPath, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot)
   448  		if err != nil {
   449  			return err
   450  		}
   451  		repo = "<local>" // should be unused; make distinctive
   452  
   453  		// Double-check where it came from.
   454  		if *getU && vcsCmd.RemoteRepo != nil {
   455  			dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
   456  			remote, err := vcsCmd.RemoteRepo(vcsCmd, dir)
   457  			if err != nil {
   458  				// Proceed anyway. The package is present; we likely just don't understand
   459  				// the repo configuration (e.g. unusual remote protocol).
   460  				blindRepo = true
   461  			}
   462  			repo = remote
   463  			if !*getF && err == nil {
   464  				if rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security); err == nil {
   465  					repo := rr.Repo
   466  					if rr.VCS.ResolveRepo != nil {
   467  						resolved, err := rr.VCS.ResolveRepo(rr.VCS, dir, repo)
   468  						if err == nil {
   469  							repo = resolved
   470  						}
   471  					}
   472  					if remote != repo && rr.IsCustom {
   473  						return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.Root, repo, dir, remote)
   474  					}
   475  				}
   476  			}
   477  		}
   478  	} else {
   479  		// Analyze the import path to determine the version control system,
   480  		// repository, and the import path for the root of the repository.
   481  		rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security)
   482  		if err != nil {
   483  			return err
   484  		}
   485  		vcsCmd, repo, rootPath = rr.VCS, rr.Repo, rr.Root
   486  	}
   487  	if !blindRepo && !vcsCmd.IsSecure(repo) && security != web.Insecure {
   488  		return fmt.Errorf("cannot download, %v uses insecure protocol", repo)
   489  	}
   490  
   491  	if p.Internal.Build.SrcRoot == "" {
   492  		// Package not found. Put in first directory of $GOPATH.
   493  		list := filepath.SplitList(cfg.BuildContext.GOPATH)
   494  		if len(list) == 0 {
   495  			return fmt.Errorf("cannot download, $GOPATH not set. For more details see: 'go help gopath'")
   496  		}
   497  		// Guard against people setting GOPATH=$GOROOT.
   498  		if filepath.Clean(list[0]) == filepath.Clean(cfg.GOROOT) {
   499  			return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'")
   500  		}
   501  		if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil {
   502  			return fmt.Errorf("cannot download, %s is a GOROOT, not a GOPATH. For more details see: 'go help gopath'", list[0])
   503  		}
   504  		p.Internal.Build.Root = list[0]
   505  		p.Internal.Build.SrcRoot = filepath.Join(list[0], "src")
   506  		p.Internal.Build.PkgRoot = filepath.Join(list[0], "pkg")
   507  	}
   508  	root := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
   509  
   510  	if err := vcs.CheckNested(vcsCmd, root, p.Internal.Build.SrcRoot); err != nil {
   511  		return err
   512  	}
   513  
   514  	// If we've considered this repository already, don't do it again.
   515  	if downloadRootCache[root] {
   516  		return nil
   517  	}
   518  	downloadRootCache[root] = true
   519  
   520  	if cfg.BuildV {
   521  		fmt.Fprintf(os.Stderr, "%s (download)\n", rootPath)
   522  	}
   523  
   524  	// Check that this is an appropriate place for the repo to be checked out.
   525  	// The target directory must either not exist or have a repo checked out already.
   526  	meta := filepath.Join(root, "."+vcsCmd.Cmd)
   527  	if _, err := os.Stat(meta); err != nil {
   528  		// Metadata file or directory does not exist. Prepare to checkout new copy.
   529  		// Some version control tools require the target directory not to exist.
   530  		// We require that too, just to avoid stepping on existing work.
   531  		if _, err := os.Stat(root); err == nil {
   532  			return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta)
   533  		}
   534  
   535  		_, err := os.Stat(p.Internal.Build.Root)
   536  		gopathExisted := err == nil
   537  
   538  		// Some version control tools require the parent of the target to exist.
   539  		parent, _ := filepath.Split(root)
   540  		if err = os.MkdirAll(parent, 0777); err != nil {
   541  			return err
   542  		}
   543  		if cfg.BuildV && !gopathExisted && p.Internal.Build.Root == cfg.BuildContext.GOPATH {
   544  			fmt.Fprintf(os.Stderr, "created GOPATH=%s; see 'go help gopath'\n", p.Internal.Build.Root)
   545  		}
   546  
   547  		if err = vcsCmd.Create(root, repo); err != nil {
   548  			return err
   549  		}
   550  	} else {
   551  		// Metadata directory does exist; download incremental updates.
   552  		if err = vcsCmd.Download(root); err != nil {
   553  			return err
   554  		}
   555  	}
   556  
   557  	if cfg.BuildN {
   558  		// Do not show tag sync in -n; it's noise more than anything,
   559  		// and since we're not running commands, no tag will be found.
   560  		// But avoid printing nothing.
   561  		fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcsCmd.Cmd)
   562  		return nil
   563  	}
   564  
   565  	// Select and sync to appropriate version of the repository.
   566  	tags, err := vcsCmd.Tags(root)
   567  	if err != nil {
   568  		return err
   569  	}
   570  	vers := runtime.Version()
   571  	if i := strings.Index(vers, " "); i >= 0 {
   572  		vers = vers[:i]
   573  	}
   574  	if err := vcsCmd.TagSync(root, selectTag(vers, tags)); err != nil {
   575  		return err
   576  	}
   577  
   578  	return nil
   579  }
   580  
   581  // selectTag returns the closest matching tag for a given version.
   582  // Closest means the latest one that is not after the current release.
   583  // Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form.
   584  // Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number).
   585  // Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
   586  //
   587  // NOTE(rsc): Eventually we will need to decide on some logic here.
   588  // For now, there is only "go1". This matches the docs in go help get.
   589  func selectTag(goVersion string, tags []string) (match string) {
   590  	for _, t := range tags {
   591  		if t == "go1" {
   592  			return "go1"
   593  		}
   594  	}
   595  	return ""
   596  }
   597  
   598  // checkImportPath is like module.CheckImportPath, but it forbids leading dots
   599  // in path elements. This can lead to 'go get' creating .git and other VCS
   600  // directories in places we might run VCS tools later.
   601  func checkImportPath(path string) error {
   602  	if err := module.CheckImportPath(path); err != nil {
   603  		return err
   604  	}
   605  	checkElem := func(elem string) error {
   606  		if elem[0] == '.' {
   607  			return fmt.Errorf("malformed import path %q: leading dot in path element", path)
   608  		}
   609  		return nil
   610  	}
   611  	elemStart := 0
   612  	for i, r := range path {
   613  		if r == '/' {
   614  			if err := checkElem(path[elemStart:]); err != nil {
   615  				return err
   616  			}
   617  			elemStart = i + 1
   618  		}
   619  	}
   620  	if err := checkElem(path[elemStart:]); err != nil {
   621  		return err
   622  	}
   623  	return nil
   624  }
   625  

View as plain text