選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

145 行
3.2KB

  1. package main
  2. import (
  3. "fmt"
  4. "go/build"
  5. "os"
  6. "os/exec"
  7. "os/signal"
  8. "path/filepath"
  9. "regexp"
  10. "runtime"
  11. "syscall"
  12. log "github.com/Sirupsen/logrus"
  13. "github.com/codegangsta/cli"
  14. "github.com/goanywhere/rex"
  15. "github.com/goanywhere/rex/internal"
  16. "github.com/goanywhere/rex/livereload"
  17. "github.com/goanywhere/cmd"
  18. "github.com/goanywhere/env"
  19. "github.com/goanywhere/fs"
  20. )
  21. var (
  22. port int
  23. watchList = regexp.MustCompile(`\.(go|html|atom|rss|xml)$`)
  24. )
  25. type app struct {
  26. dir string
  27. binary string
  28. args []string
  29. task string // script for npm.
  30. }
  31. // build compiles the application into rex-bin executable
  32. // to run & optionally compiles static assets using npm.
  33. func (self *app) build() {
  34. var done = make(chan bool)
  35. cmd.Loading(done)
  36. // * try build the application into rex-bin(.exe)
  37. command := exec.Command("go", "build", "-o", self.binary)
  38. command.Dir = self.dir
  39. if e := command.Run(); e != nil {
  40. log.Fatalf("Failed to compile the application: %v", e)
  41. }
  42. done <- true
  43. }
  44. // run executes the runnerable executable under package binary root.
  45. func (self *app) run() (gorun chan bool) {
  46. gorun = make(chan bool)
  47. go func() {
  48. var proc *os.Process
  49. for start := range gorun {
  50. if proc != nil {
  51. // try soft kill before hard one.
  52. if err := proc.Signal(os.Interrupt); err != nil {
  53. proc.Kill()
  54. }
  55. proc.Wait()
  56. }
  57. if !start {
  58. continue
  59. }
  60. command := exec.Command(self.binary, fmt.Sprintf("--port=%d", port))
  61. command.Dir = self.dir
  62. command.Stdout = os.Stdout
  63. command.Stderr = os.Stderr
  64. if err := command.Start(); err != nil {
  65. log.Fatalf("Failed to start the process: %v\n", err)
  66. }
  67. proc = command.Process
  68. }
  69. }()
  70. return
  71. }
  72. func (self *app) rerun(gorun chan bool) {
  73. self.build()
  74. livereload.Reload()
  75. gorun <- true
  76. }
  77. // Starts activates the application server along with
  78. // a daemon watcher for monitoring the files's changes.
  79. func (self *app) Start() {
  80. // ctrl-c: listen removes binary package when application stopped.
  81. channel := make(chan os.Signal, 2)
  82. signal.Notify(channel, os.Interrupt, syscall.SIGTERM)
  83. go func() {
  84. <-channel
  85. // remove the binary package on stop.
  86. os.Remove(self.binary)
  87. os.Exit(1)
  88. }()
  89. // start waiting the signal to start running.
  90. var gorun = self.run()
  91. self.build()
  92. gorun <- true
  93. watcher := fs.NewWatcher(self.dir)
  94. log.Infof("Start watching: %s", self.dir)
  95. watcher.Add(watchList, func(filename string) {
  96. relpath, _ := filepath.Rel(self.dir, filename)
  97. log.Infof("Changes on %s detected", relpath)
  98. self.rerun(gorun)
  99. })
  100. watcher.Start()
  101. }
  102. // Run creates an executable application package with livereload supports.
  103. func Run(ctx *cli.Context) {
  104. port = ctx.Int("port")
  105. if len(ctx.Args()) == 1 {
  106. cwd = ctx.Args()[0]
  107. }
  108. if abspath, err := filepath.Abs(cwd); err == nil {
  109. env.Set(internal.ROOT, abspath)
  110. } else {
  111. log.Fatalf("Failed to retrieve the directory: %v", err)
  112. }
  113. pkg, err := build.ImportDir(cwd, build.AllowBinary)
  114. if err != nil || pkg.Name != "main" {
  115. log.Fatalf("No buildable Go source files found")
  116. }
  117. app := new(app)
  118. app.dir = cwd
  119. app.binary = filepath.Join(os.TempDir(), "rex-bin")
  120. rex.Infof("App: %s", app.binary)
  121. if runtime.GOOS == "windows" {
  122. app.binary += ".exe"
  123. }
  124. app.task = ctx.String("task")
  125. app.Start()
  126. }