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 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