1 #!/usr/bin/env python 2 # 3 # Copyright (C) 2014 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 """ 18 Given a target-files zipfile that does not contain images (ie, does 19 not have an IMAGES/ top-level subdirectory), produce the images and 20 add them to the zipfile. 21 22 Usage: add_img_to_target_files target_files 23 """ 24 25 import sys 26 27 if sys.hexversion < 0x02070000: 28 print >> sys.stderr, "Python 2.7 or newer is required." 29 sys.exit(1) 30 31 import errno 32 import os 33 import re 34 import shutil 35 import subprocess 36 import tempfile 37 import zipfile 38 39 # missing in Python 2.4 and before 40 if not hasattr(os, "SEEK_SET"): 41 os.SEEK_SET = 0 42 43 import build_image 44 import common 45 46 OPTIONS = common.OPTIONS 47 48 OPTIONS.add_missing = False 49 OPTIONS.rebuild_recovery = False 50 51 def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None): 52 """Turn the contents of SYSTEM into a system image and store it in 53 output_zip.""" 54 55 prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "system.img") 56 if os.path.exists(prebuilt_path): 57 print "system.img already exists in %s, no need to rebuild..." % (prefix,) 58 return 59 60 def output_sink(fn, data): 61 ofile = open(os.path.join(OPTIONS.input_tmp,"SYSTEM",fn), "w") 62 ofile.write(data) 63 ofile.close() 64 65 if OPTIONS.rebuild_recovery: 66 print("Building new recovery patch") 67 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img, boot_img, 68 info_dict=OPTIONS.info_dict) 69 70 block_list = common.MakeTempFile(prefix="system-blocklist-", suffix=".map") 71 imgname = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict, 72 block_list=block_list) 73 with open(imgname, "rb") as f: 74 common.ZipWriteStr(output_zip, prefix + "system.img", f.read()) 75 with open(block_list, "rb") as f: 76 common.ZipWriteStr(output_zip, prefix + "system.map", f.read()) 77 78 79 def BuildSystem(input_dir, info_dict, block_list=None): 80 """Build the (sparse) system image and return the name of a temp 81 file containing it.""" 82 return CreateImage(input_dir, info_dict, "system", block_list=block_list) 83 84 85 def AddVendor(output_zip, prefix="IMAGES/"): 86 """Turn the contents of VENDOR into a vendor image and store in it 87 output_zip.""" 88 89 prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "vendor.img") 90 if os.path.exists(prebuilt_path): 91 print "vendor.img already exists in %s, no need to rebuild..." % (prefix,) 92 return 93 94 block_list = common.MakeTempFile(prefix="vendor-blocklist-", suffix=".map") 95 imgname = BuildVendor(OPTIONS.input_tmp, OPTIONS.info_dict, 96 block_list=block_list) 97 with open(imgname, "rb") as f: 98 common.ZipWriteStr(output_zip, prefix + "vendor.img", f.read()) 99 with open(block_list, "rb") as f: 100 common.ZipWriteStr(output_zip, prefix + "vendor.map", f.read()) 101 102 103 def BuildVendor(input_dir, info_dict, block_list=None): 104 """Build the (sparse) vendor image and return the name of a temp 105 file containing it.""" 106 return CreateImage(input_dir, info_dict, "vendor", block_list=block_list) 107 108 109 def CreateImage(input_dir, info_dict, what, block_list=None): 110 print "creating " + what + ".img..." 111 112 img = common.MakeTempFile(prefix=what + "-", suffix=".img") 113 114 # The name of the directory it is making an image out of matters to 115 # mkyaffs2image. It wants "system" but we have a directory named 116 # "SYSTEM", so create a symlink. 117 try: 118 os.symlink(os.path.join(input_dir, what.upper()), 119 os.path.join(input_dir, what)) 120 except OSError, e: 121 # bogus error on my mac version? 122 # File "./build/tools/releasetools/img_from_target_files", line 86, in AddSystem 123 # os.path.join(OPTIONS.input_tmp, "system")) 124 # OSError: [Errno 17] File exists 125 if (e.errno == errno.EEXIST): 126 pass 127 128 image_props = build_image.ImagePropFromGlobalDict(info_dict, what) 129 fstab = info_dict["fstab"] 130 if fstab: 131 image_props["fs_type" ] = fstab["/" + what].fs_type 132 133 if what == "system": 134 fs_config_prefix = "" 135 else: 136 fs_config_prefix = what + "_" 137 138 fs_config = os.path.join( 139 input_dir, "META/" + fs_config_prefix + "filesystem_config.txt") 140 if not os.path.exists(fs_config): fs_config = None 141 142 fc_config = os.path.join(input_dir, "BOOT/RAMDISK/file_contexts") 143 if not os.path.exists(fc_config): fc_config = None 144 145 succ = build_image.BuildImage(os.path.join(input_dir, what), 146 image_props, img, 147 fs_config=fs_config, 148 fc_config=fc_config, 149 block_list=block_list) 150 assert succ, "build " + what + ".img image failed" 151 152 return img 153 154 155 def AddUserdata(output_zip, prefix="IMAGES/"): 156 """Create an empty userdata image and store it in output_zip.""" 157 158 prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "userdata.img") 159 if os.path.exists(prebuilt_path): 160 print "userdata.img already exists in %s, no need to rebuild..." % (prefix,) 161 return 162 163 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, 164 "data") 165 # We only allow yaffs to have a 0/missing partition_size. 166 # Extfs, f2fs must have a size. Skip userdata.img if no size. 167 if (not image_props.get("fs_type", "").startswith("yaffs") and 168 not image_props.get("partition_size")): 169 return 170 171 print "creating userdata.img..." 172 173 # The name of the directory it is making an image out of matters to 174 # mkyaffs2image. So we create a temp dir, and within it we create an 175 # empty dir named "data", and build the image from that. 176 temp_dir = tempfile.mkdtemp() 177 user_dir = os.path.join(temp_dir, "data") 178 os.mkdir(user_dir) 179 img = tempfile.NamedTemporaryFile() 180 181 fstab = OPTIONS.info_dict["fstab"] 182 if fstab: 183 image_props["fs_type" ] = fstab["/data"].fs_type 184 succ = build_image.BuildImage(user_dir, image_props, img.name) 185 assert succ, "build userdata.img image failed" 186 187 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) 188 output_zip.write(img.name, prefix + "userdata.img") 189 img.close() 190 os.rmdir(user_dir) 191 os.rmdir(temp_dir) 192 193 194 def AddCache(output_zip, prefix="IMAGES/"): 195 """Create an empty cache image and store it in output_zip.""" 196 197 prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "cache.img") 198 if os.path.exists(prebuilt_path): 199 print "cache.img already exists in %s, no need to rebuild..." % (prefix,) 200 return 201 202 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, 203 "cache") 204 # The build system has to explicitly request for cache.img. 205 if "fs_type" not in image_props: 206 return 207 208 print "creating cache.img..." 209 210 # The name of the directory it is making an image out of matters to 211 # mkyaffs2image. So we create a temp dir, and within it we create an 212 # empty dir named "cache", and build the image from that. 213 temp_dir = tempfile.mkdtemp() 214 user_dir = os.path.join(temp_dir, "cache") 215 os.mkdir(user_dir) 216 img = tempfile.NamedTemporaryFile() 217 218 fstab = OPTIONS.info_dict["fstab"] 219 if fstab: 220 image_props["fs_type" ] = fstab["/cache"].fs_type 221 succ = build_image.BuildImage(user_dir, image_props, img.name) 222 assert succ, "build cache.img image failed" 223 224 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict) 225 output_zip.write(img.name, prefix + "cache.img") 226 img.close() 227 os.rmdir(user_dir) 228 os.rmdir(temp_dir) 229 230 231 def AddImagesToTargetFiles(filename): 232 OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename) 233 234 if not OPTIONS.add_missing: 235 for n in input_zip.namelist(): 236 if n.startswith("IMAGES/"): 237 print "target_files appears to already contain images." 238 sys.exit(1) 239 240 try: 241 input_zip.getinfo("VENDOR/") 242 has_vendor = True 243 except KeyError: 244 has_vendor = False 245 246 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 247 if "selinux_fc" in OPTIONS.info_dict: 248 OPTIONS.info_dict["selinux_fc"] = os.path.join( 249 OPTIONS.input_tmp, "BOOT", "RAMDISK", "file_contexts") 250 251 input_zip.close() 252 output_zip = zipfile.ZipFile(filename, "a", 253 compression=zipfile.ZIP_DEFLATED) 254 255 def banner(s): 256 print "\n\n++++ " + s + " ++++\n\n" 257 258 banner("boot") 259 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img") 260 boot_image = None 261 if os.path.exists(prebuilt_path): 262 print "boot.img already exists in IMAGES/, no need to rebuild..." 263 if OPTIONS.rebuild_recovery: 264 boot_image = common.GetBootableImage( 265 "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 266 else: 267 boot_image = common.GetBootableImage( 268 "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 269 if boot_image: 270 boot_image.AddToZip(output_zip) 271 272 banner("recovery") 273 recovery_image = None 274 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img") 275 if os.path.exists(prebuilt_path): 276 print "recovery.img already exists in IMAGES/, no need to rebuild..." 277 if OPTIONS.rebuild_recovery: 278 recovery_image = common.GetBootableImage( 279 "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") 280 else: 281 recovery_image = common.GetBootableImage( 282 "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") 283 if recovery_image: 284 recovery_image.AddToZip(output_zip) 285 286 banner("system") 287 AddSystem(output_zip, recovery_img=recovery_image, boot_img=boot_image) 288 if has_vendor: 289 banner("vendor") 290 AddVendor(output_zip) 291 banner("userdata") 292 AddUserdata(output_zip) 293 banner("cache") 294 AddCache(output_zip) 295 296 output_zip.close() 297 298 def main(argv): 299 300 def option_handler(o, a): 301 if o in ("-a", "--add_missing"): 302 OPTIONS.add_missing = True 303 elif o in ("-r", "--rebuild_recovery",): 304 OPTIONS.rebuild_recovery = True 305 else: 306 return False 307 return True 308 309 args = common.ParseOptions(argv, __doc__, 310 extra_opts="ar", 311 extra_long_opts=["add_missing", 312 "rebuild_recovery", 313 ], 314 extra_option_handler=option_handler) 315 316 317 if len(args) != 1: 318 common.Usage(__doc__) 319 sys.exit(1) 320 321 AddImagesToTargetFiles(args[0]) 322 print "done." 323 324 if __name__ == '__main__': 325 try: 326 common.CloseInheritedPipes() 327 main(sys.argv[1:]) 328 except common.ExternalError, e: 329 print 330 print " ERROR: %s" % (e,) 331 print 332 sys.exit(1) 333 finally: 334 common.Cleanup() 335