From 910f90c3c8274a38bea01c73419dec22902bb5e3 Mon Sep 17 00:00:00 2001 From: aarzilli Date: Mon, 1 Oct 2018 10:19:06 +0200 Subject: [PATCH] proc/native,Makefile: allow compiling on macOS without native backend On macOS 10.14 Apple changed the command line tools so that system headers now need to be manually installed. Instead of adding one extra install step to the install procedure add a build tag to allow compilation of delve without the native backend on macOS. By default (i.e. when using `go get`) this is how delve will be compiled on macOS, the make script is changed to enable compiling the native backend if the required dependencies have been installed. Insure that both configuration still build correctly on Travis CI and change the documentation to describe how to compile the native backend and that it isn't normally needed. Fixes #1359 --- Documentation/installation/osx/install.md | 20 ++-- pkg/proc/native/exc_user_darwin.c | 2 + pkg/proc/native/exec_darwin.c | 2 + pkg/proc/native/exec_darwin.h | 2 + pkg/proc/native/mach_exc_user_darwin.c | 2 + pkg/proc/native/nonative_darwin.go | 124 ++++++++++++++++++++++ pkg/proc/native/proc_darwin.c | 2 + pkg/proc/native/proc_darwin.go | 2 + pkg/proc/native/proc_darwin.h | 2 + pkg/proc/native/ptrace_darwin.go | 2 + pkg/proc/native/registers_darwin_amd64.go | 2 + pkg/proc/native/threads_darwin.c | 2 + pkg/proc/native/threads_darwin.go | 2 + pkg/proc/native/threads_darwin.h | 2 + pkg/proc/proc_test.go | 32 ++++-- pkg/proc/test/support.go | 17 +++ pkg/terminal/command_test.go | 7 +- scripts/make.go | 46 ++++++-- service/test/integration2_test.go | 7 +- 19 files changed, 238 insertions(+), 39 deletions(-) create mode 100644 pkg/proc/native/nonative_darwin.go diff --git a/Documentation/installation/osx/install.md b/Documentation/installation/osx/install.md index 99f02a4b..1f8fdffd 100644 --- a/Documentation/installation/osx/install.md +++ b/Documentation/installation/osx/install.md @@ -1,9 +1,5 @@ # Installation on OSX -Please use the following steps to build and install Delve on OSX. - -## Manual install - Ensure you have a proper compilation toolchain. This should be as simple as: @@ -16,15 +12,15 @@ Now you can install delve using `go get`: $ go get -u github.com/derekparker/delve/cmd/dlv ``` -With this method you will not be able to use delve's native backend. +With this method you will not be able to use delve's native backend, *but you don't need it anyway*: the native backend on macOS [has known problems](https://github.com/derekparker/delve/issues/1112) on recent issues of the OS and is not currently maintained. -Alternatively, you can clone the repo into `$GOPATH/src/github.com/derekparker/delve` and run: +## Compiling the native backend -``` -$ make install -``` -from that directory. +Only do this if you have a valid reason to use the native backend. + +1. Run `xcode-select --install` +2. On macOS 10.14 manually install the legacy include headers by running `/Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg` +3. Clone the repo into `$GOPATH/src/github.com/derekparker/delve` +4. Run `make install` in that directory The makefile will take care of creating and installing a self-signed certificate automatically. - -Note: If you are using Go 1.5 you must set `GO15VENDOREXPERIMENT=1` before continuing. The `GO15VENDOREXPERIMENT` env var simply opts into the [Go 1.5 Vendor Experiment](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo/). diff --git a/pkg/proc/native/exc_user_darwin.c b/pkg/proc/native/exc_user_darwin.c index bd2e6896..bfb590cd 100644 --- a/pkg/proc/native/exc_user_darwin.c +++ b/pkg/proc/native/exc_user_darwin.c @@ -1,3 +1,5 @@ +//+build darwin,macnative + /* * IDENTIFICATION: * stub generated Sun Feb 22 20:54:31 2015 diff --git a/pkg/proc/native/exec_darwin.c b/pkg/proc/native/exec_darwin.c index 67409e3c..40dcc751 100644 --- a/pkg/proc/native/exec_darwin.c +++ b/pkg/proc/native/exec_darwin.c @@ -1,3 +1,5 @@ +//+build darwin,macnative + #include "exec_darwin.h" #include "stdio.h" diff --git a/pkg/proc/native/exec_darwin.h b/pkg/proc/native/exec_darwin.h index 995e7e88..a0510ff8 100644 --- a/pkg/proc/native/exec_darwin.h +++ b/pkg/proc/native/exec_darwin.h @@ -1,3 +1,5 @@ +//+build darwin,macnative + #include "proc_darwin.h" #include diff --git a/pkg/proc/native/mach_exc_user_darwin.c b/pkg/proc/native/mach_exc_user_darwin.c index e4758087..56dfe43b 100644 --- a/pkg/proc/native/mach_exc_user_darwin.c +++ b/pkg/proc/native/mach_exc_user_darwin.c @@ -1,3 +1,5 @@ +//+build darwin,macnative + /* * IDENTIFICATION: * stub generated Sat Feb 21 18:10:52 2015 diff --git a/pkg/proc/native/nonative_darwin.go b/pkg/proc/native/nonative_darwin.go new file mode 100644 index 00000000..790eca8e --- /dev/null +++ b/pkg/proc/native/nonative_darwin.go @@ -0,0 +1,124 @@ +//+build darwin,!macnative + +package native + +import ( + "errors" + "sync" + + "github.com/derekparker/delve/pkg/proc" +) + +var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation") + +// Launch returns ErrNativeBackendDisabled. +func Launch(cmd []string, wd string, foreground bool) (*Process, error) { + return nil, ErrNativeBackendDisabled +} + +// Attach returns ErrNativeBackendDisabled. +func Attach(pid int) (*Process, error) { + return nil, ErrNativeBackendDisabled +} + +// WaitStatus is a synonym for the platform-specific WaitStatus +type WaitStatus struct{} + +// OSSpecificDetails holds information specific to the OSX/Darwin +// operating system / kernel. +type OSSpecificDetails struct{} + +// OSProcessDetails holds Darwin specific information. +type OSProcessDetails struct{} + +func findExecutable(path string, pid int) string { + panic(ErrNativeBackendDisabled) +} + +func killProcess(pid int) error { + panic(ErrNativeBackendDisabled) +} + +func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) { + panic(ErrNativeBackendDisabled) +} + +func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) { + panic(ErrNativeBackendDisabled) +} + +func (dbp *Process) requestManualStop() (err error) { + panic(ErrNativeBackendDisabled) +} + +func (dbp *Process) resume() error { + panic(ErrNativeBackendDisabled) +} + +func (dbp *Process) trapWait(pid int) (*Thread, error) { + panic(ErrNativeBackendDisabled) +} + +func (dbp *Process) stop(trapthread *Thread) (err error) { + panic(ErrNativeBackendDisabled) +} + +func (dbp *Process) updateThreadList() error { + panic(ErrNativeBackendDisabled) +} + +func (dbp *Process) kill() (err error) { + panic(ErrNativeBackendDisabled) +} + +func (dbp *Process) detach(kill bool) error { + panic(ErrNativeBackendDisabled) +} + +// Blocked returns true if the thread is blocked +func (t *Thread) Blocked() bool { + panic(ErrNativeBackendDisabled) +} + +// SetPC sets the value of the PC register. +func (thread *Thread) SetPC(pc uint64) error { + panic(ErrNativeBackendDisabled) +} + +// SetSP sets the value of the SP register. +func (thread *Thread) SetSP(sp uint64) error { + panic(ErrNativeBackendDisabled) +} + +// SetDX sets the value of the DX register. +func (thread *Thread) SetDX(dx uint64) error { + panic(ErrNativeBackendDisabled) +} + +// ReadMemory reads len(buf) bytes at addr into buf. +func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) { + panic(ErrNativeBackendDisabled) +} + +// WriteMemory writes the contents of data at addr. +func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) { + panic(ErrNativeBackendDisabled) +} + +func (t *Thread) resume() error { + panic(ErrNativeBackendDisabled) +} + +func (t *Thread) singleStep() error { + panic(ErrNativeBackendDisabled) +} + +func (t *Thread) restoreRegisters(sr proc.Registers) error { + panic(ErrNativeBackendDisabled) +} + +// Stopped returns whether the thread is stopped at +// the operating system level. +func (t *Thread) Stopped() bool { + panic(ErrNativeBackendDisabled) +} diff --git a/pkg/proc/native/proc_darwin.c b/pkg/proc/native/proc_darwin.c index 24a60849..8e098375 100644 --- a/pkg/proc/native/proc_darwin.c +++ b/pkg/proc/native/proc_darwin.c @@ -1,3 +1,5 @@ +//+build darwin,macnative + #include "proc_darwin.h" static const unsigned char info_plist[] diff --git a/pkg/proc/native/proc_darwin.go b/pkg/proc/native/proc_darwin.go index bb8cd10d..d2e37155 100644 --- a/pkg/proc/native/proc_darwin.go +++ b/pkg/proc/native/proc_darwin.go @@ -1,3 +1,5 @@ +//+build darwin,macnative + package native // #include "proc_darwin.h" diff --git a/pkg/proc/native/proc_darwin.h b/pkg/proc/native/proc_darwin.h index 022ab141..ba2a71f0 100644 --- a/pkg/proc/native/proc_darwin.h +++ b/pkg/proc/native/proc_darwin.h @@ -1,3 +1,5 @@ +//+build darwin,macnative + #include #include #include diff --git a/pkg/proc/native/ptrace_darwin.go b/pkg/proc/native/ptrace_darwin.go index fa19be83..87d3b92a 100644 --- a/pkg/proc/native/ptrace_darwin.go +++ b/pkg/proc/native/ptrace_darwin.go @@ -1,3 +1,5 @@ +//+build darwin,macnative + package native import sys "golang.org/x/sys/unix" diff --git a/pkg/proc/native/registers_darwin_amd64.go b/pkg/proc/native/registers_darwin_amd64.go index 64d84ca8..16fa8471 100644 --- a/pkg/proc/native/registers_darwin_amd64.go +++ b/pkg/proc/native/registers_darwin_amd64.go @@ -1,3 +1,5 @@ +//+build darwin,macnative + package native // #include "threads_darwin.h" diff --git a/pkg/proc/native/threads_darwin.c b/pkg/proc/native/threads_darwin.c index 88a92f40..2b48f8dc 100644 --- a/pkg/proc/native/threads_darwin.c +++ b/pkg/proc/native/threads_darwin.c @@ -1,3 +1,5 @@ +//+build darwin,macnative + #include "threads_darwin.h" int diff --git a/pkg/proc/native/threads_darwin.go b/pkg/proc/native/threads_darwin.go index 0cf28beb..bf422abc 100644 --- a/pkg/proc/native/threads_darwin.go +++ b/pkg/proc/native/threads_darwin.go @@ -1,3 +1,5 @@ +//+build darwin,macnative + package native // #include "threads_darwin.h" diff --git a/pkg/proc/native/threads_darwin.h b/pkg/proc/native/threads_darwin.h index 75a82b8d..ab694c75 100644 --- a/pkg/proc/native/threads_darwin.h +++ b/pkg/proc/native/threads_darwin.h @@ -1,3 +1,5 @@ +//+build darwin,macnative + #include #include #include diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index f01d4553..b54f57b5 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -38,12 +38,7 @@ func init() { func TestMain(m *testing.M) { flag.StringVar(&testBackend, "backend", "", "selects backend") flag.Parse() - if testBackend == "" { - testBackend = os.Getenv("PROCTEST") - if testBackend == "" { - testBackend = "native" - } - } + protest.DefaultTestBackend(&testBackend) os.Exit(protest.RunTestsWithFixtures(m)) } @@ -2036,14 +2031,23 @@ func TestIssue509(t *testing.T) { cmd.Dir = nomaindir assertNoError(cmd.Run(), t, "go build") exepath := filepath.Join(nomaindir, "debug") - _, err := native.Launch([]string{exepath}, ".", false) + defer os.Remove(exepath) + var err error + + switch testBackend { + case "native": + _, err = native.Launch([]string{exepath}, ".", false) + case "lldb": + _, err = gdbserial.LLDBLaunch([]string{exepath}, ".", false) + default: + t.Skip("test not valid for this backend") + } if err == nil { t.Fatalf("expected error but none was generated") } if err != proc.ErrNotExecutable { t.Fatalf("expected error \"%v\" got \"%v\"", proc.ErrNotExecutable, err) } - os.Remove(exepath) } func TestUnsupportedArch(t *testing.T) { @@ -2070,7 +2074,17 @@ func TestUnsupportedArch(t *testing.T) { } defer os.Remove(outfile) - p, err := native.Launch([]string{outfile}, ".", false) + var p proc.Process + + switch testBackend { + case "native": + p, err = native.Launch([]string{outfile}, ".", false) + case "lldb": + p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", false) + default: + t.Skip("test not valid for this backend") + } + switch err { case proc.ErrUnsupportedLinuxArch, proc.ErrUnsupportedWindowsArch, proc.ErrUnsupportedDarwinArch: // all good diff --git a/pkg/proc/test/support.go b/pkg/proc/test/support.go index 3bc5c5e1..6ed14819 100644 --- a/pkg/proc/test/support.go +++ b/pkg/proc/test/support.go @@ -266,3 +266,20 @@ func MustSupportFunctionCalls(t *testing.T, testBackend string) { t.Skip("this backend does not support function calls") } } + +// DefaultTestBackend changes the value of testBackend to be the default +// test backend for the OS, if testBackend isn't already set. +func DefaultTestBackend(testBackend *string) { + if *testBackend != "" { + return + } + *testBackend = os.Getenv("PROCTEST") + if *testBackend != "" { + return + } + if runtime.GOOS == "darwin" { + *testBackend = "lldb" + } else { + *testBackend = "native" + } +} diff --git a/pkg/terminal/command_test.go b/pkg/terminal/command_test.go index 0715e206..81392648 100644 --- a/pkg/terminal/command_test.go +++ b/pkg/terminal/command_test.go @@ -29,12 +29,7 @@ var testBackend string func TestMain(m *testing.M) { flag.StringVar(&testBackend, "backend", "", "selects backend") flag.Parse() - if testBackend == "" { - testBackend = os.Getenv("PROCTEST") - if testBackend == "" { - testBackend = "native" - } - } + test.DefaultTestBackend(&testBackend) os.Exit(test.RunTestsWithFixtures(m)) } diff --git a/scripts/make.go b/scripts/make.go index 960aa84f..57cba2d1 100644 --- a/scripts/make.go +++ b/scripts/make.go @@ -34,9 +34,9 @@ func NewMakeCommands() *cobra.Command { Use: "build", Short: "Build delve", Run: func(cmd *cobra.Command, args []string) { - checkCertCmd(nil, nil) - execute("go", "build", buildFlags(), DelveMainPackagePath) - if runtime.GOOS == "darwin" && os.Getenv("CERT") != "" { + tagFlag := prepareMacnative() + execute("go", "build", tagFlag, buildFlags(), DelveMainPackagePath) + if runtime.GOOS == "darwin" && os.Getenv("CERT") != "" && canMacnative() { codesign("./dlv") } }, @@ -46,9 +46,9 @@ func NewMakeCommands() *cobra.Command { Use: "install", Short: "Installs delve", Run: func(cmd *cobra.Command, args []string) { - checkCertCmd(nil, nil) - execute("go", "install", buildFlags(), DelveMainPackagePath) - if runtime.GOOS == "darwin" { + tagFlag := prepareMacnative() + execute("go", "install", tagFlag, buildFlags(), DelveMainPackagePath) + if runtime.GOOS == "darwin" && os.Getenv("CERT") != "" && canMacnative() { codesign(installedExecutablePath()) } }, @@ -183,6 +183,33 @@ func installedExecutablePath() string { return filepath.Join(strings.TrimSpace(gopath[0]), "bin", "dlv") } +// canMacnative returns true if we can build the native backend for macOS, +// i.e. cgo enabled and the legacy SDK headers: +// https://forums.developer.apple.com/thread/104296 +func canMacnative() bool { + if runtime.GOOS != "darwin" { + return false + } + if strings.TrimSpace(getoutput("go", "env", "CGO_ENABLED")) != "1" { + return false + } + _, err := os.Stat("/usr/include/sys/types.h") + if err != nil { + return false + } + return true +} + +// prepareMacnative checks if we can build the native backend for macOS and +// if we can checks the certificate and then returns the -tags flag. +func prepareMacnative() string { + if !canMacnative() { + return "" + } + checkCertCmd(nil, nil) + return "-tags=macnative" +} + func buildFlags() []string { buildSHA, err := exec.Command("git", "rev-parse", "HEAD").CombinedOutput() if err != nil { @@ -214,6 +241,13 @@ func testCmd(cmd *cobra.Command, args []string) { checkCertCmd(nil, nil) if os.Getenv("TRAVIS") == "true" && runtime.GOOS == "darwin" { + fmt.Println("Building with native backend") + execute("go", "build", "-tags=macnative", buildFlags(), DelveMainPackagePath) + + fmt.Println("\nBuilding without native backend") + execute("go", "build", buildFlags(), DelveMainPackagePath) + + fmt.Println("\nTesting") os.Setenv("PROCTEST", "lldb") executeq("sudo", "-E", "go", "test", testFlags(), allPackages()) return diff --git a/service/test/integration2_test.go b/service/test/integration2_test.go index 67ee5e6f..17fc21be 100644 --- a/service/test/integration2_test.go +++ b/service/test/integration2_test.go @@ -32,12 +32,7 @@ func TestMain(m *testing.M) { var logOutput string flag.StringVar(&logOutput, "log-output", "", "configures log output") flag.Parse() - if testBackend == "" { - testBackend = os.Getenv("PROCTEST") - if testBackend == "" { - testBackend = "native" - } - } + protest.DefaultTestBackend(&testBackend) logflags.Setup(logOutput != "", logOutput) os.Exit(protest.RunTestsWithFixtures(m)) }