github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/get/get.go (about)

     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  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/base"
    17  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg"
    18  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/load"
    19  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/search"
    20  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/str"
    21  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/vcs"
    22  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/web"
    23  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/work"
    24  
    25  	"github.com/bir3/gocompiler/src/xvendor/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(ctx, 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  		}
   210  
   211  		// Guard against 'go get x.go', a common mistake.
   212  		// Note that package and module paths may end with '.go', so only print an error
   213  		// if the argument has no slash or refers to an existing file.
   214  		if strings.HasSuffix(arg, ".go") {
   215  			if !strings.Contains(arg, "/") {
   216  				base.Errorf("go: %s: arguments must be package or module paths", arg)
   217  				continue
   218  			}
   219  			if fi, err := os.Stat(arg); err == nil && !fi.IsDir() {
   220  				base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", arg)
   221  			}
   222  		}
   223  	}
   224  	base.ExitIfErrors()
   225  
   226  	var pkgs []string
   227  	noModRoots := []string{}
   228  	for _, m := range search.ImportPathsQuiet(patterns, noModRoots) {
   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(ctx context.Context, 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(ctx, load.PackageOpts{}, path, base.Cwd(), nil, stk, nil, mode)
   262  		}
   263  		return load.LoadImport(ctx, 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  				noModRoots := []string{} // We're in gopath mode, so there are no modroots.
   319  				match.MatchDirs(noModRoots)
   320  				args = match.Dirs
   321  			} else {
   322  				match.MatchPackages()
   323  				args = match.Pkgs
   324  			}
   325  			for _, err := range match.Errs {
   326  				base.Errorf("%s", err)
   327  			}
   328  			isWildcard = true
   329  		}
   330  
   331  		// Clear all relevant package cache entries before
   332  		// doing any new loads.
   333  		load.ClearPackageCachePartial(args)
   334  
   335  		pkgs = pkgs[:0]
   336  		for _, arg := range args {
   337  			// Note: load calls loadPackage or loadImport,
   338  			// which push arg onto stk already.
   339  			// Do not push here too, or else stk will say arg imports arg.
   340  			p := load1(arg, mode)
   341  			if p.Error != nil {
   342  				base.Errorf("%s", p.Error)
   343  				continue
   344  			}
   345  			pkgs = append(pkgs, p)
   346  		}
   347  	}
   348  
   349  	// Process package, which might now be multiple packages
   350  	// due to wildcard expansion.
   351  	for _, p := range pkgs {
   352  		if *getFix {
   353  			files := base.RelPaths(p.InternalAllGoFiles())
   354  			base.Run(cfg.BuildToolexec, str.StringList(base.Tool("fix"), files))
   355  
   356  			// The imports might have changed, so reload again.
   357  			p = load.ReloadPackageNoFlags(arg, stk)
   358  			if p.Error != nil {
   359  				base.Errorf("%s", p.Error)
   360  				return
   361  			}
   362  		}
   363  
   364  		if isWildcard {
   365  			// Report both the real package and the
   366  			// wildcard in any error message.
   367  			stk.Push(p.ImportPath)
   368  		}
   369  
   370  		// Process dependencies, now that we know what they are.
   371  		imports := p.Imports
   372  		if mode&load.GetTestDeps != 0 {
   373  			// Process test dependencies when -t is specified.
   374  			// (But don't get test dependencies for test dependencies:
   375  			// we always pass mode 0 to the recursive calls below.)
   376  			imports = str.StringList(imports, p.TestImports, p.XTestImports)
   377  		}
   378  		for i, path := range imports {
   379  			if path == "C" {
   380  				continue
   381  			}
   382  			// Fail fast on import naming full vendor path.
   383  			// Otherwise expand path as needed for test imports.
   384  			// Note that p.Imports can have additional entries beyond p.Internal.Build.Imports.
   385  			orig := path
   386  			if i < len(p.Internal.Build.Imports) {
   387  				orig = p.Internal.Build.Imports[i]
   388  			}
   389  			if j, ok := load.FindVendor(orig); ok {
   390  				stk.Push(path)
   391  				err := &load.PackageError{
   392  					ImportStack: stk.Copy(),
   393  					Err:         load.ImportErrorf(path, "%s must be imported as %s", path, path[j+len("vendor/"):]),
   394  				}
   395  				stk.Pop()
   396  				base.Errorf("%s", err)
   397  				continue
   398  			}
   399  			// If this is a test import, apply module and vendor lookup now.
   400  			// We cannot pass ResolveImport to download, because
   401  			// download does caching based on the value of path,
   402  			// so it must be the fully qualified path already.
   403  			if i >= len(p.Imports) {
   404  				path = load.ResolveImportPath(p, path)
   405  			}
   406  			download(ctx, path, p, stk, 0)
   407  		}
   408  
   409  		if isWildcard {
   410  			stk.Pop()
   411  		}
   412  	}
   413  }
   414  
   415  // downloadPackage runs the create or download command
   416  // to make the first copy of or update a copy of the given package.
   417  func downloadPackage(p *load.Package) error {
   418  	var (
   419  		vcsCmd                  *vcs.Cmd
   420  		repo, rootPath, repoDir string
   421  		err                     error
   422  		blindRepo               bool // set if the repo has unusual configuration
   423  	)
   424  
   425  	// p can be either a real package, or a pseudo-package whose “import path” is
   426  	// actually a wildcard pattern.
   427  	// Trim the path at the element containing the first wildcard,
   428  	// and hope that it applies to the wildcarded parts too.
   429  	// This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH.
   430  	importPrefix := p.ImportPath
   431  	if i := strings.Index(importPrefix, "..."); i >= 0 {
   432  		slash := strings.LastIndexByte(importPrefix[:i], '/')
   433  		if slash < 0 {
   434  			return fmt.Errorf("cannot expand ... in %q", p.ImportPath)
   435  		}
   436  		importPrefix = importPrefix[:slash]
   437  	}
   438  	if err := checkImportPath(importPrefix); err != nil {
   439  		return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err)
   440  	}
   441  	security := web.SecureOnly
   442  	if module.MatchPrefixPatterns(cfg.GOINSECURE, importPrefix) {
   443  		security = web.Insecure
   444  	}
   445  
   446  	if p.Internal.Build.SrcRoot != "" {
   447  		// Directory exists. Look for checkout along path to src.
   448  		const allowNesting = false
   449  		repoDir, vcsCmd, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot, allowNesting)
   450  		if err != nil {
   451  			return err
   452  		}
   453  		if !str.HasFilePathPrefix(repoDir, p.Internal.Build.SrcRoot) {
   454  			panic(fmt.Sprintf("repository %q not in source root %q", repo, p.Internal.Build.SrcRoot))
   455  		}
   456  		rootPath = str.TrimFilePathPrefix(repoDir, p.Internal.Build.SrcRoot)
   457  		if err := vcs.CheckGOVCS(vcsCmd, rootPath); err != nil {
   458  			return err
   459  		}
   460  
   461  		repo = "<local>" // should be unused; make distinctive
   462  
   463  		// Double-check where it came from.
   464  		if *getU && vcsCmd.RemoteRepo != nil {
   465  			dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
   466  			remote, err := vcsCmd.RemoteRepo(vcsCmd, dir)
   467  			if err != nil {
   468  				// Proceed anyway. The package is present; we likely just don't understand
   469  				// the repo configuration (e.g. unusual remote protocol).
   470  				blindRepo = true
   471  			}
   472  			repo = remote
   473  			if !*getF && err == nil {
   474  				if rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security); err == nil {
   475  					repo := rr.Repo
   476  					if rr.VCS.ResolveRepo != nil {
   477  						resolved, err := rr.VCS.ResolveRepo(rr.VCS, dir, repo)
   478  						if err == nil {
   479  							repo = resolved
   480  						}
   481  					}
   482  					if remote != repo && rr.IsCustom {
   483  						return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.Root, repo, dir, remote)
   484  					}
   485  				}
   486  			}
   487  		}
   488  	} else {
   489  		// Analyze the import path to determine the version control system,
   490  		// repository, and the import path for the root of the repository.
   491  		rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security)
   492  		if err != nil {
   493  			return err
   494  		}
   495  		vcsCmd, repo, rootPath = rr.VCS, rr.Repo, rr.Root
   496  	}
   497  	if !blindRepo && !vcsCmd.IsSecure(repo) && security != web.Insecure {
   498  		return fmt.Errorf("cannot download: %v uses insecure protocol", repo)
   499  	}
   500  
   501  	if p.Internal.Build.SrcRoot == "" {
   502  		// Package not found. Put in first directory of $GOPATH.
   503  		list := filepath.SplitList(cfg.BuildContext.GOPATH)
   504  		if len(list) == 0 {
   505  			return fmt.Errorf("cannot download: $GOPATH not set. For more details see: 'go help gopath'")
   506  		}
   507  		// Guard against people setting GOPATH=$GOROOT.
   508  		if filepath.Clean(list[0]) == filepath.Clean(cfg.GOROOT) {
   509  			return fmt.Errorf("cannot download: $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'")
   510  		}
   511  		if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil {
   512  			return fmt.Errorf("cannot download: %s is a GOROOT, not a GOPATH. For more details see: 'go help gopath'", list[0])
   513  		}
   514  		p.Internal.Build.Root = list[0]
   515  		p.Internal.Build.SrcRoot = filepath.Join(list[0], "src")
   516  		p.Internal.Build.PkgRoot = filepath.Join(list[0], "pkg")
   517  	}
   518  	root := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
   519  
   520  	if err := vcs.CheckNested(vcsCmd, root, p.Internal.Build.SrcRoot); err != nil {
   521  		return err
   522  	}
   523  
   524  	// If we've considered this repository already, don't do it again.
   525  	if downloadRootCache[root] {
   526  		return nil
   527  	}
   528  	downloadRootCache[root] = true
   529  
   530  	if cfg.BuildV {
   531  		fmt.Fprintf(os.Stderr, "%s (download)\n", rootPath)
   532  	}
   533  
   534  	// Check that this is an appropriate place for the repo to be checked out.
   535  	// The target directory must either not exist or have a repo checked out already.
   536  	meta := filepath.Join(root, "."+vcsCmd.Cmd)
   537  	if _, err := os.Stat(meta); err != nil {
   538  		// Metadata file or directory does not exist. Prepare to checkout new copy.
   539  		// Some version control tools require the target directory not to exist.
   540  		// We require that too, just to avoid stepping on existing work.
   541  		if _, err := os.Stat(root); err == nil {
   542  			return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta)
   543  		}
   544  
   545  		_, err := os.Stat(p.Internal.Build.Root)
   546  		gopathExisted := err == nil
   547  
   548  		// Some version control tools require the parent of the target to exist.
   549  		parent, _ := filepath.Split(root)
   550  		if err = os.MkdirAll(parent, 0777); err != nil {
   551  			return err
   552  		}
   553  		if cfg.BuildV && !gopathExisted && p.Internal.Build.Root == cfg.BuildContext.GOPATH {
   554  			fmt.Fprintf(os.Stderr, "created GOPATH=%s; see 'go help gopath'\n", p.Internal.Build.Root)
   555  		}
   556  
   557  		if err = vcsCmd.Create(root, repo); err != nil {
   558  			return err
   559  		}
   560  	} else {
   561  		// Metadata directory does exist; download incremental updates.
   562  		if err = vcsCmd.Download(root); err != nil {
   563  			return err
   564  		}
   565  	}
   566  
   567  	if cfg.BuildN {
   568  		// Do not show tag sync in -n; it's noise more than anything,
   569  		// and since we're not running commands, no tag will be found.
   570  		// But avoid printing nothing.
   571  		fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcsCmd.Cmd)
   572  		return nil
   573  	}
   574  
   575  	// Select and sync to appropriate version of the repository.
   576  	tags, err := vcsCmd.Tags(root)
   577  	if err != nil {
   578  		return err
   579  	}
   580  	vers := runtime.Version()
   581  	if i := strings.Index(vers, " "); i >= 0 {
   582  		vers = vers[:i]
   583  	}
   584  	if err := vcsCmd.TagSync(root, selectTag(vers, tags)); err != nil {
   585  		return err
   586  	}
   587  
   588  	return nil
   589  }
   590  
   591  // selectTag returns the closest matching tag for a given version.
   592  // Closest means the latest one that is not after the current release.
   593  // Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form.
   594  // Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number).
   595  // Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
   596  //
   597  // NOTE(rsc): Eventually we will need to decide on some logic here.
   598  // For now, there is only "go1". This matches the docs in go help get.
   599  func selectTag(goVersion string, tags []string) (match string) {
   600  	for _, t := range tags {
   601  		if t == "go1" {
   602  			return "go1"
   603  		}
   604  	}
   605  	return ""
   606  }
   607  
   608  // checkImportPath is like module.CheckImportPath, but it forbids leading dots
   609  // in path elements. This can lead to 'go get' creating .git and other VCS
   610  // directories in places we might run VCS tools later.
   611  func checkImportPath(path string) error {
   612  	if err := module.CheckImportPath(path); err != nil {
   613  		return err
   614  	}
   615  	checkElem := func(elem string) error {
   616  		if elem[0] == '.' {
   617  			return fmt.Errorf("malformed import path %q: leading dot in path element", path)
   618  		}
   619  		return nil
   620  	}
   621  	elemStart := 0
   622  	for i, r := range path {
   623  		if r == '/' {
   624  			if err := checkElem(path[elemStart:]); err != nil {
   625  				return err
   626  			}
   627  			elemStart = i + 1
   628  		}
   629  	}
   630  	if err := checkElem(path[elemStart:]); err != nil {
   631  		return err
   632  	}
   633  	return nil
   634  }