diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/go/internal/workcmd/edit.go | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/go/internal/workcmd/edit.go')
-rw-r--r-- | src/cmd/go/internal/workcmd/edit.go | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/src/cmd/go/internal/workcmd/edit.go b/src/cmd/go/internal/workcmd/edit.go new file mode 100644 index 0000000..f5e3304 --- /dev/null +++ b/src/cmd/go/internal/workcmd/edit.go @@ -0,0 +1,318 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go work edit + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/modload" + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "golang.org/x/mod/module" + + "golang.org/x/mod/modfile" +) + +var cmdEdit = &base.Command{ + UsageLine: "go work edit [editing flags] [go.work]", + Short: "edit go.work from tools or scripts", + Long: `Edit provides a command-line interface for editing go.work, +for use primarily by tools or scripts. It only reads go.work; +it does not look up information about the modules involved. +If no file is specified, Edit looks for a go.work file in the current +directory and its parent directories + +The editing flags specify a sequence of editing operations. + +The -fmt flag reformats the go.work file without making other changes. +This reformatting is also implied by any other modifications that use or +rewrite the go.mod file. The only time this flag is needed is if no other +flags are specified, as in 'go work edit -fmt'. + +The -use=path and -dropuse=path flags +add and drop a use directive from the go.work file's set of module directories. + +The -replace=old[@v]=new[@v] flag adds a replacement of the given +module path and version pair. If the @v in old@v is omitted, a +replacement without a version on the left side is added, which applies +to all versions of the old module path. If the @v in new@v is omitted, +the new path should be a local module root directory, not a module +path. Note that -replace overrides any redundant replacements for old[@v], +so omitting @v will drop existing replacements for specific versions. + +The -dropreplace=old[@v] flag drops a replacement of the given +module path and version pair. If the @v is omitted, a replacement without +a version on the left side is dropped. + +The -use, -dropuse, -replace, and -dropreplace, +editing flags may be repeated, and the changes are applied in the order given. + +The -go=version flag sets the expected Go language version. + +The -print flag prints the final go.work in its text format instead of +writing it back to go.mod. + +The -json flag prints the final go.work file in JSON format instead of +writing it back to go.mod. The JSON output corresponds to these Go types: + + type GoWork struct { + Go string + Use []Use + Replace []Replace + } + + type Use struct { + DiskPath string + ModulePath string + } + + type Replace struct { + Old Module + New Module + } + + type Module struct { + Path string + Version string + } + +See the workspaces reference at https://go.dev/ref/mod#workspaces +for more information. +`, +} + +var ( + editFmt = cmdEdit.Flag.Bool("fmt", false, "") + editGo = cmdEdit.Flag.String("go", "", "") + editJSON = cmdEdit.Flag.Bool("json", false, "") + editPrint = cmdEdit.Flag.Bool("print", false, "") + workedits []func(file *modfile.WorkFile) // edits specified in flags +) + +type flagFunc func(string) + +func (f flagFunc) String() string { return "" } +func (f flagFunc) Set(s string) error { f(s); return nil } + +func init() { + cmdEdit.Run = runEditwork // break init cycle + + cmdEdit.Flag.Var(flagFunc(flagEditworkUse), "use", "") + cmdEdit.Flag.Var(flagFunc(flagEditworkDropUse), "dropuse", "") + cmdEdit.Flag.Var(flagFunc(flagEditworkReplace), "replace", "") + cmdEdit.Flag.Var(flagFunc(flagEditworkDropReplace), "dropreplace", "") + base.AddChdirFlag(&cmdEdit.Flag) +} + +func runEditwork(ctx context.Context, cmd *base.Command, args []string) { + if *editJSON && *editPrint { + base.Fatalf("go: cannot use both -json and -print") + } + + if len(args) > 1 { + base.Fatalf("go: 'go help work edit' accepts at most one argument") + } + var gowork string + if len(args) == 1 { + gowork = args[0] + } else { + modload.InitWorkfile() + gowork = modload.WorkFilePath() + } + + if *editGo != "" { + if !modfile.GoVersionRE.MatchString(*editGo) { + base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, modload.LatestGoVersion()) + } + } + + if gowork == "" { + base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)") + } + + anyFlags := + *editGo != "" || + *editJSON || + *editPrint || + *editFmt || + len(workedits) > 0 + + if !anyFlags { + base.Fatalf("go: no flags specified (see 'go help work edit').") + } + + workFile, err := modload.ReadWorkFile(gowork) + if err != nil { + base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gowork), err) + } + + if *editGo != "" { + if err := workFile.AddGoStmt(*editGo); err != nil { + base.Fatalf("go: internal error: %v", err) + } + } + + if len(workedits) > 0 { + for _, edit := range workedits { + edit(workFile) + } + } + + modload.UpdateWorkFile(workFile) + + workFile.SortBlocks() + workFile.Cleanup() // clean file after edits + + if *editJSON { + editPrintJSON(workFile) + return + } + + if *editPrint { + os.Stdout.Write(modfile.Format(workFile.Syntax)) + return + } + + modload.WriteWorkFile(gowork, workFile) +} + +// flagEditworkUse implements the -use flag. +func flagEditworkUse(arg string) { + workedits = append(workedits, func(f *modfile.WorkFile) { + _, mf, err := modload.ReadModFile(filepath.Join(arg, "go.mod"), nil) + modulePath := "" + if err == nil { + modulePath = mf.Module.Mod.Path + } + f.AddUse(modload.ToDirectoryPath(arg), modulePath) + if err := f.AddUse(modload.ToDirectoryPath(arg), ""); err != nil { + base.Fatalf("go: -use=%s: %v", arg, err) + } + }) +} + +// flagEditworkDropUse implements the -dropuse flag. +func flagEditworkDropUse(arg string) { + workedits = append(workedits, func(f *modfile.WorkFile) { + if err := f.DropUse(modload.ToDirectoryPath(arg)); err != nil { + base.Fatalf("go: -dropdirectory=%s: %v", arg, err) + } + }) +} + +// allowedVersionArg returns whether a token may be used as a version in go.mod. +// We don't call modfile.CheckPathVersion, because that insists on versions +// being in semver form, but here we want to allow versions like "master" or +// "1234abcdef", which the go command will resolve the next time it runs (or +// during -fix). Even so, we need to make sure the version is a valid token. +func allowedVersionArg(arg string) bool { + return !modfile.MustQuote(arg) +} + +// parsePathVersionOptional parses path[@version], using adj to +// describe any errors. +func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) { + before, after, found := strings.Cut(arg, "@") + if !found { + path = arg + } else { + path, version = strings.TrimSpace(before), strings.TrimSpace(after) + } + if err := module.CheckImportPath(path); err != nil { + if !allowDirPath || !modfile.IsDirectoryPath(path) { + return path, version, fmt.Errorf("invalid %s path: %v", adj, err) + } + } + if path != arg && !allowedVersionArg(version) { + return path, version, fmt.Errorf("invalid %s version: %q", adj, version) + } + return path, version, nil +} + +// flagEditworkReplace implements the -replace flag. +func flagEditworkReplace(arg string) { + before, after, found := strings.Cut(arg, "=") + if !found { + base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg) + } + old, new := strings.TrimSpace(before), strings.TrimSpace(after) + if strings.HasPrefix(new, ">") { + base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg) + } + oldPath, oldVersion, err := parsePathVersionOptional("old", old, false) + if err != nil { + base.Fatalf("go: -replace=%s: %v", arg, err) + } + newPath, newVersion, err := parsePathVersionOptional("new", new, true) + if err != nil { + base.Fatalf("go: -replace=%s: %v", arg, err) + } + if newPath == new && !modfile.IsDirectoryPath(new) { + base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg) + } + + workedits = append(workedits, func(f *modfile.WorkFile) { + if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil { + base.Fatalf("go: -replace=%s: %v", arg, err) + } + }) +} + +// flagEditworkDropReplace implements the -dropreplace flag. +func flagEditworkDropReplace(arg string) { + path, version, err := parsePathVersionOptional("old", arg, true) + if err != nil { + base.Fatalf("go: -dropreplace=%s: %v", arg, err) + } + workedits = append(workedits, func(f *modfile.WorkFile) { + if err := f.DropReplace(path, version); err != nil { + base.Fatalf("go: -dropreplace=%s: %v", arg, err) + } + }) +} + +type replaceJSON struct { + Old module.Version + New module.Version +} + +// editPrintJSON prints the -json output. +func editPrintJSON(workFile *modfile.WorkFile) { + var f workfileJSON + if workFile.Go != nil { + f.Go = workFile.Go.Version + } + for _, d := range workFile.Use { + f.Use = append(f.Use, useJSON{DiskPath: d.Path, ModPath: d.ModulePath}) + } + + for _, r := range workFile.Replace { + f.Replace = append(f.Replace, replaceJSON{r.Old, r.New}) + } + data, err := json.MarshalIndent(&f, "", "\t") + if err != nil { + base.Fatalf("go: internal error: %v", err) + } + data = append(data, '\n') + os.Stdout.Write(data) +} + +// workfileJSON is the -json output data structure. +type workfileJSON struct { + Go string `json:",omitempty"` + Use []useJSON + Replace []replaceJSON +} + +type useJSON struct { + DiskPath string + ModPath string `json:",omitempty"` +} |