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

View as plain text