mobile/android/debug_sign_tool.py

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:74ad393cc028
1 #!/usr/bin/env python
2
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 """
8 Sign Android packages using an Android debug keystore, creating the
9 keystore if it does not exist.
10
11 This and |zip| can be combined to replace the Android |apkbuilder|
12 tool, which was deprecated in SDK r22.
13
14 Exits with code 0 if creating the keystore and every signing succeeded,
15 or with code 1 if any creation or signing failed.
16 """
17
18 from argparse import ArgumentParser
19 import errno
20 import logging
21 import os
22 import subprocess
23 import sys
24
25
26 log = logging.getLogger(os.path.basename(__file__))
27 log.setLevel(logging.INFO)
28 sh = logging.StreamHandler(stream=sys.stdout)
29 sh.setFormatter(logging.Formatter('%(name)s: %(message)s'))
30 log.addHandler(sh)
31
32
33 class DebugKeystore:
34 """
35 A thin abstraction on top of an Android debug key store.
36 """
37 def __init__(self, keystore):
38 self._keystore = os.path.abspath(os.path.expanduser(keystore))
39 self._alias = 'androiddebugkey'
40 self.verbose = False
41 self.keytool = 'keytool'
42 self.jarsigner = 'jarsigner'
43
44 @property
45 def keystore(self):
46 return self._keystore
47
48 @property
49 def alias(self):
50 return self._alias
51
52 def _check(self, args):
53 try:
54 if self.verbose:
55 subprocess.check_call(args)
56 else:
57 subprocess.check_output(args)
58 except OSError as ex:
59 if ex.errno != errno.ENOENT:
60 raise
61 raise Exception("Could not find executable '%s'" % args[0])
62
63 def keystore_contains_alias(self):
64 args = [ self.keytool,
65 '-list',
66 '-keystore', self.keystore,
67 '-storepass', 'android',
68 '-alias', self.alias,
69 ]
70 if self.verbose:
71 args.append('-v')
72 contains = True
73 try:
74 self._check(args)
75 except subprocess.CalledProcessError as e:
76 contains = False
77 if self.verbose:
78 log.info('Keystore %s %s alias %s' %
79 (self.keystore,
80 'contains' if contains else 'does not contain',
81 self.alias))
82 return contains
83
84 def create_alias_in_keystore(self):
85 try:
86 path = os.path.dirname(self.keystore)
87 os.makedirs(path)
88 except OSError as exception:
89 if exception.errno != errno.EEXIST:
90 raise
91
92 args = [ self.keytool,
93 '-genkeypair',
94 '-keystore', self.keystore,
95 '-storepass', 'android',
96 '-alias', self.alias,
97 '-keypass', 'android',
98 '-dname', 'CN=Android Debug,O=Android,C=US',
99 '-keyalg', 'RSA',
100 '-validity', '365',
101 ]
102 if self.verbose:
103 args.append('-v')
104 self._check(args)
105 if self.verbose:
106 log.info('Created alias %s in keystore %s' %
107 (self.alias, self.keystore))
108
109 def sign(self, apk):
110 if not self.keystore_contains_alias():
111 self.create_alias_in_keystore()
112
113 args = [ self.jarsigner,
114 '-digestalg', 'SHA1',
115 '-sigalg', 'MD5withRSA',
116 '-keystore', self.keystore,
117 '-storepass', 'android',
118 apk,
119 self.alias,
120 ]
121 if self.verbose:
122 args.append('-verbose')
123 self._check(args)
124 if self.verbose:
125 log.info('Signed %s with alias %s from keystore %s' %
126 (apk, self.alias, self.keystore))
127
128
129 def parse_args(argv):
130 parser = ArgumentParser(description='Sign Android packages using an Android debug keystore.')
131 parser.add_argument('apks', nargs='+',
132 metavar='APK',
133 help='Android packages to be signed')
134 parser.add_argument('-v', '--verbose',
135 dest='verbose',
136 default=False,
137 action='store_true',
138 help='verbose output')
139 parser.add_argument('--keytool',
140 metavar='PATH',
141 default='keytool',
142 help='path to Java keytool')
143 parser.add_argument('--jarsigner',
144 metavar='PATH',
145 default='jarsigner',
146 help='path to Java jarsigner')
147 parser.add_argument('--keystore',
148 metavar='PATH',
149 default='~/.android/debug.keystore',
150 help='path to keystore (default: ~/.android/debug.keystore)')
151 parser.add_argument('-f', '--force-create-keystore',
152 dest='force',
153 default=False,
154 action='store_true',
155 help='force creating keystore')
156 return parser.parse_args(argv)
157
158
159 def main():
160 args = parse_args(sys.argv[1:])
161
162 keystore = DebugKeystore(args.keystore)
163 keystore.verbose = args.verbose
164 keystore.keytool = args.keytool
165 keystore.jarsigner = args.jarsigner
166
167 if args.force:
168 try:
169 keystore.create_alias_in_keystore()
170 except subprocess.CalledProcessError as e:
171 log.error('Failed to force-create alias %s in keystore %s' %
172 (keystore.alias, keystore.keystore))
173 log.error(e)
174 return 1
175
176 for apk in args.apks:
177 try:
178 keystore.sign(apk)
179 except subprocess.CalledProcessError as e:
180 log.error('Failed to sign %s', apk)
181 log.error(e)
182 return 1
183
184 return 0
185
186 if __name__ == '__main__':
187 sys.exit(main())

mercurial