img_from_target_files.py revision 4d09dcb2c6d5e2fe8e8ed0780ee2da6ac8bda3c4
1#!/usr/bin/env python 2# 3# Copyright (C) 2008 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17""" 18Given a target-files zipfile, produces an image zipfile suitable for 19use with 'fastboot update'. 20 21Usage: img_from_target_files [flags] input_target_files output_image_zip 22 23 -b (--board_config) <file> 24 Deprecated. 25 26 -z (--bootable_zip) 27 Include only the bootable images (eg 'boot' and 'recovery') in 28 the output. 29 30""" 31 32import sys 33 34if sys.hexversion < 0x02070000: 35 print >> sys.stderr, "Python 2.7 or newer is required." 36 sys.exit(1) 37 38import errno 39import os 40import re 41import shutil 42import subprocess 43import tempfile 44import zipfile 45 46# missing in Python 2.4 and before 47if not hasattr(os, "SEEK_SET"): 48 os.SEEK_SET = 0 49 50import build_image 51import common 52 53OPTIONS = common.OPTIONS 54 55 56def AddSystem(output_zip, sparse=True): 57 """Turn the contents of SYSTEM into a system image and store it in 58 output_zip.""" 59 data = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict, sparse=sparse) 60 common.ZipWriteStr(output_zip, "system.img", data) 61 62def BuildSystem(input_dir, info_dict, sparse=True, map_file=None): 63 return CreateImage(input_dir, info_dict, "system", 64 sparse=sparse, map_file=map_file) 65 66def AddVendor(output_zip, sparse=True): 67 data = BuildVendor(OPTIONS.input_tmp, OPTIONS.info_dict, sparse=sparse) 68 common.ZipWriteStr(output_zip, "vendor.img", data) 69 70def BuildVendor(input_dir, info_dict, sparse=True, map_file=None): 71 return CreateImage(input_dir, info_dict, "vendor", 72 sparse=sparse, map_file=map_file) 73 74 75def CreateImage(input_dir, info_dict, what, sparse=True, map_file=None): 76 print "creating " + what + ".img..." 77 78 img = tempfile.NamedTemporaryFile() 79 80 # The name of the directory it is making an image out of matters to 81 # mkyaffs2image. It wants "system" but we have a directory named 82 # "SYSTEM", so create a symlink. 83 try: 84 os.symlink(os.path.join(input_dir, what.upper()), 85 os.path.join(input_dir, what)) 86 except OSError, e: 87 # bogus error on my mac version? 88 # File "./build/tools/releasetools/img_from_target_files", line 86, in AddSystem 89 # os.path.join(OPTIONS.input_tmp, "system")) 90 # OSError: [Errno 17] File exists 91 if (e.errno == errno.EEXIST): 92 pass 93 94 image_props = build_image.ImagePropFromGlobalDict(info_dict, what) 95 fstab = info_dict["fstab"] 96 if fstab: 97 image_props["fs_type" ] = fstab["/" + what].fs_type 98 99 if what == "system": 100 fs_config_prefix = "" 101 else: 102 fs_config_prefix = what + "_" 103 104 fs_config = os.path.join( 105 input_dir, "META/" + fs_config_prefix + "filesystem_config.txt") 106 if not os.path.exists(fs_config): fs_config = None 107 108 fc_config = os.path.join(input_dir, "BOOT/RAMDISK/file_contexts") 109 if not os.path.exists(fc_config): fc_config = None 110 111 succ = build_image.BuildImage(os.path.join(input_dir, what), 112 image_props, img.name, 113 fs_config=fs_config, 114 fc_config=fc_config) 115 assert succ, "build " + what + ".img image failed" 116 117 mapdata = None 118 119 if sparse: 120 data = open(img.name).read() 121 img.close() 122 else: 123 success, name = build_image.UnsparseImage(img.name, replace=False) 124 if not success: 125 assert False, "unsparsing " + what + ".img failed" 126 127 if map_file: 128 mmap = tempfile.NamedTemporaryFile() 129 mimg = tempfile.NamedTemporaryFile(delete=False) 130 success = build_image.MappedUnsparseImage( 131 img.name, name, mmap.name, mimg.name) 132 if not success: 133 assert False, "creating sparse map failed" 134 os.unlink(name) 135 name = mimg.name 136 137 with open(mmap.name) as f: 138 mapdata = f.read() 139 140 try: 141 with open(name) as f: 142 data = f.read() 143 finally: 144 os.unlink(name) 145 146 if mapdata is None: 147 return data 148 else: 149 return mapdata, data 150 151 152def AddUserdata(output_zip): 153 """Create an empty userdata image and store it in output_zip.""" 154 155 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, 156 "data") 157 # We only allow yaffs to have a 0/missing partition_size. 158 # Extfs, f2fs must have a size. Skip userdata.img if no size. 159 if (not image_props.get("fs_type", "").startswith("yaffs") and 160 not image_props.get("partition_size")): 161 return 162 163 print "creating userdata.img..." 164 165 # The name of the directory it is making an image out of matters to 166 # mkyaffs2image. So we create a temp dir, and within it we create an 167 # empty dir named "data", and build the image from that. 168 temp_dir = tempfile.mkdtemp() 169 user_dir = os.path.join(temp_dir, "data") 170 os.mkdir(user_dir) 171 img = tempfile.NamedTemporaryFile() 172 173 fstab = OPTIONS.info_dict["fstab"] 174 if fstab: 175 image_props["fs_type" ] = fstab["/data"].fs_type 176 succ = build_image.BuildImage(user_dir, image_props, img.name) 177 assert succ, "build userdata.img image failed" 178 179 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) 180 output_zip.write(img.name, "userdata.img") 181 img.close() 182 os.rmdir(user_dir) 183 os.rmdir(temp_dir) 184 185 186def AddCache(output_zip): 187 """Create an empty cache image and store it in output_zip.""" 188 189 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, 190 "cache") 191 # The build system has to explicitly request for cache.img. 192 if "fs_type" not in image_props: 193 return 194 195 print "creating cache.img..." 196 197 # The name of the directory it is making an image out of matters to 198 # mkyaffs2image. So we create a temp dir, and within it we create an 199 # empty dir named "cache", and build the image from that. 200 temp_dir = tempfile.mkdtemp() 201 user_dir = os.path.join(temp_dir, "cache") 202 os.mkdir(user_dir) 203 img = tempfile.NamedTemporaryFile() 204 205 fstab = OPTIONS.info_dict["fstab"] 206 if fstab: 207 image_props["fs_type" ] = fstab["/cache"].fs_type 208 succ = build_image.BuildImage(user_dir, image_props, img.name) 209 assert succ, "build cache.img image failed" 210 211 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict) 212 output_zip.write(img.name, "cache.img") 213 img.close() 214 os.rmdir(user_dir) 215 os.rmdir(temp_dir) 216 217 218def CopyInfo(output_zip): 219 """Copy the android-info.txt file from the input to the output.""" 220 output_zip.write(os.path.join(OPTIONS.input_tmp, "OTA", "android-info.txt"), 221 "android-info.txt") 222 223 224def main(argv): 225 bootable_only = [False] 226 227 def option_handler(o, a): 228 if o in ("-b", "--board_config"): 229 pass # deprecated 230 if o in ("-z", "--bootable_zip"): 231 bootable_only[0] = True 232 else: 233 return False 234 return True 235 236 args = common.ParseOptions(argv, __doc__, 237 extra_opts="b:z", 238 extra_long_opts=["board_config=", 239 "bootable_zip"], 240 extra_option_handler=option_handler) 241 242 bootable_only = bootable_only[0] 243 244 if len(args) != 2: 245 common.Usage(__doc__) 246 sys.exit(1) 247 248 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0]) 249 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 250 251 # If this image was originally labelled with SELinux contexts, make sure we 252 # also apply the labels in our new image. During building, the "file_contexts" 253 # is in the out/ directory tree, but for repacking from target-files.zip it's 254 # in the root directory of the ramdisk. 255 if "selinux_fc" in OPTIONS.info_dict: 256 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK", 257 "file_contexts") 258 259 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED) 260 261 boot_image = common.GetBootableImage( 262 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 263 if boot_image: 264 boot_image.AddToZip(output_zip) 265 recovery_image = common.GetBootableImage( 266 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") 267 if recovery_image: 268 recovery_image.AddToZip(output_zip) 269 270 def banner(s): 271 print "\n\n++++ " + s + " ++++\n\n" 272 273 if not bootable_only: 274 banner("AddSystem") 275 AddSystem(output_zip) 276 try: 277 input_zip.getinfo("VENDOR/") 278 banner("AddVendor") 279 AddVendor(output_zip) 280 except KeyError: 281 pass # no vendor partition for this device 282 banner("AddUserdata") 283 AddUserdata(output_zip) 284 banner("AddCache") 285 AddCache(output_zip) 286 CopyInfo(output_zip) 287 288 print "cleaning up..." 289 output_zip.close() 290 shutil.rmtree(OPTIONS.input_tmp) 291 292 print "done." 293 294 295if __name__ == '__main__': 296 try: 297 common.CloseInheritedPipes() 298 main(sys.argv[1:]) 299 except common.ExternalError, e: 300 print 301 print " ERROR: %s" % (e,) 302 print 303 sys.exit(1) 304