main.go 4.4 KB

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