main.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2015 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package main
  5. //go:generate gomobile help documentation doc.go
  6. import (
  7. "bufio"
  8. "bytes"
  9. "flag"
  10. "fmt"
  11. "html/template"
  12. "io"
  13. "io/ioutil"
  14. "log"
  15. "os"
  16. "os/exec"
  17. "unicode"
  18. "unicode/utf8"
  19. )
  20. var (
  21. gomobileName = "gomobile"
  22. goVersion string
  23. )
  24. const minimumGoMinorVersion = 18
  25. func printUsage(w io.Writer) {
  26. bufw := bufio.NewWriter(w)
  27. if err := usageTmpl.Execute(bufw, commands); err != nil {
  28. panic(err)
  29. }
  30. bufw.Flush()
  31. }
  32. func main() {
  33. gomobileName = os.Args[0]
  34. flag.Usage = func() {
  35. printUsage(os.Stderr)
  36. os.Exit(2)
  37. }
  38. flag.Parse()
  39. log.SetFlags(0)
  40. args := flag.Args()
  41. if len(args) < 1 {
  42. flag.Usage()
  43. }
  44. if args[0] == "help" {
  45. if len(args) == 3 && args[1] == "documentation" {
  46. helpDocumentation(args[2])
  47. return
  48. }
  49. help(args[1:])
  50. return
  51. }
  52. if _, err := ensureGoVersion(); err != nil {
  53. fmt.Fprintf(os.Stderr, "%s: %v\n", gomobileName, err)
  54. os.Exit(1)
  55. }
  56. for _, cmd := range commands {
  57. if cmd.Name == args[0] {
  58. cmd.flag.Usage = func() {
  59. cmd.usage()
  60. os.Exit(1)
  61. }
  62. cmd.flag.Parse(args[1:])
  63. if err := cmd.run(cmd); err != nil {
  64. msg := err.Error()
  65. if msg != "" {
  66. fmt.Fprintf(os.Stderr, "%s: %v\n", os.Args[0], err)
  67. }
  68. os.Exit(1)
  69. }
  70. return
  71. }
  72. }
  73. fmt.Fprintf(os.Stderr, "%s: unknown subcommand %q\nRun 'gomobile help' for usage.\n", os.Args[0], args[0])
  74. os.Exit(2)
  75. }
  76. func ensureGoVersion() (string, error) {
  77. if goVersion != "" {
  78. return goVersion, nil
  79. }
  80. goVersionOut, err := exec.Command("go", "version").CombinedOutput()
  81. if err != nil {
  82. return "", fmt.Errorf("'go version' failed: %v, %s", err, goVersionOut)
  83. }
  84. var minor int
  85. if _, err := fmt.Sscanf(string(goVersionOut), "go version go1.%d", &minor); err != nil {
  86. // Ignore unknown versions; it's probably a devel version.
  87. return "", nil
  88. }
  89. goVersion = fmt.Sprintf("go1.%d", minor)
  90. if minor < minimumGoMinorVersion {
  91. return "", fmt.Errorf("Go 1.%d or newer is required", minimumGoMinorVersion)
  92. }
  93. return goVersion, nil
  94. }
  95. func help(args []string) {
  96. if len(args) == 0 {
  97. printUsage(os.Stdout)
  98. return // succeeded at helping
  99. }
  100. if len(args) != 1 {
  101. fmt.Fprintf(os.Stderr, "usage: %s help command\n\nToo many arguments given.\n", gomobileName)
  102. os.Exit(2) // failed to help
  103. }
  104. arg := args[0]
  105. for _, cmd := range commands {
  106. if cmd.Name == arg {
  107. cmd.usage()
  108. return // succeeded at helping
  109. }
  110. }
  111. fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run '%s help'.\n", arg, gomobileName)
  112. os.Exit(2)
  113. }
  114. const documentationHeader = `// Copyright 2015 The Go Authors. All rights reserved.
  115. // Use of this source code is governed by a BSD-style
  116. // license that can be found in the LICENSE file.
  117. // Code generated by 'gomobile help documentation doc.go'. DO NOT EDIT.
  118. `
  119. func helpDocumentation(path string) {
  120. w := new(bytes.Buffer)
  121. w.WriteString(documentationHeader)
  122. w.WriteString("\n/*\n")
  123. if err := usageTmpl.Execute(w, commands); err != nil {
  124. log.Fatal(err)
  125. }
  126. for _, cmd := range commands {
  127. r, rlen := utf8.DecodeRuneInString(cmd.Short)
  128. w.WriteString("\n\n")
  129. w.WriteRune(unicode.ToUpper(r))
  130. w.WriteString(cmd.Short[rlen:])
  131. w.WriteString("\n\nUsage:\n\n\tgomobile " + cmd.Name)
  132. if cmd.Usage != "" {
  133. w.WriteRune(' ')
  134. w.WriteString(cmd.Usage)
  135. }
  136. w.WriteRune('\n')
  137. w.WriteString(cmd.Long)
  138. }
  139. w.WriteString("*/\npackage main // import \"golang.org/x/mobile/cmd/gomobile\"\n")
  140. if err := ioutil.WriteFile(path, w.Bytes(), 0666); err != nil {
  141. log.Fatal(err)
  142. }
  143. }
  144. var commands = []*command{
  145. // TODO(crawshaw): cmdRun
  146. cmdBind,
  147. cmdBuild,
  148. cmdClean,
  149. cmdInit,
  150. cmdInstall,
  151. cmdVersion,
  152. }
  153. type command struct {
  154. run func(*command) error
  155. flag flag.FlagSet
  156. Name string
  157. Usage string
  158. Short string
  159. Long string
  160. }
  161. func (cmd *command) usage() {
  162. fmt.Fprintf(os.Stdout, "usage: %s %s %s\n%s", gomobileName, cmd.Name, cmd.Usage, cmd.Long)
  163. }
  164. var usageTmpl = template.Must(template.New("usage").Parse(
  165. `Gomobile is a tool for building and running mobile apps written in Go.
  166. To install:
  167. $ go install golang.org/x/mobile/cmd/gomobile@latest
  168. $ gomobile init
  169. At least Go 1.16 is required.
  170. For detailed instructions, see https://golang.org/wiki/Mobile.
  171. Usage:
  172. gomobile command [arguments]
  173. Commands:
  174. {{range .}}
  175. {{.Name | printf "%-11s"}} {{.Short}}{{end}}
  176. Use 'gomobile help [command]' for more information about that command.
  177. `))