Home | History | Annotate | Download | only in releasetools
      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 tempfile
     34 import zipfile
     35 
     36 import build_image
     37 import common
     38 
     39 OPTIONS = common.OPTIONS
     40 
     41 OPTIONS.add_missing = False
     42 OPTIONS.rebuild_recovery = False
     43 
     44 def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None):
     45   """Turn the contents of SYSTEM into a system image and store it in
     46   output_zip."""
     47 
     48   prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "system.img")
     49   if os.path.exists(prebuilt_path):
     50     print "system.img already exists in %s, no need to rebuild..." % (prefix,)
     51     return
     52 
     53   def output_sink(fn, data):
     54     ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
     55     ofile.write(data)
     56     ofile.close()
     57 
     58   if OPTIONS.rebuild_recovery:
     59     print "Building new recovery patch"
     60     common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
     61                              boot_img, info_dict=OPTIONS.info_dict)
     62 
     63   block_list = common.MakeTempFile(prefix="system-blocklist-", suffix=".map")
     64   imgname = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict,
     65                         block_list=block_list)
     66   common.ZipWrite(output_zip, imgname, prefix + "system.img")
     67   common.ZipWrite(output_zip, block_list, prefix + "system.map")
     68 
     69 
     70 def BuildSystem(input_dir, info_dict, block_list=None):
     71   """Build the (sparse) system image and return the name of a temp
     72   file containing it."""
     73   return CreateImage(input_dir, info_dict, "system", block_list=block_list)
     74 
     75 
     76 def AddVendor(output_zip, prefix="IMAGES/"):
     77   """Turn the contents of VENDOR into a vendor image and store in it
     78   output_zip."""
     79 
     80   prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "vendor.img")
     81   if os.path.exists(prebuilt_path):
     82     print "vendor.img already exists in %s, no need to rebuild..." % (prefix,)
     83     return
     84 
     85   block_list = common.MakeTempFile(prefix="vendor-blocklist-", suffix=".map")
     86   imgname = BuildVendor(OPTIONS.input_tmp, OPTIONS.info_dict,
     87                         block_list=block_list)
     88   common.ZipWrite(output_zip, imgname, prefix + "vendor.img")
     89   common.ZipWrite(output_zip, block_list, prefix + "vendor.map")
     90 
     91 
     92 def BuildVendor(input_dir, info_dict, block_list=None):
     93   """Build the (sparse) vendor image and return the name of a temp
     94   file containing it."""
     95   return CreateImage(input_dir, info_dict, "vendor", block_list=block_list)
     96 
     97 
     98 def CreateImage(input_dir, info_dict, what, block_list=None):
     99   print "creating " + what + ".img..."
    100 
    101   img = common.MakeTempFile(prefix=what + "-", suffix=".img")
    102 
    103   # The name of the directory it is making an image out of matters to
    104   # mkyaffs2image.  It wants "system" but we have a directory named
    105   # "SYSTEM", so create a symlink.
    106   try:
    107     os.symlink(os.path.join(input_dir, what.upper()),
    108                os.path.join(input_dir, what))
    109   except OSError as e:
    110     # bogus error on my mac version?
    111     #   File "./build/tools/releasetools/img_from_target_files"
    112     #     os.path.join(OPTIONS.input_tmp, "system"))
    113     # OSError: [Errno 17] File exists
    114     if e.errno == errno.EEXIST:
    115       pass
    116 
    117   image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
    118   fstab = info_dict["fstab"]
    119   if fstab:
    120     image_props["fs_type"] = fstab["/" + what].fs_type
    121 
    122   if what == "system":
    123     fs_config_prefix = ""
    124   else:
    125     fs_config_prefix = what + "_"
    126 
    127   fs_config = os.path.join(
    128       input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
    129   if not os.path.exists(fs_config):
    130     fs_config = None
    131 
    132   fc_config = os.path.join(input_dir, "BOOT/RAMDISK/file_contexts")
    133   if not os.path.exists(fc_config):
    134     fc_config = None
    135 
    136   # Override values loaded from info_dict.
    137   if fs_config:
    138     image_props["fs_config"] = fs_config
    139   if fc_config:
    140     image_props["selinux_fc"] = fc_config
    141   if block_list:
    142     image_props["block_list"] = block_list
    143   if image_props.get("system_root_image") == "true":
    144     image_props["ramdisk_dir"] = os.path.join(input_dir, "BOOT/RAMDISK")
    145     image_props["ramdisk_fs_config"] = os.path.join(
    146         input_dir, "META/boot_filesystem_config.txt")
    147 
    148   succ = build_image.BuildImage(os.path.join(input_dir, what),
    149                                 image_props, img)
    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   common.ZipWrite(output_zip, 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   common.ZipWrite(output_zip, 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   common.ZipClose(input_zip)
    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   common.ZipClose(output_zip)
    297 
    298 def main(argv):
    299   def option_handler(o, _):
    300     if o in ("-a", "--add_missing"):
    301       OPTIONS.add_missing = True
    302     elif o in ("-r", "--rebuild_recovery",):
    303       OPTIONS.rebuild_recovery = True
    304     else:
    305       return False
    306     return True
    307 
    308   args = common.ParseOptions(
    309       argv, __doc__, extra_opts="ar",
    310       extra_long_opts=["add_missing", "rebuild_recovery"],
    311       extra_option_handler=option_handler)
    312 
    313 
    314   if len(args) != 1:
    315     common.Usage(__doc__)
    316     sys.exit(1)
    317 
    318   AddImagesToTargetFiles(args[0])
    319   print "done."
    320 
    321 if __name__ == '__main__':
    322   try:
    323     common.CloseInheritedPipes()
    324     main(sys.argv[1:])
    325   except common.ExternalError as e:
    326     print
    327     print "   ERROR: %s" % (e,)
    328     print
    329     sys.exit(1)
    330   finally:
    331     common.Cleanup()
    332