Add config file and command alias config options.

This commit is contained in:
Tyler Bunnell 2015-08-16 21:41:25 -06:00 committed by Derek Parker
parent 38e0051308
commit be2d9c3a84
5 changed files with 145 additions and 36 deletions

@ -28,6 +28,7 @@ deps: check-cert
go get -u github.com/spf13/cobra go get -u github.com/spf13/cobra
go get -u golang.org/x/sys/unix go get -u golang.org/x/sys/unix
go get -u github.com/davecheney/profile go get -u github.com/davecheney/profile
go get -u gopkg.in/yaml.v2
build: deps build: deps
go build $(FLAGS) github.com/derekparker/delve/cmd/dlv go build $(FLAGS) github.com/derekparker/delve/cmd/dlv

@ -12,10 +12,12 @@ import (
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/config"
"github.com/derekparker/delve/service" "github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api" "github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/rpc" "github.com/derekparker/delve/service/rpc"
"github.com/derekparker/delve/terminal" "github.com/derekparker/delve/terminal"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -28,6 +30,9 @@ var (
) )
func main() { func main() {
// Config setup and load.
conf := config.LoadConfig()
// Main dlv root command. // Main dlv root command.
rootCommand := &cobra.Command{ rootCommand := &cobra.Command{
Use: "dlv", Use: "dlv",
@ -88,7 +93,7 @@ starts and attaches to it, and enables you to immediately begin debugging your p
defer os.Remove(fp) defer os.Remove(fp)
processArgs := append([]string{"./" + debugname}, args...) processArgs := append([]string{"./" + debugname}, args...)
return execute(0, processArgs) return execute(0, processArgs, conf)
}() }()
os.Exit(status) 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]", Use: "exec [./path/to/binary]",
Short: "Runs precompiled binary, attaches and begins debug session.", Short: "Runs precompiled binary, attaches and begins debug session.",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
os.Exit(execute(0, args)) os.Exit(execute(0, args, conf))
}, },
} }
rootCommand.AddCommand(execCommand) 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) defer os.Remove(debugname)
processArgs := append([]string{debugname}, args...) processArgs := append([]string{debugname}, args...)
return execute(0, processArgs) return execute(0, processArgs, conf)
}() }()
os.Exit(status) 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]) fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0])
os.Exit(1) os.Exit(1)
} }
os.Exit(execute(pid, nil)) os.Exit(execute(pid, nil, conf))
}, },
} }
rootCommand.AddCommand(attachCommand) 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") fmt.Fprintf(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n")
os.Exit(1) os.Exit(1)
} }
os.Exit(connect(addr)) os.Exit(connect(addr, conf))
}, },
} }
rootCommand.AddCommand(connectCommand) rootCommand.AddCommand(connectCommand)
@ -266,11 +271,11 @@ starts and attaches to it, and enable you to immediately begin debugging your pr
rootCommand.Execute() rootCommand.Execute()
} }
func connect(addr string) int { func connect(addr string, conf *config.Config) int {
// Create and start a terminal - attach to running instance // Create and start a terminal - attach to running instance
var client service.Client var client service.Client
client = rpc.NewClient(addr) client = rpc.NewClient(addr)
term := terminal.New(client) term := terminal.New(client, conf)
err, status := term.Run() err, status := term.Run()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -278,7 +283,7 @@ func connect(addr string) int {
return status return status
} }
func execute(attachPid int, processArgs []string) int { func execute(attachPid int, processArgs []string, conf *config.Config) int {
// Make a TCP listener // Make a TCP listener
listener, err := net.Listen("tcp", Addr) listener, err := net.Listen("tcp", Addr)
if err != nil { if err != nil {
@ -303,7 +308,7 @@ func execute(attachPid int, processArgs []string) int {
// Create and start a terminal // Create and start a terminal
var client service.Client var client service.Client
client = rpc.NewClient(listener.Addr().String()) client = rpc.NewClient(listener.Addr().String())
term := terminal.New(client) term := terminal.New(client, conf)
err, status = term.Run() err, status = term.Run()
} else { } else {
ch := make(chan os.Signal) ch := make(chan os.Signal)

112
config/config.go Normal file

@ -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
}

@ -110,6 +110,15 @@ func (c *Commands) Find(cmdstr string) cmdfunc {
return noCmdAvailable 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 { func CommandFunc(fn func() error) cmdfunc {
return func(client service.Client, args ...string) error { return func(client service.Client, args ...string) error {
return fn() return fn()

@ -5,41 +5,36 @@ import (
"io" "io"
"os" "os"
"os/signal" "os/signal"
"os/user"
"path"
"strings" "strings"
"github.com/peterh/liner" "github.com/peterh/liner"
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/config"
"github.com/derekparker/delve/service" "github.com/derekparker/delve/service"
) )
const configDir string = ".dlv"
const historyFile string = ".dbg_history" const historyFile string = ".dbg_history"
type Term struct { type Term struct {
client service.Client client service.Client
prompt string prompt string
line *liner.State line *liner.State
conf *config.Config
} }
func New(client service.Client) *Term { func New(client service.Client, conf *config.Config) *Term {
return &Term{ return &Term{
prompt: "(dlv) ", prompt: "(dlv) ",
line: liner.NewLiner(), line: liner.NewLiner(),
client: client, client: client,
conf: conf,
} }
} }
func (t *Term) Run() (error, int) { func (t *Term) Run() (error, int) {
defer t.line.Close() 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 // Send the debugger a halt command on SIGINT
ch := make(chan os.Signal) ch := make(chan os.Signal)
signal.Notify(ch, sys.SIGINT) signal.Notify(ch, sys.SIGINT)
@ -53,7 +48,10 @@ func (t *Term) Run() (error, int) {
}() }()
cmds := DebugCommands(t.client) 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 { if err != nil {
fmt.Printf("Unable to load history file: %v.", err) 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) { func (t *Term) handleExit() (error, int) {
fullHistoryFile, err := getConfigFilePath(historyFile) fullHistoryFile, err := config.GetConfigFilePath(historyFile)
if err != nil { if err != nil {
fmt.Println("Error saving history file:", err) fmt.Println("Error saving history file:", err)
} else { } else {
@ -150,19 +148,3 @@ func parseCommand(cmdstr string) (string, []string) {
vals := strings.Split(cmdstr, " ") vals := strings.Split(cmdstr, " ")
return vals[0], vals[1:] 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
}