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