Home | History | Annotate | Download | only in releasetools
      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