service/dap: add go build stderr to error response (#2448)

* service/dap: add go build stderr to error response

* service/dap: add go build stderr to error response

* Skip message check for build errors

* test for flag provided message
This commit is contained in:
Suzy Mueller 2021-04-26 13:31:59 -04:00 committed by GitHub
parent ee5729e107
commit bbae9a9d12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 23 deletions

@ -54,32 +54,58 @@ func optflags(args []string) []string {
// GoBuild builds non-test files in 'pkgs' with the specified 'buildflags' // GoBuild builds non-test files in 'pkgs' with the specified 'buildflags'
// and writes the output at 'debugname'. // and writes the output at 'debugname'.
func GoBuild(debugname string, pkgs []string, buildflags string) error { func GoBuild(debugname string, pkgs []string, buildflags string) error {
args := []string{"-o", debugname} args := goBuildArgs(debugname, pkgs, buildflags, false)
args = optflags(args) return gocommandRun("build", args...)
if buildflags != "" {
args = append(args, config.SplitQuotedFields(buildflags, '\'')...)
}
args = append(args, pkgs...)
return gocommand("build", args...)
} }
// GoBuild builds test files 'pkgs' with the specified 'buildflags' // GoBuildCombinedOutput builds non-test files in 'pkgs' with the specified 'buildflags'
// and writes the output at 'debugname'.
func GoBuildCombinedOutput(debugname string, pkgs []string, buildflags string) ([]byte, error) {
args := goBuildArgs(debugname, pkgs, buildflags, false)
return gocommandCombinedOutput("build", args...)
}
// GoTestBuild builds test files 'pkgs' with the specified 'buildflags'
// and writes the output at 'debugname'. // and writes the output at 'debugname'.
func GoTestBuild(debugname string, pkgs []string, buildflags string) error { func GoTestBuild(debugname string, pkgs []string, buildflags string) error {
args := []string{"-c", "-o", debugname} args := goBuildArgs(debugname, pkgs, buildflags, true)
return gocommandRun("test", args...)
}
// GoTestBuildCombinedOutput builds test files 'pkgs' with the specified 'buildflags'
// and writes the output at 'debugname'.
func GoTestBuildCombinedOutput(debugname string, pkgs []string, buildflags string) ([]byte, error) {
args := goBuildArgs(debugname, pkgs, buildflags, true)
return gocommandCombinedOutput("test", args...)
}
func goBuildArgs(debugname string, pkgs []string, buildflags string, isTest bool) []string {
args := []string{"-o", debugname}
if isTest {
args = append([]string{"-c"}, args...)
}
args = optflags(args) args = optflags(args)
if buildflags != "" { if buildflags != "" {
args = append(args, config.SplitQuotedFields(buildflags, '\'')...) args = append(args, config.SplitQuotedFields(buildflags, '\'')...)
} }
args = append(args, pkgs...) args = append(args, pkgs...)
return gocommand("test", args...) return args
} }
func gocommand(command string, args ...string) error { func gocommandRun(command string, args ...string) error {
goBuild := gocommandExecCmd(command, args...)
goBuild.Stderr = os.Stdout
goBuild.Stdout = os.Stderr
return goBuild.Run()
}
func gocommandCombinedOutput(command string, args ...string) ([]byte, error) {
return gocommandExecCmd(command, args...).CombinedOutput()
}
func gocommandExecCmd(command string, args ...string) *exec.Cmd {
allargs := []string{command} allargs := []string{command}
allargs = append(allargs, args...) allargs = append(allargs, args...)
goBuild := exec.Command("go", allargs...) goBuild := exec.Command("go", allargs...)
goBuild.Stderr = os.Stderr return goBuild
goBuild.Stdout = os.Stdout
return goBuild.Run()
} }

@ -566,16 +566,17 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
} }
s.log.Debugf("building binary at %s", debugbinary) s.log.Debugf("building binary at %s", debugbinary)
var out []byte
switch mode { switch mode {
case "debug": case "debug":
err = gobuild.GoBuild(debugbinary, []string{program}, buildFlags) out, err = gobuild.GoBuildCombinedOutput(debugbinary, []string{program}, buildFlags)
case "test": case "test":
err = gobuild.GoTestBuild(debugbinary, []string{program}, buildFlags) out, err = gobuild.GoTestBuildCombinedOutput(debugbinary, []string{program}, buildFlags)
} }
if err != nil { if err != nil {
s.sendErrorResponse(request.Request, s.sendErrorResponse(request.Request,
FailedToLaunch, "Failed to launch", FailedToLaunch, "Failed to launch",
fmt.Sprintf("Build error: %s", err.Error())) fmt.Sprintf("Build error: %s (%s)", strings.TrimSpace(string(out)), err.Error()))
return return
} }
program = debugbinary program = debugbinary

@ -2999,6 +2999,14 @@ func TestBadLaunchRequests(t *testing.T) {
} }
} }
expectFailedToLaunchWithMessageRegex := func(response *dap.ErrorResponse, errmsg string) {
t.Helper()
expectFailedToLaunch(response)
if matched, _ := regexp.MatchString(errmsg, response.Body.Error.Format); !matched {
t.Errorf("\ngot %q\nwant %q", response.Body.Error.Format, errmsg)
}
}
// Test for the DAP-specific detailed error message. // Test for the DAP-specific detailed error message.
client.LaunchRequest("exec", "", stopOnEntry) client.LaunchRequest("exec", "", stopOnEntry)
expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
@ -3080,18 +3088,18 @@ func TestBadLaunchRequests(t *testing.T) {
expectFailedToLaunch(client.ExpectErrorResponse(t)) // No such file or directory expectFailedToLaunch(client.ExpectErrorResponse(t)) // No such file or directory
client.LaunchRequest("debug", fixture.Path+"_does_not_exist", stopOnEntry) client.LaunchRequest("debug", fixture.Path+"_does_not_exist", stopOnEntry)
expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1") expectFailedToLaunch(client.ExpectErrorResponse(t))
client.LaunchRequest("" /*debug by default*/, fixture.Path+"_does_not_exist", stopOnEntry) client.LaunchRequest("" /*debug by default*/, fixture.Path+"_does_not_exist", stopOnEntry)
expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1") expectFailedToLaunch(client.ExpectErrorResponse(t))
client.LaunchRequest("exec", fixture.Source, stopOnEntry) client.LaunchRequest("exec", fixture.Source, stopOnEntry)
expectFailedToLaunch(client.ExpectErrorResponse(t)) // Not an executable expectFailedToLaunch(client.ExpectErrorResponse(t)) // Not an executable
client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": "bad flags"}) client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": "-bad -flags"})
expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1") expectFailedToLaunchWithMessageRegex(client.ExpectErrorResponse(t), `Failed to launch: Build error: .*flag provided but not defined.*`)
client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "buildFlags": "bad flags"}) client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "buildFlags": "-bad -flags"})
expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1") expectFailedToLaunchWithMessageRegex(client.ExpectErrorResponse(t), `Failed to launch: Build error: .*flag provided but not defined.*`)
// Bad "wd". // Bad "wd".
client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": false, "cwd": "dir/invalid"}) client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": false, "cwd": "dir/invalid"})