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

     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: modules not implemented")
   118  	}
   119  
   120  	work.BuildInit()
   121  
   122  	if *getF && !*getU {
   123  		base.Fatalf("go: cannot use -f flag without -u")
   124  	}
   125  	if *getInsecure {
   126  		base.Fatalf("go: -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: %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: %s exists as a file, but 'go get' requires package arguments", arg)
   222  			}
   223  		}
   224  	}
   225  	base.ExitIfErrors()
   226  
   227  	var pkgs []string
   228  	noModRoots := []string{}
   229  	for _, m := range search.ImportPathsQuiet(patterns, noModRoots) {
   230  		if len(m.Pkgs) == 0 && strings.Contains(m.Pattern(), "...") {
   231  			pkgs = append(pkgs, m.Pattern())
   232  		} else {
   233  			pkgs = append(pkgs, m.Pkgs...)
   234  		}
   235  	}
   236  	return pkgs
   237  }
   238  
   239  // downloadCache records the import paths we have already
   240  // considered during the download, to avoid duplicate work when
   241  // there is more than one dependency sequence leading to
   242  // a particular package.
   243  var downloadCache = map[string]bool{}
   244  
   245  // downloadRootCache records the version control repository
   246  // root directories we have already considered during the download.
   247  // For example, all the packages in the github.com/google/codesearch repo
   248  // share the same root (the directory for that path), and we only need
   249  // to run the hg commands to consider each repository once.
   250  var downloadRootCache = map[string]bool{}
   251  
   252  // download runs the download half of the get command
   253  // for the package or pattern named by the argument.
   254  func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) {
   255  	if mode&load.ResolveImport != 0 {
   256  		// Caller is responsible for expanding vendor paths.
   257  		panic("internal error: download mode has useVendor set")
   258  	}
   259  	load1 := func(path string, mode int) *load.Package {
   260  		if parent == nil {
   261  			mode := 0 // don't do module or vendor resolution
   262  			return load.LoadImport(context.TODO(), load.PackageOpts{}, path, base.Cwd(), nil, stk, nil, mode)
   263  		}
   264  		return load.LoadImport(context.TODO(), load.PackageOpts{}, path, parent.Dir, parent, stk, nil, mode|load.ResolveModule)
   265  	}
   266  
   267  	p := load1(arg, mode)
   268  	if p.Error != nil && p.Error.Hard {
   269  		base.Errorf("%s", p.Error)
   270  		return
   271  	}
   272  
   273  	// loadPackage inferred the canonical ImportPath from arg.
   274  	// Use that in the following to prevent hysteresis effects
   275  	// in e.g. downloadCache and packageCache.
   276  	// This allows invocations such as:
   277  	//   mkdir -p $GOPATH/src/github.com/user
   278  	//   cd $GOPATH/src/github.com/user
   279  	//   go get ./foo
   280  	// see: golang.org/issue/9767
   281  	arg = p.ImportPath
   282  
   283  	// There's nothing to do if this is a package in the standard library.
   284  	if p.Standard {
   285  		return
   286  	}
   287  
   288  	// Only process each package once.
   289  	// (Unless we're fetching test dependencies for this package,
   290  	// in which case we want to process it again.)
   291  	if downloadCache[arg] && mode&load.GetTestDeps == 0 {
   292  		return
   293  	}
   294  	downloadCache[arg] = true
   295  
   296  	pkgs := []*load.Package{p}
   297  	wildcardOkay := len(*stk) == 0
   298  	isWildcard := false
   299  
   300  	// Download if the package is missing, or update if we're using -u.
   301  	if p.Dir == "" || *getU {
   302  		// The actual download.
   303  		stk.Push(arg)
   304  		err := downloadPackage(p)
   305  		if err != nil {
   306  			base.Errorf("%s", &load.PackageError{ImportStack: stk.Copy(), Err: err})
   307  			stk.Pop()
   308  			return
   309  		}
   310  		stk.Pop()
   311  
   312  		args := []string{arg}
   313  		// If the argument has a wildcard in it, re-evaluate the wildcard.
   314  		// We delay this until after reloadPackage so that the old entry
   315  		// for p has been replaced in the package cache.
   316  		if wildcardOkay && strings.Contains(arg, "...") {
   317  			match := search.NewMatch(arg)
   318  			if match.IsLocal() {
   319  				noModRoots := []string{} // We're in gopath mode, so there are no modroots.
   320  				match.MatchDirs(noModRoots)
   321  				args = match.Dirs
   322  			} else {
   323  				match.MatchPackages()
   324  				args = match.Pkgs
   325  			}
   326  			for _, err := range match.Errs {
   327  				base.Errorf("%s", err)
   328  			}
   329  			isWildcard = true
   330  		}
   331  
   332  		// Clear all relevant package cache entries before
   333  		// doing any new loads.
   334  		load.ClearPackageCachePartial(args)
   335  
   336  		pkgs = pkgs[:0]
   337  		for _, arg := range args {
   338  			// Note: load calls loadPackage or loadImport,
   339  			// which push arg onto stk already.
   340  			// Do not push here too, or else stk will say arg imports arg.
   341  			p := load1(arg, mode)
   342  			if p.Error != nil {
   343  				base.Errorf("%s", p.Error)
   344  				continue
   345  			}
   346  			pkgs = append(pkgs, p)
   347  		}
   348  	}
   349  
   350  	// Process package, which might now be multiple packages
   351  	// due to wildcard expansion.
   352  	for _, p := range pkgs {
   353  		if *getFix {
   354  			files := base.RelPaths(p.InternalAllGoFiles())
   355  			base.Run(cfg.BuildToolexec, str.StringList(base.Tool("fix"), files))
   356  
   357  			// The imports might have changed, so reload again.
   358  			p = load.ReloadPackageNoFlags(arg, stk)
   359  			if p.Error != nil {
   360  				base.Errorf("%s", p.Error)
   361  				return
   362  			}
   363  		}
   364  
   365  		if isWildcard {
   366  			// Report both the real package and the
   367  			// wildcard in any error message.
   368  			stk.Push(p.ImportPath)
   369  		}
   370  
   371  		// Process dependencies, now that we know what they are.
   372  		imports := p.Imports
   373  		if mode&load.GetTestDeps != 0 {
   374  			// Process test dependencies when -t is specified.
   375  			// (But don't get test dependencies for test dependencies:
   376  			// we always pass mode 0 to the recursive calls below.)
   377  			imports = str.StringList(imports, p.TestImports, p.XTestImports)
   378  		}
   379  		for i, path := range imports {
   380  			if path == "C" {
   381  				continue
   382  			}
   383  			// Fail fast on import naming full vendor path.
   384  			// Otherwise expand path as needed for test imports.
   385  			// Note that p.Imports can have additional entries beyond p.Internal.Build.Imports.
   386  			orig := path
   387  			if i < len(p.Internal.Build.Imports) {
   388  				orig = p.Internal.Build.Imports[i]
   389  			}
   390  			if j, ok := load.FindVendor(orig); ok {
   391  				stk.Push(path)
   392  				err := &load.PackageError{
   393  					ImportStack: stk.Copy(),
   394  					Err:         load.ImportErrorf(path, "%s must be imported as %s", path, path[j+len("vendor/"):]),
   395  				}
   396  				stk.Pop()
   397  				base.Errorf("%s", err)
   398  				continue
   399  			}
   400  			// If this is a test import, apply module and vendor lookup now.
   401  			// We cannot pass ResolveImport to download, because
   402  			// download does caching based on the value of path,
   403  			// so it must be the fully qualified path already.
   404  			if i >= len(p.Imports) {
   405  				path = load.ResolveImportPath(p, path)
   406  			}
   407  			download(path, p, stk, 0)
   408  		}
   409  
   410  		if isWildcard {
   411  			stk.Pop()
   412  		}
   413  	}
   414  }
   415  
   416  // downloadPackage runs the create or download command
   417  // to make the first copy of or update a copy of the given package.
   418  func downloadPackage(p *load.Package) error {
   419  	var (
   420  		vcsCmd                  *vcs.Cmd
   421  		repo, rootPath, repoDir string
   422  		err                     error
   423  		blindRepo               bool // set if the repo has unusual configuration
   424  	)
   425  
   426  	// p can be either a real package, or a pseudo-package whose “import path” is
   427  	// actually a wildcard pattern.
   428  	// Trim the path at the element containing the first wildcard,
   429  	// and hope that it applies to the wildcarded parts too.
   430  	// This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH.
   431  	importPrefix := p.ImportPath
   432  	if i := strings.Index(importPrefix, "..."); i >= 0 {
   433  		slash := strings.LastIndexByte(importPrefix[:i], '/')
   434  		if slash < 0 {
   435  			return fmt.Errorf("cannot expand ... in %q", p.ImportPath)
   436  		}
   437  		importPrefix = importPrefix[:slash]
   438  	}
   439  	if err := checkImportPath(importPrefix); err != nil {
   440  		return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err)
   441  	}
   442  	security := web.SecureOnly
   443  	if module.MatchPrefixPatterns(cfg.GOINSECURE, importPrefix) {
   444  		security = web.Insecure
   445  	}
   446  
   447  	if p.Internal.Build.SrcRoot != "" {
   448  		// Directory exists. Look for checkout along path to src.
   449  		const allowNesting = false
   450  		repoDir, vcsCmd, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot, allowNesting)
   451  		if err != nil {
   452  			return err
   453  		}
   454  		if !str.HasFilePathPrefix(repoDir, p.Internal.Build.SrcRoot) {
   455  			panic(fmt.Sprintf("repository %q not in source root %q", repo, p.Internal.Build.SrcRoot))
   456  		}
   457  		rootPath = str.TrimFilePathPrefix(repoDir, p.Internal.Build.SrcRoot)
   458  		if err := vcs.CheckGOVCS(vcsCmd, rootPath); err != nil {
   459  			return err
   460  		}
   461  
   462  		repo = "<local>" // should be unused; make distinctive
   463  
   464  		// Double-check where it came from.
   465  		if *getU && vcsCmd.RemoteRepo != nil {
   466  			dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
   467  			remote, err := vcsCmd.RemoteRepo(vcsCmd, dir)
   468  			if err != nil {
   469  				// Proceed anyway. The package is present; we likely just don't understand
   470  				// the repo configuration (e.g. unusual remote protocol).
   471  				blindRepo = true
   472  			}
   473  			repo = remote
   474  			if !*getF && err == nil {
   475  				if rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security); err == nil {
   476  					repo := rr.Repo
   477  					if rr.VCS.ResolveRepo != nil {
   478  						resolved, err := rr.VCS.ResolveRepo(rr.VCS, dir, repo)
   479  						if err == nil {
   480  							repo = resolved
   481  						}
   482  					}
   483  					if remote != repo && rr.IsCustom {
   484  						return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.Root, repo, dir, remote)
   485  					}
   486  				}
   487  			}
   488  		}
   489  	} else {
   490  		// Analyze the import path to determine the version control system,
   491  		// repository, and the import path for the root of the repository.
   492  		rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security)
   493  		if err != nil {
   494  			return err
   495  		}
   496  		vcsCmd, repo, rootPath = rr.VCS, rr.Repo, rr.Root
   497  	}
   498  	if !blindRepo && !vcsCmd.IsSecure(repo) && security != web.Insecure {
   499  		return fmt.Errorf("cannot download, %v uses insecure protocol", repo)
   500  	}
   501  
   502  	if p.Internal.Build.SrcRoot == "" {
   503  		// Package not found. Put in first directory of $GOPATH.
   504  		list := filepath.SplitList(cfg.BuildContext.GOPATH)
   505  		if len(list) == 0 {
   506  			return fmt.Errorf("cannot download, $GOPATH not set. For more details see: 'go help gopath'")
   507  		}
   508  		// Guard against people setting GOPATH=$GOROOT.
   509  		if filepath.Clean(list[0]) == filepath.Clean(cfg.GOROOT) {
   510  			return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'")
   511  		}
   512  		if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil {
   513  			return fmt.Errorf("cannot download, %s is a GOROOT, not a GOPATH. For more details see: 'go help gopath'", list[0])
   514  		}
   515  		p.Internal.Build.Root = list[0]
   516  		p.Internal.Build.SrcRoot = filepath.Join(list[0], "src")
   517  		p.Internal.Build.PkgRoot = filepath.Join(list[0], "pkg")
   518  	}
   519  	root := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
   520  
   521  	if err := vcs.CheckNested(vcsCmd, root, p.Internal.Build.SrcRoot); err != nil {
   522  		return err
   523  	}
   524  
   525  	// If we've considered this repository already, don't do it again.
   526  	if downloadRootCache[root] {
   527  		return nil
   528  	}
   529  	downloadRootCache[root] = true
   530  
   531  	if cfg.BuildV {
   532  		fmt.Fprintf(os.Stderr, "%s (download)\n", rootPath)
   533  	}
   534  
   535  	// Check that this is an appropriate place for the repo to be checked out.
   536  	// The target directory must either not exist or have a repo checked out already.
   537  	meta := filepath.Join(root, "."+vcsCmd.Cmd)
   538  	if _, err := os.Stat(meta); err != nil {
   539  		// Metadata file or directory does not exist. Prepare to checkout new copy.
   540  		// Some version control tools require the target directory not to exist.
   541  		// We require that too, just to avoid stepping on existing work.
   542  		if _, err := os.Stat(root); err == nil {
   543  			return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta)
   544  		}
   545  
   546  		_, err := os.Stat(p.Internal.Build.Root)
   547  		gopathExisted := err == nil
   548  
   549  		// Some version control tools require the parent of the target to exist.
   550  		parent, _ := filepath.Split(root)
   551  		if err = os.MkdirAll(parent, 0777); err != nil {
   552  			return err
   553  		}
   554  		if cfg.BuildV && !gopathExisted && p.Internal.Build.Root == cfg.BuildContext.GOPATH {
   555  			fmt.Fprintf(os.Stderr, "created GOPATH=%s; see 'go help gopath'\n", p.Internal.Build.Root)
   556  		}
   557  
   558  		if err = vcsCmd.Create(root, repo); err != nil {
   559  			return err
   560  		}
   561  	} else {
   562  		// Metadata directory does exist; download incremental updates.
   563  		if err = vcsCmd.Download(root); err != nil {
   564  			return err
   565  		}
   566  	}
   567  
   568  	if cfg.BuildN {
   569  		// Do not show tag sync in -n; it's noise more than anything,
   570  		// and since we're not running commands, no tag will be found.
   571  		// But avoid printing nothing.
   572  		fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcsCmd.Cmd)
   573  		return nil
   574  	}
   575  
   576  	// Select and sync to appropriate version of the repository.
   577  	tags, err := vcsCmd.Tags(root)
   578  	if err != nil {
   579  		return err
   580  	}
   581  	vers := runtime.Version()
   582  	if i := strings.Index(vers, " "); i >= 0 {
   583  		vers = vers[:i]
   584  	}
   585  	if err := vcsCmd.TagSync(root, selectTag(vers, tags)); err != nil {
   586  		return err
   587  	}
   588  
   589  	return nil
   590  }
   591  
   592  // selectTag returns the closest matching tag for a given version.
   593  // Closest means the latest one that is not after the current release.
   594  // Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form.
   595  // Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number).
   596  // Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
   597  //
   598  // NOTE(rsc): Eventually we will need to decide on some logic here.
   599  // For now, there is only "go1". This matches the docs in go help get.
   600  func selectTag(goVersion string, tags []string) (match string) {
   601  	for _, t := range tags {
   602  		if t == "go1" {
   603  			return "go1"
   604  		}
   605  	}
   606  	return ""
   607  }
   608  
   609  // checkImportPath is like module.CheckImportPath, but it forbids leading dots
   610  // in path elements. This can lead to 'go get' creating .git and other VCS
   611  // directories in places we might run VCS tools later.
   612  func checkImportPath(path string) error {
   613  	if err := module.CheckImportPath(path); err != nil {
   614  		return err
   615  	}
   616  	checkElem := func(elem string) error {
   617  		if elem[0] == '.' {
   618  			return fmt.Errorf("malformed import path %q: leading dot in path element", path)
   619  		}
   620  		return nil
   621  	}
   622  	elemStart := 0
   623  	for i, r := range path {
   624  		if r == '/' {
   625  			if err := checkElem(path[elemStart:]); err != nil {
   626  				return err
   627  			}
   628  			elemStart = i + 1
   629  		}
   630  	}
   631  	if err := checkElem(path[elemStart:]); err != nil {
   632  		return err
   633  	}
   634  	return nil
   635  }
   636  

View as plain text