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  `