diff --git a/Makefile b/Makefile index aed7c073..26c07182 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ deps: check-cert go get -u github.com/spf13/cobra go get -u golang.org/x/sys/unix go get -u github.com/davecheney/profile + go get -u gopkg.in/yaml.v2 build: deps go build $(FLAGS) github.com/derekparker/delve/cmd/dlv diff --git a/cmd/dlv/main.go b/cmd/dlv/main.go index 0e2b58a3..44af9eae 100644 --- a/cmd/dlv/main.go +++ b/cmd/dlv/main.go @@ -12,10 +12,12 @@ import ( sys "golang.org/x/sys/unix" + "github.com/derekparker/delve/config" "github.com/derekparker/delve/service" "github.com/derekparker/delve/service/api" "github.com/derekparker/delve/service/rpc" "github.com/derekparker/delve/terminal" + "github.com/spf13/cobra" ) @@ -28,6 +30,9 @@ var ( ) func main() { + // Config setup and load. + conf := config.LoadConfig() + // Main dlv root command. rootCommand := &cobra.Command{ Use: "dlv", @@ -88,7 +93,7 @@ starts and attaches to it, and enables you to immediately begin debugging your p defer os.Remove(fp) processArgs := append([]string{"./" + debugname}, args...) - return execute(0, processArgs) + return execute(0, processArgs, conf) }() os.Exit(status) }, @@ -100,7 +105,7 @@ starts and attaches to it, and enables you to immediately begin debugging your p Use: "exec [./path/to/binary]", Short: "Runs precompiled binary, attaches and begins debug session.", Run: func(cmd *cobra.Command, args []string) { - os.Exit(execute(0, args)) + os.Exit(execute(0, args, conf)) }, } rootCommand.AddCommand(execCommand) @@ -220,7 +225,7 @@ starts and attaches to it, and enable you to immediately begin debugging your pr defer os.Remove(debugname) processArgs := append([]string{debugname}, args...) - return execute(0, processArgs) + return execute(0, processArgs, conf) }() os.Exit(status) }, @@ -238,7 +243,7 @@ starts and attaches to it, and enable you to immediately begin debugging your pr fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0]) os.Exit(1) } - os.Exit(execute(pid, nil)) + os.Exit(execute(pid, nil, conf)) }, } rootCommand.AddCommand(attachCommand) @@ -258,7 +263,7 @@ starts and attaches to it, and enable you to immediately begin debugging your pr fmt.Fprintf(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n") os.Exit(1) } - os.Exit(connect(addr)) + os.Exit(connect(addr, conf)) }, } rootCommand.AddCommand(connectCommand) @@ -266,11 +271,11 @@ starts and attaches to it, and enable you to immediately begin debugging your pr rootCommand.Execute() } -func connect(addr string) int { +func connect(addr string, conf *config.Config) int { // Create and start a terminal - attach to running instance var client service.Client client = rpc.NewClient(addr) - term := terminal.New(client) + term := terminal.New(client, conf) err, status := term.Run() if err != nil { fmt.Println(err) @@ -278,7 +283,7 @@ func connect(addr string) int { return status } -func execute(attachPid int, processArgs []string) int { +func execute(attachPid int, processArgs []string, conf *config.Config) int { // Make a TCP listener listener, err := net.Listen("tcp", Addr) if err != nil { @@ -303,7 +308,7 @@ func execute(attachPid int, processArgs []string) int { // Create and start a terminal var client service.Client client = rpc.NewClient(listener.Addr().String()) - term := terminal.New(client) + term := terminal.New(client, conf) err, status = term.Run() } else { ch := make(chan os.Signal) diff --git a/config/config.go b/config/config.go new file mode 100644 index 00000000..7381c5d9 --- /dev/null +++ b/config/config.go @@ -0,0 +1,112 @@ +package config + +import ( + "fmt" + "io/ioutil" + "os" + "os/user" + "path" + + yaml "gopkg.in/yaml.v2" +) + +const ( + configDir string = ".dlv" + configFile string = "config.yml" +) + +// Config defines all configuration options available to be set through the config file. +type Config struct { + Aliases map[string][]string +} + +// LoadConfig attempts to populate a Config object from the config.yml file. +func LoadConfig() *Config { + err := createConfigPath() + if err != nil { + fmt.Printf("Could not create config directory: %v.") + return nil + } + fullConfigFile, err := GetConfigFilePath(configFile) + if err != nil { + fmt.Printf("Unable to get config file path: %v.", err) + return nil + } + + f, err := os.Open(fullConfigFile) + if err != nil { + createDefaultConfig(fullConfigFile) + return nil + } + defer func() { + err := f.Close() + if err != nil { + fmt.Printf("Closing config file failed: %v.", err) + } + }() + + data, err := ioutil.ReadAll(f) + if err != nil { + fmt.Printf("Unable to read config data: %v.", err) + return nil + } + + var c Config + err = yaml.Unmarshal(data, &c) + if err != nil { + fmt.Printf("Unable to decode config file: %v.", err) + return nil + } + + return &c +} + +func createDefaultConfig(path string) { + f, err := os.Create(path) + if err != nil { + fmt.Printf("Unable to create config file: %v.", err) + return + } + defer func() { + err := f.Close() + if err != nil { + fmt.Printf("Closing config file failed: %v.", err) + } + }() + err = writeDefaultConfig(f) + if err != nil { + fmt.Printf("Unable to write default configuration: %v.", err) + } +} + +func writeDefaultConfig(f *os.File) error { + _, err := f.WriteString( + `# Configuration file for the delve debugger. + +# This is the default configuration file. Available options are provided, but disabled. +# Delete the leading hash mark to enable an item. + +# Provided aliases will be added to the default aliases for a given command. +aliases: + # command: ["alias1", "alias2"] +`) + return err +} + +// createConfigPath creates the directory structure at which all config files are saved. +func createConfigPath() error { + path, err := GetConfigFilePath("") + if err != nil { + return err + } + return os.MkdirAll(path, 0700) +} + +// GetConfigFilePath gets the full path to the given config file name. +func GetConfigFilePath(file string) (string, error) { + usr, err := user.Current() + if err != nil { + return "", err + } + return path.Join(usr.HomeDir, configDir, file), nil +} diff --git a/terminal/command.go b/terminal/command.go index 3706afdc..55a6b42d 100644 --- a/terminal/command.go +++ b/terminal/command.go @@ -110,6 +110,15 @@ func (c *Commands) Find(cmdstr string) cmdfunc { return noCmdAvailable } +// Merge takes aliases defined in the config struct and merges them with the default aliases. +func (c *Commands) Merge(allAliases map[string][]string) { + for i := range c.cmds { + if aliases, ok := allAliases[c.cmds[i].aliases[0]]; ok { + c.cmds[i].aliases = append(c.cmds[i].aliases, aliases...) + } + } +} + func CommandFunc(fn func() error) cmdfunc { return func(client service.Client, args ...string) error { return fn() diff --git a/terminal/terminal.go b/terminal/terminal.go index c6ddf615..1434613d 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -5,41 +5,36 @@ import ( "io" "os" "os/signal" - "os/user" - "path" "strings" "github.com/peterh/liner" sys "golang.org/x/sys/unix" + "github.com/derekparker/delve/config" "github.com/derekparker/delve/service" ) -const configDir string = ".dlv" const historyFile string = ".dbg_history" type Term struct { client service.Client prompt string line *liner.State + conf *config.Config } -func New(client service.Client) *Term { +func New(client service.Client, conf *config.Config) *Term { return &Term{ prompt: "(dlv) ", line: liner.NewLiner(), client: client, + conf: conf, } } func (t *Term) Run() (error, int) { defer t.line.Close() - err := createConfigPath() - if err != nil { - fmt.Printf("Could not create config directory: %v.") - } - // Send the debugger a halt command on SIGINT ch := make(chan os.Signal) signal.Notify(ch, sys.SIGINT) @@ -53,7 +48,10 @@ func (t *Term) Run() (error, int) { }() cmds := DebugCommands(t.client) - fullHistoryFile, err := getConfigFilePath(historyFile) + if t.conf != nil && t.conf.Aliases != nil { + cmds.Merge(t.conf.Aliases) + } + fullHistoryFile, err := config.GetConfigFilePath(historyFile) if err != nil { fmt.Printf("Unable to load history file: %v.", err) } @@ -117,7 +115,7 @@ func (t *Term) promptForInput() (string, error) { } func (t *Term) handleExit() (error, int) { - fullHistoryFile, err := getConfigFilePath(historyFile) + fullHistoryFile, err := config.GetConfigFilePath(historyFile) if err != nil { fmt.Println("Error saving history file:", err) } else { @@ -150,19 +148,3 @@ func parseCommand(cmdstr string) (string, []string) { vals := strings.Split(cmdstr, " ") return vals[0], vals[1:] } - -func createConfigPath() error { - path, err := getConfigFilePath("") - if err != nil { - return err - } - return os.MkdirAll(path, 0700) -} - -func getConfigFilePath(file string) (string, error) { - usr, err := user.Current() - if err != nil { - return "", err - } - return path.Join(usr.HomeDir, configDir, file), nil -}