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 common 51 52 OPTIONS = common.OPTIONS 53 54 def AddUserdata(output_zip): 55 """Create an empty userdata image and store it in output_zip.""" 56 57 print "creating userdata.img..." 58 59 # The name of the directory it is making an image out of matters to 60 # mkyaffs2image. So we create a temp dir, and within it we create an 61 # empty dir named "data", and build the image from that. 62 temp_dir = tempfile.mkdtemp() 63 user_dir = os.path.join(temp_dir, "data") 64 os.mkdir(user_dir) 65 img = tempfile.NamedTemporaryFile() 66 67 build_command = [] 68 fstab = OPTIONS.info_dict["fstab"] 69 if fstab and fstab["/data"].fs_type.startswith("ext"): 70 build_command = ["mkuserimg.sh"] 71 if "extfs_sparse_flag" in OPTIONS.info_dict: 72 build_command.append(OPTIONS.info_dict["extfs_sparse_flag"]) 73 build_command.extend([user_dir, img.name, 74 fstab["/data"].fs_type, "data"]) 75 if "userdata_size" in OPTIONS.info_dict: 76 build_command.append(str(OPTIONS.info_dict["userdata_size"])) 77 else: 78 build_command = ["mkyaffs2image", "-f"] 79 extra = OPTIONS.info_dict.get("mkyaffs2_extra_flags", None) 80 if extra: 81 build_command.extend(extra.split()) 82 build_command.append(user_dir) 83 build_command.append(img.name) 84 85 p = common.Run(build_command); 86 p.communicate() 87 assert p.returncode == 0, "build userdata.img image failed" 88 89 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) 90 output_zip.write(img.name, "userdata.img") 91 img.close() 92 os.rmdir(user_dir) 93 os.rmdir(temp_dir) 94 95 96 def AddSystem(output_zip): 97 """Turn the contents of SYSTEM into a system image and store it in 98 output_zip.""" 99 100 print "creating system.img..." 101 102 img = tempfile.NamedTemporaryFile() 103 104 # The name of the directory it is making an image out of matters to 105 # mkyaffs2image. It wants "system" but we have a directory named 106 # "SYSTEM", so create a symlink. 107 try: 108 os.symlink(os.path.join(OPTIONS.input_tmp, "SYSTEM"), 109 os.path.join(OPTIONS.input_tmp, "system")) 110 except OSError, e: 111 # bogus error on my mac version? 112 # File "./build/tools/releasetools/img_from_target_files", line 86, in AddSystem 113 # os.path.join(OPTIONS.input_tmp, "system")) 114 # OSError: [Errno 17] File exists 115 if (e.errno == errno.EEXIST): 116 pass 117 118 build_command = [] 119 fstab = OPTIONS.info_dict["fstab"] 120 if fstab and fstab["/system"].fs_type.startswith("ext"): 121 122 build_command = ["mkuserimg.sh"] 123 if "extfs_sparse_flag" in OPTIONS.info_dict: 124 build_command.append(OPTIONS.info_dict["extfs_sparse_flag"]) 125 build_command.extend([os.path.join(OPTIONS.input_tmp, "system"), img.name, 126 fstab["/system"].fs_type, "system"]) 127 if "system_size" in OPTIONS.info_dict: 128 build_command.append(str(OPTIONS.info_dict["system_size"])) 129 else: 130 build_command = ["mkyaffs2image", "-f"] 131 extra = OPTIONS.info_dict.get("mkyaffs2_extra_flags", None) 132 if extra: 133 build_command.extend(extra.split()) 134 build_command.append(os.path.join(OPTIONS.input_tmp, "system")) 135 build_command.append(img.name) 136 137 p = common.Run(build_command) 138 p.communicate() 139 assert p.returncode == 0, "build system.img image failed" 140 141 img.seek(os.SEEK_SET, 0) 142 data = img.read() 143 img.close() 144 145 common.CheckSize(data, "system.img", OPTIONS.info_dict) 146 common.ZipWriteStr(output_zip, "system.img", data) 147 148 149 def CopyInfo(output_zip): 150 """Copy the android-info.txt file from the input to the output.""" 151 output_zip.write(os.path.join(OPTIONS.input_tmp, "OTA", "android-info.txt"), 152 "android-info.txt") 153 154 155 def main(argv): 156 bootable_only = [False] 157 158 def option_handler(o, a): 159 if o in ("-b", "--board_config"): 160 pass # deprecated 161 if o in ("-z", "--bootable_zip"): 162 bootable_only[0] = True 163 else: 164 return False 165 return True 166 167 args = common.ParseOptions(argv, __doc__, 168 extra_opts="b:z", 169 extra_long_opts=["board_config=", 170 "bootable_zip"], 171 extra_option_handler=option_handler) 172 173 bootable_only = bootable_only[0] 174 175 if len(args) != 2: 176 common.Usage(__doc__) 177 sys.exit(1) 178 179 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0]) 180 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 181 182 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED) 183 184 common.GetBootableImage( 185 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT").AddToZip(output_zip) 186 common.GetBootableImage( 187 "recovery.img", "recovery.img", OPTIONS.input_tmp, 188 "RECOVERY").AddToZip(output_zip) 189 190 if not bootable_only: 191 AddSystem(output_zip) 192 AddUserdata(output_zip) 193 CopyInfo(output_zip) 194 195 print "cleaning up..." 196 output_zip.close() 197 shutil.rmtree(OPTIONS.input_tmp) 198 199 print "done." 200 201 202 if __name__ == '__main__': 203 try: 204 common.CloseInheritedPipes() 205 main(sys.argv[1:]) 206 except common.ExternalError, e: 207 print 208 print " ERROR: %s" % (e,) 209 print 210 sys.exit(1) 211