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  	anyFlags := *editModule != "" ||
   213  		*editGo != "" ||
   214  		*editToolchain != "" ||
   215  		*editJSON ||
   216  		*editPrint ||
   217  		*editFmt ||
   218  		len(edits) > 0
   219  
   220  	if !anyFlags {
   221  		base.Fatalf("go: no flags specified (see 'go help mod edit').")
   222  	}
   223  
   224  	if *editJSON && *editPrint {
   225  		base.Fatalf("go: cannot use both -json and -print")
   226  	}
   227  
   228  	if len(args) > 1 {
   229  		base.Fatalf("go: too many arguments")
   230  	}
   231  	var gomod string
   232  	if len(args) == 1 {
   233  		gomod = args[0]
   234  	} else {
   235  		gomod = modload.ModFilePath()
   236  	}
   237  
   238  	if *editModule != "" {
   239  		err := module.CheckImportPath(*editModule)
   240  		if err == nil {
   241  			err = modload.CheckReservedModulePath(*editModule)
   242  		}
   243  		if err != nil {
   244  			base.Fatalf("go: invalid -module: %v", err)
   245  		}
   246  	}
   247  
   248  	if *editGo != "" && *editGo != "none" {
   249  		if !modfile.GoVersionRE.MatchString(*editGo) {
   250  			base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, gover.Local())
   251  		}
   252  	}
   253  	if *editToolchain != "" && *editToolchain != "none" {
   254  		if !modfile.ToolchainRE.MatchString(*editToolchain) {
   255  			base.Fatalf(`go mod: invalid -toolchain option; expecting something like "-toolchain go%s"`, gover.Local())
   256  		}
   257  	}
   258  
   259  	data, err := lockedfile.Read(gomod)
   260  	if err != nil {
   261  		base.Fatal(err)
   262  	}
   263  
   264  	modFile, err := modfile.Parse(gomod, data, nil)
   265  	if err != nil {
   266  		base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gomod), err)
   267  	}
   268  
   269  	if *editModule != "" {
   270  		modFile.AddModuleStmt(*editModule)
   271  	}
   272  
   273  	if *editGo == "none" {
   274  		modFile.DropGoStmt()
   275  	} else if *editGo != "" {
   276  		if err := modFile.AddGoStmt(*editGo); err != nil {
   277  			base.Fatalf("go: internal error: %v", err)
   278  		}
   279  	}
   280  	if *editToolchain == "none" {
   281  		modFile.DropToolchainStmt()
   282  	} else if *editToolchain != "" {
   283  		if err := modFile.AddToolchainStmt(*editToolchain); err != nil {
   284  			base.Fatalf("go: internal error: %v", err)
   285  		}
   286  	}
   287  
   288  	if len(edits) > 0 {
   289  		for _, edit := range edits {
   290  			edit(modFile)
   291  		}
   292  	}
   293  	modFile.SortBlocks()
   294  	modFile.Cleanup() // clean file after edits
   295  
   296  	if *editJSON {
   297  		editPrintJSON(modFile)
   298  		return
   299  	}
   300  
   301  	out, err := modFile.Format()
   302  	if err != nil {
   303  		base.Fatal(err)
   304  	}
   305  
   306  	if *editPrint {
   307  		os.Stdout.Write(out)
   308  		return
   309  	}
   310  
   311  	// Make a best-effort attempt to acquire the side lock, only to exclude
   312  	// previous versions of the 'go' command from making simultaneous edits.
   313  	if unlock, err := modfetch.SideLock(ctx); err == nil {
   314  		defer unlock()
   315  	}
   316  
   317  	err = lockedfile.Transform(gomod, func(lockedData []byte) ([]byte, error) {
   318  		if !bytes.Equal(lockedData, data) {
   319  			return nil, errors.New("go.mod changed during editing; not overwriting")
   320  		}
   321  		return out, nil
   322  	})
   323  	if err != nil {
   324  		base.Fatal(err)
   325  	}
   326  }
   327  
   328  // parsePathVersion parses -flag=arg expecting arg to be path@version.
   329  func parsePathVersion(flag, arg string) (path, version string) {
   330  	before, after, found := strings.Cut(arg, "@")
   331  	if !found {
   332  		base.Fatalf("go: -%s=%s: need path@version", flag, arg)
   333  	}
   334  	path, version = strings.TrimSpace(before), strings.TrimSpace(after)
   335  	if err := module.CheckImportPath(path); err != nil {
   336  		base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
   337  	}
   338  
   339  	if !allowedVersionArg(version) {
   340  		base.Fatalf("go: -%s=%s: invalid version %q", flag, arg, version)
   341  	}
   342  
   343  	return path, version
   344  }
   345  
   346  // parsePath parses -flag=arg expecting arg to be path (not path@version).
   347  func parsePath(flag, arg string) (path string) {
   348  	if strings.Contains(arg, "@") {
   349  		base.Fatalf("go: -%s=%s: need just path, not path@version", flag, arg)
   350  	}
   351  	path = arg
   352  	if err := module.CheckImportPath(path); err != nil {
   353  		base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
   354  	}
   355  	return path
   356  }
   357  
   358  // parsePathVersionOptional parses path[@version], using adj to
   359  // describe any errors.
   360  func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) {
   361  	if allowDirPath && modfile.IsDirectoryPath(arg) {
   362  		return arg, "", nil
   363  	}
   364  	before, after, found := strings.Cut(arg, "@")
   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