build_androidapp.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. import (
  6. "bytes"
  7. "crypto/x509"
  8. "encoding/base64"
  9. "encoding/pem"
  10. "encoding/xml"
  11. "errors"
  12. "fmt"
  13. "io"
  14. "log"
  15. "os"
  16. "path"
  17. "path/filepath"
  18. "strings"
  19. "golang.org/x/mobile/internal/binres"
  20. "golang.org/x/tools/go/packages"
  21. )
  22. func goAndroidBuild(pkg *packages.Package, targets []targetInfo) (map[string]bool, error) {
  23. ndkRoot, err := ndkRoot(targets...)
  24. if err != nil {
  25. return nil, err
  26. }
  27. appName := path.Base(pkg.PkgPath)
  28. libName := androidPkgName(appName)
  29. // TODO(hajimehoshi): This works only with Go tools that assume all source files are in one directory.
  30. // Fix this to work with other Go tools.
  31. dir := filepath.Dir(pkg.GoFiles[0])
  32. manifestPath := filepath.Join(dir, "AndroidManifest.xml")
  33. manifestData, err := os.ReadFile(manifestPath)
  34. if err != nil {
  35. if !os.IsNotExist(err) {
  36. return nil, err
  37. }
  38. buf := new(bytes.Buffer)
  39. buf.WriteString(`<?xml version="1.0" encoding="utf-8"?>`)
  40. err := manifestTmpl.Execute(buf, manifestTmplData{
  41. // TODO(crawshaw): a better package path.
  42. JavaPkgPath: "org.golang.todo." + libName,
  43. Name: strings.Title(appName),
  44. LibName: libName,
  45. })
  46. if err != nil {
  47. return nil, err
  48. }
  49. manifestData = buf.Bytes()
  50. if buildV {
  51. fmt.Fprintf(os.Stderr, "generated AndroidManifest.xml:\n%s\n", manifestData)
  52. }
  53. } else {
  54. libName, err = manifestLibName(manifestData)
  55. if err != nil {
  56. return nil, fmt.Errorf("error parsing %s: %v", manifestPath, err)
  57. }
  58. }
  59. libFiles := []string{}
  60. nmpkgs := make(map[string]map[string]bool) // map: arch -> extractPkgs' output
  61. for _, t := range targets {
  62. toolchain := ndk.Toolchain(t.arch)
  63. libPath := "lib/" + toolchain.abi + "/lib" + libName + ".so"
  64. libAbsPath := filepath.Join(tmpdir, libPath)
  65. if err := mkdir(filepath.Dir(libAbsPath)); err != nil {
  66. return nil, err
  67. }
  68. err = goBuild(
  69. pkg.PkgPath,
  70. androidEnv[t.arch],
  71. "-buildmode=c-shared",
  72. "-o", libAbsPath,
  73. )
  74. if err != nil {
  75. return nil, err
  76. }
  77. nmpkgs[t.arch], err = extractPkgs(toolchain.Path(ndkRoot, "nm"), libAbsPath)
  78. if err != nil {
  79. return nil, err
  80. }
  81. libFiles = append(libFiles, libPath)
  82. }
  83. block, _ := pem.Decode([]byte(debugCert))
  84. if block == nil {
  85. return nil, errors.New("no debug cert")
  86. }
  87. privKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  88. if err != nil {
  89. return nil, err
  90. }
  91. if buildO == "" {
  92. buildO = androidPkgName(path.Base(pkg.PkgPath)) + ".apk"
  93. }
  94. if !strings.HasSuffix(buildO, ".apk") {
  95. return nil, fmt.Errorf("output file name %q does not end in '.apk'", buildO)
  96. }
  97. var out io.Writer
  98. if !buildN {
  99. f, err := os.Create(buildO)
  100. if err != nil {
  101. return nil, err
  102. }
  103. defer func() {
  104. if cerr := f.Close(); err == nil {
  105. err = cerr
  106. }
  107. }()
  108. out = f
  109. }
  110. var apkw *Writer
  111. if !buildN {
  112. apkw = NewWriter(out, privKey)
  113. }
  114. apkwCreate := func(name string) (io.Writer, error) {
  115. if buildV {
  116. fmt.Fprintf(os.Stderr, "apk: %s\n", name)
  117. }
  118. if buildN {
  119. return io.Discard, nil
  120. }
  121. return apkw.Create(name)
  122. }
  123. apkwWriteFile := func(dst, src string) error {
  124. w, err := apkwCreate(dst)
  125. if err != nil {
  126. return err
  127. }
  128. if !buildN {
  129. f, err := os.Open(src)
  130. if err != nil {
  131. return err
  132. }
  133. defer f.Close()
  134. if _, err := io.Copy(w, f); err != nil {
  135. return err
  136. }
  137. }
  138. return nil
  139. }
  140. w, err := apkwCreate("classes.dex")
  141. if err != nil {
  142. return nil, err
  143. }
  144. dexData, err := base64.StdEncoding.DecodeString(dexStr)
  145. if err != nil {
  146. log.Fatalf("internal error bad dexStr: %v", err)
  147. }
  148. if _, err := w.Write(dexData); err != nil {
  149. return nil, err
  150. }
  151. for _, libFile := range libFiles {
  152. if err := apkwWriteFile(libFile, filepath.Join(tmpdir, libFile)); err != nil {
  153. return nil, err
  154. }
  155. }
  156. for _, t := range targets {
  157. toolchain := ndk.Toolchain(t.arch)
  158. if nmpkgs[t.arch]["golang.org/x/mobile/exp/audio/al"] {
  159. dst := "lib/" + toolchain.abi + "/libopenal.so"
  160. src := filepath.Join(gomobilepath, dst)
  161. if _, err := os.Stat(src); err != nil {
  162. return nil, errors.New("the Android requires the golang.org/x/mobile/exp/audio/al, but the OpenAL libraries was not found. Please run gomobile init with the -openal flag pointing to an OpenAL source directory.")
  163. }
  164. if err := apkwWriteFile(dst, src); err != nil {
  165. return nil, err
  166. }
  167. }
  168. }
  169. // Add any assets.
  170. var arsc struct {
  171. iconPath string
  172. }
  173. assetsDir := filepath.Join(dir, "assets")
  174. assetsDirExists := true
  175. fi, err := os.Stat(assetsDir)
  176. if err != nil {
  177. if os.IsNotExist(err) {
  178. assetsDirExists = false
  179. } else {
  180. return nil, err
  181. }
  182. } else {
  183. assetsDirExists = fi.IsDir()
  184. }
  185. if assetsDirExists {
  186. // if assets is a symlink, follow the symlink.
  187. assetsDir, err = filepath.EvalSymlinks(assetsDir)
  188. if err != nil {
  189. return nil, err
  190. }
  191. err = filepath.Walk(assetsDir, func(path string, info os.FileInfo, err error) error {
  192. if err != nil {
  193. return err
  194. }
  195. if name := filepath.Base(path); strings.HasPrefix(name, ".") {
  196. // Do not include the hidden files.
  197. return nil
  198. }
  199. if info.IsDir() {
  200. return nil
  201. }
  202. if rel, err := filepath.Rel(assetsDir, path); rel == "icon.png" && err == nil {
  203. arsc.iconPath = path
  204. // TODO returning here does not write the assets/icon.png to the final assets output,
  205. // making it unavailable via the assets API. Should the file be duplicated into assets
  206. // or should assets API be able to retrieve files from the generated resource table?
  207. return nil
  208. }
  209. name := "assets/" + path[len(assetsDir)+1:]
  210. return apkwWriteFile(name, path)
  211. })
  212. if err != nil {
  213. return nil, fmt.Errorf("asset %v", err)
  214. }
  215. }
  216. bxml, err := binres.UnmarshalXML(bytes.NewReader(manifestData), arsc.iconPath != "")
  217. if err != nil {
  218. return nil, err
  219. }
  220. // generate resources.arsc identifying single xxxhdpi icon resource.
  221. if arsc.iconPath != "" {
  222. pkgname, err := bxml.RawValueByName("manifest", xml.Name{Local: "package"})
  223. if err != nil {
  224. return nil, err
  225. }
  226. tbl, name := binres.NewMipmapTable(pkgname)
  227. if err := apkwWriteFile(name, arsc.iconPath); err != nil {
  228. return nil, err
  229. }
  230. w, err := apkwCreate("resources.arsc")
  231. if err != nil {
  232. return nil, err
  233. }
  234. bin, err := tbl.MarshalBinary()
  235. if err != nil {
  236. return nil, err
  237. }
  238. if _, err := w.Write(bin); err != nil {
  239. return nil, err
  240. }
  241. }
  242. w, err = apkwCreate("AndroidManifest.xml")
  243. if err != nil {
  244. return nil, err
  245. }
  246. bin, err := bxml.MarshalBinary()
  247. if err != nil {
  248. return nil, err
  249. }
  250. if _, err := w.Write(bin); err != nil {
  251. return nil, err
  252. }
  253. // TODO: add gdbserver to apk?
  254. if !buildN {
  255. if err := apkw.Close(); err != nil {
  256. return nil, err
  257. }
  258. }
  259. // TODO: return nmpkgs
  260. return nmpkgs[targets[0].arch], nil
  261. }
  262. // androidPkgName sanitizes the go package name to be acceptable as a android
  263. // package name part. The android package name convention is similar to the
  264. // java package name convention described in
  265. // https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.5.3.1
  266. // but not exactly same.
  267. func androidPkgName(name string) string {
  268. var res []rune
  269. for _, r := range name {
  270. switch {
  271. case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z', '0' <= r && r <= '9':
  272. res = append(res, r)
  273. default:
  274. res = append(res, '_')
  275. }
  276. }
  277. if len(res) == 0 || res[0] == '_' || ('0' <= res[0] && res[0] <= '9') {
  278. // Android does not seem to allow the package part starting with _.
  279. res = append([]rune{'g', 'o'}, res...)
  280. }
  281. s := string(res)
  282. // Look for Java keywords that are not Go keywords, and avoid using
  283. // them as a package name.
  284. //
  285. // This is not a problem for normal Go identifiers as we only expose
  286. // exported symbols. The upper case first letter saves everything
  287. // from accidentally matching except for the package name.
  288. //
  289. // Note that basic type names (like int) are not keywords in Go.
  290. switch s {
  291. case "abstract", "assert", "boolean", "byte", "catch", "char", "class",
  292. "do", "double", "enum", "extends", "final", "finally", "float",
  293. "implements", "instanceof", "int", "long", "native", "private",
  294. "protected", "public", "short", "static", "strictfp", "super",
  295. "synchronized", "this", "throw", "throws", "transient", "try",
  296. "void", "volatile", "while":
  297. s += "_"
  298. }
  299. return s
  300. }
  301. // A random uninteresting private key.
  302. // Must be consistent across builds so newer app versions can be installed.
  303. const debugCert = `
  304. -----BEGIN RSA PRIVATE KEY-----
  305. MIIEowIBAAKCAQEAy6ItnWZJ8DpX9R5FdWbS9Kr1U8Z7mKgqNByGU7No99JUnmyu
  306. NQ6Uy6Nj0Gz3o3c0BXESECblOC13WdzjsH1Pi7/L9QV8jXOXX8cvkG5SJAyj6hcO
  307. LOapjDiN89NXjXtyv206JWYvRtpexyVrmHJgRAw3fiFI+m4g4Qop1CxcIF/EgYh7
  308. rYrqh4wbCM1OGaCleQWaOCXxZGm+J5YNKQcWpjZRrDrb35IZmlT0bK46CXUKvCqK
  309. x7YXHgfhC8ZsXCtsScKJVHs7gEsNxz7A0XoibFw6DoxtjKzUCktnT0w3wxdY7OTj
  310. 9AR8mobFlM9W3yirX8TtwekWhDNTYEu8dwwykwIDAQABAoIBAA2hjpIhvcNR9H9Z
  311. BmdEecydAQ0ZlT5zy1dvrWI++UDVmIp+Ve8BSd6T0mOqV61elmHi3sWsBN4M1Rdz
  312. 3N38lW2SajG9q0fAvBpSOBHgAKmfGv3Ziz5gNmtHgeEXfZ3f7J95zVGhlHqWtY95
  313. JsmuplkHxFMyITN6WcMWrhQg4A3enKLhJLlaGLJf9PeBrvVxHR1/txrfENd2iJBH
  314. FmxVGILL09fIIktJvoScbzVOneeWXj5vJGzWVhB17DHBbANGvVPdD5f+k/s5aooh
  315. hWAy/yLKocr294C4J+gkO5h2zjjjSGcmVHfrhlXQoEPX+iW1TGoF8BMtl4Llc+jw
  316. lKWKfpECgYEA9C428Z6CvAn+KJ2yhbAtuRo41kkOVoiQPtlPeRYs91Pq4+NBlfKO
  317. 2nWLkyavVrLx4YQeCeaEU2Xoieo9msfLZGTVxgRlztylOUR+zz2FzDBYGicuUD3s
  318. EqC0Wv7tiX6dumpWyOcVVLmR9aKlOUzA9xemzIsWUwL3PpyONhKSq7kCgYEA1X2F
  319. f2jKjoOVzglhtuX4/SP9GxS4gRf9rOQ1Q8DzZhyH2LZ6Dnb1uEQvGhiqJTU8CXxb
  320. 7odI0fgyNXq425Nlxc1Tu0G38TtJhwrx7HWHuFcbI/QpRtDYLWil8Zr7Q3BT9rdh
  321. moo4m937hLMvqOG9pyIbyjOEPK2WBCtKW5yabqsCgYEAu9DkUBr1Qf+Jr+IEU9I8
  322. iRkDSMeusJ6gHMd32pJVCfRRQvIlG1oTyTMKpafmzBAd/rFpjYHynFdRcutqcShm
  323. aJUq3QG68U9EAvWNeIhA5tr0mUEz3WKTt4xGzYsyWES8u4tZr3QXMzD9dOuinJ1N
  324. +4EEumXtSPKKDG3M8Qh+KnkCgYBUEVSTYmF5EynXc2xOCGsuy5AsrNEmzJqxDUBI
  325. SN/P0uZPmTOhJIkIIZlmrlW5xye4GIde+1jajeC/nG7U0EsgRAV31J4pWQ5QJigz
  326. 0+g419wxIUFryGuIHhBSfpP472+w1G+T2mAGSLh1fdYDq7jx6oWE7xpghn5vb9id
  327. EKLjdwKBgBtz9mzbzutIfAW0Y8F23T60nKvQ0gibE92rnUbjPnw8HjL3AZLU05N+
  328. cSL5bhq0N5XHK77sscxW9vXjG0LJMXmFZPp9F6aV6ejkMIXyJ/Yz/EqeaJFwilTq
  329. Mc6xR47qkdzu0dQ1aPm4XD7AWDtIvPo/GG2DKOucLBbQc2cOWtKS
  330. -----END RSA PRIVATE KEY-----
  331. `