dlv: bugfix: Allow quoting in build flags argument (#639)

Allows quoted substrings in build-flags flag. This fixes a build
problem on windows where the default build flags must contain a space.

Fixes #634 and #638
This commit is contained in:
Alessandro Arzilli 2016-09-25 17:26:59 +02:00 committed by Derek Parker
parent 4d8daeb1a8
commit 5d78c04e62
5 changed files with 152 additions and 5 deletions

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("hello world!")
}

@ -1,6 +1,7 @@
package cmds
import (
"bytes"
"errors"
"fmt"
"net"
@ -10,8 +11,8 @@ import (
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
"unicode"
"github.com/derekparker/delve/config"
"github.com/derekparker/delve/service"
@ -68,7 +69,7 @@ func New() *cobra.Command {
buildFlagsDefault := ""
if runtime.GOOS == "windows" {
// Work-around for https://github.com/golang/go/issues/13154
buildFlagsDefault = "-ldflags=-linkmode internal"
buildFlagsDefault = "-ldflags='-linkmode internal'"
}
// Main dlv root command.
@ -447,7 +448,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, kind exec
func gobuild(debugname, pkg string) error {
args := []string{"-gcflags", "-N -l", "-o", debugname}
if BuildFlags != "" {
args = append(args, strings.Fields(BuildFlags)...)
args = append(args, splitQuotedFields(BuildFlags)...)
}
args = append(args, pkg)
return gocommand("build", args...)
@ -456,7 +457,7 @@ func gobuild(debugname, pkg string) error {
func gotestbuild(pkg string) error {
args := []string{"-gcflags", "-N -l", "-c", "-o", testdebugname}
if BuildFlags != "" {
args = append(args, strings.Fields(BuildFlags)...)
args = append(args, splitQuotedFields(BuildFlags)...)
}
args = append(args, pkg)
return gocommand("test", args...)
@ -469,3 +470,60 @@ func gocommand(command string, args ...string) error {
goBuild.Stderr = os.Stderr
return goBuild.Run()
}
// Like strings.Fields but ignores spaces inside areas surrounded
// by single quotes.
// To specify a single quote use backslash to escape it: '\''
func splitQuotedFields(in string) []string {
type stateEnum int
const (
inSpace stateEnum = iota
inField
inQuote
inQuoteEscaped
)
state := inSpace
r := []string{}
var buf bytes.Buffer
for _, ch := range in {
switch state {
case inSpace:
if ch == '\'' {
state = inQuote
} else if !unicode.IsSpace(ch) {
buf.WriteRune(ch)
state = inField
}
case inField:
if ch == '\'' {
state = inQuote
} else if unicode.IsSpace(ch) {
r = append(r, buf.String())
buf.Reset()
} else {
buf.WriteRune(ch)
}
case inQuote:
if ch == '\'' {
state = inField
} else if ch == '\\' {
state = inQuoteEscaped
} else {
buf.WriteRune(ch)
}
case inQuoteEscaped:
buf.WriteRune(ch)
state = inQuote
}
}
if buf.Len() != 0 {
r = append(r, buf.String())
}
return r
}

@ -0,0 +1,21 @@
package cmds
import (
"testing"
)
func TestSplitQuotedFields(t *testing.T) {
in := `field'A' 'fieldB' fie'l\'d'C fieldD 'another field' fieldE`
tgt := []string{"fieldA", "fieldB", "fiel'dC", "fieldD", "another field", "fieldE"}
out := splitQuotedFields(in)
if len(tgt) != len(out) {
t.Fatalf("expected %#v, got %#v (len mismatch)", tgt, out)
}
for i := range tgt {
if tgt[i] != out[i] {
t.Fatalf(" expected %#v, got %#v (mismatch at %d)", tgt, out, i)
}
}
}

61
cmd/dlv/dlv_test.go Normal file

@ -0,0 +1,61 @@
package main
import (
"bufio"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
protest "github.com/derekparker/delve/proc/test"
"github.com/derekparker/delve/service/rpc2"
)
func assertNoError(err error, t testing.TB, s string) {
if err != nil {
_, file, line, _ := runtime.Caller(1)
fname := filepath.Base(file)
t.Fatalf("failed assertion at %s:%d: %s - %s\n", fname, line, s, err)
}
}
func TestBuild(t *testing.T) {
const listenAddr = "localhost:40573"
cmd := exec.Command("go", "build", "github.com/derekparker/delve/cmd/dlv")
assertNoError(cmd.Run(), t, "go build")
wd, _ := os.Getwd()
dlvbin := filepath.Join(wd, "dlv")
defer os.Remove(dlvbin)
fixtures := protest.FindFixturesDir()
buildtestdir := filepath.Join(fixtures, "buildtest")
cmd = exec.Command(dlvbin, "debug", "--headless=true", "--listen="+listenAddr, "--api-version=2")
cmd.Dir = buildtestdir
stdout, err := cmd.StdoutPipe()
assertNoError(err, t, "stdout pipe")
cmd.Start()
defer func() {
cmd.Process.Signal(os.Interrupt)
cmd.Wait()
}()
scan := bufio.NewScanner(stdout)
// wait for the debugger to start
scan.Scan()
go func() {
for scan.Scan() {
// keep pipe empty
}
}()
client := rpc2.NewClient(listenAddr)
state := <-client.Continue()
if !state.Exited {
t.Fatal("Program did not exit")
}
}

@ -13,8 +13,8 @@ import (
"github.com/derekparker/delve/proc/test"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/rpccommon"
"github.com/derekparker/delve/service/rpc2"
"github.com/derekparker/delve/service/rpccommon"
)
type FakeTerminal struct {