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:
parent
4d8daeb1a8
commit
5d78c04e62
7
_fixtures/buildtest/main.go
Normal file
7
_fixtures/buildtest/main.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("hello world!")
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package cmds
|
package cmds
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -10,8 +11,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/derekparker/delve/config"
|
"github.com/derekparker/delve/config"
|
||||||
"github.com/derekparker/delve/service"
|
"github.com/derekparker/delve/service"
|
||||||
@ -68,7 +69,7 @@ func New() *cobra.Command {
|
|||||||
buildFlagsDefault := ""
|
buildFlagsDefault := ""
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
// Work-around for https://github.com/golang/go/issues/13154
|
// Work-around for https://github.com/golang/go/issues/13154
|
||||||
buildFlagsDefault = "-ldflags=-linkmode internal"
|
buildFlagsDefault = "-ldflags='-linkmode internal'"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main dlv root command.
|
// 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 {
|
func gobuild(debugname, pkg string) error {
|
||||||
args := []string{"-gcflags", "-N -l", "-o", debugname}
|
args := []string{"-gcflags", "-N -l", "-o", debugname}
|
||||||
if BuildFlags != "" {
|
if BuildFlags != "" {
|
||||||
args = append(args, strings.Fields(BuildFlags)...)
|
args = append(args, splitQuotedFields(BuildFlags)...)
|
||||||
}
|
}
|
||||||
args = append(args, pkg)
|
args = append(args, pkg)
|
||||||
return gocommand("build", args...)
|
return gocommand("build", args...)
|
||||||
@ -456,7 +457,7 @@ func gobuild(debugname, pkg string) error {
|
|||||||
func gotestbuild(pkg string) error {
|
func gotestbuild(pkg string) error {
|
||||||
args := []string{"-gcflags", "-N -l", "-c", "-o", testdebugname}
|
args := []string{"-gcflags", "-N -l", "-c", "-o", testdebugname}
|
||||||
if BuildFlags != "" {
|
if BuildFlags != "" {
|
||||||
args = append(args, strings.Fields(BuildFlags)...)
|
args = append(args, splitQuotedFields(BuildFlags)...)
|
||||||
}
|
}
|
||||||
args = append(args, pkg)
|
args = append(args, pkg)
|
||||||
return gocommand("test", args...)
|
return gocommand("test", args...)
|
||||||
@ -469,3 +470,60 @@ func gocommand(command string, args ...string) error {
|
|||||||
goBuild.Stderr = os.Stderr
|
goBuild.Stderr = os.Stderr
|
||||||
return goBuild.Run()
|
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
|
||||||
|
}
|
||||||
|
|||||||
21
cmd/dlv/cmds/commands_test.go
Normal file
21
cmd/dlv/cmds/commands_test.go
Normal file
@ -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
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/proc/test"
|
||||||
"github.com/derekparker/delve/service"
|
"github.com/derekparker/delve/service"
|
||||||
"github.com/derekparker/delve/service/api"
|
"github.com/derekparker/delve/service/api"
|
||||||
"github.com/derekparker/delve/service/rpccommon"
|
|
||||||
"github.com/derekparker/delve/service/rpc2"
|
"github.com/derekparker/delve/service/rpc2"
|
||||||
|
"github.com/derekparker/delve/service/rpccommon"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FakeTerminal struct {
|
type FakeTerminal struct {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user