build_androidapp.go 10.0 KB

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