github.com/jmigpin/editor@v1.6.0/core/godebug/flags.go (about) 1 package godebug 2 3 import ( 4 "flag" 5 "fmt" 6 "io" 7 "path/filepath" 8 9 "github.com/jmigpin/editor/util/flagutil" 10 ) 11 12 type flags struct { 13 stderr io.Writer 14 15 mode struct { 16 run bool 17 test bool 18 build bool 19 connect bool 20 } 21 22 verbose bool 23 work bool 24 syncSend bool 25 stringifyBytesRunes bool 26 srcLines bool 27 toolExec string // ex: "wine" will run "wine args..." 28 outFilename string // build, ex: -o filename 29 address string // build/connect 30 env []string 31 paths []string // dirs/files to annotate (args from cmd line) 32 usePkgLinks bool 33 34 unknownArgs []string // unknown args to pass down to tooling 35 unnamedArgs []string // args without name (ex: filenames) 36 binaryArgs []string // to be passed to the binary when running 37 38 } 39 40 func (fl *flags) parseArgs(args []string) error { 41 if len(args) == 0 { 42 return fl.usageErr() 43 } 44 name := "GoDebug " + args[0] 45 switch args[0] { 46 case "run": 47 fl.mode.run = true 48 return fl.parseRunArgs(name, args[1:]) 49 case "test": 50 fl.mode.test = true 51 return fl.parseTestArgs(name, args[1:]) 52 case "build": 53 fl.mode.build = true 54 return fl.parseBuildArgs(name, args[1:]) 55 case "connect": 56 fl.mode.connect = true 57 return fl.parseConnectArgs(name, args[1:]) 58 default: 59 return fl.usageErr() 60 } 61 } 62 63 func (fl *flags) usageErr() error { 64 fl.printCmdUsage() 65 return flag.ErrHelp 66 } 67 68 func (fl *flags) printCmdUsage() { 69 fmt.Fprint(fl.stderr, cmdUsage()) 70 } 71 72 //---------- 73 74 func (fl *flags) parseRunArgs(name string, args []string) error { 75 fs := fl.newFlagSet(name) 76 77 fl.addPathsFlag(fs) 78 fl.addWorkFlag(fs) 79 fl.addVerboseFlag(fs) 80 fl.addToolExecFlag(fs) 81 fl.addSyncSendFlag(fs) 82 fl.addStringifyBytesRunesFlag(fs) 83 fl.addSrcLinesFlag(fs) 84 fl.addEnvFlag(fs) 85 fl.addUsePkgLinksFlag(fs) 86 87 m := goBuildBooleanFlags() 88 return fl.parse(name, fs, args, m) 89 } 90 91 func (fl *flags) parseTestArgs(name string, args []string) error { 92 // support test "-args" special flag 93 for i, a := range args { 94 if a == "-args" || a == "--args" { 95 args, fl.binaryArgs = args[:i], args[i+1:] // exclude 96 break 97 } 98 } 99 100 fs := fl.newFlagSet(name) 101 102 fl.addPathsFlag(fs) 103 fl.addWorkFlag(fs) 104 fl.addVerboseFlag(fs) 105 fl.addToolExecFlag(fs) 106 fl.addSyncSendFlag(fs) 107 fl.addStringifyBytesRunesFlag(fs) 108 fl.addSrcLinesFlag(fs) 109 fl.addEnvFlag(fs) 110 fl.addTestRunFlag(fs) 111 fl.addTestVFlag(fs) 112 fl.addUsePkgLinksFlag(fs) 113 114 m := joinMaps(goBuildBooleanFlags(), goTestBooleanFlags()) 115 return fl.parse(name, fs, args, m) 116 } 117 118 func (fl *flags) parseBuildArgs(name string, args []string) error { 119 fs := fl.newFlagSet(name) 120 121 fl.addPathsFlag(fs) 122 fl.addWorkFlag(fs) 123 fl.addVerboseFlag(fs) 124 fl.addSyncSendFlag(fs) 125 fl.addStringifyBytesRunesFlag(fs) 126 fl.addSrcLinesFlag(fs) 127 fl.addEnvFlag(fs) 128 fl.addAddressFlag(fs) 129 fl.addOutFilenameFlag(fs) 130 fl.addUsePkgLinksFlag(fs) 131 132 m := goBuildBooleanFlags() 133 return fl.parse(name, fs, args, m) 134 } 135 136 func (fl *flags) parseConnectArgs(name string, args []string) error { 137 fs := fl.newFlagSet(name) 138 139 fl.addAddressFlag(fs) 140 fl.addToolExecFlag(fs) 141 142 // commented: strict parsing, no unknown flags allowed 143 //return fl.parse(name, fs, args) 144 return fs.Parse(args) 145 } 146 147 //---------- 148 149 func (fl *flags) addWorkFlag(fs *flag.FlagSet) { 150 fs.BoolVar(&fl.work, "work", false, "print workdir and don't cleanup on exit") 151 } 152 func (fl *flags) addVerboseFlag(fs *flag.FlagSet) { 153 fs.BoolVar(&fl.verbose, "verbose", false, "print extra godebug build info") 154 } 155 func (fl *flags) addSyncSendFlag(fs *flag.FlagSet) { 156 fs.BoolVar(&fl.syncSend, "syncsend", false, "Don't send msgs in chunks (slow). Useful to get msgs before a crash.") 157 } 158 func (fl *flags) addStringifyBytesRunesFlag(fs *flag.FlagSet) { 159 fs.BoolVar(&fl.stringifyBytesRunes, "sbr", false, "Stringify bytes/runes as string (ex: [97 98 99] outputs as \"abc\")") 160 } 161 func (fl *flags) addSrcLinesFlag(fs *flag.FlagSet) { 162 fs.BoolVar(&fl.srcLines, "srclines", true, "add src reference lines to the compilation such that in case of panics, the stack refers to the original src file") 163 } 164 func (fl *flags) addToolExecFlag(fs *flag.FlagSet) { 165 fs.StringVar(&fl.toolExec, "toolexec", "", "a program to invoke before the program argument. Useful to run a tool with the output file (ex: wine)") 166 } 167 func (fl *flags) addAddressFlag(fs *flag.FlagSet) { 168 fs.StringVar(&fl.address, "addr", ":8078", "address to connect to, built into the binary") 169 } 170 func (fl *flags) addOutFilenameFlag(fs *flag.FlagSet) { 171 fs.StringVar(&fl.outFilename, "o", "", "output filename") 172 } 173 func (fl *flags) addTestRunFlag(fs *flag.FlagSet) { 174 ff := flagutil.StringFuncFlag(func(s string) error { 175 u := "-test.run=" + s 176 fl.binaryArgs = append([]string{u}, fl.binaryArgs...) 177 return nil 178 }) 179 fs.Var(ff, "run", "`string` with test name pattern to run") 180 } 181 func (fl *flags) addTestVFlag(fs *flag.FlagSet) { 182 ff := flagutil.BoolFuncFlag(func(s string) error { 183 u := "-test.v" 184 fl.binaryArgs = append([]string{u}, fl.binaryArgs...) 185 return nil 186 }) 187 fs.Var(ff, "v", "`bool` verbose") 188 } 189 190 func (fl *flags) addEnvFlag(fs *flag.FlagSet) { 191 ff := flagutil.StringFuncFlag(func(s string) error { 192 fl.env = filepath.SplitList(s) 193 return nil 194 }) 195 // The type in usage doc is the backquoted "string" (detected by flagset) 196 usage := fmt.Sprintf("`string` with env variables (ex: \"N1=V1%c...\"'", filepath.ListSeparator) 197 fs.Var(ff, "env", usage) 198 } 199 200 func (fl *flags) addPathsFlag(fs *flag.FlagSet) { 201 ff := flagutil.StringFuncFlag(func(s string) error { 202 fl.paths = splitCommaList(s) 203 return nil 204 }) 205 fs.Var(ff, "paths", "comma-separated `string` of dirs/files to annotate (also see the \"//godebug:annotate*\" source code directives to set files to be annotated)") 206 } 207 208 func (fl *flags) addUsePkgLinksFlag(fs *flag.FlagSet) { 209 fs.BoolVar(&fl.usePkgLinks, "usepkglinks", true, "Use symbolic links to some pkgs directories to build the godebug binary. Helps solving new behaviour introduced by go1.19.x. that fails when an overlaid file depends on a new external module.") 210 } 211 212 //---------- 213 214 func (fl *flags) newFlagSet(name string) *flag.FlagSet { 215 fs := flag.NewFlagSet(name, flag.ContinueOnError) 216 return fs 217 } 218 219 func (fl *flags) parse(name string, fs *flag.FlagSet, args []string, isBool map[string]bool) error { 220 221 // don't show err "flag provided but not defined" 222 fs.SetOutput(io.Discard) 223 224 unknown, unnamed, binary, err := flagutil.ParseFlagSetSets(fs, args, isBool) 225 if err != nil { 226 if err == flag.ErrHelp { 227 fmt.Fprintf(fl.stderr, "Usage of %v:\n", name) 228 fs.SetOutput(fl.stderr) 229 fs.PrintDefaults() 230 } 231 return err 232 } 233 fl.unknownArgs = unknown 234 fl.unnamedArgs = unnamed 235 fl.binaryArgs = append(fl.binaryArgs, binary...) 236 237 //spew.Dump(fl.flags) 238 239 return nil 240 } 241 242 //---------- 243 //---------- 244 //---------- 245 246 func cmdUsage() string { 247 return `Usage: 248 GoDebug <command> [arguments] 249 The commands are: 250 run build and run program with godebug data 251 test test packages compiled with godebug data 252 build build binary with godebug data (allows remote debug) 253 connect connect to a binary built with godebug data (allows remote debug) 254 Env variables: 255 GODEBUG_BUILD_FLAGS comma separated flags for build 256 Examples: 257 GoDebug run 258 GoDebug run -help 259 GoDebug run main.go -arg1 -arg2 260 GoDebug run -paths=dir1,file2.go,dir3 main.go -arg1 -arg2 261 GoDebug run -tags=xproto main.go 262 GoDebug run -env=GODEBUG_BUILD_FLAGS=-tags=xproto main.go 263 GoDebug test 264 GoDebug test -help 265 GoDebug test -run=mytest -v 266 GoDebug test a_test.go -test.run=mytest -test.v 267 GoDebug test a_test.go -test.count 5 268 GoDebug build -help 269 GoDebug build -addr=:8078 main.go 270 GoDebug connect -help 271 GoDebug connect -addr=:8078 272 ` 273 } 274 275 //---------- 276 //---------- 277 //---------- 278 279 func joinMaps(ms ...map[string]bool) map[string]bool { 280 m := map[string]bool{} 281 for _, m2 := range ms { 282 for k, v := range m2 { 283 m[k] = v 284 } 285 } 286 return m 287 } 288 289 func goBuildBooleanFlags() map[string]bool { 290 return map[string]bool{ 291 "a": true, 292 "i": true, 293 "n": true, 294 "v": true, 295 "x": true, 296 "race": true, 297 "msan": true, 298 "asan": true, 299 "work": true, 300 "linkshared": true, 301 "modcacherw": true, 302 "trimpath": true, 303 "buildvcs": true, 304 } 305 } 306 func goTestBooleanFlags() map[string]bool { 307 return map[string]bool{ 308 "c": true, 309 "json": true, 310 "cover": true, 311 "failfast": true, 312 "short": true, 313 "benchmem": true, 314 } 315 }