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

     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  // go mod edit
     6  
     7  package modcmd
     8  
     9  import (
    10  	"bytes"
    11  	"context"
    12  	"encoding/json"
    13  	"errors"
    14  	"fmt"
    15  	"os"
    16  	"strings"
    17  
    18  	"cmd/go/internal/base"
    19  	"cmd/go/internal/gover"
    20  	"cmd/go/internal/lockedfile"
    21  	"cmd/go/internal/modfetch"
    22  	"cmd/go/internal/modload"
    23  
    24  	"golang.org/x/mod/modfile"
    25  	"golang.org/x/mod/module"
    26  )
    27  
    28  var cmdEdit = &base.Command{
    29  	UsageLine: "go mod edit [editing flags] [-fmt|-print|-json] [go.mod]",
    30  	Short:     "edit go.mod from tools or scripts",
    31  	Long: `
    32  Edit provides a command-line interface for editing go.mod,
    33  for use primarily by tools or scripts. It reads only go.mod;
    34  it does not look up information about the modules involved.
    35  By default, edit reads and writes the go.mod file of the main module,
    36  but a different target file can be specified after the editing flags.
    37  
    38  The editing flags specify a sequence of editing operations.
    39  
    40  The -fmt flag reformats the go.mod file without making other changes.
    41  This reformatting is also implied by any other modifications that use or
    42  rewrite the go.mod file. The only time this flag is needed is if no other
    43  flags are specified, as in 'go mod edit -fmt'.
    44  
    45  The -module flag changes the module's path (the go.mod file's module line).
    46  
    47  The -godebug=key=value flag adds a godebug key=value line,
    48  replacing any existing godebug lines with the given key.
    49  
    50  The -dropgodebug=key flag drops any existing godebug lines
    51  with the given key.
    52  
    53  The -require=path@version and -droprequire=path flags
    54  add and drop a requirement on the given module path and version.
    55  Note that -require overrides any existing requirements on path.
    56  These flags are mainly for tools that understand the module graph.
    57  Users should prefer 'go get path@version' or 'go get path@none',
    58  which make other go.mod adjustments as needed to satisfy
    59  constraints imposed by other modules.
    60  
    61  The -go=version flag sets the expected Go language version.
    62  This flag is mainly for tools that understand Go version dependencies.
    63  Users should prefer 'go get go@version'.
    64  
    65  The -toolchain=version flag sets the Go toolchain to use.
    66  This flag is mainly for tools that understand Go version dependencies.
    67  Users should prefer 'go get toolchain@version'.
    68  
    69  The -exclude=path@version and -dropexclude=path@version flags
    70  add and drop an exclusion for the given module path and version.
    71  Note that -exclude=path@version is a no-op if that exclusion already exists.
    72  
    73  The -replace=old[@v]=new[@v] flag adds a replacement of the given
    74  module path and version pair. If the @v in old@v is omitted, a
    75  replacement without a version on the left side is added, which applies
    76  to all versions of the old module path. If the @v in new@v is omitted,
    77  the new path should be a local module root directory, not a module
    78  path. Note that -replace overrides any redundant replacements for old[@v],
    79  so omitting @v will drop existing replacements for specific versions.
    80  
    81  The -dropreplace=old[@v] flag drops a replacement of the given
    82  module path and version pair. If the @v is omitted, a replacement without
    83  a version on the left side is dropped.
    84  
    85  The -retract=version and -dropretract=version flags add and drop a
    86  retraction on the given version. The version may be a single version
    87  like "v1.2.3" or a closed interval like "[v1.1.0,v1.1.9]". Note that
    88  -retract=version is a no-op if that retraction already exists.
    89  
    90  The -tool=path and -droptool=path flags add and drop a tool declaration
    91  for the given path.
    92  
    93  The -ignore=path and -dropignore=path flags add and drop a ignore declaration
    94  for the given path.
    95  
    96  The -godebug, -dropgodebug, -require, -droprequire, -exclude, -dropexclude,
    97  -replace, -dropreplace, -retract, -dropretract, -tool, -droptool, -ignore,
    98  and -dropignore editing flags may be repeated, and the changes are applied
    99  in the order given.
   100  
   101  The -print flag prints the final go.mod in its text format instead of
   102  writing it back to go.mod.
   103  
   104  The -json flag prints the final go.mod file in JSON format instead of
   105  writing it back to go.mod. The JSON output corresponds to these Go types:
   106  
   107  	type Module struct {
   108  		Path    string
   109  		Version string
   110  	}
   111  
   112  	type GoMod struct {
   113  		Module    ModPath
   114  		Go        string
   115  		Toolchain string
   116  		Godebug   []Godebug
   117  		Require   []Require
   118  		Exclude   []Module
   119  		Replace   []Replace
   120  		Retract   []Retract
   121  	}
   122  
   123  	type ModPath struct {
   124  		Path       string
   125  		Deprecated string
   126  	}
   127  
   128  	type Godebug struct {
   129  		Key   string
   130  		Value string
   131  	}
   132  
   133  	type Require struct {
   134  		Path     string
   135  		Version  string
   136  		Indirect bool
   137  	}
   138  
   139  	type Replace struct {
   140  		Old Module
   141  		New Module
   142  	}
   143  
   144  	type Retract struct {
   145  		Low       string
   146  		High      string
   147  		Rationale string
   148  	}
   149  
   150  	type Tool struct {
   151  		Path string
   152  	}
   153  
   154  	type Ignore struct {
   155  		Path string
   156  	}
   157  
   158  Retract entries representing a single version (not an interval) will have
   159  the "Low" and "High" fields set to the same value.
   160  
   161  Note that this only describes the go.mod file itself, not other modules
   162  referred to indirectly. For the full set of modules available to a build,
   163  use 'go list -m -json all'.
   164  
   165  Edit also provides the -C, -n, and -x build flags.
   166  
   167  See https://golang.org/ref/mod#go-mod-edit for more about 'go mod edit'.
   168  	`,
   169  }
   170  
   171  var (
   172  	editFmt       = cmdEdit.Flag.Bool("fmt", false, "")
   173  	editGo        = cmdEdit.Flag.String("go", "", "")
   174  	editToolchain = cmdEdit.Flag.String("toolchain", "", "")
   175  	editJSON      = cmdEdit.Flag.Bool("json", false, "")
   176  	editPrint     = cmdEdit.Flag.Bool("print", false, "")
   177  	editModule    = cmdEdit.Flag.String("module", "", "")
   178  	edits         []func(*modfile.File) // edits specified in flags
   179  )
   180  
   181  type flagFunc func(string)
   182  
   183  func (f flagFunc) String() string     { return "" }
   184  func (f flagFunc) Set(s string) error { f(s); return nil }
   185  
   186  func init() {
   187  	cmdEdit.Run = runEdit // break init cycle
   188  
   189  	cmdEdit.Flag.Var(flagFunc(flagGodebug), "godebug", "")
   190  	cmdEdit.Flag.Var(flagFunc(flagDropGodebug), "dropgodebug", "")
   191  	cmdEdit.Flag.Var(flagFunc(flagRequire), "require", "")
   192  	cmdEdit.Flag.Var(flagFunc(flagDropRequire), "droprequire", "")
   193  	cmdEdit.Flag.Var(flagFunc(flagExclude), "exclude", "")
   194  	cmdEdit.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
   195  	cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "")
   196  	cmdEdit.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "")
   197  	cmdEdit.Flag.Var(flagFunc(flagRetract), "retract", "")
   198  	cmdEdit.Flag.Var(flagFunc(flagDropRetract), "dropretract", "")
   199  	cmdEdit.Flag.Var(flagFunc(flagTool), "tool", "")
   200  	cmdEdit.Flag.Var(flagFunc(flagDropTool), "droptool", "")
   201  	cmdEdit.Flag.Var(flagFunc(flagIgnore), "ignore", "")
   202  	cmdEdit.Flag.Var(flagFunc(flagDropIgnore), "dropignore", "")
   203  
   204  	base.AddBuildFlagsNX(&cmdEdit.Flag)
   205  	base.AddChdirFlag(&cmdEdit.Flag)
   206  	base.AddModCommonFlags(&cmdEdit.Flag)
   207  }
   208  
   209  func runEdit(ctx context.Context, cmd *base.Command, args []string) {
   210  	anyFlags := *editModule != "" ||
   211  		*editGo != "" ||
   212  		*editToolchain != "" ||
   213  		*editJSON ||
   214  		*editPrint ||
   215  		*editFmt ||
   216  		len(edits) > 0
   217  
   218  	if !anyFlags {
   219  		base.Fatalf("go: no flags specified (see 'go help mod edit').")
   220  	}
   221  
   222  	if *editJSON && *editPrint {
   223  		base.Fatalf("go: cannot use both -json and -print")
   224  	}
   225  
   226  	if len(args) > 1 {
   227  		base.Fatalf("go: too many arguments")
   228  	}
   229  	var gomod string
   230  	if len(args) == 1 {
   231  		gomod = args[0]
   232  	} else {
   233  		gomod = modload.ModFilePath()
   234  	}
   235  
   236  	if *editModule != "" {
   237  		if err := module.CheckImportPath(*editModule); err != nil {
   238  			base.Fatalf("go: invalid -module: %v", err)
   239  		}
   240  	}
   241  
   242  	if *editGo != "" && *editGo != "none" {
   243  		if !modfile.GoVersionRE.MatchString(*editGo) {
   244  			base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, gover.Local())
   245  		}
   246  	}
   247  	if *editToolchain != "" && *editToolchain != "none" {
   248  		if !modfile.ToolchainRE.MatchString(*editToolchain) {
   249  			base.Fatalf(`go mod: invalid -toolchain option; expecting something like "-toolchain go%s"`, gover.Local())
   250  		}
   251  	}
   252  
   253  	data, err := lockedfile.Read(gomod)
   254  	if err != nil {
   255  		base.Fatal(err)
   256  	}
   257  
   258  	modFile, err := modfile.Parse(gomod, data, nil)
   259  	if err != nil {
   260  		base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gomod), err)
   261  	}
   262  
   263  	if *editModule != "" {
   264  		modFile.AddModuleStmt(*editModule)
   265  	}
   266  
   267  	if *editGo == "none" {
   268  		modFile.DropGoStmt()
   269  	} else if *editGo != "" {
   270  		if err := modFile.AddGoStmt(*editGo); err != nil {
   271  			base.Fatalf("go: internal error: %v", err)
   272  		}
   273  	}
   274  	if *editToolchain == "none" {
   275  		modFile.DropToolchainStmt()
   276  	} else if *editToolchain != "" {
   277  		if err := modFile.AddToolchainStmt(*editToolchain); err != nil {
   278  			base.Fatalf("go: internal error: %v", err)
   279  		}
   280  	}
   281  
   282  	if len(edits) > 0 {
   283  		for _, edit := range edits {
   284  			edit(modFile)
   285  		}
   286  	}
   287  	modFile.SortBlocks()
   288  	modFile.Cleanup() // clean file after edits
   289  
   290  	if *editJSON {
   291  		editPrintJSON(modFile)
   292  		return
   293  	}
   294  
   295  	out, err := modFile.Format()
   296  	if err != nil {
   297  		base.Fatal(err)
   298  	}
   299  
   300  	if *editPrint {
   301  		os.Stdout.Write(out)
   302  		return
   303  	}
   304  
   305  	// Make a best-effort attempt to acquire the side lock, only to exclude
   306  	// previous versions of the 'go' command from making simultaneous edits.
   307  	if unlock, err := modfetch.SideLock(ctx); err == nil {
   308  		defer unlock()
   309  	}
   310  
   311  	err = lockedfile.Transform(gomod, func(lockedData []byte) ([]byte, error) {
   312  		if !bytes.Equal(lockedData, data) {
   313  			return nil, errors.New("go.mod changed during editing; not overwriting")
   314  		}
   315  		return out, nil
   316  	})
   317  	if err != nil {
   318  		base.Fatal(err)
   319  	}
   320  }
   321  
   322  // parsePathVersion parses -flag=arg expecting arg to be path@version.
   323  func parsePathVersion(flag, arg string) (path, version string) {
   324  	before, after, found := strings.Cut(arg, "@")
   325  	if !found {
   326  		base.Fatalf("go: -%s=%s: need path@version", flag, arg)
   327  	}
   328  	path, version = strings.TrimSpace(before), strings.TrimSpace(after)
   329  	if err := module.CheckImportPath(path); err != nil {
   330  		base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
   331  	}
   332  
   333  	if !allowedVersionArg(version) {
   334  		base.Fatalf("go: -%s=%s: invalid version %q", flag, arg, version)
   335  	}
   336  
   337  	return path, version
   338  }
   339  
   340  // parsePath parses -flag=arg expecting arg to be path (not path@version).
   341  func parsePath(flag, arg string) (path string) {
   342  	if strings.Contains(arg, "@") {
   343  		base.Fatalf("go: -%s=%s: need just path, not path@version", flag, arg)
   344  	}
   345  	path = arg
   346  	if err := module.CheckImportPath(path); err != nil {
   347  		base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
   348  	}
   349  	return path
   350  }
   351  
   352  // parsePathVersionOptional parses path[@version], using adj to
   353  // describe any errors.
   354  func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) {
   355  	if allowDirPath && modfile.IsDirectoryPath(arg) {
   356  		return arg, "", nil
   357  	}
   358  	before, after, found := strings.Cut(arg, "@")
   359  	if !found {
   360  		path = arg
   361  	} else {
   362  		path, version = strings.TrimSpace(before), strings.TrimSpace(after)
   363  	}
   364  	if err := module.CheckImportPath(path); err != nil {
   365  		return path, version, fmt.Errorf("invalid %s path: %v", adj, err)
   366  	}
   367  	if path != arg && !allowedVersionArg(version) {
   368  		return path, version, fmt.Errorf("invalid %s version: %q", adj, version)
   369  	}
   370  	return path, version, nil
   371  }
   372  
   373  // parseVersionInterval parses a single version like "v1.2.3" or a closed
   374  // interval like "[v1.2.3,v1.4.5]". Note that a single version has the same
   375  // representation as an interval with equal upper and lower bounds: both
   376  // Low and High are set.
   377  func parseVersionInterval(arg string) (modfile.VersionInterval, error) {
   378  	if !strings.HasPrefix(arg, "[") {
   379  		if !allowedVersionArg(arg) {
   380  			return modfile.VersionInterval{}, fmt.Errorf("invalid version: %q", arg)
   381  		}
   382  		return modfile.VersionInterval{Low: arg, High: arg}, nil
   383  	}
   384  	if !strings.HasSuffix(arg, "]") {
   385  		return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
   386  	}
   387  	s := arg[1 : len(arg)-1]
   388  	before, after, found := strings.Cut(s, ",")
   389  	if !found {
   390  		return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
   391  	}
   392  	low := strings.TrimSpace(before)
   393  	high := strings.TrimSpace(after)
   394  	if !allowedVersionArg(low) || !allowedVersionArg(high) {
   395  		return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
   396  	}
   397  	return modfile.VersionInterval{Low: low, High: high}, nil
   398  }
   399  
   400  // allowedVersionArg returns whether a token may be used as a version in go.mod.
   401  // We don't call modfile.CheckPathVersion, because that insists on versions
   402  // being in semver form, but here we want to allow versions like "master" or
   403  // "1234abcdef", which the go command will resolve the next time it runs (or
   404  // during -fix).  Even so, we need to make sure the version is a valid token.
   405  func allowedVersionArg(arg string) bool {
   406  	return !modfile.MustQuote(arg)
   407  }
   408  
   409  // flagGodebug implements the -godebug flag.
   410  func flagGodebug(arg string) {
   411  	key, value, ok := strings.Cut(arg, "=")
   412  	if !ok || strings.ContainsAny(arg, "\"`',") {
   413  		base.Fatalf("go: -godebug=%s: need key=value", arg)
   414  	}
   415  	edits = append(edits, func(f *modfile.File) {
   416  		if err := f.AddGodebug(key, value); err != nil {
   417  			base.Fatalf("go: -godebug=%s: %v", arg, err)
   418  		}
   419  	})
   420  }
   421  
   422  // flagDropGodebug implements the -dropgodebug flag.
   423  func flagDropGodebug(arg string) {
   424  	edits = append(edits, func(f *modfile.File) {
   425  		if err := f.DropGodebug(arg); err != nil {
   426  			base.Fatalf("go: -dropgodebug=%s: %v", arg, err)
   427  		}
   428  	})
   429  }
   430  
   431  // flagRequire implements the -require flag.
   432  func flagRequire(arg string) {
   433  	path, version := parsePathVersion("require", arg)
   434  	edits = append(edits, func(f *modfile.File) {
   435  		if err := f.AddRequire(path, version); err != nil {
   436  			base.Fatalf("go: -require=%s: %v", arg, err)
   437  		}
   438  	})
   439  }
   440  
   441  // flagDropRequire implements the -droprequire flag.
   442  func flagDropRequire(arg string) {
   443  	path := parsePath("droprequire", arg)
   444  	edits = append(edits, func(f *modfile.File) {
   445  		if err := f.DropRequire(path); err != nil {
   446  			base.Fatalf("go: -droprequire=%s: %v", arg, err)
   447  		}
   448  	})
   449  }
   450  
   451  // flagExclude implements the -exclude flag.
   452  func flagExclude(arg string) {
   453  	path, version := parsePathVersion("exclude", arg)
   454  	edits = append(edits, func(f *modfile.File) {
   455  		if err := f.AddExclude(path, version); err != nil {
   456  			base.Fatalf("go: -exclude=%s: %v", arg, err)
   457  		}
   458  	})
   459  }
   460  
   461  // flagDropExclude implements the -dropexclude flag.
   462  func flagDropExclude(arg string) {
   463  	path, version := parsePathVersion("dropexclude", arg)
   464  	edits = append(edits, func(f *modfile.File) {
   465  		if err := f.DropExclude(path, version); err != nil {
   466  			base.Fatalf("go: -dropexclude=%s: %v", arg, err)
   467  		}
   468  	})
   469  }
   470  
   471  // flagReplace implements the -replace flag.
   472  func flagReplace(arg string) {
   473  	before, after, found := strings.Cut(arg, "=")
   474  	if !found {
   475  		base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
   476  	}
   477  	old, new := strings.TrimSpace(before), strings.TrimSpace(after)
   478  	if strings.HasPrefix(new, ">") {
   479  		base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg)
   480  	}
   481  	oldPath, oldVersion, err := parsePathVersionOptional("old", old, false)
   482  	if err != nil {
   483  		base.Fatalf("go: -replace=%s: %v", arg, err)
   484  	}
   485  	newPath, newVersion, err := parsePathVersionOptional("new", new, true)
   486  	if err != nil {
   487  		base.Fatalf("go: -replace=%s: %v", arg, err)
   488  	}
   489  	if newPath == new && !modfile.IsDirectoryPath(new) {
   490  		base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg)
   491  	}
   492  
   493  	edits = append(edits, func(f *modfile.File) {
   494  		if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
   495  			base.Fatalf("go: -replace=%s: %v", arg, err)
   496  		}
   497  	})
   498  }
   499  
   500  // flagDropReplace implements the -dropreplace flag.
   501  func flagDropReplace(arg string) {
   502  	path, version, err := parsePathVersionOptional("old", arg, true)
   503  	if err != nil {
   504  		base.Fatalf("go: -dropreplace=%s: %v", arg, err)
   505  	}
   506  	edits = append(edits, func(f *modfile.File) {
   507  		if err := f.DropReplace(path, version); err != nil {
   508  			base.Fatalf("go: -dropreplace=%s: %v", arg, err)
   509  		}
   510  	})
   511  }
   512  
   513  // flagRetract implements the -retract flag.
   514  func flagRetract(arg string) {
   515  	vi, err := parseVersionInterval(arg)
   516  	if err != nil {
   517  		base.Fatalf("go: -retract=%s: %v", arg, err)
   518  	}
   519  	edits = append(edits, func(f *modfile.File) {
   520  		if err := f.AddRetract(vi, ""); err != nil {
   521  			base.Fatalf("go: -retract=%s: %v", arg, err)
   522  		}
   523  	})
   524  }
   525  
   526  // flagDropRetract implements the -dropretract flag.
   527  func flagDropRetract(arg string) {
   528  	vi, err := parseVersionInterval(arg)
   529  	if err != nil {
   530  		base.Fatalf("go: -dropretract=%s: %v", arg, err)
   531  	}
   532  	edits = append(edits, func(f *modfile.File) {
   533  		if err := f.DropRetract(vi); err != nil {
   534  			base.Fatalf("go: -dropretract=%s: %v", arg, err)
   535  		}
   536  	})
   537  }
   538  
   539  // flagTool implements the -tool flag.
   540  func flagTool(arg string) {
   541  	path := parsePath("tool", arg)
   542  	edits = append(edits, func(f *modfile.File) {
   543  		if err := f.AddTool(path); err != nil {
   544  			base.Fatalf("go: -tool=%s: %v", arg, err)
   545  		}
   546  	})
   547  }
   548  
   549  // flagDropTool implements the -droptool flag.
   550  func flagDropTool(arg string) {
   551  	path := parsePath("droptool", arg)
   552  	edits = append(edits, func(f *modfile.File) {
   553  		if err := f.DropTool(path); err != nil {
   554  			base.Fatalf("go: -droptool=%s: %v", arg, err)
   555  		}
   556  	})
   557  }
   558  
   559  // flagIgnore implements the -ignore flag.
   560  func flagIgnore(arg string) {
   561  	edits = append(edits, func(f *modfile.File) {
   562  		if err := f.AddIgnore(arg); err != nil {
   563  			base.Fatalf("go: -ignore=%s: %v", arg, err)
   564  		}
   565  	})
   566  }
   567  
   568  // flagDropIgnore implements the -dropignore flag.
   569  func flagDropIgnore(arg string) {
   570  	edits = append(edits, func(f *modfile.File) {
   571  		if err := f.DropIgnore(arg); err != nil {
   572  			base.Fatalf("go: -dropignore=%s: %v", arg, err)
   573  		}
   574  	})
   575  }
   576  
   577  // fileJSON is the -json output data structure.
   578  type fileJSON struct {
   579  	Module    editModuleJSON
   580  	Go        string `json:",omitempty"`
   581  	Toolchain string `json:",omitempty"`
   582  	Require   []requireJSON
   583  	Exclude   []module.Version
   584  	Replace   []replaceJSON
   585  	Retract   []retractJSON
   586  	Tool      []toolJSON
   587  	Ignore    []ignoreJSON
   588  }
   589  
   590  type editModuleJSON struct {
   591  	Path       string
   592  	Deprecated string `json:",omitempty"`
   593  }
   594  
   595  type requireJSON struct {
   596  	Path     string
   597  	Version  string `json:",omitempty"`
   598  	Indirect bool   `json:",omitempty"`
   599  }
   600  
   601  type replaceJSON struct {
   602  	Old module.Version
   603  	New module.Version
   604  }
   605  
   606  type retractJSON struct {
   607  	Low       string `json:",omitempty"`
   608  	High      string `json:",omitempty"`
   609  	Rationale string `json:",omitempty"`
   610  }
   611  
   612  type toolJSON struct {
   613  	Path string
   614  }
   615  
   616  type ignoreJSON struct {
   617  	Path string
   618  }
   619  
   620  // editPrintJSON prints the -json output.
   621  func editPrintJSON(modFile *modfile.File) {
   622  	var f fileJSON
   623  	if modFile.Module != nil {
   624  		f.Module = editModuleJSON{
   625  			Path:       modFile.Module.Mod.Path,
   626  			Deprecated: modFile.Module.Deprecated,
   627  		}
   628  	}
   629  	if modFile.Go != nil {
   630  		f.Go = modFile.Go.Version
   631  	}
   632  	if modFile.Toolchain != nil {
   633  		f.Toolchain = modFile.Toolchain.Name
   634  	}
   635  	for _, r := range modFile.Require {
   636  		f.Require = append(f.Require, requireJSON{Path: r.Mod.Path, Version: r.Mod.Version, Indirect: r.Indirect})
   637  	}
   638  	for _, x := range modFile.Exclude {
   639  		f.Exclude = append(f.Exclude, x.Mod)
   640  	}
   641  	for _, r := range modFile.Replace {
   642  		f.Replace = append(f.Replace, replaceJSON{r.Old, r.New})
   643  	}
   644  	for _, r := range modFile.Retract {
   645  		f.Retract = append(f.Retract, retractJSON{r.Low, r.High, r.Rationale})
   646  	}
   647  	for _, t := range modFile.Tool {
   648  		f.Tool = append(f.Tool, toolJSON{t.Path})
   649  	}
   650  	for _, i := range modFile.Ignore {
   651  		f.Ignore = append(f.Ignore, ignoreJSON{i.Path})
   652  	}
   653  	data, err := json.MarshalIndent(&f, "", "\t")
   654  	if err != nil {
   655  		base.Fatalf("go: internal error: %v", err)
   656  	}
   657  	data = append(data, '\n')
   658  	os.Stdout.Write(data)
   659  }
   660  

View as plain text