Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

165 lines
4.3KB

  1. /* ----------------------------------------------------------------------
  2. * ______ ___ __
  3. * / ____/___ / | ____ __ ___ __/ /_ ___ ________
  4. * / / __/ __ \/ /| | / __ \/ / / / | /| / / __ \/ _ \/ ___/ _ \
  5. * / /_/ / /_/ / ___ |/ / / / /_/ /| |/ |/ / / / / __/ / / __/
  6. * \____/\____/_/ |_/_/ /_/\__. / |__/|__/_/ /_/\___/_/ \___/
  7. * /____/
  8. *
  9. * (C) Copyright 2015 GoAnywhere (http://goanywhere.io).
  10. * ----------------------------------------------------------------------
  11. * Licensed under the Apache License, Version 2.0 (the "License");
  12. * you may not use this file except in compliance with the License.
  13. * You may obtain a copy of the License at
  14. *
  15. * http://www.apache.org/licenses/LICENSE-2.0
  16. *
  17. * Unless required by applicable law or agreed to in writing, software
  18. * distributed under the License is distributed on an "AS IS" BASIS,
  19. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  20. * See the License for the specific language governing permissions and
  21. * limitations under the License.
  22. * ----------------------------------------------------------------------*/
  23. package main
  24. import (
  25. "fmt"
  26. "go/build"
  27. "os"
  28. "os/exec"
  29. "os/signal"
  30. "path/filepath"
  31. "regexp"
  32. "runtime"
  33. "syscall"
  34. log "github.com/Sirupsen/logrus"
  35. "github.com/codegangsta/cli"
  36. "github.com/goanywhere/rex/internal"
  37. "github.com/goanywhere/rex/livereload"
  38. "github.com/goanywhere/x/cmd"
  39. "github.com/goanywhere/x/env"
  40. "github.com/goanywhere/x/fs"
  41. )
  42. var (
  43. port int
  44. watchList = regexp.MustCompile(`\.(go|html|atom|rss|xml)$`)
  45. )
  46. type app struct {
  47. dir string
  48. binary string
  49. args []string
  50. task string // script for npm.
  51. }
  52. // build compiles the application into rex-bin executable
  53. // to run & optionally compiles static assets using npm.
  54. func (self *app) build() {
  55. var done = make(chan bool)
  56. cmd.Loading(done)
  57. // * try build the application into rex-bin(.exe)
  58. command := exec.Command("go", "build", "-o", self.binary)
  59. command.Dir = self.dir
  60. if e := command.Run(); e != nil {
  61. log.Fatalf("Failed to compile the application: %v", e)
  62. }
  63. done <- true
  64. }
  65. // run executes the runnerable executable under package binary root.
  66. func (self *app) run() (gorun chan bool) {
  67. gorun = make(chan bool)
  68. go func() {
  69. var proc *os.Process
  70. for start := range gorun {
  71. if proc != nil {
  72. // try soft kill before hard one.
  73. if err := proc.Signal(os.Interrupt); err != nil {
  74. proc.Kill()
  75. }
  76. proc.Wait()
  77. }
  78. if !start {
  79. continue
  80. }
  81. command := exec.Command(self.binary, fmt.Sprintf("--port=%d", port))
  82. command.Dir = self.dir
  83. command.Stdout = os.Stdout
  84. command.Stderr = os.Stderr
  85. if err := command.Start(); err != nil {
  86. log.Fatalf("Failed to start the process: %v\n", err)
  87. }
  88. proc = command.Process
  89. }
  90. }()
  91. return
  92. }
  93. func (self *app) rerun(gorun chan bool) {
  94. self.build()
  95. livereload.Reload()
  96. gorun <- true
  97. }
  98. // Starts activates the application server along with
  99. // a daemon watcher for monitoring the files's changes.
  100. func (self *app) Start() {
  101. // ctrl-c: listen removes binary package when application stopped.
  102. channel := make(chan os.Signal, 2)
  103. signal.Notify(channel, os.Interrupt, syscall.SIGTERM)
  104. go func() {
  105. <-channel
  106. // remove the binary package on stop.
  107. os.Remove(self.binary)
  108. os.Exit(1)
  109. }()
  110. // start waiting the signal to start running.
  111. var gorun = self.run()
  112. self.build()
  113. gorun <- true
  114. watcher := fs.NewWatcher(self.dir)
  115. log.Infof("Start watching: %s", self.dir)
  116. watcher.Add(watchList, func(filename string) {
  117. relpath, _ := filepath.Rel(self.dir, filename)
  118. log.Infof("Changes on %s detected", relpath)
  119. self.rerun(gorun)
  120. })
  121. watcher.Start()
  122. }
  123. // Run creates an executable application package with livereload supports.
  124. func Run(ctx *cli.Context) {
  125. port = ctx.Int("port")
  126. if len(ctx.Args()) == 1 {
  127. cwd = ctx.Args()[0]
  128. }
  129. if abspath, err := filepath.Abs(cwd); err == nil {
  130. env.Set(internal.ROOT, abspath)
  131. } else {
  132. log.Fatalf("Failed to retrieve the directory: %v", err)
  133. }
  134. pkg, err := build.ImportDir(cwd, build.AllowBinary)
  135. if err != nil || pkg.Name != "main" {
  136. log.Fatalf("No buildable Go source files found")
  137. }
  138. app := new(app)
  139. app.dir = cwd
  140. app.binary = filepath.Join(os.TempDir(), "rex-bin")
  141. if runtime.GOOS == "windows" {
  142. app.binary += ".exe"
  143. }
  144. app.task = ctx.String("task")
  145. app.Start()
  146. }