build_commands.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package common
  2. import (
  3. "fmt"
  4. "os/exec"
  5. "regexp"
  6. "strings"
  7. )
  8. type Command struct {
  9. Id string `json:"id"`
  10. Cmd string `json:"cmd"`
  11. Description string `json:"description"`
  12. IgnoreErros bool `json:"ignore.errors"`
  13. }
  14. type BuildSet struct {
  15. Modes map[string]*BuildMode `json:"modes"`
  16. Cmd string `json:"cmd"`
  17. Workdir string `json:"workdir"`
  18. Steps []*Command `json:"steps"`
  19. Before []*Command `json:"beforeCompile"`
  20. After []*Command `json:"afterCompile"`
  21. Options *BuildOptions `json:"options"`
  22. }
  23. func (b *BuildSet) Commands() []*Command {
  24. var (
  25. mode *BuildMode
  26. ok, ignore bool
  27. steps = []*Command{}
  28. modeKey = b.Options.Mode
  29. )
  30. if modeKey == "" {
  31. modeKey = "default"
  32. }
  33. if mode, ok = b.Modes[modeKey]; !ok {
  34. panic(fmt.Sprintf("Build mode '%s' not defined", modeKey))
  35. }
  36. if (len(mode.Ignore) == 0) &&
  37. len(mode.ExecuteOnly) == 0 {
  38. return b.Steps
  39. }
  40. ignore = len(mode.Ignore) > 0
  41. if ignore && len(mode.ExecuteOnly) > 0 {
  42. panic("Ignore and ExecuteOnly are mutually exclusive")
  43. }
  44. ids := map[string]bool{}
  45. for _, v := range append(mode.Ignore, mode.ExecuteOnly...) {
  46. ids[v] = true
  47. }
  48. if ignore {
  49. for _, step := range b.Steps {
  50. if _, ok := ids[step.Id]; !ok {
  51. steps = append(steps, step)
  52. }
  53. }
  54. } else {
  55. for _, step := range b.Steps {
  56. if _, ok := ids[step.Id]; ok {
  57. steps = append(steps, step)
  58. }
  59. }
  60. }
  61. return steps
  62. }
  63. type BuildMode struct {
  64. Ignore []string `json:"ignore"`
  65. ExecuteOnly []string `json:"executeOnly"`
  66. }
  67. var (
  68. expressionRegexp = regexp.MustCompile(`\{([\w\.]+)\}`)
  69. )
  70. func NewBuildOptions() *BuildOptions {
  71. return &BuildOptions{
  72. Mode: "default",
  73. ProjectDotNotation: map[string]interface{}{},
  74. }
  75. }
  76. func runBuildCommads(project *Project, client *Client, buildOptions *BuildOptions) (err error) {
  77. var (
  78. instance *exec.Cmd
  79. translateOptions *BuildSet
  80. step int
  81. cmd string
  82. out []byte
  83. // variables = map[string]interface{}{}
  84. buildfile = fmt.Sprintf("build/%s", client.Id)
  85. mode = project.Mode
  86. buildSet = &BuildSet{
  87. Steps: []*Command{},
  88. Modes: map[string]*BuildMode{
  89. "default": {},
  90. },
  91. }
  92. )
  93. if buildOptions == nil {
  94. buildOptions = NewBuildOptions()
  95. }
  96. buildOptions.Parse()
  97. if err = JsonParseMode(buildfile, mode, &buildSet); err != nil {
  98. err = nil
  99. fmt.Printf("[warning] running '%s' without build file", client.Id)
  100. buildOptions.Mode = "default"
  101. }
  102. buildSet.Options = buildOptions
  103. if bo, found := project.TranslatorBuildOptionsMap[client.Id]; found {
  104. translateOptions, err = bo(project, buildOptions)
  105. if translateOptions.Before != nil {
  106. buildSet.Steps = append(translateOptions.Before, buildSet.Steps...)
  107. }
  108. if translateOptions.After != nil {
  109. buildSet.Steps = append(buildSet.Steps, translateOptions.After...)
  110. }
  111. }
  112. if buildSet.After != nil {
  113. buildSet.Steps = append(buildSet.Steps, buildSet.After...)
  114. }
  115. fmt.Println("Running build commands... on mode ", mode)
  116. fmt.Println("RUN translate ", client.Id)
  117. cmds := buildSet.Commands()
  118. total := len(cmds)
  119. for k, command := range cmds {
  120. if cmd, err = parseCommand(command, buildOptions.ProjectDotNotation); err != nil {
  121. return
  122. }
  123. step = k + 1
  124. // goterm.MoveCursor(1, 1)
  125. if buildOptions.IgnoreStep(step) {
  126. // goterm.Printf("\tStep (%d / %d) - [ Skiped ] %s\n", step, total, cmd)
  127. fmt.Printf("\tStep (%d / %d) - [ Skiped ] %s\n", step, total, cmd)
  128. continue
  129. }
  130. // goterm.Printf("\tStep (%d / %d) - %s\n", step, total, cmd)
  131. fmt.Printf("\tStep (%d / %d) - %s\n", step, total, cmd)
  132. instance = exec.Command("sh", "-c", cmd)
  133. instance.Dir = buildSet.Workdir
  134. // goterm.Flush() // Call it every time at the end of rendering
  135. if out, err = instance.CombinedOutput(); err != nil {
  136. fmt.Println(string(out))
  137. return
  138. }
  139. }
  140. return
  141. }
  142. func parseCommand(c *Command, variables map[string]interface{}) (string, error) {
  143. var (
  144. expression, replace, dotPathName string
  145. cmd = c.Cmd
  146. matches = expressionRegexp.FindAllStringSubmatch(cmd, -1)
  147. )
  148. for _, match := range matches {
  149. dotPathName = match[1]
  150. if value, exists := variables[dotPathName]; !exists || value == nil {
  151. return "", fmt.Errorf("Value for expression '%s' not defined", dotPathName)
  152. }
  153. expression = match[0]
  154. replace = fmt.Sprintf("%v", variables[dotPathName])
  155. cmd = strings.ReplaceAll(cmd, expression, replace)
  156. }
  157. return cmd, nil
  158. }