whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,538 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-delve/delve/pkg/goversion"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const DelveMainPackagePath = "github.com/go-delve/delve/cmd/dlv"
|
||||
|
||||
var Verbose bool
|
||||
var NOTimeout bool
|
||||
var TestIncludePIE bool
|
||||
var TestSet, TestRegex, TestBackend, TestBuildMode string
|
||||
var Tags *[]string
|
||||
var Architecture string
|
||||
var OS string
|
||||
var DisableGit bool
|
||||
|
||||
func NewMakeCommands() *cobra.Command {
|
||||
RootCommand := &cobra.Command{
|
||||
Use: "make.go",
|
||||
Short: "make script for delve.",
|
||||
}
|
||||
|
||||
RootCommand.AddCommand(&cobra.Command{
|
||||
Use: "check-cert",
|
||||
Short: "Check certificate for macOS.",
|
||||
Run: checkCertCmd,
|
||||
})
|
||||
|
||||
buildCmd := &cobra.Command{
|
||||
Use: "build",
|
||||
Short: "Build delve",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
envflags := []string{}
|
||||
if len(Architecture) > 0 {
|
||||
envflags = append(envflags, "GOARCH="+Architecture)
|
||||
}
|
||||
if len(OS) > 0 {
|
||||
envflags = append(envflags, "GOOS="+OS)
|
||||
}
|
||||
if len(envflags) > 0 {
|
||||
executeEnv(envflags, "go", "build", "-ldflags", "-extldflags -static", tagFlags(), buildFlags(), DelveMainPackagePath)
|
||||
} else {
|
||||
execute("go", "build", "-ldflags", "-extldflags -static", tagFlags(), buildFlags(), DelveMainPackagePath)
|
||||
}
|
||||
if runtime.GOOS == "darwin" && os.Getenv("CERT") != "" && canMacnative() && !isCodesigned("./dlv") {
|
||||
codesign("./dlv")
|
||||
}
|
||||
},
|
||||
}
|
||||
Tags = buildCmd.PersistentFlags().StringArray("tags", []string{}, "Build tags")
|
||||
buildCmd.PersistentFlags().BoolVarP(&DisableGit, "no-git", "G", false, "Do not use git")
|
||||
buildCmd.PersistentFlags().StringVar(&Architecture, "GOARCH", "", "Architecture to build for")
|
||||
buildCmd.PersistentFlags().StringVar(&OS, "GOOS", "", "OS to build for")
|
||||
RootCommand.AddCommand(buildCmd)
|
||||
|
||||
RootCommand.AddCommand(&cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Installs delve",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
execute("go", "install", tagFlags(), buildFlags(), DelveMainPackagePath)
|
||||
if runtime.GOOS == "darwin" && os.Getenv("CERT") != "" && canMacnative() && !isCodesigned(installedExecutablePath()) {
|
||||
codesign(installedExecutablePath())
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
RootCommand.AddCommand(&cobra.Command{
|
||||
Use: "uninstall",
|
||||
Short: "Uninstalls delve",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
execute("go", "clean", "-i", DelveMainPackagePath)
|
||||
},
|
||||
})
|
||||
|
||||
test := &cobra.Command{
|
||||
Use: "test",
|
||||
Short: "Tests delve",
|
||||
Long: `Tests delve.
|
||||
|
||||
Use the flags -s, -r and -b to specify which tests to run. Specifying nothing will run all tests relevant for the current environment (see testStandard).
|
||||
`,
|
||||
Run: testCmd,
|
||||
}
|
||||
test.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "Verbose tests")
|
||||
test.PersistentFlags().BoolVarP(&NOTimeout, "timeout", "t", false, "Set infinite timeouts")
|
||||
test.PersistentFlags().StringVarP(&TestSet, "test-set", "s", "", `Select the set of tests to run, one of either:
|
||||
all tests all packages
|
||||
basic tests proc, integration and terminal
|
||||
integration tests github.com/go-delve/delve/service/test
|
||||
package-name test the specified package only
|
||||
`)
|
||||
test.PersistentFlags().StringVarP(&TestRegex, "test-run", "r", "", `Only runs the tests matching the specified regex. This option can only be specified if testset is a single package`)
|
||||
test.PersistentFlags().StringVarP(&TestBackend, "test-backend", "b", "", `Runs tests for the specified backend only, one of either:
|
||||
default the default backend
|
||||
lldb lldb backend
|
||||
rr rr backend
|
||||
|
||||
This option can only be specified if testset is basic or a single package.`)
|
||||
test.PersistentFlags().StringVarP(&TestBuildMode, "test-build-mode", "m", "", `Runs tests compiling with the specified build mode, one of either:
|
||||
normal normal buildmode (default)
|
||||
pie PIE buildmode
|
||||
|
||||
This option can only be specified if testset is basic or a single package.`)
|
||||
test.PersistentFlags().BoolVarP(&TestIncludePIE, "pie", "", true, "Standard testing should include PIE")
|
||||
|
||||
RootCommand.AddCommand(test)
|
||||
|
||||
RootCommand.AddCommand(&cobra.Command{
|
||||
Use: "vendor",
|
||||
Short: "vendors dependencies",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
execute("go", "mod", "vendor")
|
||||
},
|
||||
})
|
||||
|
||||
return RootCommand
|
||||
}
|
||||
|
||||
func checkCert() bool {
|
||||
// If we're on OSX make sure the proper CERT env var is set.
|
||||
if runtime.GOOS != "darwin" || os.Getenv("CERT") != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
x := exec.Command("_scripts/gencert.sh")
|
||||
x.Stdout = os.Stdout
|
||||
x.Stderr = os.Stderr
|
||||
x.Env = os.Environ()
|
||||
err := x.Run()
|
||||
if x.ProcessState != nil && !x.ProcessState.Success() {
|
||||
fmt.Printf("An error occurred when generating and installing a new certificate\n")
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf("An error occurred when generating and installing a new certificate: %v\n", err)
|
||||
return false
|
||||
}
|
||||
os.Setenv("CERT", "dlv-cert")
|
||||
return true
|
||||
}
|
||||
|
||||
func checkCertCmd(cmd *cobra.Command, args []string) {
|
||||
if !checkCert() {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func strflatten(v []interface{}) []string {
|
||||
r := []string{}
|
||||
for _, s := range v {
|
||||
switch s := s.(type) {
|
||||
case []string:
|
||||
r = append(r, s...)
|
||||
case string:
|
||||
if s != "" {
|
||||
r = append(r, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func executeq(env []string, cmd string, args ...interface{}) {
|
||||
x := exec.Command(cmd, strflatten(args)...)
|
||||
x.Stdout = os.Stdout
|
||||
x.Stderr = os.Stderr
|
||||
x.Env = os.Environ()
|
||||
for _, e := range env {
|
||||
x.Env = append(x.Env, e)
|
||||
}
|
||||
err := x.Run()
|
||||
if x.ProcessState != nil && !x.ProcessState.Success() {
|
||||
os.Exit(1)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func execute(cmd string, args ...interface{}) {
|
||||
fmt.Printf("%s %s\n", cmd, strings.Join(quotemaybe(strflatten(args)), " "))
|
||||
env := []string{}
|
||||
executeq(env, cmd, args...)
|
||||
}
|
||||
|
||||
func executeEnv(env []string, cmd string, args ...interface{}) {
|
||||
fmt.Printf("%s %s %s\n", strings.Join(env, " "),
|
||||
cmd, strings.Join(quotemaybe(strflatten(args)), " "))
|
||||
executeq(env, cmd, args...)
|
||||
}
|
||||
|
||||
func quotemaybe(args []string) []string {
|
||||
for i := range args {
|
||||
if strings.Index(args[i], " ") >= 0 {
|
||||
args[i] = fmt.Sprintf("%q", args[i])
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func getoutput(cmd string, args ...interface{}) string {
|
||||
x := exec.Command(cmd, strflatten(args)...)
|
||||
x.Env = os.Environ()
|
||||
out, err := x.Output()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error executing %s %v\n", cmd, args)
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !x.ProcessState.Success() {
|
||||
fmt.Fprintf(os.Stderr, "Error executing %s %v\n", cmd, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func isCodesigned(path string) bool {
|
||||
x := exec.Command("codesign", "--verify", path)
|
||||
x.Stdout = io.Discard
|
||||
x.Stderr = io.Discard
|
||||
x.Env = os.Environ()
|
||||
err := x.Run()
|
||||
return err == nil && x.ProcessState != nil && x.ProcessState.Success()
|
||||
}
|
||||
|
||||
func codesign(path string) {
|
||||
execute("codesign", "-s", os.Getenv("CERT"), path)
|
||||
}
|
||||
|
||||
func installedExecutablePath() string {
|
||||
if gobin := os.Getenv("GOBIN"); gobin != "" {
|
||||
return filepath.Join(gobin, "dlv")
|
||||
}
|
||||
gopath := strings.Split(getoutput("go", "env", "GOPATH"), ":")
|
||||
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" && runtime.GOARCH == "amd64") {
|
||||
return false
|
||||
}
|
||||
if strings.TrimSpace(getoutput("go", "env", "CGO_ENABLED")) != "1" {
|
||||
return false
|
||||
}
|
||||
|
||||
macOSVersion := strings.Split(strings.TrimSpace(getoutput("/usr/bin/sw_vers", "-productVersion")), ".")
|
||||
|
||||
major, err := strconv.ParseInt(macOSVersion[0], 10, 64)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
minor, err := strconv.ParseInt(macOSVersion[1], 10, 64)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
typesHeader := "/usr/include/sys/types.h"
|
||||
if major >= 11 || (major == 10 && minor >= 15) {
|
||||
sdkpath := strings.TrimSpace(getoutput("xcrun", "--sdk", "macosx", "--show-sdk-path"))
|
||||
typesHeader = filepath.Join(sdkpath, "usr", "include", "sys", "types.h")
|
||||
}
|
||||
_, err = os.Stat(typesHeader)
|
||||
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 ""
|
||||
}
|
||||
if !checkCert() {
|
||||
return ""
|
||||
}
|
||||
return "macnative"
|
||||
}
|
||||
|
||||
func tagFlags() string {
|
||||
var tags []string
|
||||
if mactags := prepareMacnative(); mactags != "" {
|
||||
tags = append(tags, mactags)
|
||||
}
|
||||
if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" {
|
||||
tags = append(tags, "exp.winarm64")
|
||||
}
|
||||
if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" {
|
||||
tags = append(tags, "exp.linuxppc64le")
|
||||
}
|
||||
if Tags != nil && len(*Tags) > 0 {
|
||||
tags = append(tags, *Tags...)
|
||||
}
|
||||
if len(tags) == 0 {
|
||||
return ""
|
||||
}
|
||||
return "-tags=" + strings.Join(tags, ",")
|
||||
}
|
||||
|
||||
func buildFlags() []string {
|
||||
var ldFlags string
|
||||
buildSHA, err := getBuildSHA()
|
||||
if err != nil {
|
||||
log.Printf("error getting build SHA via git: %v", err)
|
||||
} else {
|
||||
ldFlags = "-X main.Build=" + buildSHA
|
||||
}
|
||||
if runtime.GOOS == "darwin" {
|
||||
ldFlags = "-s " + ldFlags
|
||||
}
|
||||
return []string{fmt.Sprintf("-ldflags=%s", ldFlags)}
|
||||
}
|
||||
|
||||
func testFlags() []string {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
testFlags := []string{"-count", "1", "-p", "1"}
|
||||
if Verbose {
|
||||
testFlags = append(testFlags, "-v")
|
||||
}
|
||||
if NOTimeout {
|
||||
testFlags = append(testFlags, "-timeout", "0")
|
||||
}
|
||||
if len(os.Getenv("TEAMCITY_VERSION")) > 0 {
|
||||
testFlags = append(testFlags, "-json")
|
||||
}
|
||||
if runtime.GOOS == "darwin" {
|
||||
testFlags = append(testFlags, "-exec="+wd+"/_scripts/testsign")
|
||||
}
|
||||
return testFlags
|
||||
}
|
||||
|
||||
func testCmd(cmd *cobra.Command, args []string) {
|
||||
checkCertCmd(nil, nil)
|
||||
|
||||
if TestSet == "" && TestBackend == "" && TestBuildMode == "" {
|
||||
if TestRegex != "" {
|
||||
fmt.Printf("Can not use --test-run without --test-set\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
testStandard()
|
||||
return
|
||||
}
|
||||
|
||||
if TestSet == "" {
|
||||
TestSet = "all"
|
||||
}
|
||||
|
||||
if TestBackend == "" {
|
||||
TestBackend = "default"
|
||||
}
|
||||
|
||||
if TestBuildMode == "" {
|
||||
TestBuildMode = "normal"
|
||||
}
|
||||
|
||||
testCmdIntl(TestSet, TestRegex, TestBackend, TestBuildMode)
|
||||
}
|
||||
|
||||
func testStandard() {
|
||||
fmt.Println("Testing default backend")
|
||||
testCmdIntl("all", "", "default", "normal")
|
||||
if inpath("lldb-server") && !goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) {
|
||||
fmt.Println("\nTesting LLDB backend")
|
||||
testCmdIntl("basic", "", "lldb", "normal")
|
||||
}
|
||||
if inpath("rr") {
|
||||
fmt.Println("\nTesting RR backend")
|
||||
testCmdIntl("basic", "", "rr", "normal")
|
||||
}
|
||||
if TestIncludePIE {
|
||||
dopie := false
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
if runtime.GOARCH != "ppc64le" {
|
||||
dopie = true
|
||||
}
|
||||
case "windows":
|
||||
// windows/arm64 always uses pie buildmode, no need to test everything again.
|
||||
// only on Go 1.15 or later, with CGO_ENABLED and gcc found in path
|
||||
if runtime.GOARCH != "arm64" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {
|
||||
out, err := exec.Command("go", "env", "CGO_ENABLED").CombinedOutput()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if strings.TrimSpace(string(out)) == "1" {
|
||||
if _, err = exec.LookPath("gcc"); err == nil {
|
||||
dopie = true
|
||||
}
|
||||
}
|
||||
}
|
||||
case "darwin":
|
||||
if runtime.GOARCH == "amd64" {
|
||||
// arm64 can only build in pie mode
|
||||
dopie = true
|
||||
}
|
||||
}
|
||||
if dopie {
|
||||
fmt.Println("\nTesting PIE buildmode, default backend")
|
||||
testCmdIntl("basic", "", "default", "pie")
|
||||
testCmdIntl("core", "", "default", "pie")
|
||||
}
|
||||
}
|
||||
if runtime.GOOS == "linux" && inpath("rr") {
|
||||
fmt.Println("\nTesting PIE buildmode, RR backend")
|
||||
testCmdIntl("basic", "", "rr", "pie")
|
||||
}
|
||||
}
|
||||
|
||||
func testCmdIntl(testSet, testRegex, testBackend, testBuildMode string) {
|
||||
testPackages := testSetToPackages(testSet)
|
||||
if len(testPackages) == 0 {
|
||||
fmt.Printf("Unknown test set %q\n", testSet)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if testRegex != "" && len(testPackages) != 1 {
|
||||
fmt.Printf("Can not use test-run with test set %q\n", testSet)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
backendFlag := ""
|
||||
if testBackend != "" && testBackend != "default" {
|
||||
if testSet != "basic" && len(testPackages) != 1 {
|
||||
fmt.Printf("Can not use test-backend with test set %q\n", testSet)
|
||||
os.Exit(1)
|
||||
}
|
||||
backendFlag = "-backend=" + testBackend
|
||||
}
|
||||
|
||||
buildModeFlag := ""
|
||||
if testBuildMode != "" && testBuildMode != "normal" {
|
||||
if testSet != "basic" && len(testPackages) != 1 {
|
||||
fmt.Printf("Can not use test-buildmode with test set %q\n", testSet)
|
||||
os.Exit(1)
|
||||
}
|
||||
buildModeFlag = "-test-buildmode=" + testBuildMode
|
||||
}
|
||||
|
||||
env := []string{}
|
||||
if os.Getenv("CI") != "" {
|
||||
env = os.Environ()
|
||||
}
|
||||
|
||||
if len(testPackages) > 3 {
|
||||
executeq(env, "go", "test", testFlags(), buildFlags(), tagFlags(), testPackages, backendFlag, buildModeFlag)
|
||||
} else if testRegex != "" {
|
||||
executeq(env, "go", "test", testFlags(), buildFlags(), tagFlags(), testPackages, "-run="+testRegex, backendFlag, buildModeFlag)
|
||||
} else {
|
||||
executeq(env, "go", "test", testFlags(), buildFlags(), tagFlags(), testPackages, backendFlag, buildModeFlag)
|
||||
}
|
||||
}
|
||||
|
||||
func testSetToPackages(testSet string) []string {
|
||||
switch testSet {
|
||||
case "", "all":
|
||||
return allPackages()
|
||||
|
||||
case "basic":
|
||||
return []string{"github.com/go-delve/delve/pkg/proc", "github.com/go-delve/delve/service/test", "github.com/go-delve/delve/pkg/terminal"}
|
||||
|
||||
case "integration":
|
||||
return []string{"github.com/go-delve/delve/service/test"}
|
||||
|
||||
default:
|
||||
for _, pkg := range allPackages() {
|
||||
if pkg == testSet || strings.HasSuffix(pkg, "/"+testSet) {
|
||||
return []string{pkg}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func defaultBackend() string {
|
||||
if runtime.GOOS == "darwin" {
|
||||
return "lldb"
|
||||
}
|
||||
return "native"
|
||||
}
|
||||
|
||||
func inpath(exe string) bool {
|
||||
path, _ := exec.LookPath(exe)
|
||||
return path != ""
|
||||
}
|
||||
|
||||
func allPackages() []string {
|
||||
r := []string{}
|
||||
for _, dir := range strings.Split(getoutput("go", "list", "-mod=vendor", tagFlags(), "./..."), "\n") {
|
||||
dir = strings.TrimSpace(dir)
|
||||
if dir == "" || strings.Contains(dir, "/vendor/") || strings.Contains(dir, "/_scripts") {
|
||||
continue
|
||||
}
|
||||
r = append(r, dir)
|
||||
}
|
||||
sort.Strings(r)
|
||||
return r
|
||||
}
|
||||
|
||||
// getBuildSHA will invoke git to return the current SHA of the commit at HEAD.
|
||||
// If invoking git has been disabled, it will return an empty string instead.
|
||||
func getBuildSHA() (string, error) {
|
||||
if DisableGit {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
buildSHA, err := exec.Command("git", "rev-parse", "HEAD").CombinedOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
shaStr := strings.TrimSpace(string(buildSHA))
|
||||
return shaStr, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
allPackages() // checks that vendor directory is synced as a side effect
|
||||
NewMakeCommands().Execute()
|
||||
}
|
||||
Reference in New Issue
Block a user