diff options
Diffstat (limited to 'src/cmd/go/internal/get/get.go')
-rw-r--r-- | src/cmd/go/internal/get/get.go | 640 |
1 files changed, 0 insertions, 640 deletions
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go deleted file mode 100644 index 06b567ab28..0000000000 --- a/src/cmd/go/internal/get/get.go +++ /dev/null @@ -1,640 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package get implements the “go get” command. -package get - -import ( - "context" - "fmt" - "os" - "path/filepath" - "runtime" - "strings" - - "cmd/go/internal/base" - "cmd/go/internal/cfg" - "cmd/go/internal/load" - "cmd/go/internal/search" - "cmd/go/internal/str" - "cmd/go/internal/vcs" - "cmd/go/internal/web" - "cmd/go/internal/work" - - "golang.org/x/mod/module" -) - -var CmdGet = &base.Command{ - UsageLine: "go get [-d] [-f] [-t] [-u] [-v] [-fix] [build flags] [packages]", - Short: "download and install packages and dependencies", - Long: ` -Get downloads the packages named by the import paths, along with their -dependencies. It then installs the named packages, like 'go install'. - -The -d flag instructs get to stop after downloading the packages; that is, -it instructs get not to install the packages. - -The -f flag, valid only when -u is set, forces get -u not to verify that -each package has been checked out from the source control repository -implied by its import path. This can be useful if the source is a local fork -of the original. - -The -fix flag instructs get to run the fix tool on the downloaded packages -before resolving dependencies or building the code. - -The -t flag instructs get to also download the packages required to build -the tests for the specified packages. - -The -u flag instructs get to use the network to update the named packages -and their dependencies. By default, get uses the network to check out -missing packages but does not use it to look for updates to existing packages. - -The -v flag enables verbose progress and debug output. - -Get also accepts build flags to control the installation. See 'go help build'. - -When checking out a new package, get creates the target directory -GOPATH/src/<import-path>. If the GOPATH contains multiple entries, -get uses the first one. For more details see: 'go help gopath'. - -When checking out or updating a package, get looks for a branch or tag -that matches the locally installed version of Go. The most important -rule is that if the local installation is running version "go1", get -searches for a branch or tag named "go1". If no such version exists -it retrieves the default branch of the package. - -When go get checks out or updates a Git repository, -it also updates any git submodules referenced by the repository. - -Get never checks out or updates code stored in vendor directories. - -For more about build flags, see 'go help build'. - -For more about specifying packages, see 'go help packages'. - -For more about how 'go get' finds source code to -download, see 'go help importpath'. - -This text describes the behavior of get when using GOPATH -to manage source code and dependencies. -If instead the go command is running in module-aware mode, -the details of get's flags and effects change, as does 'go help get'. -See 'go help modules' and 'go help module-get'. - -See also: go build, go install, go clean. - `, -} - -var HelpGopathGet = &base.Command{ - UsageLine: "gopath-get", - Short: "legacy GOPATH go get", - Long: ` -The 'go get' command changes behavior depending on whether the -go command is running in module-aware mode or legacy GOPATH mode. -This help text, accessible as 'go help gopath-get' even in module-aware mode, -describes 'go get' as it operates in legacy GOPATH mode. - -Usage: ` + CmdGet.UsageLine + ` -` + CmdGet.Long, -} - -var ( - getD = CmdGet.Flag.Bool("d", false, "") - getF = CmdGet.Flag.Bool("f", false, "") - getT = CmdGet.Flag.Bool("t", false, "") - getU = CmdGet.Flag.Bool("u", false, "") - getFix = CmdGet.Flag.Bool("fix", false, "") - getInsecure = CmdGet.Flag.Bool("insecure", false, "") -) - -func init() { - work.AddBuildFlags(CmdGet, work.OmitModFlag|work.OmitModCommonFlags) - CmdGet.Run = runGet // break init loop -} - -func runGet(ctx context.Context, cmd *base.Command, args []string) { - if cfg.ModulesEnabled { - // Should not happen: main.go should install the separate module-enabled get code. - base.Fatalf("go: modules not implemented") - } - - work.BuildInit() - - if *getF && !*getU { - base.Fatalf("go: cannot use -f flag without -u") - } - if *getInsecure { - base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead") - } - - // Disable any prompting for passwords by Git itself. - // Only has an effect for 2.3.0 or later, but avoiding - // the prompt in earlier versions is just too hard. - // If user has explicitly set GIT_TERMINAL_PROMPT=1, keep - // prompting. - // See golang.org/issue/9341 and golang.org/issue/12706. - if os.Getenv("GIT_TERMINAL_PROMPT") == "" { - os.Setenv("GIT_TERMINAL_PROMPT", "0") - } - - // Also disable prompting for passwords by the 'ssh' subprocess spawned by - // Git, because apparently GIT_TERMINAL_PROMPT isn't sufficient to do that. - // Adding '-o BatchMode=yes' should do the trick. - // - // If a Git subprocess forks a child into the background to cache a new connection, - // that child keeps stdout/stderr open. After the Git subprocess exits, - // os /exec expects to be able to read from the stdout/stderr pipe - // until EOF to get all the data that the Git subprocess wrote before exiting. - // The EOF doesn't come until the child exits too, because the child - // is holding the write end of the pipe. - // This is unfortunate, but it has come up at least twice - // (see golang.org/issue/13453 and golang.org/issue/16104) - // and confuses users when it does. - // If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND, - // assume they know what they are doing and don't step on it. - // But default to turning off ControlMaster. - if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" { - os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no -o BatchMode=yes") - } - - // And one more source of Git prompts: the Git Credential Manager Core for Windows. - // - // See https://github.com/microsoft/Git-Credential-Manager-Core/blob/master/docs/environment.md#gcm_interactive. - if os.Getenv("GCM_INTERACTIVE") == "" { - os.Setenv("GCM_INTERACTIVE", "never") - } - - // Phase 1. Download/update. - var stk load.ImportStack - mode := 0 - if *getT { - mode |= load.GetTestDeps - } - for _, pkg := range downloadPaths(args) { - download(ctx, pkg, nil, &stk, mode) - } - base.ExitIfErrors() - - // Phase 2. Rescan packages and re-evaluate args list. - - // Code we downloaded and all code that depends on it - // needs to be evicted from the package cache so that - // the information will be recomputed. Instead of keeping - // track of the reverse dependency information, evict - // everything. - load.ClearPackageCache() - - pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args) - load.CheckPackageErrors(pkgs) - - // Phase 3. Install. - if *getD { - // Download only. - // Check delayed until now so that downloadPaths - // and CheckPackageErrors have a chance to print errors. - return - } - - work.InstallPackages(ctx, args, pkgs) -} - -// downloadPaths prepares the list of paths to pass to download. -// It expands ... patterns that can be expanded. If there is no match -// for a particular pattern, downloadPaths leaves it in the result list, -// in the hope that we can figure out the repository from the -// initial ...-free prefix. -func downloadPaths(patterns []string) []string { - for _, arg := range patterns { - if strings.Contains(arg, "@") { - base.Fatalf("go: can only use path@version syntax with 'go get' and 'go install' in module-aware mode") - } - - // Guard against 'go get x.go', a common mistake. - // Note that package and module paths may end with '.go', so only print an error - // if the argument has no slash or refers to an existing file. - if strings.HasSuffix(arg, ".go") { - if !strings.Contains(arg, "/") { - base.Errorf("go: %s: arguments must be package or module paths", arg) - continue - } - if fi, err := os.Stat(arg); err == nil && !fi.IsDir() { - base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", arg) - } - } - } - base.ExitIfErrors() - - var pkgs []string - noModRoots := []string{} - for _, m := range search.ImportPathsQuiet(patterns, noModRoots) { - if len(m.Pkgs) == 0 && strings.Contains(m.Pattern(), "...") { - pkgs = append(pkgs, m.Pattern()) - } else { - pkgs = append(pkgs, m.Pkgs...) - } - } - return pkgs -} - -// downloadCache records the import paths we have already -// considered during the download, to avoid duplicate work when -// there is more than one dependency sequence leading to -// a particular package. -var downloadCache = map[string]bool{} - -// downloadRootCache records the version control repository -// root directories we have already considered during the download. -// For example, all the packages in the github.com/google/codesearch repo -// share the same root (the directory for that path), and we only need -// to run the hg commands to consider each repository once. -var downloadRootCache = map[string]bool{} - -// download runs the download half of the get command -// for the package or pattern named by the argument. -func download(ctx context.Context, arg string, parent *load.Package, stk *load.ImportStack, mode int) { - if mode&load.ResolveImport != 0 { - // Caller is responsible for expanding vendor paths. - panic("internal error: download mode has useVendor set") - } - load1 := func(path string, mode int) *load.Package { - if parent == nil { - mode := 0 // don't do module or vendor resolution - return load.LoadPackage(ctx, load.PackageOpts{}, path, base.Cwd(), stk, nil, mode) - } - p, err := load.LoadImport(ctx, load.PackageOpts{}, path, parent.Dir, parent, stk, nil, mode|load.ResolveModule) - if err != nil { - base.Errorf("%s", err) - } - return p - } - - p := load1(arg, mode) - if p.Error != nil && p.Error.Hard { - base.Errorf("%s", p.Error) - return - } - - // loadPackage inferred the canonical ImportPath from arg. - // Use that in the following to prevent hysteresis effects - // in e.g. downloadCache and packageCache. - // This allows invocations such as: - // mkdir -p $GOPATH/src/github.com/user - // cd $GOPATH/src/github.com/user - // go get ./foo - // see: golang.org/issue/9767 - arg = p.ImportPath - - // There's nothing to do if this is a package in the standard library. - if p.Standard { - return - } - - // Only process each package once. - // (Unless we're fetching test dependencies for this package, - // in which case we want to process it again.) - if downloadCache[arg] && mode&load.GetTestDeps == 0 { - return - } - downloadCache[arg] = true - - pkgs := []*load.Package{p} - wildcardOkay := len(*stk) == 0 - isWildcard := false - - // Download if the package is missing, or update if we're using -u. - if p.Dir == "" || *getU { - // The actual download. - stk.Push(arg) - err := downloadPackage(p) - if err != nil { - base.Errorf("%s", &load.PackageError{ImportStack: stk.Copy(), Err: err}) - stk.Pop() - return - } - stk.Pop() - - args := []string{arg} - // If the argument has a wildcard in it, re-evaluate the wildcard. - // We delay this until after reloadPackage so that the old entry - // for p has been replaced in the package cache. - if wildcardOkay && strings.Contains(arg, "...") { - match := search.NewMatch(arg) - if match.IsLocal() { - noModRoots := []string{} // We're in gopath mode, so there are no modroots. - match.MatchDirs(noModRoots) - args = match.Dirs - } else { - match.MatchPackages() - args = match.Pkgs - } - for _, err := range match.Errs { - base.Errorf("%s", err) - } - isWildcard = true - } - - // Clear all relevant package cache entries before - // doing any new loads. - load.ClearPackageCachePartial(args) - - pkgs = pkgs[:0] - for _, arg := range args { - // Note: load calls loadPackage or loadImport, - // which push arg onto stk already. - // Do not push here too, or else stk will say arg imports arg. - p := load1(arg, mode) - if p.Error != nil { - base.Errorf("%s", p.Error) - continue - } - pkgs = append(pkgs, p) - } - } - - // Process package, which might now be multiple packages - // due to wildcard expansion. - for _, p := range pkgs { - if *getFix { - files := base.RelPaths(p.InternalAllGoFiles()) - base.Run(cfg.BuildToolexec, str.StringList(base.Tool("fix"), files)) - - // The imports might have changed, so reload again. - p = load.ReloadPackageNoFlags(arg, stk) - if p.Error != nil { - base.Errorf("%s", p.Error) - return - } - } - - if isWildcard { - // Report both the real package and the - // wildcard in any error message. - stk.Push(p.ImportPath) - } - - // Process dependencies, now that we know what they are. - imports := p.Imports - if mode&load.GetTestDeps != 0 { - // Process test dependencies when -t is specified. - // (But don't get test dependencies for test dependencies: - // we always pass mode 0 to the recursive calls below.) - imports = str.StringList(imports, p.TestImports, p.XTestImports) - } - for i, path := range imports { - if path == "C" { - continue - } - // Fail fast on import naming full vendor path. - // Otherwise expand path as needed for test imports. - // Note that p.Imports can have additional entries beyond p.Internal.Build.Imports. - orig := path - if i < len(p.Internal.Build.Imports) { - orig = p.Internal.Build.Imports[i] - } - if j, ok := load.FindVendor(orig); ok { - stk.Push(path) - err := &load.PackageError{ - ImportStack: stk.Copy(), - Err: load.ImportErrorf(path, "%s must be imported as %s", path, path[j+len("vendor/"):]), - } - stk.Pop() - base.Errorf("%s", err) - continue - } - // If this is a test import, apply module and vendor lookup now. - // We cannot pass ResolveImport to download, because - // download does caching based on the value of path, - // so it must be the fully qualified path already. - if i >= len(p.Imports) { - path = load.ResolveImportPath(p, path) - } - download(ctx, path, p, stk, 0) - } - - if isWildcard { - stk.Pop() - } - } -} - -// downloadPackage runs the create or download command -// to make the first copy of or update a copy of the given package. -func downloadPackage(p *load.Package) error { - var ( - vcsCmd *vcs.Cmd - repo, rootPath, repoDir string - err error - blindRepo bool // set if the repo has unusual configuration - ) - - // p can be either a real package, or a pseudo-package whose “import path” is - // actually a wildcard pattern. - // Trim the path at the element containing the first wildcard, - // and hope that it applies to the wildcarded parts too. - // This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH. - importPrefix := p.ImportPath - if i := strings.Index(importPrefix, "..."); i >= 0 { - slash := strings.LastIndexByte(importPrefix[:i], '/') - if slash < 0 { - return fmt.Errorf("cannot expand ... in %q", p.ImportPath) - } - importPrefix = importPrefix[:slash] - } - if err := checkImportPath(importPrefix); err != nil { - return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err) - } - security := web.SecureOnly - if module.MatchPrefixPatterns(cfg.GOINSECURE, importPrefix) { - security = web.Insecure - } - - if p.Internal.Build.SrcRoot != "" { - // Directory exists. Look for checkout along path to src. - const allowNesting = false - repoDir, vcsCmd, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot, allowNesting) - if err != nil { - return err - } - if !str.HasFilePathPrefix(repoDir, p.Internal.Build.SrcRoot) { - panic(fmt.Sprintf("repository %q not in source root %q", repo, p.Internal.Build.SrcRoot)) - } - rootPath = str.TrimFilePathPrefix(repoDir, p.Internal.Build.SrcRoot) - if err := vcs.CheckGOVCS(vcsCmd, rootPath); err != nil { - return err - } - - repo = "<local>" // should be unused; make distinctive - - // Double-check where it came from. - if *getU && vcsCmd.RemoteRepo != nil { - dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath)) - remote, err := vcsCmd.RemoteRepo(vcsCmd, dir) - if err != nil { - // Proceed anyway. The package is present; we likely just don't understand - // the repo configuration (e.g. unusual remote protocol). - blindRepo = true - } - repo = remote - if !*getF && err == nil { - if rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security); err == nil { - repo := rr.Repo - if rr.VCS.ResolveRepo != nil { - resolved, err := rr.VCS.ResolveRepo(rr.VCS, dir, repo) - if err == nil { - repo = resolved - } - } - if remote != repo && rr.IsCustom { - return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.Root, repo, dir, remote) - } - } - } - } - } else { - // Analyze the import path to determine the version control system, - // repository, and the import path for the root of the repository. - rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security) - if err != nil { - return err - } - vcsCmd, repo, rootPath = rr.VCS, rr.Repo, rr.Root - } - if !blindRepo && !vcsCmd.IsSecure(repo) && security != web.Insecure { - return fmt.Errorf("cannot download: %v uses insecure protocol", repo) - } - - if p.Internal.Build.SrcRoot == "" { - // Package not found. Put in first directory of $GOPATH. - list := filepath.SplitList(cfg.BuildContext.GOPATH) - if len(list) == 0 { - return fmt.Errorf("cannot download: $GOPATH not set. For more details see: 'go help gopath'") - } - // Guard against people setting GOPATH=$GOROOT. - if filepath.Clean(list[0]) == filepath.Clean(cfg.GOROOT) { - return fmt.Errorf("cannot download: $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'") - } - if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil { - return fmt.Errorf("cannot download: %s is a GOROOT, not a GOPATH. For more details see: 'go help gopath'", list[0]) - } - p.Internal.Build.Root = list[0] - p.Internal.Build.SrcRoot = filepath.Join(list[0], "src") - p.Internal.Build.PkgRoot = filepath.Join(list[0], "pkg") - } - root := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath)) - - if err := vcs.CheckNested(vcsCmd, root, p.Internal.Build.SrcRoot); err != nil { - return err - } - - // If we've considered this repository already, don't do it again. - if downloadRootCache[root] { - return nil - } - downloadRootCache[root] = true - - if cfg.BuildV { - fmt.Fprintf(os.Stderr, "%s (download)\n", rootPath) - } - - // Check that this is an appropriate place for the repo to be checked out. - // The target directory must either not exist or have a repo checked out already. - meta := filepath.Join(root, "."+vcsCmd.Cmd) - if _, err := os.Stat(meta); err != nil { - // Metadata file or directory does not exist. Prepare to checkout new copy. - // Some version control tools require the target directory not to exist. - // We require that too, just to avoid stepping on existing work. - if _, err := os.Stat(root); err == nil { - return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta) - } - - _, err := os.Stat(p.Internal.Build.Root) - gopathExisted := err == nil - - // Some version control tools require the parent of the target to exist. - parent, _ := filepath.Split(root) - if err = os.MkdirAll(parent, 0777); err != nil { - return err - } - if cfg.BuildV && !gopathExisted && p.Internal.Build.Root == cfg.BuildContext.GOPATH { - fmt.Fprintf(os.Stderr, "created GOPATH=%s; see 'go help gopath'\n", p.Internal.Build.Root) - } - - if err = vcsCmd.Create(root, repo); err != nil { - return err - } - } else { - // Metadata directory does exist; download incremental updates. - if err = vcsCmd.Download(root); err != nil { - return err - } - } - - if cfg.BuildN { - // Do not show tag sync in -n; it's noise more than anything, - // and since we're not running commands, no tag will be found. - // But avoid printing nothing. - fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcsCmd.Cmd) - return nil - } - - // Select and sync to appropriate version of the repository. - tags, err := vcsCmd.Tags(root) - if err != nil { - return err - } - vers := runtime.Version() - if i := strings.Index(vers, " "); i >= 0 { - vers = vers[:i] - } - if err := vcsCmd.TagSync(root, selectTag(vers, tags)); err != nil { - return err - } - - return nil -} - -// selectTag returns the closest matching tag for a given version. -// Closest means the latest one that is not after the current release. -// Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form. -// Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number). -// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD". -// -// NOTE(rsc): Eventually we will need to decide on some logic here. -// For now, there is only "go1". This matches the docs in go help get. -func selectTag(goVersion string, tags []string) (match string) { - for _, t := range tags { - if t == "go1" { - return "go1" - } - } - return "" -} - -// checkImportPath is like module.CheckImportPath, but it forbids leading dots -// in path elements. This can lead to 'go get' creating .git and other VCS -// directories in places we might run VCS tools later. -func checkImportPath(path string) error { - if err := module.CheckImportPath(path); err != nil { - return err - } - checkElem := func(elem string) error { - if elem[0] == '.' { - return fmt.Errorf("malformed import path %q: leading dot in path element", path) - } - return nil - } - elemStart := 0 - for i, r := range path { - if r == '/' { - if err := checkElem(path[elemStart:]); err != nil { - return err - } - elemStart = i + 1 - } - } - if err := checkElem(path[elemStart:]); err != nil { - return err - } - return nil -} |