mirror of
https://github.com/beego/bee.git
synced 2024-11-21 23:50:54 +00:00
Resolves #393
When new changes are introduced, re-build the debug binary and restart the Delve Debugger client. It also adds a verbose mode for dlv command which notifies the user if enabled.
This commit is contained in:
parent
f80384a9f5
commit
1e478bb32e
@ -1 +1,16 @@
|
||||
// Copyright 2017 bee authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
// Package dlv ...
|
||||
package dlv
|
||||
|
@ -23,6 +23,8 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beego/bee/cmd/commands"
|
||||
"github.com/beego/bee/cmd/commands/version"
|
||||
@ -32,11 +34,12 @@ import (
|
||||
"github.com/derekparker/delve/service/rpc2"
|
||||
"github.com/derekparker/delve/service/rpccommon"
|
||||
"github.com/derekparker/delve/terminal"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
var cmdDlv = &commands.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "dlv [-package=\"\"] [-port=8181]",
|
||||
UsageLine: "dlv [-package=\"\"] [-port=8181] [-verbose=false]",
|
||||
Short: "Start a debugging session using Delve",
|
||||
Long: `dlv command start a debugging session using debugging tool Delve.
|
||||
|
||||
@ -50,43 +53,89 @@ var cmdDlv = &commands.Command{
|
||||
|
||||
var (
|
||||
packageName string
|
||||
verbose bool
|
||||
port int
|
||||
)
|
||||
|
||||
func init() {
|
||||
fs := flag.NewFlagSet("dlv", flag.ContinueOnError)
|
||||
fs.IntVar(&port, "port", 8181, "Port to listen to for clients")
|
||||
fs.StringVar(&packageName, "package", "", "The package to debug (Must have a main package)")
|
||||
fs.BoolVar(&verbose, "verbose", false, "Enable verbose mode")
|
||||
fs.IntVar(&port, "port", 8181, "Port to listen to for clients")
|
||||
cmdDlv.Flag = *fs
|
||||
commands.AvailableCommands = append(commands.AvailableCommands, cmdDlv)
|
||||
}
|
||||
|
||||
func runDlv(cmd *commands.Command, args []string) int {
|
||||
if err := cmd.Flag.Parse(args); err != nil {
|
||||
beeLogger.Log.Fatalf("Error parsing flags: %v", err.Error())
|
||||
beeLogger.Log.Fatalf("Error while parsing flags: %v", err.Error())
|
||||
}
|
||||
|
||||
debugname := "debug"
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", port)
|
||||
return runDelve(addr, debugname)
|
||||
var (
|
||||
addr = fmt.Sprintf("127.0.0.1:%d", port)
|
||||
paths = make([]string, 0)
|
||||
notifyChan = make(chan int)
|
||||
)
|
||||
|
||||
if err := loadPathsToWatch(&paths); err != nil {
|
||||
beeLogger.Log.Fatalf("Error while loading paths to watch: %v", err.Error())
|
||||
}
|
||||
go startWatcher(paths, notifyChan)
|
||||
return startDelveDebugger(addr, notifyChan)
|
||||
}
|
||||
|
||||
// runDelve runs the Delve debugger server
|
||||
func runDelve(addr, debugname string) int {
|
||||
beeLogger.Log.Info("Starting Delve Debugger...")
|
||||
|
||||
err := gobuild(debugname, packageName)
|
||||
if err != nil {
|
||||
beeLogger.Log.Fatalf("%v", err)
|
||||
// buildDebug builds a debug binary in the current working directory
|
||||
func buildDebug() (string, error) {
|
||||
args := []string{"-gcflags", "-N -l", "-o", "debug"}
|
||||
args = append(args, utils.SplitQuotedFields("-ldflags='-linkmode internal'")...)
|
||||
args = append(args, packageName)
|
||||
if err := utils.GoCommand("build", args...); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fp, err := filepath.Abs("./" + debugname)
|
||||
fp, err := filepath.Abs("./debug")
|
||||
if err != nil {
|
||||
beeLogger.Log.Fatalf("%v", err)
|
||||
return "", err
|
||||
}
|
||||
return fp, nil
|
||||
}
|
||||
|
||||
// loadPathsToWatch loads the paths that needs to be watched for changes
|
||||
func loadPathsToWatch(paths *[]string) error {
|
||||
directory, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filepath.Walk(directory, func(path string, info os.FileInfo, _ error) error {
|
||||
if strings.HasSuffix(info.Name(), "docs") {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if strings.HasSuffix(info.Name(), "swagger") {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if strings.HasSuffix(info.Name(), "vendor") {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
if filepath.Ext(info.Name()) == ".go" {
|
||||
*paths = append(*paths, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// startDelveDebugger starts the Delve debugger server
|
||||
func startDelveDebugger(addr string, ch chan int) int {
|
||||
beeLogger.Log.Info("Starting Delve Debugger...")
|
||||
|
||||
fp, err := buildDebug()
|
||||
if err != nil {
|
||||
beeLogger.Log.Fatalf("Error while building debug binary: %v", err)
|
||||
}
|
||||
defer os.Remove(fp)
|
||||
|
||||
abs, err := filepath.Abs(debugname)
|
||||
abs, err := filepath.Abs("./debug")
|
||||
if err != nil {
|
||||
beeLogger.Log.Fatalf("%v", err)
|
||||
}
|
||||
@ -103,7 +152,7 @@ func runDelve(addr, debugname string) int {
|
||||
AcceptMulti: true,
|
||||
AttachPid: 0,
|
||||
APIVersion: 2,
|
||||
WorkingDir: "./",
|
||||
WorkingDir: ".",
|
||||
ProcessArgs: []string{abs},
|
||||
}, false)
|
||||
if err := server.Run(); err != nil {
|
||||
@ -112,26 +161,87 @@ func runDelve(addr, debugname string) int {
|
||||
|
||||
// Start the Delve client REPL
|
||||
client := rpc2.NewClient(addr)
|
||||
term := terminal.New(client, nil)
|
||||
// Make sure the client is restarted when new changes are introduced
|
||||
go func() {
|
||||
for {
|
||||
if val := <-ch; val == 0 {
|
||||
if _, err := client.Restart(); err != nil {
|
||||
utils.Notify("Error while restarting the client: "+err.Error(), "bee")
|
||||
} else {
|
||||
if verbose {
|
||||
utils.Notify("Delve Debugger Restarted", "bee")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Create the terminal and connect it to the client debugger
|
||||
term := terminal.New(client, nil)
|
||||
status, err := term.Run()
|
||||
if err != nil {
|
||||
beeLogger.Log.Fatalf("Could not start Delve REPL: %v", err)
|
||||
}
|
||||
defer term.Close()
|
||||
|
||||
// Stop and kill the debugger server once
|
||||
// user quits the REPL
|
||||
// Stop and kill the debugger server once user quits the REPL
|
||||
if err := server.Stop(true); err != nil {
|
||||
beeLogger.Log.Fatalf("Could not stop Delve server: %v", err)
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
// gobuild runs the "go build" command on the specified package
|
||||
func gobuild(debugname, pkg string) error {
|
||||
args := []string{"-gcflags", "-N -l", "-o", debugname}
|
||||
args = append(args, utils.SplitQuotedFields("-ldflags='-linkmode internal'")...)
|
||||
args = append(args, pkg)
|
||||
return utils.GoCommand("build", args...)
|
||||
var eventsModTime = make(map[string]int64)
|
||||
|
||||
// startWatcher starts the fsnotify watcher on the passed paths
|
||||
func startWatcher(paths []string, ch chan int) {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
beeLogger.Log.Fatalf("Could not start the watcher: %v", err)
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
// Feed the paths to the watcher
|
||||
for _, path := range paths {
|
||||
if err := watcher.Add(path); err != nil {
|
||||
beeLogger.Log.Fatalf("Could not set a watch on path: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case evt := <-watcher.Events:
|
||||
build := true
|
||||
if filepath.Ext(evt.Name) != ".go" {
|
||||
continue
|
||||
}
|
||||
|
||||
mt := utils.GetFileModTime(evt.Name)
|
||||
if t := eventsModTime[evt.Name]; mt == t {
|
||||
build = false
|
||||
}
|
||||
eventsModTime[evt.Name] = mt
|
||||
|
||||
if build {
|
||||
go func() {
|
||||
if verbose {
|
||||
utils.Notify("Rebuilding application with the new changes", "bee")
|
||||
}
|
||||
|
||||
// Wait 1s before re-build until there is no file change
|
||||
scheduleTime := time.Now().Add(1 * time.Second)
|
||||
time.Sleep(scheduleTime.Sub(time.Now()))
|
||||
_, err := buildDebug()
|
||||
if err != nil {
|
||||
utils.Notify("Build Failed: "+err.Error(), "bee")
|
||||
} else {
|
||||
ch <- 0 // Notify listeners
|
||||
}
|
||||
}()
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
if err != nil {
|
||||
ch <- -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user