Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/go/internal/modcmd/download.go

Documentation: cmd/go/internal/modcmd

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package modcmd
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"os"
    11  	"runtime"
    12  
    13  	"cmd/go/internal/base"
    14  	"cmd/go/internal/cfg"
    15  	"cmd/go/internal/modfetch"
    16  	"cmd/go/internal/modload"
    17  
    18  	"golang.org/x/mod/module"
    19  )
    20  
    21  var cmdDownload = &base.Command{
    22  	UsageLine: "go mod download [-x] [-json] [modules]",
    23  	Short:     "download modules to local cache",
    24  	Long: `
    25  Download downloads the named modules, which can be module patterns selecting
    26  dependencies of the main module or module queries of the form path@version.
    27  With no arguments, download applies to all dependencies of the main module
    28  (equivalent to 'go mod download all').
    29  
    30  The go command will automatically download modules as needed during ordinary
    31  execution. The "go mod download" command is useful mainly for pre-filling
    32  the local cache or to compute the answers for a Go module proxy.
    33  
    34  By default, download writes nothing to standard output. It may print progress
    35  messages and errors to standard error.
    36  
    37  The -json flag causes download to print a sequence of JSON objects
    38  to standard output, describing each downloaded module (or failure),
    39  corresponding to this Go struct:
    40  
    41      type Module struct {
    42          Path     string // module path
    43          Version  string // module version
    44          Error    string // error loading module
    45          Info     string // absolute path to cached .info file
    46          GoMod    string // absolute path to cached .mod file
    47          Zip      string // absolute path to cached .zip file
    48          Dir      string // absolute path to cached source root directory
    49          Sum      string // checksum for path, version (as in go.sum)
    50          GoModSum string // checksum for go.mod (as in go.sum)
    51      }
    52  
    53  The -x flag causes download to print the commands download executes.
    54  
    55  See https://golang.org/ref/mod#go-mod-download for more about 'go mod download'.
    56  
    57  See https://golang.org/ref/mod#version-queries for more about version queries.
    58  	`,
    59  }
    60  
    61  var downloadJSON = cmdDownload.Flag.Bool("json", false, "")
    62  
    63  func init() {
    64  	cmdDownload.Run = runDownload // break init cycle
    65  
    66  	// TODO(jayconrod): https://golang.org/issue/35849 Apply -x to other 'go mod' commands.
    67  	cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
    68  	base.AddModCommonFlags(&cmdDownload.Flag)
    69  }
    70  
    71  type moduleJSON struct {
    72  	Path     string `json:",omitempty"`
    73  	Version  string `json:",omitempty"`
    74  	Error    string `json:",omitempty"`
    75  	Info     string `json:",omitempty"`
    76  	GoMod    string `json:",omitempty"`
    77  	Zip      string `json:",omitempty"`
    78  	Dir      string `json:",omitempty"`
    79  	Sum      string `json:",omitempty"`
    80  	GoModSum string `json:",omitempty"`
    81  }
    82  
    83  func runDownload(ctx context.Context, cmd *base.Command, args []string) {
    84  	// Check whether modules are enabled and whether we're in a module.
    85  	modload.ForceUseModules = true
    86  	if !modload.HasModRoot() && len(args) == 0 {
    87  		base.Fatalf("go mod download: no modules specified (see 'go help mod download')")
    88  	}
    89  	haveExplicitArgs := len(args) > 0
    90  	if !haveExplicitArgs {
    91  		args = []string{"all"}
    92  	}
    93  	if modload.HasModRoot() {
    94  		modload.LoadModFile(ctx) // to fill Target
    95  		targetAtUpgrade := modload.Target.Path + "@upgrade"
    96  		targetAtPatch := modload.Target.Path + "@patch"
    97  		for _, arg := range args {
    98  			switch arg {
    99  			case modload.Target.Path, targetAtUpgrade, targetAtPatch:
   100  				os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n")
   101  			}
   102  		}
   103  	}
   104  
   105  	downloadModule := func(m *moduleJSON) {
   106  		var err error
   107  		m.Info, err = modfetch.InfoFile(m.Path, m.Version)
   108  		if err != nil {
   109  			m.Error = err.Error()
   110  			return
   111  		}
   112  		m.GoMod, err = modfetch.GoModFile(m.Path, m.Version)
   113  		if err != nil {
   114  			m.Error = err.Error()
   115  			return
   116  		}
   117  		m.GoModSum, err = modfetch.GoModSum(m.Path, m.Version)
   118  		if err != nil {
   119  			m.Error = err.Error()
   120  			return
   121  		}
   122  		mod := module.Version{Path: m.Path, Version: m.Version}
   123  		m.Zip, err = modfetch.DownloadZip(ctx, mod)
   124  		if err != nil {
   125  			m.Error = err.Error()
   126  			return
   127  		}
   128  		m.Sum = modfetch.Sum(mod)
   129  		m.Dir, err = modfetch.Download(ctx, mod)
   130  		if err != nil {
   131  			m.Error = err.Error()
   132  			return
   133  		}
   134  	}
   135  
   136  	var mods []*moduleJSON
   137  	type token struct{}
   138  	sem := make(chan token, runtime.GOMAXPROCS(0))
   139  	infos, infosErr := modload.ListModules(ctx, args, 0)
   140  	if !haveExplicitArgs {
   141  		// 'go mod download' is sometimes run without arguments to pre-populate the
   142  		// module cache. It may fetch modules that aren't needed to build packages
   143  		// in the main mdoule. This is usually not intended, so don't save sums for
   144  		// downloaded modules (golang.org/issue/45332).
   145  		// TODO(golang.org/issue/45551): For now, in ListModules, save sums needed
   146  		// to load the build list (same as 1.15 behavior). In the future, report an
   147  		// error if go.mod or go.sum need to be updated after loading the build
   148  		// list.
   149  		modload.DisallowWriteGoMod()
   150  	}
   151  
   152  	for _, info := range infos {
   153  		if info.Replace != nil {
   154  			info = info.Replace
   155  		}
   156  		if info.Version == "" && info.Error == nil {
   157  			// main module or module replaced with file path.
   158  			// Nothing to download.
   159  			continue
   160  		}
   161  		m := &moduleJSON{
   162  			Path:    info.Path,
   163  			Version: info.Version,
   164  		}
   165  		mods = append(mods, m)
   166  		if info.Error != nil {
   167  			m.Error = info.Error.Err
   168  			continue
   169  		}
   170  		sem <- token{}
   171  		go func() {
   172  			downloadModule(m)
   173  			<-sem
   174  		}()
   175  	}
   176  
   177  	// Fill semaphore channel to wait for goroutines to finish.
   178  	for n := cap(sem); n > 0; n-- {
   179  		sem <- token{}
   180  	}
   181  
   182  	if *downloadJSON {
   183  		for _, m := range mods {
   184  			b, err := json.MarshalIndent(m, "", "\t")
   185  			if err != nil {
   186  				base.Fatalf("go mod download: %v", err)
   187  			}
   188  			os.Stdout.Write(append(b, '\n'))
   189  			if m.Error != "" {
   190  				base.SetExitStatus(1)
   191  			}
   192  		}
   193  	} else {
   194  		for _, m := range mods {
   195  			if m.Error != "" {
   196  				base.Errorf("go mod download: %v", m.Error)
   197  			}
   198  		}
   199  		base.ExitIfErrors()
   200  	}
   201  
   202  	// If there were explicit arguments, update go.mod and especially go.sum.
   203  	// 'go mod download mod@version' is a useful way to add a sum without using
   204  	// 'go get mod@version', which may have other side effects. We print this in
   205  	// some error message hints.
   206  	//
   207  	// Don't save sums for 'go mod download' without arguments; see comment above.
   208  	if haveExplicitArgs {
   209  		modload.WriteGoMod(ctx)
   210  	}
   211  
   212  	// If there was an error matching some of the requested packages, emit it now
   213  	// (after we've written the checksums for the modules that were downloaded
   214  	// successfully).
   215  	if infosErr != nil {
   216  		base.Errorf("go mod download: %v", infosErr)
   217  	}
   218  }
   219  

View as plain text