github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/workcmd/edit.go (about) 1 // Copyright 2021 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 work edit 6 7 package workcmd 8 9 import ( 10 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 11 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modload" 12 "context" 13 "encoding/json" 14 "fmt" 15 "os" 16 "path/filepath" 17 "strings" 18 19 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/module" 20 21 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/modfile" 22 ) 23 24 var cmdEdit = &base.Command{ 25 UsageLine: "go work edit [editing flags] [go.work]", 26 Short: "edit go.work from tools or scripts", 27 Long: `Edit provides a command-line interface for editing go.work, 28 for use primarily by tools or scripts. It only reads go.work; 29 it does not look up information about the modules involved. 30 If no file is specified, Edit looks for a go.work file in the current 31 directory and its parent directories 32 33 The editing flags specify a sequence of editing operations. 34 35 The -fmt flag reformats the go.work file without making other changes. 36 This reformatting is also implied by any other modifications that use or 37 rewrite the go.mod file. The only time this flag is needed is if no other 38 flags are specified, as in 'go work edit -fmt'. 39 40 The -use=path and -dropuse=path flags 41 add and drop a use directive from the go.work file's set of module directories. 42 43 The -replace=old[@v]=new[@v] flag adds a replacement of the given 44 module path and version pair. If the @v in old@v is omitted, a 45 replacement without a version on the left side is added, which applies 46 to all versions of the old module path. If the @v in new@v is omitted, 47 the new path should be a local module root directory, not a module 48 path. Note that -replace overrides any redundant replacements for old[@v], 49 so omitting @v will drop existing replacements for specific versions. 50 51 The -dropreplace=old[@v] flag drops a replacement of the given 52 module path and version pair. If the @v is omitted, a replacement without 53 a version on the left side is dropped. 54 55 The -use, -dropuse, -replace, and -dropreplace, 56 editing flags may be repeated, and the changes are applied in the order given. 57 58 The -go=version flag sets the expected Go language version. 59 60 The -print flag prints the final go.work in its text format instead of 61 writing it back to go.mod. 62 63 The -json flag prints the final go.work file in JSON format instead of 64 writing it back to go.mod. The JSON output corresponds to these Go types: 65 66 type GoWork struct { 67 Go string 68 Use []Use 69 Replace []Replace 70 } 71 72 type Use struct { 73 DiskPath string 74 ModulePath string 75 } 76 77 type Replace struct { 78 Old Module 79 New Module 80 } 81 82 type Module struct { 83 Path string 84 Version string 85 } 86 87 See the workspaces reference at https://go.dev/ref/mod#workspaces 88 for more information. 89 `, 90 } 91 92 var ( 93 editFmt = cmdEdit.Flag.Bool("fmt", false, "") 94 editGo = cmdEdit.Flag.String("go", "", "") 95 editJSON = cmdEdit.Flag.Bool("json", false, "") 96 editPrint = cmdEdit.Flag.Bool("print", false, "") 97 workedits []func(file *modfile.WorkFile) // edits specified in flags 98 ) 99 100 type flagFunc func(string) 101 102 func (f flagFunc) String() string { return "" } 103 func (f flagFunc) Set(s string) error { f(s); return nil } 104 105 func init() { 106 cmdEdit.Run = runEditwork // break init cycle 107 108 cmdEdit.Flag.Var(flagFunc(flagEditworkUse), "use", "") 109 cmdEdit.Flag.Var(flagFunc(flagEditworkDropUse), "dropuse", "") 110 cmdEdit.Flag.Var(flagFunc(flagEditworkReplace), "replace", "") 111 cmdEdit.Flag.Var(flagFunc(flagEditworkDropReplace), "dropreplace", "") 112 base.AddChdirFlag(&cmdEdit.Flag) 113 } 114 115 func runEditwork(ctx context.Context, cmd *base.Command, args []string) { 116 if *editJSON && *editPrint { 117 base.Fatalf("go: cannot use both -json and -print") 118 } 119 120 if len(args) > 1 { 121 base.Fatalf("go: 'go help work edit' accepts at most one argument") 122 } 123 var gowork string 124 if len(args) == 1 { 125 gowork = args[0] 126 } else { 127 modload.InitWorkfile() 128 gowork = modload.WorkFilePath() 129 } 130 131 if *editGo != "" { 132 if !modfile.GoVersionRE.MatchString(*editGo) { 133 base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, modload.LatestGoVersion()) 134 } 135 } 136 137 if gowork == "" { 138 base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)") 139 } 140 141 anyFlags := 142 *editGo != "" || 143 *editJSON || 144 *editPrint || 145 *editFmt || 146 len(workedits) > 0 147 148 if !anyFlags { 149 base.Fatalf("go: no flags specified (see 'go help work edit').") 150 } 151 152 workFile, err := modload.ReadWorkFile(gowork) 153 if err != nil { 154 base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gowork), err) 155 } 156 157 if *editGo != "" { 158 if err := workFile.AddGoStmt(*editGo); err != nil { 159 base.Fatalf("go: internal error: %v", err) 160 } 161 } 162 163 if len(workedits) > 0 { 164 for _, edit := range workedits { 165 edit(workFile) 166 } 167 } 168 169 modload.UpdateWorkFile(workFile) 170 171 workFile.SortBlocks() 172 workFile.Cleanup() // clean file after edits 173 174 if *editJSON { 175 editPrintJSON(workFile) 176 return 177 } 178 179 if *editPrint { 180 os.Stdout.Write(modfile.Format(workFile.Syntax)) 181 return 182 } 183 184 modload.WriteWorkFile(gowork, workFile) 185 } 186 187 // flagEditworkUse implements the -use flag. 188 func flagEditworkUse(arg string) { 189 workedits = append(workedits, func(f *modfile.WorkFile) { 190 _, mf, err := modload.ReadModFile(filepath.Join(arg, "go.mod"), nil) 191 modulePath := "" 192 if err == nil { 193 modulePath = mf.Module.Mod.Path 194 } 195 f.AddUse(modload.ToDirectoryPath(arg), modulePath) 196 if err := f.AddUse(modload.ToDirectoryPath(arg), ""); err != nil { 197 base.Fatalf("go: -use=%s: %v", arg, err) 198 } 199 }) 200 } 201 202 // flagEditworkDropUse implements the -dropuse flag. 203 func flagEditworkDropUse(arg string) { 204 workedits = append(workedits, func(f *modfile.WorkFile) { 205 if err := f.DropUse(modload.ToDirectoryPath(arg)); err != nil { 206 base.Fatalf("go: -dropdirectory=%s: %v", arg, err) 207 } 208 }) 209 } 210 211 // allowedVersionArg returns whether a token may be used as a version in go.mod. 212 // We don't call modfile.CheckPathVersion, because that insists on versions 213 // being in semver form, but here we want to allow versions like "master" or 214 // "1234abcdef", which the go command will resolve the next time it runs (or 215 // during -fix). Even so, we need to make sure the version is a valid token. 216 func allowedVersionArg(arg string) bool { 217 return !modfile.MustQuote(arg) 218 } 219 220 // parsePathVersionOptional parses path[@version], using adj to 221 // describe any errors. 222 func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) { 223 before, after, found := strings.Cut(arg, "@") 224 if !found { 225 path = arg 226 } else { 227 path, version = strings.TrimSpace(before), strings.TrimSpace(after) 228 } 229 if err := module.CheckImportPath(path); err != nil { 230 if !allowDirPath || !modfile.IsDirectoryPath(path) { 231 return path, version, fmt.Errorf("invalid %s path: %v", adj, err) 232 } 233 } 234 if path != arg && !allowedVersionArg(version) { 235 return path, version, fmt.Errorf("invalid %s version: %q", adj, version) 236 } 237 return path, version, nil 238 } 239 240 // flagEditworkReplace implements the -replace flag. 241 func flagEditworkReplace(arg string) { 242 before, after, found := strings.Cut(arg, "=") 243 if !found { 244 base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg) 245 } 246 old, new := strings.TrimSpace(before), strings.TrimSpace(after) 247 if strings.HasPrefix(new, ">") { 248 base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg) 249 } 250 oldPath, oldVersion, err := parsePathVersionOptional("old", old, false) 251 if err != nil { 252 base.Fatalf("go: -replace=%s: %v", arg, err) 253 } 254 newPath, newVersion, err := parsePathVersionOptional("new", new, true) 255 if err != nil { 256 base.Fatalf("go: -replace=%s: %v", arg, err) 257 } 258 if newPath == new && !modfile.IsDirectoryPath(new) { 259 base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg) 260 } 261 262 workedits = append(workedits, func(f *modfile.WorkFile) { 263 if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil { 264 base.Fatalf("go: -replace=%s: %v", arg, err) 265 } 266 }) 267 } 268 269 // flagEditworkDropReplace implements the -dropreplace flag. 270 func flagEditworkDropReplace(arg string) { 271 path, version, err := parsePathVersionOptional("old", arg, true) 272 if err != nil { 273 base.Fatalf("go: -dropreplace=%s: %v", arg, err) 274 } 275 workedits = append(workedits, func(f *modfile.WorkFile) { 276 if err := f.DropReplace(path, version); err != nil { 277 base.Fatalf("go: -dropreplace=%s: %v", arg, err) 278 } 279 }) 280 } 281 282 type replaceJSON struct { 283 Old module.Version 284 New module.Version 285 } 286 287 // editPrintJSON prints the -json output. 288 func editPrintJSON(workFile *modfile.WorkFile) { 289 var f workfileJSON 290 if workFile.Go != nil { 291 f.Go = workFile.Go.Version 292 } 293 for _, d := range workFile.Use { 294 f.Use = append(f.Use, useJSON{DiskPath: d.Path, ModPath: d.ModulePath}) 295 } 296 297 for _, r := range workFile.Replace { 298 f.Replace = append(f.Replace, replaceJSON{r.Old, r.New}) 299 } 300 data, err := json.MarshalIndent(&f, "", "\t") 301 if err != nil { 302 base.Fatalf("go: internal error: %v", err) 303 } 304 data = append(data, '\n') 305 os.Stdout.Write(data) 306 } 307 308 // workfileJSON is the -json output data structure. 309 type workfileJSON struct { 310 Go string `json:",omitempty"` 311 Use []useJSON 312 Replace []replaceJSON 313 } 314 315 type useJSON struct { 316 DiskPath string 317 ModPath string `json:",omitempty"` 318 }