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

View as plain text