diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go index 3352ff30..14b6be2e 100644 --- a/cmd/dlv/cmds/commands.go +++ b/cmd/dlv/cmds/commands.go @@ -15,6 +15,7 @@ import ( "unicode" "github.com/derekparker/delve/pkg/config" + "github.com/derekparker/delve/pkg/goversion" "github.com/derekparker/delve/pkg/terminal" "github.com/derekparker/delve/pkg/version" "github.com/derekparker/delve/service" @@ -76,8 +77,11 @@ func New(docCall bool) *cobra.Command { conf = config.LoadConfig() buildFlagsDefault := "" if runtime.GOOS == "windows" { - // Work-around for https://github.com/golang/go/issues/13154 - buildFlagsDefault = "-ldflags='-linkmode internal'" + ver, _ := goversion.Installed() + if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) { + // Work-around for https://github.com/golang/go/issues/13154 + buildFlagsDefault = "-ldflags='-linkmode internal'" + } } // Main dlv root command. diff --git a/pkg/proc/go_version.go b/pkg/goversion/go_version.go similarity index 82% rename from pkg/proc/go_version.go rename to pkg/goversion/go_version.go index 448edeee..05fc238d 100644 --- a/pkg/proc/go_version.go +++ b/pkg/goversion/go_version.go @@ -1,6 +1,7 @@ -package proc +package goversion import ( + "os/exec" "strconv" "strings" ) @@ -13,7 +14,7 @@ type GoVersion struct { Minor int Rev int Beta int - RC int + RC int Proposal string } @@ -21,7 +22,8 @@ var ( GoVer18Beta = GoVersion{1, 8, -1, 0, 0, ""} ) -func ParseVersionString(ver string) (GoVersion, bool) { +// Parse parses a go verison string +func Parse(ver string) (GoVersion, bool) { var r GoVersion var err1, err2, err3 error @@ -130,3 +132,21 @@ func (v *GoVersion) AfterOrEqual(b GoVersion) bool { func (v *GoVersion) IsDevel() bool { return v.Major < 0 } + +const goVersionPrefix = "go version " + +// Installed runs "go verison" and parses the output +func Installed() (GoVersion, bool) { + out, err := exec.Command("go", "version").CombinedOutput() + if err != nil { + return GoVersion{}, false + } + + s := string(out) + + if !strings.HasPrefix(s, goVersionPrefix) { + return GoVersion{}, false + } + + return Parse(s[len(goVersionPrefix):]) +} diff --git a/pkg/goversion/version_test.go b/pkg/goversion/version_test.go new file mode 100644 index 00000000..43c09eac --- /dev/null +++ b/pkg/goversion/version_test.go @@ -0,0 +1,52 @@ +package goversion + +import ( + "runtime" + "testing" +) + +func versionAfterOrEqual(t *testing.T, verStr string, ver GoVersion) { + pver, ok := Parse(verStr) + if !ok { + t.Fatalf("Could not parse version string <%s>", verStr) + } + if !pver.AfterOrEqual(ver) { + t.Fatalf("Version <%s> parsed as %v not after %v", verStr, pver, ver) + } + t.Logf("version string <%s> → %v", verStr, ver) +} + +func TestParseVersionString(t *testing.T) { + versionAfterOrEqual(t, "go1.4", GoVersion{1, 4, 0, 0, 0, ""}) + versionAfterOrEqual(t, "go1.5.0", GoVersion{1, 5, 0, 0, 0, ""}) + versionAfterOrEqual(t, "go1.4.2", GoVersion{1, 4, 2, 0, 0, ""}) + versionAfterOrEqual(t, "go1.5beta2", GoVersion{1, 5, -1, 2, 0, ""}) + versionAfterOrEqual(t, "go1.5rc2", GoVersion{1, 5, -1, 0, 2, ""}) + versionAfterOrEqual(t, "go1.6.1 (appengine-1.9.37)", GoVersion{1, 6, 1, 0, 0, ""}) + versionAfterOrEqual(t, "go1.8.1.typealias", GoVersion{1, 6, 1, 0, 0, ""}) + ver, ok := Parse("devel +17efbfc Tue Jul 28 17:39:19 2015 +0000 linux/amd64") + if !ok { + t.Fatalf("Could not parse devel version string") + } + if !ver.IsDevel() { + t.Fatalf("Devel version string not correctly recognized") + } +} + +func TestInstalled(t *testing.T) { + installedVersion, ok := Installed() + if !ok { + t.Fatalf("could not parse output of go version") + } + runtimeVersion, ok := Parse(runtime.Version()) + if !ok { + t.Fatalf("could not parse output of runtime.Version() %q", runtime.Version()) + } + + t.Logf("installed: %v", installedVersion) + t.Logf("runtime: %v", runtimeVersion) + + if installedVersion != runtimeVersion { + t.Fatalf("version mismatch %#v %#v", installedVersion, runtimeVersion) + } +} diff --git a/pkg/proc/proc.go b/pkg/proc/proc.go index 6692e3c0..0dc590c5 100644 --- a/pkg/proc/proc.go +++ b/pkg/proc/proc.go @@ -9,7 +9,6 @@ import ( "go/token" "path/filepath" "strconv" - "strings" ) type functionDebugInfo struct { diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 4e6b6793..e9c467b9 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -20,6 +20,7 @@ import ( "time" "github.com/derekparker/delve/pkg/dwarf/frame" + "github.com/derekparker/delve/pkg/goversion" "github.com/derekparker/delve/pkg/proc" "github.com/derekparker/delve/pkg/proc/gdbserial" "github.com/derekparker/delve/pkg/proc/native" @@ -429,9 +430,9 @@ func testseq(program string, contFunc contFunc, testcases []nextTest, initialLoc func TestNextGeneral(t *testing.T) { var testcases []nextTest - ver, _ := proc.ParseVersionString(runtime.Version()) + ver, _ := goversion.Parse(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0, ""}) { + if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) { testcases = []nextTest{ {17, 19}, {19, 20}, @@ -582,9 +583,9 @@ func TestNextFunctionReturn(t *testing.T) { func TestNextFunctionReturnDefer(t *testing.T) { var testcases []nextTest - ver, _ := proc.ParseVersionString(runtime.Version()) + ver, _ := goversion.Parse(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 9, -1, 0, 0, ""}) { + if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) { testcases = []nextTest{ {5, 6}, {6, 9}, @@ -1062,34 +1063,6 @@ func TestContinueMulti(t *testing.T) { }) } -func versionAfterOrEqual(t *testing.T, verStr string, ver proc.GoVersion) { - pver, ok := proc.ParseVersionString(verStr) - if !ok { - t.Fatalf("Could not parse version string <%s>", verStr) - } - if !pver.AfterOrEqual(ver) { - t.Fatalf("Version <%s> parsed as %v not after %v", verStr, pver, ver) - } - t.Logf("version string <%s> → %v", verStr, ver) -} - -func TestParseVersionString(t *testing.T) { - versionAfterOrEqual(t, "go1.4", proc.GoVersion{1, 4, 0, 0, 0, ""}) - versionAfterOrEqual(t, "go1.5.0", proc.GoVersion{1, 5, 0, 0, 0, ""}) - versionAfterOrEqual(t, "go1.4.2", proc.GoVersion{1, 4, 2, 0, 0, ""}) - versionAfterOrEqual(t, "go1.5beta2", proc.GoVersion{1, 5, -1, 2, 0, ""}) - versionAfterOrEqual(t, "go1.5rc2", proc.GoVersion{1, 5, -1, 0, 2, ""}) - versionAfterOrEqual(t, "go1.6.1 (appengine-1.9.37)", proc.GoVersion{1, 6, 1, 0, 0, ""}) - versionAfterOrEqual(t, "go1.8.1.typealias", proc.GoVersion{1, 6, 1, 0, 0, ""}) - ver, ok := proc.ParseVersionString("devel +17efbfc Tue Jul 28 17:39:19 2015 +0000 linux/amd64") - if !ok { - t.Fatalf("Could not parse devel version string") - } - if !ver.IsDevel() { - t.Fatalf("Devel version string not correctly recognized") - } -} - func TestBreakpointOnFunctionEntry(t *testing.T) { protest.AllowRecording(t) withTestProcess("testprog", t, func(p proc.Process, fixture protest.Fixture) { @@ -1882,8 +1855,8 @@ func TestPackageVariables(t *testing.T) { } func TestIssue149(t *testing.T) { - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major > 0 && !ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0, ""}) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) { return } // setting breakpoint on break statement @@ -2074,8 +2047,8 @@ func TestIssue509(t *testing.T) { } func TestUnsupportedArch(t *testing.T) { - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major < 0 || !ver.AfterOrEqual(proc.GoVersion{1, 6, -1, 0, 0, ""}) || ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0, ""}) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major < 0 || !ver.AfterOrEqual(goversion.GoVersion{1, 6, -1, 0, 0, ""}) || ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) { // cross compile (with -N?) works only on select versions of go return } @@ -2184,8 +2157,8 @@ func TestStepCallPtr(t *testing.T) { func TestStepReturnAndPanic(t *testing.T) { // Tests that Step works correctly when returning from functions // and when a deferred function is called when panic'ing. - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 9, -1, 0, 0, ""}) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) { testseq("defercall", contStep, []nextTest{ {17, 5}, {5, 6}, @@ -2227,9 +2200,9 @@ func TestStepDeferReturn(t *testing.T) { func TestStepIgnorePrivateRuntime(t *testing.T) { // Tests that Step will ignore calls to private runtime functions // (such as runtime.convT2E in this case) - ver, _ := proc.ParseVersionString(runtime.Version()) + ver, _ := goversion.Parse(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0, ""}) { + if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) { testseq("teststepprog", contStep, []nextTest{ {21, 13}, {13, 14}, @@ -2705,7 +2678,7 @@ func TestStacktraceWithBarriers(t *testing.T) { // struct. // In Go 1.9 stack barriers have been removed and this test must be disabled. - if ver, _ := proc.ParseVersionString(runtime.Version()); ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 9, -1, 0, 0, ""}) { + if ver, _ := goversion.Parse(runtime.Version()); ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) { return } diff --git a/service/test/integration1_test.go b/service/test/integration1_test.go index 82f93660..601f2812 100644 --- a/service/test/integration1_test.go +++ b/service/test/integration1_test.go @@ -13,7 +13,7 @@ import ( protest "github.com/derekparker/delve/pkg/proc/test" - "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/goversion" "github.com/derekparker/delve/service" "github.com/derekparker/delve/service/api" "github.com/derekparker/delve/service/rpc1" @@ -240,9 +240,9 @@ func testnext(testcases []nextTest, initialLocation string, t *testing.T) { func Test1NextGeneral(t *testing.T) { var testcases []nextTest - ver, _ := proc.ParseVersionString(runtime.Version()) + ver, _ := goversion.Parse(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0, ""}) { + if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) { testcases = []nextTest{ {17, 19}, {19, 20}, @@ -1025,8 +1025,8 @@ func Test1SkipPrologue2(t *testing.T) { callme3 := findLocationHelper(t, c, "main.callme3", false, 1, 0)[0] callme3Z := findLocationHelper(t, c, "main.callme3:0", false, 1, 0)[0] - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(proc.GoVer18Beta) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVer18Beta) { findLocationHelper(t, c, "callme.go:19", false, 1, callme3) } else { // callme3 does not have local variables therefore the first line of the diff --git a/service/test/integration2_test.go b/service/test/integration2_test.go index 01f14d91..f93ed76a 100644 --- a/service/test/integration2_test.go +++ b/service/test/integration2_test.go @@ -15,7 +15,7 @@ import ( protest "github.com/derekparker/delve/pkg/proc/test" - "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/goversion" "github.com/derekparker/delve/service" "github.com/derekparker/delve/service/api" "github.com/derekparker/delve/service/rpc2" @@ -285,9 +285,9 @@ func testnext2(testcases []nextTest, initialLocation string, t *testing.T) { func TestNextGeneral(t *testing.T) { var testcases []nextTest - ver, _ := proc.ParseVersionString(runtime.Version()) + ver, _ := goversion.Parse(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0, ""}) { + if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) { testcases = []nextTest{ {17, 19}, {19, 20}, @@ -1099,8 +1099,8 @@ func TestSkipPrologue2(t *testing.T) { callme3 := findLocationHelper(t, c, "main.callme3", false, 1, 0)[0] callme3Z := findLocationHelper(t, c, "main.callme3:0", false, 1, 0)[0] - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(proc.GoVer18Beta) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVer18Beta) { findLocationHelper(t, c, "callme.go:19", false, 1, callme3) } else { // callme3 does not have local variables therefore the first line of the @@ -1200,8 +1200,8 @@ func TestClientServer_Issue528(t *testing.T) { // Was fixed in go 1.7 // Commit that fixes the issue in go: // f744717d1924340b8f5e5a385e99078693ad9097 - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major > 0 && !ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0, ""}) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) { t.Log("Test skipped") return } diff --git a/service/test/variables_test.go b/service/test/variables_test.go index d81005be..f6196a0e 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/derekparker/delve/pkg/goversion" "github.com/derekparker/delve/pkg/proc" "github.com/derekparker/delve/pkg/proc/gdbserial" "github.com/derekparker/delve/pkg/proc/native" @@ -441,8 +442,8 @@ func TestEmbeddedStruct(t *testing.T) { } assertNoError(proc.Continue(p), t, "Continue()") - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major >= 0 && !ver.AfterOrEqual(proc.GoVersion{1, 9, -1, 0, 0, ""}) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) { // on go < 1.9 embedded fields had different names for i := range testcases { if testcases[i].name == "b2" { @@ -711,8 +712,8 @@ func TestEvalExpression(t *testing.T) { {"tm", false, "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [...]}", "main.truncatedMap", nil}, } - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major >= 0 && !ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0, ""}) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) { for i := range testcases { if testcases[i].name == "iface3" { testcases[i].value = "interface {}(*map[string]go/constant.Value) *[]" @@ -836,8 +837,8 @@ func TestIssue426(t *testing.T) { {"anoniface1", `interface { OtherFunction(int, int); SomeFunction(struct { val go/constant.Value }) }`}, } - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 8, -1, 0, 0, ""}) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 8, -1, 0, 0, ""}) { testcases[2].typ = `struct { main.val go/constant.Value }` testcases[3].typ = `func(struct { main.i int }, interface {}, struct { main.val go/constant.Value })` testcases[4].typ = `struct { main.i int; main.j int }` @@ -892,8 +893,8 @@ func TestPackageRenames(t *testing.T) { {"iface2iface", true, `interface {}(*interface { AMethod(int) int; AnotherMethod(int) int }) **github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType {X: 4}`, "", "interface {}", nil}, } - ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major > 0 && !ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0, ""}) { + ver, _ := goversion.Parse(runtime.Version()) + if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) { // Not supported on 1.6 or earlier return } @@ -902,7 +903,7 @@ func TestPackageRenames(t *testing.T) { withTestProcess("pkgrenames", t, func(p proc.Process, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue() returned an error") for _, tc := range testcases { - if ver.Major > 0 && !ver.AfterOrEqual(proc.GoVersion{1, 9, -1, 0, 0, ""}) { + if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) { // before 1.9 embedded struct field have fieldname == type if tc.name == "astruct2" { tc.value = `interface {}(*struct { github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType; X int }) *{github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType: github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType {X: 1, Y: 2}, X: 10}`