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

View as plain text