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 """ 18 Given a target-files zipfile, produces an image zipfile suitable for 19 use with 'fastboot update'. 20 21 Usage: 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 32 import sys 33 34 if sys.hexversion < 0x02040000: 35 print >> sys.stderr, "Python 2.4 or newer is required." 36 sys.exit(1) 37 38 import errno 39 import os 40 import re 41 import shutil 42 import subprocess 43 import tempfile 44 import zipfile 45 46 # missing in Python 2.4 and before 47 if not hasattr(os, "SEEK_SET"): 48 os.SEEK_SET = 0 49 50 import build_image 51 import common 52 53 OPTIONS = common.OPTIONS 54 55 56 def AddSystem(output_zip): 57 """Turn the contents of SYSTEM into a system image and store it in 58 output_zip.""" 59 60 print "creating system.img..." 61 62 img = tempfile.NamedTemporaryFile() 63 64 # The name of the directory it is making an image out of matters to 65 # mkyaffs2image. It wants "system" but we have a directory named 66 # "SYSTEM", so create a symlink. 67 try: 68 os.symlink(os.path.join(OPTIONS.input_tmp, "SYSTEM"), 69 os.path.join(OPTIONS.input_tmp, "system")) 70 except OSError, e: 71 # bogus error on my mac version? 72 # File "./build/tools/releasetools/img_from_target_files", line 86, in AddSystem 73 # os.path.join(OPTIONS.input_tmp, "system")) 74 # OSError: [Errno 17] File exists 75 if (e.errno == errno.EEXIST): 76 pass 77 78 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, 79 "system") 80 fstab = OPTIONS.info_dict["fstab"] 81 if fstab: 82 image_props["fs_type" ] = fstab["/system"].fs_type 83 succ = build_image.BuildImage(os.path.join(OPTIONS.input_tmp, "system"), 84 image_props, img.name) 85 assert succ, "build system.img image failed" 86 87 img.seek(os.SEEK_SET, 0) 88 data = img.read() 89 img.close() 90 91 common.CheckSize(data, "system.img", OPTIONS.info_dict) 92 common.ZipWriteStr(output_zip, "system.img", data) 93 94 95 def AddVendor(output_zip): 96 """Turn the contents of VENDOR into vendor.img and store it in 97 output_zip.""" 98 99 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, 100 "vendor") 101 # The build system has to explicitly request for vendor.img. 102 if "fs_type" not in image_props: 103 return 104 105 print "creating vendor.img..." 106 107 img = tempfile.NamedTemporaryFile() 108 109 # The name of the directory it is making an image out of matters to 110 # mkyaffs2image. It wants "vendor" but we have a directory named 111 # "VENDOR", so create a symlink or an empty directory if VENDOR does not 112 # exist. 113 if not os.path.exists(os.path.join(OPTIONS.input_tmp, "vendor")): 114 if os.path.exists(os.path.join(OPTIONS.input_tmp, "VENDOR")): 115 os.symlink(os.path.join(OPTIONS.input_tmp, "VENDOR"), 116 os.path.join(OPTIONS.input_tmp, "vendor")) 117 else: 118 os.mkdir(os.path.join(OPTIONS.input_tmp, "vendor")) 119 120 img = tempfile.NamedTemporaryFile() 121 122 fstab = OPTIONS.info_dict["fstab"] 123 if fstab: 124 image_props["fs_type" ] = fstab["/vendor"].fs_type 125 succ = build_image.BuildImage(os.path.join(OPTIONS.input_tmp, "vendor"), 126 image_props, img.name) 127 assert succ, "build vendor.img image failed" 128 129 common.CheckSize(img.name, "vendor.img", OPTIONS.info_dict) 130 output_zip.write(img.name, "vendor.img") 131 img.close() 132 133 134 def AddUserdata(output_zip): 135 """Create an empty userdata image and store it in output_zip.""" 136 137 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, 138 "data") 139 # If no userdata_size is provided for extfs, skip userdata.img. 140 if (image_props.get("fs_type", "").startswith("ext") and 141 not image_props.get("partition_size")): 142 return 143 144 print "creating userdata.img..." 145 146 # The name of the directory it is making an image out of matters to 147 # mkyaffs2image. So we create a temp dir, and within it we create an 148 # empty dir named "data", and build the image from that. 149 temp_dir = tempfile.mkdtemp() 150 user_dir = os.path.join(temp_dir, "data") 151 os.mkdir(user_dir) 152 img = tempfile.NamedTemporaryFile() 153 154 fstab = OPTIONS.info_dict["fstab"] 155 if fstab: 156 image_props["fs_type" ] = fstab["/data"].fs_type 157 succ = build_image.BuildImage(user_dir, image_props, img.name) 158 assert succ, "build userdata.img image failed" 159 160 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) 161 output_zip.write(img.name, "userdata.img") 162 img.close() 163 os.rmdir(user_dir) 164 os.rmdir(temp_dir) 165 166 167 def AddCache(output_zip): 168 """Create an empty cache image and store it in output_zip.""" 169 170 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, 171 "cache") 172 # The build system has to explicitly request for cache.img. 173 if "fs_type" not in image_props: 174 return 175 176 print "creating cache.img..." 177 178 # The name of the directory it is making an image out of matters to 179 # mkyaffs2image. So we create a temp dir, and within it we create an 180 # empty dir named "cache", and build the image from that. 181 temp_dir = tempfile.mkdtemp() 182 user_dir = os.path.join(temp_dir, "cache") 183 os.mkdir(user_dir) 184 img = tempfile.NamedTemporaryFile() 185 186 fstab = OPTIONS.info_dict["fstab"] 187 if fstab: 188 image_props["fs_type" ] = fstab["/cache"].fs_type 189 succ = build_image.BuildImage(user_dir, image_props, img.name) 190 assert succ, "build cache.img image failed" 191 192 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict) 193 output_zip.write(img.name, "cache.img") 194 img.close() 195 os.rmdir(user_dir) 196 os.rmdir(temp_dir) 197 198 199 def CopyInfo(output_zip): 200 """Copy the android-info.txt file from the input to the output.""" 201 output_zip.write(os.path.join(OPTIONS.input_tmp, "OTA", "android-info.txt"), 202 "android-info.txt") 203 204 205 def main(argv): 206 bootable_only = [False] 207 208 def option_handler(o, a): 209 if o in ("-b", "--board_config"): 210 pass # deprecated 211 if o in ("-z", "--bootable_zip"): 212 bootable_only[0] = True 213 else: 214 return False 215 return True 216 217 args = common.ParseOptions(argv, __doc__, 218 extra_opts="b:z", 219 extra_long_opts=["board_config=", 220 "bootable_zip"], 221 extra_option_handler=option_handler) 222 223 bootable_only = bootable_only[0] 224 225 if len(args) != 2: 226 common.Usage(__doc__) 227 sys.exit(1) 228 229 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0]) 230 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 231 232 # If this image was originally labelled with SELinux contexts, make sure we 233 # also apply the labels in our new image. During building, the "file_contexts" 234 # is in the out/ directory tree, but for repacking from target-files.zip it's 235 # in the root directory of the ramdisk. 236 if "selinux_fc" in OPTIONS.info_dict: 237 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK", 238 "file_contexts") 239 240 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED) 241 242 common.GetBootableImage( 243 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT").AddToZip(output_zip) 244 common.GetBootableImage( 245 "recovery.img", "recovery.img", OPTIONS.input_tmp, 246 "RECOVERY").AddToZip(output_zip) 247 248 if not bootable_only: 249 AddSystem(output_zip) 250 AddVendor(output_zip) 251 AddUserdata(output_zip) 252 AddCache(output_zip) 253 CopyInfo(output_zip) 254 255 print "cleaning up..." 256 output_zip.close() 257 shutil.rmtree(OPTIONS.input_tmp) 258 259 print "done." 260 261 262 if __name__ == '__main__': 263 try: 264 common.CloseInheritedPipes() 265 main(sys.argv[1:]) 266 except common.ExternalError, e: 267 print 268 print " ERROR: %s" % (e,) 269 print 270 sys.exit(1) 271