diff --git a/cmd/xgo/main.go b/cmd/xgo/main.go index 404abd9f..c32ea412 100644 --- a/cmd/xgo/main.go +++ b/cmd/xgo/main.go @@ -132,6 +132,8 @@ func handleBuild(cmd string, args []string) error { } remainArgs := opts.remainArgs testArgs := opts.testArgs + buildFlags := opts.buildFlags + progFlags := opts.progFlags flagA := opts.flagA projectDir := opts.projectDir output := opts.output @@ -568,6 +570,12 @@ func handleBuild(cmd string, args []string) error { buildCmdArgs = append(buildCmdArgs, "-o", finalBuildOutput) } } + if len(buildFlags) > 0 { + buildCmdArgs = append(buildCmdArgs, buildFlags...) + } + if len(progFlags) > 0 { + runFlagsAfterBuild = append(runFlagsAfterBuild, progFlags...) + } if len(remainArgs) > 0 { if !runDebug { buildCmdArgs = append(buildCmdArgs, remainArgs...) @@ -586,6 +594,9 @@ func handleBuild(cmd string, args []string) error { } } logDebug("command: %s %v", instrumentGo, buildCmdArgs) + if len(runFlagsAfterBuild) > 0 { + logDebug("prog flags: %v", runFlagsAfterBuild) + } execCmd = exec.Command(instrumentGo, buildCmdArgs...) } else { logDebug("command: %v", remainArgs) diff --git a/cmd/xgo/option.go b/cmd/xgo/option.go index 4c88fbe5..fb588382 100644 --- a/cmd/xgo/option.go +++ b/cmd/xgo/option.go @@ -80,7 +80,9 @@ type options struct { remainArgs []string - testArgs []string + testArgs []string + buildFlags []string + progFlags []string } func parseOptions(cmd string, args []string) (*options, error) { @@ -131,6 +133,8 @@ func parseOptions(cmd string, args []string) (*options, error) { var remainArgs []string var testArgs []string + var buildFlags []string + var progFlags []string nArg := len(args) type FlagValue struct { @@ -220,6 +224,18 @@ func parseOptions(cmd string, args []string) (*options, error) { logDebug = &v }, }, + { + Flags: []string{"--build-flag"}, + Set: func(v string) { + buildFlags = append(buildFlags, v) + }, + }, + { + Flags: []string{"--prog-flag"}, + Set: func(v string) { + progFlags = append(progFlags, v) + }, + }, } if isDevelopment { @@ -254,7 +270,7 @@ func parseOptions(cmd string, args []string) (*options, error) { break } if arg == "-" { - return nil, fmt.Errorf("unrecognized flag:%s", arg) + return nil, fmt.Errorf("unrecognized flag: %s", arg) } if arg == "-a" { flagA = true @@ -402,7 +418,7 @@ func parseOptions(cmd string, args []string) (*options, error) { continue } - return nil, fmt.Errorf("unrecognized flag:%s", arg) + return nil, fmt.Errorf("unrecognized flag: %s", arg) } return &options{ @@ -452,6 +468,8 @@ func parseOptions(cmd string, args []string) (*options, error) { remainArgs: remainArgs, testArgs: testArgs, + buildFlags: buildFlags, + progFlags: progFlags, }, nil } diff --git a/cmd/xgo/test-explorer/config.go b/cmd/xgo/test-explorer/config.go index b66c0284..20c3285f 100644 --- a/cmd/xgo/test-explorer/config.go +++ b/cmd/xgo/test-explorer/config.go @@ -27,6 +27,8 @@ type TestConfig struct { Flags []string `json:"flags"` Args []string `json:"args"` + BypassGoFlags bool `json:"bypass_go_flags"` + MockRules []string `json:"mock_rules"` Xgo *XgoConfig `json:"xgo,omitempty"` } @@ -137,6 +139,14 @@ func parseTestConfig(config string) (*TestConfig, error) { } conf.Args = list } + e, ok = m["bypass_go_flags"] + if ok { + b, err := toBoolean(e) + if err != nil { + return nil, fmt.Errorf("args: %w", err) + } + conf.BypassGoFlags = b + } e, ok = m["mock_rules"] if ok { @@ -287,6 +297,25 @@ func toStringList(e interface{}) ([]string, error) { return strList, nil } +func toBoolean(e interface{}) (bool, error) { + if e == nil { + return false, nil + } + b, ok := e.(bool) + if ok { + return b, nil + } + s, ok := e.(string) + if ok { + if s == "true" { + return true, nil + } + if s == "false" { + return true, nil + } + } + return false, fmt.Errorf("expecting true or false, actual: %v", e) +} func toMarshaledStrings(e interface{}) ([]string, error) { if e == nil { return nil, nil diff --git a/cmd/xgo/test-explorer/main.go b/cmd/xgo/test-explorer/main.go index a1eb4dfd..873b4957 100644 --- a/cmd/xgo/test-explorer/main.go +++ b/cmd/xgo/test-explorer/main.go @@ -354,7 +354,7 @@ func handle(opts *Options, args []string) error { pathArgs := formatPathArgs(paths) runNames := formatRunNames(names) testArgs := joinTestArgs(pathArgs, runNames) - return runTest(conf.GoCmd, projectDir, conf.Flags, testArgs, conf.Args, conf.CmdEnv(), nil, nil) + return runTest(conf.GoCmd, projectDir, conf.Flags, testArgs, conf.BypassGoFlags, conf.Args, conf.CmdEnv(), nil, nil) } server := &http.ServeMux{} diff --git a/cmd/xgo/test-explorer/run.go b/cmd/xgo/test-explorer/run.go index 114b3fde..16d0ccdf 100644 --- a/cmd/xgo/test-explorer/run.go +++ b/cmd/xgo/test-explorer/run.go @@ -58,7 +58,9 @@ type runSession struct { goCmd string env []string testFlags []string - progArgs []string + + bypassGoFlags bool + progArgs []string pathPrefix []string diff --git a/cmd/xgo/test-explorer/run_session.go b/cmd/xgo/test-explorer/run_session.go index 30a7fea6..305e4ed2 100644 --- a/cmd/xgo/test-explorer/run_session.go +++ b/cmd/xgo/test-explorer/run_session.go @@ -71,12 +71,13 @@ func setupRunHandler(server *http.ServeMux, projectDir string, logConsole bool, } runSess := &runSession{ - dir: projectDir, - absDir: absDir, - goCmd: config.GoCmd, - env: config.CmdEnv(), - testFlags: config.Flags, - progArgs: config.Args, + dir: projectDir, + absDir: absDir, + goCmd: config.GoCmd, + env: config.CmdEnv(), + testFlags: config.Flags, + bypassGoFlags: config.BypassGoFlags, + progArgs: config.Args, pathPrefix: []string{getRootName(absDir)}, @@ -338,7 +339,7 @@ func (c *runSession) Start() error { if !debug { testArgs := joinTestArgs(pathArgs, runNames) - err = runTest(c.goCmd, c.dir, testFlags, testArgs, c.progArgs, c.env, stdout, stderr) + err = runTest(c.goCmd, c.dir, testFlags, testArgs, c.bypassGoFlags, c.progArgs, c.env, stdout, stderr) } else { err = debugTest(c.goCmd, c.dir, item.File, testFlags, pathArgs, runNames, stdout, stderr, c.progArgs, c.env) } @@ -537,7 +538,7 @@ func (c *pathMapping) traverse(prefix []string, f func(path []string, status Run } } -func runTest(goCmd string, dir string, customFlags []string, testArgs []string, progArgs []string, env []string, stdout io.Writer, stderr io.Writer) error { +func runTest(goCmd string, dir string, customFlags []string, testArgs []string, bypassGoFlags bool, progArgs []string, env []string, stdout io.Writer, stderr io.Writer) error { if goCmd == "" { goCmd = "go" } @@ -546,7 +547,17 @@ func runTest(goCmd string, dir string, customFlags []string, testArgs []string, testFlags = append(testFlags, customFlags...) testFlags = append(testFlags, testArgs...) if len(progArgs) > 0 { + // -args after pkg can be accepted by go test testFlags = append(testFlags, "-args") + + // this extra '--' makes test binary skip + // treating flags starts with '-' as + // test flags and stop complaining + // -something provided but not defined + // see https://github.com/xhd2015/xgo/issues/263 + if bypassGoFlags { + testFlags = append(testFlags, "--") + } testFlags = append(testFlags, progArgs...) } diff --git a/cmd/xgo/version.go b/cmd/xgo/version.go index 4de0950a..dbcd83f2 100644 --- a/cmd/xgo/version.go +++ b/cmd/xgo/version.go @@ -4,8 +4,8 @@ import "fmt" // auto updated const VERSION = "1.0.48" -const REVISION = "e82e0eba1db860999c91f1e94af190e17e1c9ce5+1" -const NUMBER = 306 +const REVISION = "40aa40fc76231d2c9ee681be5456b26d4255f123+1" +const NUMBER = 307 // manually updated const CORE_VERSION = "1.0.48" diff --git a/doc/test-explorer/README.md b/doc/test-explorer/README.md index 0ae02d03..4f873331 100644 --- a/doc/test-explorer/README.md +++ b/doc/test-explorer/README.md @@ -70,6 +70,15 @@ This option is a symptom to `go test -args ...`. Default: `null`. +## `bypass_go_flags` +When running test, this will add a leading `--` before the `args` list, effectively bypassing go's builtin flag parsing procedure. + +The reason why this is needed, is that a go built test binary will parse every flag on the command line, which terminates the program upon missing a flag, with complaining about it. However, that flag is expected to be parsed by the program itself. + +See https://github.com/xhd2015/xgo/issues/263. + +Default: false. + ## `mock_rules` A list of `Rule` config to specify which packages and functions can be mocked.