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  		err := module.CheckImportPath(*editModule)
   238  		if err == nil {
   239  			err = modload.CheckReservedModulePath(*editModule)
   240  		}
   241  		if err != nil {
   242  			base.Fatalf("go: invalid -module: %v", err)
   243  		}
   244  	}
   245  
   246  	if *editGo != "" && *editGo != "none" {
   247  		if !modfile.GoVersionRE.MatchString(*editGo) {
   248  			base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, gover.Local())
   249  		}
   250  	}
   251  	if *editToolchain != "" && *editToolchain != "none" {
   252  		if !modfile.ToolchainRE.MatchString(*editToolchain) {
   253  			base.Fatalf(`go mod: invalid -toolchain option; expecting something like "-toolchain go%s"`, gover.Local())
   254  		}
   255  	}
   256  
   257  	data, err := lockedfile.Read(gomod)
   258  	if err != nil {
   259  		base.Fatal(err)
   260  	}
   261  
   262  	modFile, err := modfile.Parse(gomod, data, nil)
   263  	if err != nil {
   264  		base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gomod), err)
   265  	}
   266  
   267  	if *editModule != "" {
   268  		modFile.AddModuleStmt(*editModule)
   269  	}
   270  
   271  	if *editGo == "none" {
   272  		modFile.DropGoStmt()
   273  	} else if *editGo != "" {
   274  		if err := modFile.AddGoStmt(*editGo); err != nil {
   275  			base.Fatalf("go: internal error: %v", err)
   276  		}
   277  	}
   278  	if *editToolchain == "none" {
   279  		modFile.DropToolchainStmt()
   280  	} else if *editToolchain != "" {
   281  		if err := modFile.AddToolchainStmt(*editToolchain); err != nil {
   282  			base.Fatalf("go: internal error: %v", err)
   283  		}
   284  	}
   285  
   286  	if len(edits) > 0 {
   287  		for _, edit := range edits {
   288  			edit(modFile)
   289  		}
   290  	}
   291  	modFile.SortBlocks()
   292  	modFile.Cleanup() // clean file after edits
   293  
   294  	if *editJSON {
   295  		editPrintJSON(modFile)
   296  		return
   297  	}
   298  
   299  	out, err := modFile.Format()
   300  	if err != nil {
   301  		base.Fatal(err)
   302  	}
   303  
   304  	if *editPrint {
   305  		os.Stdout.Write(out)
   306  		return
   307  	}
   308  
   309  	// Make a best-effort attempt to acquire the side lock, only to exclude
   310  	// previous versions of the 'go' command from making simultaneous edits.
   311  	if unlock, err := modfetch.SideLock(ctx); err == nil {
   312  		defer unlock()
   313  	}
   314  
   315  	err = lockedfile.Transform(gomod, func(lockedData []byte) ([]byte, error) {
   316  		if !bytes.Equal(lockedData, data) {
   317  			return nil, errors.New("go.mod changed during editing; not overwriting")
   318  		}
   319  		return out, nil
   320  	})
   321  	if err != nil {
   322  		base.Fatal(err)
   323  	}
   324  }
   325  
   326  // parsePathVersion parses -flag=arg expecting arg to be path@version.
   327  func parsePathVersion(flag, arg string) (path, version string) {
   328  	before, after, found := strings.Cut(arg, "@")
   329  	if !found {
   330  		base.Fatalf("go: -%s=%s: need path@version", flag, arg)
   331  	}
   332  	path, version = strings.TrimSpace(before), strings.TrimSpace(after)
   333  	if err := module.CheckImportPath(path); err != nil {
   334  		base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
   335  	}
   336  
   337  	if !allowedVersionArg(version) {
   338  		base.Fatalf("go: -%s=%s: invalid version %q", flag, arg, version)
   339  	}
   340  
   341  	return path, version
   342  }
   343  
   344  // parsePath parses -flag=arg expecting arg to be path (not path@version).
   345  func parsePath(flag, arg string) (path string) {
   346  	if strings.Contains(arg, "@") {
   347  		base.Fatalf("go: -%s=%s: need just path, not path@version", flag, arg)
   348  	}
   349  	path = arg
   350  	if err := module.CheckImportPath(path); err != nil {
   351  		base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
   352  	}
   353  	return path
   354  }
   355  
   356  // parsePathVersionOptional parses path[@version], using adj to
   357  // describe any errors.
   358  func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) {
   359  	if allowDirPath && modfile.IsDirectoryPath(arg) {
   360  		return arg, "", nil
   361  	}
   362  	before, after, found := strings.Cut(arg, "@")
   363  	if !found {
   364  		path = arg
   365  	} else {
   366  		path, version = strings.TrimSpace(before), strings.TrimSpace(after)
   367  	}
   368  	if err := module.CheckImportPath(path); err != nil {
   369  		return path, version, fmt.Errorf("invalid %s path: %v", adj, err)
   370  	}
   371  	if path != arg && !allowedVersionArg(version) {
   372  		return path, version, fmt.Errorf("invalid %s version: %q", adj, version)
   373  	}
   374  	return path, version, nil
   375  }
   376  
   377  // parseVersionInterval parses a single version like "v1.2.3" or a closed
   378  // interval like "[v1.2.3,v1.4.5]". Note that a single version has the same
   379  // representation as an interval with equal upper and lower bounds: both
   380  // Low and High are set.
   381  func parseVersionInterval(arg string) (modfile.VersionInterval, error) {
   382  	if !strings.HasPrefix(arg, "[") {
   383  		if !allowedVersionArg(arg) {
   384  			return modfile.VersionInterval{}, fmt.Errorf("invalid version: %q", arg)
   385  		}
   386  		return modfile.VersionInterval{Low: arg, High: arg}, nil
   387  	}
   388  	if !strings.HasSuffix(arg, "]") {
   389  		return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
   390  	}
   391  	s := arg[1 : len(arg)-1]
   392  	before, after, found := strings.Cut(s, ",")
   393  	if !found {
   394  		return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
   395  	}
   396  	low := strings.TrimSpace(before)
   397  	high := strings.TrimSpace(after)
   398  	if !allowedVersionArg(low) || !allowedVersionArg(high) {
   399  		return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
   400  	}
   401  	return modfile.VersionInterval{Low: low, High: high}, nil
   402  }
   403  
   404  // allowedVersionArg returns whether a token may be used as a version in go.mod.
   405  // We don't call modfile.CheckPathVersion, because that insists on versions
   406  // being in semver form, but here we want to allow versions like "master" or
   407  // "1234abcdef", which the go command will resolve the next time it runs (or
   408  // during -fix).  Even so, we need to make sure the version is a valid token.
   409  func allowedVersionArg(arg string) bool {
   410  	return !modfile.MustQuote(arg)
   411  }
   412  
   413  // flagGodebug implements the -godebug flag.
   414  func flagGodebug(arg string) {
   415  	key, value, ok := strings.Cut(arg, "=")
   416  	if !ok || strings.ContainsAny(arg, "\"`',") {
   417  		base.Fatalf("go: -godebug=%s: need key=value", arg)
   418  	}
   419  	edits = append(edits, func(f *modfile.File) {
   420  		if err := f.AddGodebug(key, value); err != nil {
   421  			base.Fatalf("go: -godebug=%s: %v", arg, err)
   422  		}
   423  	})
   424  }
   425  
   426  // flagDropGodebug implements the -dropgodebug flag.
   427  func flagDropGodebug(arg string) {
   428  	edits = append(edits, func(f *modfile.File) {
   429  		if err := f.DropGodebug(arg); err != nil {
   430  			base.Fatalf("go: -dropgodebug=%s: %v", arg, err)
   431  		}
   432  	})
   433  }
   434  
   435  // flagRequire implements the -require flag.
   436  func flagRequire(arg string) {
   437  	path, version := parsePathVersion("require", arg)
   438  	edits = append(edits, func(f *modfile.File) {
   439  		if err := f.AddRequire(path, version); err != nil {
   440  			base.Fatalf("go: -require=%s: %v", arg, err)
   441  		}
   442  	})
   443  }
   444  
   445  // flagDropRequire implements the -droprequire flag.
   446  func flagDropRequire(arg string) {
   447  	path := parsePath("droprequire", arg)
   448  	edits = append(edits, func(f *modfile.File) {
   449  		if err := f.DropRequire(path); err != nil {
   450  			base.Fatalf("go: -droprequire=%s: %v", arg, err)
   451  		}
   452  	})
   453  }
   454  
   455  // flagExclude implements the -exclude flag.
   456  func flagExclude(arg string) {
   457  	path, version := parsePathVersion("exclude", arg)
   458  	edits = append(edits, func(f *modfile.File) {
   459  		if err := f.AddExclude(path, version); err != nil {
   460  			base.Fatalf("go: -exclude=%s: %v", arg, err)
   461  		}
   462  	})
   463  }
   464  
   465  // flagDropExclude implements the -dropexclude flag.
   466  func flagDropExclude(arg string) {
   467  	path, version := parsePathVersion("dropexclude", arg)
   468  	edits = append(edits, func(f *modfile.File) {
   469  		if err := f.DropExclude(path, version); err != nil {
   470  			base.Fatalf("go: -dropexclude=%s: %v", arg, err)
   471  		}
   472  	})
   473  }
   474  
   475  // flagReplace implements the -replace flag.
   476  func flagReplace(arg string) {
   477  	before, after, found := strings.Cut(arg, "=")
   478  	if !found {
   479  		base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
   480  	}
   481  	old, new := strings.TrimSpace(before), strings.TrimSpace(after)
   482  	if strings.HasPrefix(new, ">") {
   483  		base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg)
   484  	}
   485  	oldPath, oldVersion, err := parsePathVersionOptional("old", old, false)
   486  	if err != nil {
   487  		base.Fatalf("go: -replace=%s: %v", arg, err)
   488  	}
   489  	newPath, newVersion, err := parsePathVersionOptional("new", new, true)
   490  	if err != nil {
   491  		base.Fatalf("go: -replace=%s: %v", arg, err)
   492  	}
   493  	if newPath == new && !modfile.IsDirectoryPath(new) {
   494  		base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg)
   495  	}
   496  
   497  	edits = append(edits, func(f *modfile.File) {
   498  		if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
   499  			base.Fatalf("go: -replace=%s: %v", arg, err)
   500  		}
   501  	})
   502  }
   503  
   504  // flagDropReplace implements the -dropreplace flag.
   505  func flagDropReplace(arg string) {
   506  	path, version, err := parsePathVersionOptional("old", arg, true)
   507  	if err != nil {
   508  		base.Fatalf("go: -dropreplace=%s: %v", arg, err)
   509  	}
   510  	edits = append(edits, func(f *modfile.File) {
   511  		if err := f.DropReplace(path, version); err != nil {
   512  			base.Fatalf("go: -dropreplace=%s: %v", arg, err)
   513  		}
   514  	})
   515  }
   516  
   517  // flagRetract implements the -retract flag.
   518  func flagRetract(arg string) {
   519  	vi, err := parseVersionInterval(arg)
   520  	if err != nil {
   521  		base.Fatalf("go: -retract=%s: %v", arg, err)
   522  	}
   523  	edits = append(edits, func(f *modfile.File) {
   524  		if err := f.AddRetract(vi, ""); err != nil {
   525  			base.Fatalf("go: -retract=%s: %v", arg, err)
   526  		}
   527  	})
   528  }
   529  
   530  // flagDropRetract implements the -dropretract flag.
   531  func flagDropRetract(arg string) {
   532  	vi, err := parseVersionInterval(arg)
   533  	if err != nil {
   534  		base.Fatalf("go: -dropretract=%s: %v", arg, err)
   535  	}
   536  	edits = append(edits, func(f *modfile.File) {
   537  		if err := f.DropRetract(vi); err != nil {
   538  			base.Fatalf("go: -dropretract=%s: %v", arg, err)
   539  		}
   540  	})
   541  }
   542  
   543  // flagTool implements the -tool flag.
   544  func flagTool(arg string) {
   545  	path := parsePath("tool", arg)
   546  	edits = append(edits, func(f *modfile.File) {
   547  		if err := f.AddTool(path); err != nil {
   548  			base.Fatalf("go: -tool=%s: %v", arg, err)
   549  		}
   550  	})
   551  }
   552  
   553  // flagDropTool implements the -droptool flag.
   554  func flagDropTool(arg string) {
   555  	path := parsePath("droptool", arg)
   556  	edits = append(edits, func(f *modfile.File) {
   557  		if err := f.DropTool(path); err != nil {
   558  			base.Fatalf("go: -droptool=%s: %v", arg, err)
   559  		}
   560  	})
   561  }
   562  
   563  // flagIgnore implements the -ignore flag.
   564  func flagIgnore(arg string) {
   565  	edits = append(edits, func(f *modfile.File) {
   566  		if err := f.AddIgnore(arg); err != nil {
   567  			base.Fatalf("go: -ignore=%s: %v", arg, err)
   568  		}
   569  	})
   570  }
   571  
   572  // flagDropIgnore implements the -dropignore flag.
   573  func flagDropIgnore(arg string) {
   574  	edits = append(edits, func(f *modfile.File) {
   575  		if err := f.DropIgnore(arg); err != nil {
   576  			base.Fatalf("go: -dropignore=%s: %v", arg, err)
   577  		}
   578  	})
   579  }
   580  
   581  // fileJSON is the -json output data structure.
   582  type fileJSON struct {
   583  	Module    editModuleJSON
   584  	Go        string `json:",omitempty"`
   585  	Toolchain string `json:",omitempty"`
   586  	Require   []requireJSON
   587  	Exclude   []module.Version
   588  	Replace   []replaceJSON
   589  	Retract   []retractJSON
   590  	Tool      []toolJSON
   591  	Ignore    []ignoreJSON
   592  }
   593  
   594  type editModuleJSON struct {
   595  	Path       string
   596  	Deprecated string `json:",omitempty"`
   597  }
   598  
   599  type requireJSON struct {
   600  	Path     string
   601  	Version  string `json:",omitempty"`
   602  	Indirect bool   `json:",omitempty"`
   603  }
   604  
   605  type replaceJSON struct {
   606  	Old module.Version
   607  	New module.Version
   608  }
   609  
   610  type retractJSON struct {
   611  	Low       string `json:",omitempty"`
   612  	High      string `json:",omitempty"`
   613  	Rationale string `json:",omitempty"`
   614  }
   615  
   616  type toolJSON struct {
   617  	Path string
   618  }
   619  
   620  type ignoreJSON struct {
   621  	Path string
   622  }
   623  
   624  // editPrintJSON prints the -json output.
   625  func editPrintJSON(modFile *modfile.File) {
   626  	var f fileJSON
   627  	if modFile.Module != nil {
   628  		f.Module = editModuleJSON{
   629  			Path:       modFile.Module.Mod.Path,
   630  			Deprecated: modFile.Module.Deprecated,
   631  		}
   632  	}
   633  	if modFile.Go != nil {
   634  		f.Go = modFile.Go.Version
   635  	}
   636  	if modFile.Toolchain != nil {
   637  		f.Toolchain = modFile.Toolchain.Name
   638  	}
   639  	for _, r := range modFile.Require {
   640  		f.Require = append(f.Require, requireJSON{Path: r.Mod.Path, Version: r.Mod.Version, Indirect: r.Indirect})
   641  	}
   642  	for _, x := range modFile.Exclude {
   643  		f.Exclude = append(f.Exclude, x.Mod)
   644  	}
   645  	for _, r := range modFile.Replace {
   646  		f.Replace = append(f.Replace, replaceJSON{r.Old, r.New})
   647  	}
   648  	for _, r := range modFile.Retract {
   649  		f.Retract = append(f.Retract, retractJSON{r.Low, r.High, r.Rationale})
   650  	}
   651  	for _, t := range modFile.Tool {
   652  		f.Tool = append(f.Tool, toolJSON{t.Path})
   653  	}
   654  	for _, i := range modFile.Ignore {
   655  		f.Ignore = append(f.Ignore, ignoreJSON{i.Path})
   656  	}
   657  	data, err := json.MarshalIndent(&f, "", "\t")
   658  	if err != nil {
   659  		base.Fatalf("go: internal error: %v", err)
   660  	}
   661  	data = append(data, '\n')
   662  	os.Stdout.Write(data)
   663  }
   664  

View as plain text