diff --git a/createapp.go b/createapp.go new file mode 100644 index 0000000..61b8d5e --- /dev/null +++ b/createapp.go @@ -0,0 +1,163 @@ +package main + +import ( + "fmt" + "os" + "path" + "strings" +) + +var cmdCreate = &Command{ + UsageLine: "create [appname]", + Short: "create an application base on beego framework", + Long: ` +create an application base on beego framework + +In the current path, will create a folder named [appname] + +In the appname folder has the follow struct: + + |- main.go + |- conf + |- app.conf + |- controllers + |- default.go + |- models + |- static + |- js + |- css + |- img + |- views + index.tpl + +`, +} + +func init() { + cmdCreate.Run = createapp +} + +func createapp(cmd *Command, args []string) { + crupath, _ := os.Getwd() + if len(args) != 1 { + fmt.Println("error args") + os.Exit(2) + } + gopath := os.Getenv("GOPATH") + if gopath == "" { + fmt.Println("you should set GOPATH in the env") + os.Exit(2) + } + haspath := false + if crupath != path.Join(gopath, "src") { + wgopath := strings.Split(gopath, ";") + if len(wgopath) >= 1 { + for _, wg := range wgopath { + wg = wg + `\src` + if crupath == wg { + haspath = true + break + } + } + } + if !haspath { + lgopath := strings.Split(gopath, ":") + if len(lgopath) >= 1 { + for _, wg := range lgopath { + if crupath == path.Join(wg, "src") { + haspath = true + break + } + } + } + } + + } else { + haspath = true + } + if !haspath { + fmt.Println("can't create application outside of GOPATH") + fmt.Println("you first should `cd $GOPATH/src` then use create") + os.Exit(2) + } + apppath := path.Join(crupath, args[0]) + os.Mkdir(apppath, 0755) + fmt.Println("create app folder:", apppath) + os.Mkdir(path.Join(apppath, "conf"), 0755) + fmt.Println("create conf:", path.Join(apppath, "conf")) + os.Mkdir(path.Join(apppath, "controllers"), 0755) + fmt.Println("create controllers:", path.Join(apppath, "controllers")) + os.Mkdir(path.Join(apppath, "conf"), 0755) + fmt.Println("create models:", path.Join(apppath, "models")) + os.Mkdir(path.Join(apppath, "models"), 0755) + fmt.Println("create static:", path.Join(apppath, "static")) + os.Mkdir(path.Join(apppath, "static", "js"), 0755) + fmt.Println("create static js:", path.Join(apppath, "static", "js")) + os.Mkdir(path.Join(apppath, "static", "css"), 0755) + fmt.Println("create static css:", path.Join(apppath, "static", "css")) + os.Mkdir(path.Join(apppath, "static", "img"), 0755) + fmt.Println("create static img:", path.Join(apppath, "static", "img")) + fmt.Println("create views:", path.Join(apppath, "views")) + os.Mkdir(path.Join(apppath, "views"), 0755) + fmt.Println("create conf app.conf:", path.Join(apppath, "conf", "app.conf")) + writetofile(path.Join(apppath, "conf", "app.conf"), "appname = "+args[0]) + + fmt.Println("create controllers default.go:", path.Join(apppath, "controllers", "default.go")) + writetofile(path.Join(apppath, "controllers", "default.go"), controllers) + + fmt.Println("create views index.tpl:", path.Join(apppath, "views", "index.tpl")) + writetofile(path.Join(apppath, "views", "index.tpl"), indextpl) + + fmt.Println("create main.go:", path.Join(apppath, "main.go")) + writetofile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", args[0], -1)) +} + +var maingo = `package main + +import ( + "{{.Appname}}/controllers" + "github.com/astaxie/beego" +) + +func main() { + beego.BeeApp.RegisterController("/", &controllers.MainController{}) + beego.BeeApp.Run() +} + +` +var controllers = `package controllers + +import ( + "github.com/astaxie/beego" +) + +type MainController struct { + beego.Controller +} + +func (this *MainController) Get() { + this.Data["Username"] = "astaxie" + this.Data["Email"] = "astaxie@gmail.com" + this.TplNames = "index.tpl" +} +` + +var indextpl = ` + + + beego welcome template + + +

Hello, world!{{.Username}},{{.Email}}

+ + +` + +func writetofile(filename, content string) { + f, err := os.Create(filename) + if err != nil { + panic(err) + } + defer f.Close() + f.WriteString(content) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..bd309e9 --- /dev/null +++ b/main.go @@ -0,0 +1,160 @@ +package main + +import ( + "flag" + "fmt" + "html/template" + "io" + "log" + "os" + "strings" +) + +type Command struct { + // Run runs the command. + // The args are the arguments after the command name. + Run func(cmd *Command, args []string) + + // UsageLine is the one-line usage message. + // The first word in the line is taken to be the command name. + UsageLine string + + // Short is the short description shown in the 'go help' output. + Short string + + // Long is the long message shown in the 'go help ' output. + Long string + + // Flag is a set of flags specific to this command. + Flag flag.FlagSet + + // CustomFlags indicates that the command will do its own + // flag parsing. + CustomFlags bool +} + +// Name returns the command's name: the first word in the usage line. +func (c *Command) Name() string { + name := c.UsageLine + i := strings.Index(name, " ") + if i >= 0 { + name = name[:i] + } + return name +} + +func (c *Command) Usage() { + fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine) + fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long)) + os.Exit(2) +} + +// Runnable reports whether the command can be run; otherwise +// it is a documentation pseudo-command such as importpath. +func (c *Command) Runnable() bool { + return c.Run != nil +} + +var commands = []*Command{ + cmdCreate, + cmdStart, + //cmdReStart, +} + +func main() { + flag.Usage = usage + flag.Parse() + log.SetFlags(0) + + args := flag.Args() + if len(args) < 1 { + usage() + } + + if args[0] == "help" { + help(args[1:]) + return + } + + for _, cmd := range commands { + if cmd.Name() == args[0] && cmd.Run != nil { + cmd.Flag.Usage = func() { cmd.Usage() } + if cmd.CustomFlags { + args = args[1:] + } else { + cmd.Flag.Parse(args[1:]) + args = cmd.Flag.Args() + } + cmd.Run(cmd, args) + os.Exit(2) + return + } + } + + fmt.Fprintf(os.Stderr, "go: unknown subcommand %q\nRun 'go help' for usage.\n", args[0]) + os.Exit(2) +} + +var usageTemplate = `Bee is a tool for managing beego framework. + +Usage: + + bee command [arguments] + +The commands are: +{{range .}}{{if .Runnable}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + +Use "bee help [command]" for more information about a command. + +Additional help topics: +{{range .}}{{if not .Runnable}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + +Use "bee help [topic]" for more information about that topic. + +` + +var helpTemplate = `{{if .Runnable}}usage: bee {{.UsageLine}} + +{{end}}{{.Long | trim}} +` + +func usage() { + tmpl(os.Stdout, usageTemplate, commands) + os.Exit(2) +} + +func tmpl(w io.Writer, text string, data interface{}) { + t := template.New("top") + t.Funcs(template.FuncMap{"trim": strings.TrimSpace}) + template.Must(t.Parse(text)) + if err := t.Execute(w, data); err != nil { + panic(err) + } +} + +func help(args []string) { + if len(args) == 0 { + usage() + // not exit 2: succeeded at 'go help'. + return + } + if len(args) != 1 { + fmt.Fprintf(os.Stdout, "usage: bee help command\n\nToo many arguments given.\n") + os.Exit(2) // failed at 'bee help' + } + + arg := args[0] + + for _, cmd := range commands { + if cmd.Name() == arg { + tmpl(os.Stdout, helpTemplate, cmd) + // not exit 2: succeeded at 'go help cmd'. + return + } + } + + fmt.Fprintf(os.Stdout, "Unknown help topic %#q. Run 'bee help'.\n", arg) + os.Exit(2) // failed at 'bee help cmd' +} diff --git a/start.go b/start.go new file mode 100644 index 0000000..a63a16e --- /dev/null +++ b/start.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "os" + "path" +) + +var cmdStart = &Command{ + UsageLine: "start [appname]", + Short: "start the app which can hot compile", + Long: ` +start the appname throw exec.Command + +then start a inotify watch for current dir + +when the file has changed bee will auto go build and restart the app + + file changed + | + checked is go file + | + yes no + | | + go build do nothing + | + restart app +`, +} + +func init() { + cmdStart.Run = startapp +} + +func startapp(cmd *Command, args []string) { + if len(args) != 1 { + fmt.Println("error args") + os.Exit(2) + } + crupath, _ := os.Getwd() + var paths []string + paths = append(paths, path.Join(crupath, "controllers"), path.Join(crupath, "models")) + NewWatcher(paths) + go Start(args[0]) + for { + select { + case <-restart: + go Start(args[0]) + case <-builderror: + fmt.Println("build error:", builderror) + } + } +} diff --git a/watch.go b/watch.go new file mode 100644 index 0000000..97a65f9 --- /dev/null +++ b/watch.go @@ -0,0 +1,120 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/howeyc/fsnotify" + "io" + "log" + "os" + "os/exec" + "runtime" +) + +var ( + builderror chan string + restart chan bool + cmd *exec.Cmd +) + +func init() { + builderror = make(chan string) + restart = make(chan bool) +} + +func NewWatcher(paths []string) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + + go func() { + for { + select { + case e := <-watcher.Event: + fmt.Println(e) + go Autobuild() + case err := <-watcher.Error: + log.Fatal("error:", err) + } + } + }() + for _, path := range paths { + fmt.Println(path) + err = watcher.Watch(path) + if err != nil { + log.Fatal(err) + } + } + +} + +func Autobuild() { + defer func() { + if err := recover(); err != nil { + str := "" + for i := 1; ; i += 1 { + _, file, line, ok := runtime.Caller(i) + if !ok { + break + } + str = str + fmt.Sprintf("%v,%v", file, line) + } + builderror <- str + + } + }() + fmt.Println("Autobuild") + path, _ := os.Getwd() + os.Chdir(path) + bcmd := exec.Command("go", "build") + var out bytes.Buffer + var berr bytes.Buffer + bcmd.Stdout = &out + bcmd.Stderr = &berr + err := bcmd.Run() + if err != nil { + fmt.Println("run error", err) + } + if out.String() == "" { + Kill() + } else { + builderror <- berr.String() + } +} + +func Kill() { + err := cmd.Process.Kill() + if err != nil { + panic(err) + } +} + +func Start(appname string) { + fmt.Println("start", appname) + cmd = exec.Command(appname) + stdout, err := cmd.StdoutPipe() + if err != nil { + fmt.Println("stdout:", err) + } + stderr, err := cmd.StderrPipe() + if err != nil { + fmt.Println("stdin:", err) + } + r := io.MultiReader(stdout, stderr) + err = cmd.Start() + if err != nil { + fmt.Println("cmd start:", err) + } + for { + buf := make([]byte, 1024) + count, err := r.Read(buf) + if err != nil || count == 0 { + fmt.Println("process exit") + restart <- true + return + } else { + fmt.Println("result:", string(buf)) + } + } +}