1 # Copyright (C) 2008 The Android Open Source Project
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
24 # missing in Python 2.4 and before
25 if not hasattr(os, "SEEK_SET"):
28 class Options(object): pass
30 OPTIONS.signapk_jar = "out/host/linux-x86/framework/signapk.jar"
31 OPTIONS.dumpkey_jar = "out/host/linux-x86/framework/dumpkey.jar"
32 OPTIONS.max_image_size = {}
33 OPTIONS.verbose = False
34 OPTIONS.tempfiles = []
37 class ExternalError(RuntimeError): pass
40 def Run(args, **kwargs):
41 """Create and return a subprocess.Popen object, printing the command
42 line on the terminal if -v was specified."""
44 print " running: ", " ".join(args)
45 return subprocess.Popen(args, **kwargs)
48 def LoadBoardConfig(fn):
49 """Parse a board_config.mk file looking for lines that specify the
50 maximum size of various images, and parse them into the
51 OPTIONS.max_image_size dict."""
52 OPTIONS.max_image_size = {}
55 m = re.match(r"BOARD_(BOOT|RECOVERY|SYSTEM|USERDATA)IMAGE_MAX_SIZE"
56 r"\s*:=\s*(\d+)", line)
59 OPTIONS.max_image_size[m.group(1).lower() + ".img"] = int(m.group(2))
62 def BuildAndAddBootableImage(sourcedir, targetname, output_zip):
63 """Take a kernel, cmdline, and ramdisk directory from the input (in
64 'sourcedir'), and turn them into a boot image. Put the boot image
65 into the output zip file under the name 'targetname'."""
67 print "creating %s..." % (targetname,)
69 img = BuildBootableImage(sourcedir)
71 CheckSize(img, targetname)
72 output_zip.writestr(targetname, img)
74 def BuildBootableImage(sourcedir):
75 """Take a kernel, cmdline, and ramdisk directory from the input (in
76 'sourcedir'), and turn them into a boot image. Return the image data."""
78 ramdisk_img = tempfile.NamedTemporaryFile()
79 img = tempfile.NamedTemporaryFile()
81 p1 = Run(["mkbootfs", os.path.join(sourcedir, "RAMDISK")],
82 stdout=subprocess.PIPE)
83 p2 = Run(["gzip", "-n"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
87 assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (targetname,)
88 assert p2.returncode == 0, "gzip of %s ramdisk failed" % (targetname,)
90 cmdline = open(os.path.join(sourcedir, "cmdline")).read().rstrip("\n")
92 "--kernel", os.path.join(sourcedir, "kernel"),
94 "--ramdisk", ramdisk_img.name,
95 "--output", img.name],
96 stdout=subprocess.PIPE)
98 assert p.returncode == 0, "mkbootimg of %s image failed" % (targetname,)
100 img.seek(os.SEEK_SET, 0)
109 def AddRecovery(output_zip):
110 BuildAndAddBootableImage(os.path.join(OPTIONS.input_tmp, "RECOVERY"),
111 "recovery.img", output_zip)
113 def AddBoot(output_zip):
114 BuildAndAddBootableImage(os.path.join(OPTIONS.input_tmp, "BOOT"),
115 "boot.img", output_zip)
117 def UnzipTemp(filename):
118 """Unzip the given archive into a temporary directory and return the name."""
120 tmp = tempfile.mkdtemp(prefix="targetfiles-")
121 OPTIONS.tempfiles.append(tmp)
122 p = Run(["unzip", "-q", filename, "-d", tmp], stdout=subprocess.PIPE)
124 if p.returncode != 0:
125 raise ExternalError("failed to unzip input target-files \"%s\"" %
130 def GetKeyPasswords(keylist):
131 """Given a list of keys, prompt the user to enter passwords for
132 those which require them. Return a {key: password} dict. password
133 will be None if the key has no password."""
136 devnull = open("/dev/null", "w+b")
137 for k in sorted(keylist):
138 # An empty-string key is used to mean don't re-sign this package.
139 # Obviously we don't need a password for this non-key.
141 key_passwords[k] = None
144 p = subprocess.Popen(["openssl", "pkcs8", "-in", k+".pk8",
145 "-inform", "DER", "-nocrypt"],
146 stdin=devnull.fileno(),
147 stdout=devnull.fileno(),
148 stderr=subprocess.STDOUT)
150 if p.returncode == 0:
151 print "%s.pk8 does not require a password" % (k,)
152 key_passwords[k] = None
154 key_passwords[k] = getpass.getpass("Enter password for %s.pk8> " % (k,))
160 def SignFile(input_name, output_name, key, password, align=None):
161 """Sign the input_name zip/jar/apk, producing output_name. Use the
162 given key and password (the latter may be None if the key does not
165 If align is an integer > 1, zipalign is run to align stored files in
166 the output zip on 'align'-byte boundaries.
168 if align == 0 or align == 1:
172 temp = tempfile.NamedTemporaryFile()
173 sign_name = temp.name
175 sign_name = output_name
177 p = subprocess.Popen(["java", "-jar", OPTIONS.signapk_jar,
180 input_name, sign_name],
181 stdin=subprocess.PIPE,
182 stdout=subprocess.PIPE)
183 if password is not None:
185 p.communicate(password)
186 if p.returncode != 0:
187 raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,))
190 p = subprocess.Popen(["zipalign", "-f", str(align), sign_name, output_name])
192 if p.returncode != 0:
193 raise ExternalError("zipalign failed: return code %s" % (p.returncode,))
197 def CheckSize(data, target):
198 """Check the data string passed against the max size limit, if
199 any, for the given target. Raise exception if the data is too big.
200 Print a warning if the data is nearing the maximum size."""
201 limit = OPTIONS.max_image_size.get(target, None)
202 if limit is None: return
205 pct = float(size) * 100.0 / limit
206 msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
208 raise ExternalError(msg)
211 print " WARNING: ", msg
213 elif OPTIONS.verbose:
217 COMMON_DOCSTRING = """
219 Prepend <dir> to the list of places to search for binaries run
223 Show command lines being executed.
226 Display this usage message and exit.
229 def Usage(docstring):
230 print docstring.rstrip("\n")
231 print COMMON_DOCSTRING
234 def ParseOptions(argv,
236 extra_opts="", extra_long_opts=(),
237 extra_option_handler=None):
238 """Parse the options in argv and return any arguments that aren't
239 flags. docstring is the calling module's docstring, to be displayed
240 for errors and -h. extra_opts and extra_long_opts are for flags
241 defined by the caller, which are processed by passing them to
242 extra_option_handler."""
245 opts, args = getopt.getopt(
246 argv, "hvp:" + extra_opts,
247 ["help", "verbose", "path="] + list(extra_long_opts))
248 except getopt.GetoptError, err:
250 print "**", str(err), "**"
253 path_specified = False
256 if o in ("-h", "--help"):
259 elif o in ("-v", "--verbose"):
260 OPTIONS.verbose = True
261 elif o in ("-p", "--path"):
262 os.environ["PATH"] = a + os.pathsep + os.environ["PATH"]
263 path_specified = True
265 if extra_option_handler is None or not extra_option_handler(o, a):
266 assert False, "unknown option \"%s\"" % (o,)
268 if not path_specified:
269 os.environ["PATH"] = ("out/host/linux-x86/bin" + os.pathsep +
276 for i in OPTIONS.tempfiles: