diff options
Diffstat (limited to 'tools/releasetools')
-rwxr-xr-x | tools/releasetools/build_image.py | 145 | ||||
-rw-r--r-- | tools/releasetools/common.py | 56 | ||||
-rw-r--r-- | tools/releasetools/edify_generator.py | 14 | ||||
-rwxr-xr-x | tools/releasetools/img_from_target_files | 110 | ||||
-rwxr-xr-x | tools/releasetools/ota_from_target_files | 58 |
5 files changed, 253 insertions, 130 deletions
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py new file mode 100755 index 0000000..a615d1a --- /dev/null +++ b/tools/releasetools/build_image.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Build image output_image_file from input_directory and properties_file. + +Usage: build_image input_directory properties_file output_image_file + +""" +import os +import subprocess +import sys + + +def BuildImage(in_dir, prop_dict, out_file): + """Build an image to out_file from in_dir with property prop_dict. + + Args: + in_dir: path of input directory. + prop_dict: property dictionary. + out_file: path of the output image file. + + Returns: + True iff the image is built successfully. + """ + build_command = [] + fs_type = prop_dict.get("fs_type", "") + if fs_type.startswith("ext"): + build_command = ["mkuserimg.sh"] + if "extfs_sparse_flag" in prop_dict: + build_command.append(prop_dict["extfs_sparse_flag"]) + build_command.extend([in_dir, out_file, fs_type, + prop_dict["mount_point"]]) + if "partition_size" in prop_dict: + build_command.append(prop_dict["partition_size"]) + if "selinux_fc" in prop_dict: + build_command.append(prop_dict["selinux_fc"]) + else: + build_command = ["mkyaffs2image", "-f"] + if prop_dict.get("mkyaffs2_extra_flags", None): + build_command.extend(prop_dict["mkyaffs2_extra_flags"].split()) + build_command.append(in_dir) + build_command.append(out_file) + if "selinux_fc" in prop_dict: + build_command.append(prop_dict["selinux_fc"]) + build_command.append(prop_dict["mount_point"]) + + print "Running: ", " ".join(build_command) + p = subprocess.Popen(build_command); + p.communicate() + return p.returncode == 0 + + +def ImagePropFromGlobalDict(glob_dict, mount_point): + """Build an image property dictionary from the global dictionary. + + Args: + glob_dict: the global dictionary from the build system. + mount_point: such as "system", "data" etc. + """ + d = {} + + def copy_prop(src_p, dest_p): + if src_p in glob_dict: + d[dest_p] = str(glob_dict[src_p]) + + common_props = ( + "extfs_sparse_flag", + "mkyaffs2_extra_flags", + "selinux_fc", + ) + for p in common_props: + copy_prop(p, p) + + d["mount_point"] = mount_point + if mount_point == "system": + copy_prop("fs_type", "fs_type") + copy_prop("system_size", "partition_size") + elif mount_point == "data": + copy_prop("fs_type", "fs_type") + copy_prop("userdata_size", "partition_size") + elif mount_point == "cache": + copy_prop("cache_fs_type", "fs_type") + copy_prop("cache_size", "partition_size") + + return d + + +def LoadGlobalDict(filename): + """Load "name=value" pairs from filename""" + d = {} + f = open(filename) + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + k, v = line.split("=", 1) + d[k] = v + f.close() + return d + + +def main(argv): + if len(argv) != 3: + print __doc__ + sys.exit(1) + + in_dir = argv[0] + glob_dict_file = argv[1] + out_file = argv[2] + + glob_dict = LoadGlobalDict(glob_dict_file) + image_filename = os.path.basename(out_file) + mount_point = "" + if image_filename == "system.img": + mount_point = "system" + elif image_filename == "userdata.img": + mount_point = "data" + elif image_filename == "cache.img": + mount_point = "cache" + else: + print >> sys.stderr, "error: unknown image file name ", image_filename + exit(1) + + image_properties = ImagePropFromGlobalDict(glob_dict, mount_point) + if not BuildImage(in_dir, image_properties, out_file): + print >> sys.stderr, "error: failed to build %s from %s" % (out_file, in_dir) + exit(1) + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py index 4957354..5e4055a 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -138,6 +138,7 @@ def LoadInfoDict(zip): makeint("blocksize") makeint("system_size") makeint("userdata_size") + makeint("cache_size") makeint("recovery_size") makeint("boot_size") @@ -195,7 +196,7 @@ def DumpInfoDict(d): for k, v in sorted(d.items()): print "%-25s = (%s) %s" % (k, type(v).__name__, v) -def BuildBootableImage(sourcedir): +def BuildBootableImage(sourcedir, fs_config_file): """Take a kernel, cmdline, and ramdisk directory from the input (in 'sourcedir'), and turn them into a boot image. Return the image data, or None if sourcedir does not appear to contains files for @@ -208,8 +209,11 @@ def BuildBootableImage(sourcedir): ramdisk_img = tempfile.NamedTemporaryFile() img = tempfile.NamedTemporaryFile() - p1 = Run(["mkbootfs", os.path.join(sourcedir, "RAMDISK")], - stdout=subprocess.PIPE) + if os.access(fs_config_file, os.F_OK): + cmd = ["mkbootfs", "-f", fs_config_file, os.path.join(sourcedir, "RAMDISK")] + else: + cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")] + p1 = Run(cmd, stdout=subprocess.PIPE) p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno()) @@ -264,7 +268,9 @@ def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir): return File.FromLocalFile(name, prebuilt_path) else: print "building image from target_files %s..." % (tree_subdir,) - return File(name, BuildBootableImage(os.path.join(unpack_dir, tree_subdir))) + fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt" + return File(name, BuildBootableImage(os.path.join(unpack_dir, tree_subdir), + os.path.join(unpack_dir, fs_config))) def UnzipTemp(filename, pattern=None): @@ -389,24 +395,27 @@ def CheckSize(data, target, info_dict): if mount_point == "/userdata": mount_point = "/data" p = info_dict["fstab"][mount_point] fs_type = p.fs_type - limit = info_dict.get(p.device + "_size", None) + device = p.device + if "/" in device: + device = device[device.rfind("/")+1:] + limit = info_dict.get(device + "_size", None) if not fs_type or not limit: return if fs_type == "yaffs2": # image size should be increased by 1/64th to account for the # spare area (64 bytes per 2k page) limit = limit / 2048 * (2048+64) - size = len(data) - pct = float(size) * 100.0 / limit - msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit) - if pct >= 99.0: - raise ExternalError(msg) - elif pct >= 95.0: - print - print " WARNING: ", msg - print - elif OPTIONS.verbose: - print " ", msg + size = len(data) + pct = float(size) * 100.0 / limit + msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit) + if pct >= 99.0: + raise ExternalError(msg) + elif pct >= 95.0: + print + print " WARNING: ", msg + print + elif OPTIONS.verbose: + print " ", msg def ReadApkCerts(tf_zip): @@ -659,6 +668,10 @@ class DeviceSpecificParams(object): assertions they like.""" return self._DoCall("FullOTA_Assertions") + def FullOTA_InstallBegin(self): + """Called at the start of full OTA installation.""" + return self._DoCall("FullOTA_InstallBegin") + def FullOTA_InstallEnd(self): """Called at the end of full OTA installation; typically this is used to install the image for the device's baseband processor.""" @@ -670,12 +683,23 @@ class DeviceSpecificParams(object): additional assertions they like.""" return self._DoCall("IncrementalOTA_Assertions") + def IncrementalOTA_VerifyBegin(self): + """Called at the start of the verification phase of incremental + OTA installation; additional checks can be placed here to abort + the script before any changes are made.""" + return self._DoCall("IncrementalOTA_VerifyBegin") + def IncrementalOTA_VerifyEnd(self): """Called at the end of the verification phase of incremental OTA installation; additional checks can be placed here to abort the script before any changes are made.""" return self._DoCall("IncrementalOTA_VerifyEnd") + def IncrementalOTA_InstallBegin(self): + """Called at the start of incremental OTA installation (after + verification is complete).""" + return self._DoCall("IncrementalOTA_InstallBegin") + def IncrementalOTA_InstallEnd(self): """Called at the end of incremental OTA installation; typically this is used to install the image for the device's baseband diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py index 12bb48b..5672b5a 100644 --- a/tools/releasetools/edify_generator.py +++ b/tools/releasetools/edify_generator.py @@ -228,20 +228,6 @@ class EdifyGenerator(object): ",\0".join(['"' + i + '"' for i in sorted(links)]) + ");") self.script.append(self._WordWrap(cmd)) - def RetouchBinaries(self, file_list): - """Execute the retouch instructions in files listed.""" - cmd = ('retouch_binaries(' + - ', '.join(['"' + i[0] + '", "' + i[1] + '"' for i in file_list]) + - ');') - self.script.append(self._WordWrap(cmd)) - - def UndoRetouchBinaries(self, file_list): - """Undo the retouching (retouch to zero offset).""" - cmd = ('undo_retouch_binaries(' + - ', '.join(['"' + i[0] + '", "' + i[1] + '"' for i in file_list]) + - ');') - self.script.append(self._WordWrap(cmd)) - def AppendExtra(self, extra): """Append text verbatim to the output script.""" self.script.append(extra) diff --git a/tools/releasetools/img_from_target_files b/tools/releasetools/img_from_target_files index ad03398..002e6e6 100755 --- a/tools/releasetools/img_from_target_files +++ b/tools/releasetools/img_from_target_files @@ -27,10 +27,6 @@ Usage: img_from_target_files [flags] input_target_files output_image_zip Include only the bootable images (eg 'boot' and 'recovery') in the output. - -S (--file_context) <file> - the file contexts configuration used to assign SELinux file - context attributes. - """ import sys @@ -51,10 +47,10 @@ import zipfile if not hasattr(os, "SEEK_SET"): os.SEEK_SET = 0 +import build_image import common OPTIONS = common.OPTIONS -OPTIONS.selinux_fc = None def AddUserdata(output_zip): """Create an empty userdata image and store it in output_zip.""" @@ -69,32 +65,13 @@ def AddUserdata(output_zip): os.mkdir(user_dir) img = tempfile.NamedTemporaryFile() - build_command = [] + image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, + "data") fstab = OPTIONS.info_dict["fstab"] - if fstab and fstab["/data"].fs_type.startswith("ext"): - build_command = ["mkuserimg.sh"] - if "extfs_sparse_flag" in OPTIONS.info_dict: - build_command.append(OPTIONS.info_dict["extfs_sparse_flag"]) - build_command.extend([user_dir, img.name, - fstab["/data"].fs_type, "data"]) - if "userdata_size" in OPTIONS.info_dict: - build_command.append(str(OPTIONS.info_dict["userdata_size"])) - if OPTIONS.selinux_fc is not None: - build_command.append(OPTIONS.selinux_fc) - else: - build_command = ["mkyaffs2image", "-f"] - extra = OPTIONS.info_dict.get("mkyaffs2_extra_flags", None) - if extra: - build_command.extend(extra.split()) - build_command.append(user_dir) - build_command.append(img.name) - if OPTIONS.selinux_fc is not None: - build_command.append(OPTIONS.selinux_fc) - build_command.append("/data") - - p = common.Run(build_command); - p.communicate() - assert p.returncode == 0, "build userdata.img image failed" + if fstab: + image_props["fs_type" ] = fstab["/data"].fs_type + succ = build_image.BuildImage(user_dir, image_props, img.name) + assert succ, "build userdata.img image failed" common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) output_zip.write(img.name, "userdata.img") @@ -103,6 +80,38 @@ def AddUserdata(output_zip): os.rmdir(temp_dir) +def AddCache(output_zip): + """Create an empty cache image and store it in output_zip.""" + + image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, + "cache") + # The build system has to explicitly request for cache.img. + if "fs_type" not in image_props: + return + + print "creating cache.img..." + + # The name of the directory it is making an image out of matters to + # mkyaffs2image. So we create a temp dir, and within it we create an + # empty dir named "cache", and build the image from that. + temp_dir = tempfile.mkdtemp() + user_dir = os.path.join(temp_dir, "cache") + os.mkdir(user_dir) + img = tempfile.NamedTemporaryFile() + + fstab = OPTIONS.info_dict["fstab"] + if fstab: + image_props["fs_type" ] = fstab["/cache"].fs_type + succ = build_image.BuildImage(user_dir, image_props, img.name) + assert succ, "build cache.img image failed" + + common.CheckSize(img.name, "cache.img", OPTIONS.info_dict) + output_zip.write(img.name, "cache.img") + img.close() + os.rmdir(user_dir) + os.rmdir(temp_dir) + + def AddSystem(output_zip): """Turn the contents of SYSTEM into a system image and store it in output_zip.""" @@ -125,33 +134,14 @@ def AddSystem(output_zip): if (e.errno == errno.EEXIST): pass - build_command = [] + image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, + "system") fstab = OPTIONS.info_dict["fstab"] - if fstab and fstab["/system"].fs_type.startswith("ext"): - - build_command = ["mkuserimg.sh"] - if "extfs_sparse_flag" in OPTIONS.info_dict: - build_command.append(OPTIONS.info_dict["extfs_sparse_flag"]) - build_command.extend([os.path.join(OPTIONS.input_tmp, "system"), img.name, - fstab["/system"].fs_type, "system"]) - if "system_size" in OPTIONS.info_dict: - build_command.append(str(OPTIONS.info_dict["system_size"])) - if OPTIONS.selinux_fc is not None: - build_command.append(OPTIONS.selinux_fc) - else: - build_command = ["mkyaffs2image", "-f"] - extra = OPTIONS.info_dict.get("mkyaffs2_extra_flags", None) - if extra: - build_command.extend(extra.split()) - build_command.append(os.path.join(OPTIONS.input_tmp, "system")) - build_command.append(img.name) - if OPTIONS.selinux_fc is not None: - build_command.append(OPTIONS.selinux_fc) - build_command.append("/system") - - p = common.Run(build_command) - p.communicate() - assert p.returncode == 0, "build system.img image failed" + if fstab: + image_props["fs_type" ] = fstab["/system"].fs_type + succ = build_image.BuildImage(os.path.join(OPTIONS.input_tmp, "system"), + image_props, img.name) + assert succ, "build system.img image failed" img.seek(os.SEEK_SET, 0) data = img.read() @@ -175,17 +165,14 @@ def main(argv): pass # deprecated if o in ("-z", "--bootable_zip"): bootable_only[0] = True - if o in ("-S", "--file_context"): - OPTIONS.selinux_fc = a else: return False return True args = common.ParseOptions(argv, __doc__, - extra_opts="b:zS:", + extra_opts="b:z", extra_long_opts=["board_config=", - "bootable_zip", - "file_context="], + "bootable_zip"], extra_option_handler=option_handler) bootable_only = bootable_only[0] @@ -208,6 +195,7 @@ def main(argv): if not bootable_only: AddSystem(output_zip) AddUserdata(output_zip) + AddCache(output_zip) CopyInfo(output_zip) print "cleaning up..." diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files index f838c22..3dcfbee 100755 --- a/tools/releasetools/ota_from_target_files +++ b/tools/releasetools/ota_from_target_files @@ -52,10 +52,6 @@ Usage: ota_from_target_files [flags] input_target_files output_ota_package -a (--aslr_mode) <on|off> Specify whether to turn on ASLR for the package (on by default). - -S (--file_context) <file> - the file contexts configuration used to assign SELinux file - context attributes - """ import sys @@ -92,7 +88,6 @@ OPTIONS.omit_prereq = False OPTIONS.extra_script = None OPTIONS.aslr_mode = True OPTIONS.worker_threads = 3 -OPTIONS.selinux_fc = None def MostPopularKey(d, default): """Given a dict, return the key corresponding to the largest @@ -266,15 +261,13 @@ def CopySystemFiles(input_zip, output_zip=None, substitute=None): """Copies files underneath system/ in the input zip to the output zip. Populates the Item class with their metadata, and returns a - list of symlinks as well as a list of files that will be retouched. - output_zip may be None, in which case the copy is skipped (but the - other side effects still happen). substitute is an optional dict - of {output filename: contents} to be output instead of certain input - files. + list of symlinks. output_zip may be None, in which case the copy is + skipped (but the other side effects still happen). substitute is an + optional dict of {output filename: contents} to be output instead of + certain input files. """ symlinks = [] - retouch_files = [] for info in input_zip.infolist(): if info.filename.startswith("SYSTEM/"): @@ -292,9 +285,6 @@ def CopySystemFiles(input_zip, output_zip=None, data = substitute[fn] else: data = input_zip.read(info.filename) - if info.filename.startswith("SYSTEM/lib/") and IsRegular(info): - retouch_files.append(("/system/" + basefilename, - common.sha1(data).hexdigest())) output_zip.writestr(info2, data) if fn.endswith("/"): Item.Get(fn[:-1], dir=True) @@ -302,7 +292,7 @@ def CopySystemFiles(input_zip, output_zip=None, Item.Get(fn, dir=False) symlinks.sort() - return (symlinks, retouch_files) + return symlinks def SignOutput(temp_zip_name, output_zip_name): @@ -388,26 +378,23 @@ def WriteFullOTAPackage(input_zip, output_zip): AppendAssertions(script, input_zip) device_specific.FullOTA_Assertions() + device_specific.FullOTA_InstallBegin() script.ShowProgress(0.5, 0) if OPTIONS.wipe_user_data: script.FormatPartition("/data") - if OPTIONS.selinux_fc is not None: - WritePolicyConfig(OPTIONS.selinux_fc, output_zip) + if "selinux_fc" in OPTIONS.info_dict: + WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip) script.FormatPartition("/system") script.Mount("/system") script.UnpackPackageDir("recovery", "/system") script.UnpackPackageDir("system", "/system") - (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip) + symlinks = CopySystemFiles(input_zip, output_zip) script.MakeSymlinks(symlinks) - if OPTIONS.aslr_mode: - script.RetouchBinaries(retouch_files) - else: - script.UndoRetouchBinaries(retouch_files) boot_img = common.GetBootableImage("boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") @@ -450,17 +437,13 @@ def LoadSystemFiles(z): """Load all the files from SYSTEM/... in a given target-files ZipFile, and return a dict of {filename: File object}.""" out = {} - retouch_files = [] for info in z.infolist(): if info.filename.startswith("SYSTEM/") and not IsSymlink(info): basefilename = info.filename[7:] fn = "system/" + basefilename data = z.read(info.filename) out[fn] = common.File(fn, data) - if info.filename.startswith("SYSTEM/lib/") and IsRegular(info): - retouch_files.append(("/system/" + basefilename, - out[fn].sha1)) - return (out, retouch_files) + return out def GetBuildProp(property, z): @@ -499,9 +482,9 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): info_dict=OPTIONS.info_dict) print "Loading target..." - (target_data, target_retouch_files) = LoadSystemFiles(target_zip) + target_data = LoadSystemFiles(target_zip) print "Loading source..." - (source_data, source_retouch_files) = LoadSystemFiles(source_zip) + source_data = LoadSystemFiles(source_zip) verbatim_targets = [] patch_list = [] @@ -570,6 +553,8 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): script.Print("Verifying current system...") + device_specific.IncrementalOTA_VerifyBegin() + script.ShowProgress(0.1, 0) total_verify_size = float(sum([i[2].size for i in patch_list]) + 1) if updating_boot: @@ -605,6 +590,8 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): script.Comment("---- start making changes here ----") + device_specific.IncrementalOTA_InstallBegin() + if OPTIONS.wipe_user_data: script.Print("Erasing user data...") script.FormatPartition("/data") @@ -671,7 +658,7 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): script.ShowProgress(0.1, 10) - (target_symlinks, target_retouch_dummies) = CopySystemFiles(target_zip, None) + target_symlinks = CopySystemFiles(target_zip, None) target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks]) temp_script = script.MakeTemporary() @@ -680,7 +667,7 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): # Note that this call will mess up the tree of Items, so make sure # we're done with it. - (source_symlinks, source_retouch_dummies) = CopySystemFiles(source_zip, None) + source_symlinks = CopySystemFiles(source_zip, None) source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks]) # Delete all the symlinks in source that aren't in target. This @@ -714,10 +701,6 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): to_create.append((dest, link)) script.DeleteFiles([i[1] for i in to_create]) script.MakeSymlinks(to_create) - if OPTIONS.aslr_mode: - script.RetouchBinaries(target_retouch_files) - else: - script.UndoRetouchBinaries(target_retouch_files) # Now that the symlinks are created, we can set all the # permissions. @@ -764,14 +747,12 @@ def main(argv): OPTIONS.aslr_mode = False elif o in ("--worker_threads"): OPTIONS.worker_threads = int(a) - elif o in ("-S", "--file_context"): - OPTIONS.selinux_fc = a else: return False return True args = common.ParseOptions(argv, __doc__, - extra_opts="b:k:i:d:wne:a:S:", + extra_opts="b:k:i:d:wne:a:", extra_long_opts=["board_config=", "package_key=", "incremental_from=", @@ -780,7 +761,6 @@ def main(argv): "extra_script=", "worker_threads=", "aslr_mode=", - "file_context=", ], extra_option_handler=option_handler) |