util.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package build
  17. import (
  18. "bufio"
  19. "bytes"
  20. "flag"
  21. "fmt"
  22. "go/parser"
  23. "go/token"
  24. "io"
  25. "log"
  26. "os"
  27. "os/exec"
  28. "path"
  29. "path/filepath"
  30. "strconv"
  31. "strings"
  32. "text/template"
  33. "time"
  34. )
  35. var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands")
  36. // MustRun executes the given command and exits the host process for
  37. // any error.
  38. func MustRun(cmd *exec.Cmd) {
  39. fmt.Println(">>>", printArgs(cmd.Args))
  40. if !*DryRunFlag {
  41. cmd.Stderr = os.Stderr
  42. cmd.Stdout = os.Stdout
  43. if err := cmd.Run(); err != nil {
  44. log.Fatal(err)
  45. }
  46. }
  47. }
  48. func printArgs(args []string) string {
  49. var s strings.Builder
  50. for i, arg := range args {
  51. if i > 0 {
  52. s.WriteByte(' ')
  53. }
  54. if strings.IndexByte(arg, ' ') >= 0 {
  55. arg = strconv.QuoteToASCII(arg)
  56. }
  57. s.WriteString(arg)
  58. }
  59. return s.String()
  60. }
  61. func MustRunCommand(cmd string, args ...string) {
  62. MustRun(exec.Command(cmd, args...))
  63. }
  64. var warnedAboutGit bool
  65. // RunGit runs a git subcommand and returns its output.
  66. // The command must complete successfully.
  67. func RunGit(args ...string) string {
  68. cmd := exec.Command("git", args...)
  69. var stdout, stderr bytes.Buffer
  70. cmd.Stdout, cmd.Stderr = &stdout, &stderr
  71. if err := cmd.Run(); err != nil {
  72. if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
  73. if !warnedAboutGit {
  74. log.Println("Warning: can't find 'git' in PATH")
  75. warnedAboutGit = true
  76. }
  77. return ""
  78. }
  79. log.Fatal(strings.Join(cmd.Args, " "), ": ", err, "\n", stderr.String())
  80. }
  81. return strings.TrimSpace(stdout.String())
  82. }
  83. // readGitFile returns content of file in .git directory.
  84. func readGitFile(file string) string {
  85. content, err := os.ReadFile(path.Join(".git", file))
  86. if err != nil {
  87. return ""
  88. }
  89. return strings.TrimSpace(string(content))
  90. }
  91. // Render renders the given template file into outputFile.
  92. func Render(templateFile, outputFile string, outputPerm os.FileMode, x interface{}) {
  93. tpl := template.Must(template.ParseFiles(templateFile))
  94. render(tpl, outputFile, outputPerm, x)
  95. }
  96. // RenderString renders the given template string into outputFile.
  97. func RenderString(templateContent, outputFile string, outputPerm os.FileMode, x interface{}) {
  98. tpl := template.Must(template.New("").Parse(templateContent))
  99. render(tpl, outputFile, outputPerm, x)
  100. }
  101. func render(tpl *template.Template, outputFile string, outputPerm os.FileMode, x interface{}) {
  102. if err := os.MkdirAll(filepath.Dir(outputFile), 0755); err != nil {
  103. log.Fatal(err)
  104. }
  105. out, err := os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY|os.O_EXCL, outputPerm)
  106. if err != nil {
  107. log.Fatal(err)
  108. }
  109. if err := tpl.Execute(out, x); err != nil {
  110. log.Fatal(err)
  111. }
  112. if err := out.Close(); err != nil {
  113. log.Fatal(err)
  114. }
  115. }
  116. // UploadSFTP uploads files to a remote host using the sftp command line tool.
  117. // The destination host may be specified either as [user@]host[: or as a URI in
  118. // the form sftp://[user@]host[:port].
  119. func UploadSFTP(identityFile, host, dir string, files []string) error {
  120. sftp := exec.Command("sftp")
  121. sftp.Stderr = os.Stderr
  122. if identityFile != "" {
  123. sftp.Args = append(sftp.Args, "-i", identityFile)
  124. }
  125. sftp.Args = append(sftp.Args, host)
  126. fmt.Println(">>>", printArgs(sftp.Args))
  127. if *DryRunFlag {
  128. return nil
  129. }
  130. stdin, err := sftp.StdinPipe()
  131. if err != nil {
  132. return fmt.Errorf("can't create stdin pipe for sftp: %v", err)
  133. }
  134. stdout, err := sftp.StdoutPipe()
  135. if err != nil {
  136. return fmt.Errorf("can't create stdout pipe for sftp: %v", err)
  137. }
  138. if err := sftp.Start(); err != nil {
  139. return err
  140. }
  141. in := io.MultiWriter(stdin, os.Stdout)
  142. for _, f := range files {
  143. fmt.Fprintln(in, "put", f, path.Join(dir, filepath.Base(f)))
  144. }
  145. fmt.Fprintln(in, "exit")
  146. // Some issue with the PPA sftp server makes it so the server does not
  147. // respond properly to a 'bye', 'exit' or 'quit' from the client.
  148. // To work around that, we check the output, and when we see the client
  149. // exit command, we do a hard exit.
  150. // See
  151. // https://github.com/kolban-google/sftp-gcs/issues/23
  152. // https://github.com/mscdex/ssh2/pull/1111
  153. aborted := false
  154. go func() {
  155. scanner := bufio.NewScanner(stdout)
  156. for scanner.Scan() {
  157. txt := scanner.Text()
  158. fmt.Println(txt)
  159. if txt == "sftp> exit" {
  160. // Give it .5 seconds to exit (server might be fixed), then
  161. // hard kill it from the outside
  162. time.Sleep(500 * time.Millisecond)
  163. aborted = true
  164. sftp.Process.Kill()
  165. }
  166. }
  167. }()
  168. stdin.Close()
  169. err = sftp.Wait()
  170. if aborted {
  171. return nil
  172. }
  173. return err
  174. }
  175. // FindMainPackages finds all 'main' packages in the given directory and returns their
  176. // package paths.
  177. func FindMainPackages(dir string) []string {
  178. var commands []string
  179. cmds, err := os.ReadDir(dir)
  180. if err != nil {
  181. log.Fatal(err)
  182. }
  183. for _, cmd := range cmds {
  184. pkgdir := filepath.Join(dir, cmd.Name())
  185. pkgs, err := parser.ParseDir(token.NewFileSet(), pkgdir, nil, parser.PackageClauseOnly)
  186. if err != nil {
  187. log.Fatal(err)
  188. }
  189. for name := range pkgs {
  190. if name == "main" {
  191. path := "./" + filepath.ToSlash(pkgdir)
  192. commands = append(commands, path)
  193. break
  194. }
  195. }
  196. }
  197. return commands
  198. }