Backed out changeset cc0306c09d59 (bug 914888) for frequent xpcshell failures. a...
[gecko.git] / mobile / android / debug_sign_tool.py
blob2d3e2099f80ae076613ae370f33edd30ce617b0f
1 #!/usr/bin/env python
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/.
7 """
8 Sign Android packages using an Android debug keystore, creating the
9 keystore if it does not exist.
11 This and |zip| can be combined to replace the Android |apkbuilder|
12 tool, which was deprecated in SDK r22.
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 """
18 from argparse import ArgumentParser
19 import errno
20 import logging
21 import os
22 import subprocess
23 import sys
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)
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'
44 @property
45 def keystore(self):
46 return self._keystore
48 @property
49 def alias(self):
50 return self._alias
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])
63 def keystore_contains_alias(self):
64 args = [ self.keytool,
65 '-list',
66 '-keystore', self.keystore,
67 '-storepass', 'android',
68 '-alias', self.alias,
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
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
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',
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))
109 def sign(self, apk):
110 if not self.keystore_contains_alias():
111 self.create_alias_in_keystore()
113 args = [ self.jarsigner,
114 '-digestalg', 'SHA1',
115 '-sigalg', 'MD5withRSA',
116 '-keystore', self.keystore,
117 '-storepass', 'android',
118 apk,
119 self.alias,
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))
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)
159 def main():
160 args = parse_args(sys.argv[1:])
162 keystore = DebugKeystore(args.keystore)
163 keystore.verbose = args.verbose
164 keystore.keytool = args.keytool
165 keystore.jarsigner = args.jarsigner
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
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
184 return 0
186 if __name__ == '__main__':
187 sys.exit(main())