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  	if len(args) == 0 {
    90  		args = []string{"all"}
    91  	} else if modload.HasModRoot() {
    92  		modload.LoadModFile(ctx) // to fill Target
    93  		targetAtUpgrade := modload.Target.Path + "@upgrade"
    94  		targetAtPatch := modload.Target.Path + "@patch"
    95  		for _, arg := range args {
    96  			switch arg {
    97  			case modload.Target.Path, targetAtUpgrade, targetAtPatch:
    98  				os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n")
    99  			}
   100  		}
   101  	}
   102  
   103  	downloadModule := func(m *moduleJSON) {
   104  		var err error
   105  		m.Info, err = modfetch.InfoFile(m.Path, m.Version)
   106  		if err != nil {
   107  			m.Error = err.Error()
   108  			return
   109  		}
   110  		m.GoMod, err = modfetch.GoModFile(m.Path, m.Version)
   111  		if err != nil {
   112  			m.Error = err.Error()
   113  			return
   114  		}
   115  		m.GoModSum, err = modfetch.GoModSum(m.Path, m.Version)
   116  		if err != nil {
   117  			m.Error = err.Error()
   118  			return
   119  		}
   120  		mod := module.Version{Path: m.Path, Version: m.Version}
   121  		m.Zip, err = modfetch.DownloadZip(ctx, mod)
   122  		if err != nil {
   123  			m.Error = err.Error()
   124  			return
   125  		}
   126  		m.Sum = modfetch.Sum(mod)
   127  		m.Dir, err = modfetch.Download(ctx, mod)
   128  		if err != nil {
   129  			m.Error = err.Error()
   130  			return
   131  		}
   132  	}
   133  
   134  	var mods []*moduleJSON
   135  	listU := false
   136  	listVersions := false
   137  	listRetractions := false
   138  	type token struct{}
   139  	sem := make(chan token, runtime.GOMAXPROCS(0))
   140  	for _, info := range modload.ListModules(ctx, args, listU, listVersions, listRetractions) {
   141  		if info.Replace != nil {
   142  			info = info.Replace
   143  		}
   144  		if info.Version == "" && info.Error == nil {
   145  			// main module or module replaced with file path.
   146  			// Nothing to download.
   147  			continue
   148  		}
   149  		m := &moduleJSON{
   150  			Path:    info.Path,
   151  			Version: info.Version,
   152  		}
   153  		mods = append(mods, m)
   154  		if info.Error != nil {
   155  			m.Error = info.Error.Err
   156  			continue
   157  		}
   158  		sem <- token{}
   159  		go func() {
   160  			downloadModule(m)
   161  			<-sem
   162  		}()
   163  	}
   164  
   165  	// Fill semaphore channel to wait for goroutines to finish.
   166  	for n := cap(sem); n > 0; n-- {
   167  		sem <- token{}
   168  	}
   169  
   170  	if *downloadJSON {
   171  		for _, m := range mods {
   172  			b, err := json.MarshalIndent(m, "", "\t")
   173  			if err != nil {
   174  				base.Fatalf("go mod download: %v", err)
   175  			}
   176  			os.Stdout.Write(append(b, '\n'))
   177  			if m.Error != "" {
   178  				base.SetExitStatus(1)
   179  			}
   180  		}
   181  	} else {
   182  		for _, m := range mods {
   183  			if m.Error != "" {
   184  				base.Errorf("go mod download: %v", m.Error)
   185  			}
   186  		}
   187  		base.ExitIfErrors()
   188  	}
   189  
   190  	// Update go.mod and especially go.sum if needed.
   191  	modload.WriteGoMod()
   192  }
   193  

View as plain text