add_img_to_target_files.py revision 2a04839aecb1cbdcf5ca9e76ebfce9448eec0e13
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"""
18Given a target-files zipfile that does not contain images (ie, does
19not have an IMAGES/ top-level subdirectory), produce the images and
20add them to the zipfile.
21
22Usage:  add_img_to_target_files target_files
23"""
24
25import sys
26
27if sys.hexversion < 0x02070000:
28  print >> sys.stderr, "Python 2.7 or newer is required."
29  sys.exit(1)
30
31import errno
32import os
33import shutil
34import tempfile
35import zipfile
36
37import build_image
38import common
39
40OPTIONS = common.OPTIONS
41
42OPTIONS.add_missing = False
43OPTIONS.rebuild_recovery = False
44
45def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None):
46  """Turn the contents of SYSTEM into a system image and store it in
47  output_zip."""
48
49  prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "system.img")
50  if os.path.exists(prebuilt_path):
51    print "system.img already exists in %s, no need to rebuild..." % (prefix,)
52    return
53
54  def output_sink(fn, data):
55    ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
56    ofile.write(data)
57    ofile.close()
58
59  if OPTIONS.rebuild_recovery:
60    print "Building new recovery patch"
61    common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
62                             boot_img, info_dict=OPTIONS.info_dict)
63
64  block_list = common.MakeTempFile(prefix="system-blocklist-", suffix=".map")
65  imgname = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict,
66                        block_list=block_list)
67  common.ZipWrite(output_zip, imgname, prefix + "system.img")
68  common.ZipWrite(output_zip, block_list, prefix + "system.map")
69
70
71def BuildSystem(input_dir, info_dict, block_list=None):
72  """Build the (sparse) system image and return the name of a temp
73  file containing it."""
74  return CreateImage(input_dir, info_dict, "system", block_list=block_list)
75
76
77def AddVendor(output_zip, prefix="IMAGES/"):
78  """Turn the contents of VENDOR into a vendor image and store in it
79  output_zip."""
80
81  prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "vendor.img")
82  if os.path.exists(prebuilt_path):
83    print "vendor.img already exists in %s, no need to rebuild..." % (prefix,)
84    return
85
86  block_list = common.MakeTempFile(prefix="vendor-blocklist-", suffix=".map")
87  imgname = BuildVendor(OPTIONS.input_tmp, OPTIONS.info_dict,
88                        block_list=block_list)
89  common.ZipWrite(output_zip, imgname, prefix + "vendor.img")
90  common.ZipWrite(output_zip, block_list, prefix + "vendor.map")
91
92
93def BuildVendor(input_dir, info_dict, block_list=None):
94  """Build the (sparse) vendor image and return the name of a temp
95  file containing it."""
96  return CreateImage(input_dir, info_dict, "vendor", block_list=block_list)
97
98
99def CreateImage(input_dir, info_dict, what, block_list=None):
100  print "creating " + what + ".img..."
101
102  img = common.MakeTempFile(prefix=what + "-", suffix=".img")
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(input_dir, what.upper()),
109               os.path.join(input_dir, what))
110  except OSError as e:
111    # bogus error on my mac version?
112    #   File "./build/tools/releasetools/img_from_target_files"
113    #     os.path.join(OPTIONS.input_tmp, "system"))
114    # OSError: [Errno 17] File exists
115    if e.errno == errno.EEXIST:
116      pass
117
118  image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
119  fstab = info_dict["fstab"]
120  if fstab:
121    image_props["fs_type"] = fstab["/" + what].fs_type
122
123  if what == "system":
124    fs_config_prefix = ""
125  else:
126    fs_config_prefix = what + "_"
127
128  fs_config = os.path.join(
129      input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
130  if not os.path.exists(fs_config):
131    fs_config = None
132
133  fc_config = os.path.join(input_dir, "BOOT/RAMDISK/file_contexts")
134  if not os.path.exists(fc_config):
135    fc_config = None
136
137  # Override values loaded from info_dict.
138  if fs_config:
139    image_props["fs_config"] = fs_config
140  if fc_config:
141    image_props["selinux_fc"] = fc_config
142  if block_list:
143    image_props["block_list"] = block_list
144  if image_props.get("system_root_image") == "true":
145    image_props["ramdisk_dir"] = os.path.join(input_dir, "BOOT/RAMDISK")
146    image_props["ramdisk_fs_config"] = os.path.join(
147        input_dir, "META/boot_filesystem_config.txt")
148
149  succ = build_image.BuildImage(os.path.join(input_dir, what),
150                                image_props, img)
151  assert succ, "build " + what + ".img image failed"
152
153  return img
154
155
156def AddUserdata(output_zip, prefix="IMAGES/"):
157  """Create a userdata image and store it in output_zip.
158
159  In most case we just create and store an empty userdata.img;
160  But the invoker can also request to create userdata.img with real
161  data from the target files, by setting "userdata_img_with_data=true"
162  in OPTIONS.info_dict.
163  """
164
165  prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "userdata.img")
166  if os.path.exists(prebuilt_path):
167    print "userdata.img already exists in %s, no need to rebuild..." % (prefix,)
168    return
169
170  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict,
171                                                    "data")
172  # We only allow yaffs to have a 0/missing partition_size.
173  # Extfs, f2fs must have a size. Skip userdata.img if no size.
174  if (not image_props.get("fs_type", "").startswith("yaffs") and
175      not image_props.get("partition_size")):
176    return
177
178  print "creating userdata.img..."
179
180  # The name of the directory it is making an image out of matters to
181  # mkyaffs2image.  So we create a temp dir, and within it we create an
182  # empty dir named "data", or a symlink to the DATA dir,
183  # and build the image from that.
184  temp_dir = tempfile.mkdtemp()
185  user_dir = os.path.join(temp_dir, "data")
186  empty = (OPTIONS.info_dict.get("userdata_img_with_data") != "true")
187  if empty:
188    # Create an empty dir.
189    os.mkdir(user_dir)
190  else:
191    # Symlink to the DATA dir.
192    os.symlink(os.path.join(OPTIONS.input_tmp, "DATA"),
193               user_dir)
194
195  img = tempfile.NamedTemporaryFile()
196
197  fstab = OPTIONS.info_dict["fstab"]
198  if fstab:
199    image_props["fs_type"] = fstab["/data"].fs_type
200  succ = build_image.BuildImage(user_dir, image_props, img.name)
201  assert succ, "build userdata.img image failed"
202
203  common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
204  common.ZipWrite(output_zip, img.name, prefix + "userdata.img")
205  img.close()
206  shutil.rmtree(temp_dir)
207
208
209def AddCache(output_zip, prefix="IMAGES/"):
210  """Create an empty cache image and store it in output_zip."""
211
212  prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "cache.img")
213  if os.path.exists(prebuilt_path):
214    print "cache.img already exists in %s, no need to rebuild..." % (prefix,)
215    return
216
217  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict,
218                                                    "cache")
219  # The build system has to explicitly request for cache.img.
220  if "fs_type" not in image_props:
221    return
222
223  print "creating cache.img..."
224
225  # The name of the directory it is making an image out of matters to
226  # mkyaffs2image.  So we create a temp dir, and within it we create an
227  # empty dir named "cache", and build the image from that.
228  temp_dir = tempfile.mkdtemp()
229  user_dir = os.path.join(temp_dir, "cache")
230  os.mkdir(user_dir)
231  img = tempfile.NamedTemporaryFile()
232
233  fstab = OPTIONS.info_dict["fstab"]
234  if fstab:
235    image_props["fs_type"] = fstab["/cache"].fs_type
236  succ = build_image.BuildImage(user_dir, image_props, img.name)
237  assert succ, "build cache.img image failed"
238
239  common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
240  common.ZipWrite(output_zip, img.name, prefix + "cache.img")
241  img.close()
242  os.rmdir(user_dir)
243  os.rmdir(temp_dir)
244
245
246def AddImagesToTargetFiles(filename):
247  OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)
248
249  if not OPTIONS.add_missing:
250    for n in input_zip.namelist():
251      if n.startswith("IMAGES/"):
252        print "target_files appears to already contain images."
253        sys.exit(1)
254
255  try:
256    input_zip.getinfo("VENDOR/")
257    has_vendor = True
258  except KeyError:
259    has_vendor = False
260
261  OPTIONS.info_dict = common.LoadInfoDict(input_zip)
262  if "selinux_fc" in OPTIONS.info_dict:
263    OPTIONS.info_dict["selinux_fc"] = os.path.join(
264        OPTIONS.input_tmp, "BOOT", "RAMDISK", "file_contexts")
265
266  common.ZipClose(input_zip)
267  output_zip = zipfile.ZipFile(filename, "a",
268                               compression=zipfile.ZIP_DEFLATED)
269
270  def banner(s):
271    print "\n\n++++ " + s + " ++++\n\n"
272
273  banner("boot")
274  prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
275  boot_image = None
276  if os.path.exists(prebuilt_path):
277    print "boot.img already exists in IMAGES/, no need to rebuild..."
278    if OPTIONS.rebuild_recovery:
279      boot_image = common.GetBootableImage(
280          "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
281  else:
282    boot_image = common.GetBootableImage(
283        "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
284    if boot_image:
285      boot_image.AddToZip(output_zip)
286
287  banner("recovery")
288  recovery_image = None
289  prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img")
290  if os.path.exists(prebuilt_path):
291    print "recovery.img already exists in IMAGES/, no need to rebuild..."
292    if OPTIONS.rebuild_recovery:
293      recovery_image = common.GetBootableImage(
294          "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
295  else:
296    recovery_image = common.GetBootableImage(
297        "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
298    if recovery_image:
299      recovery_image.AddToZip(output_zip)
300
301  banner("system")
302  AddSystem(output_zip, recovery_img=recovery_image, boot_img=boot_image)
303  if has_vendor:
304    banner("vendor")
305    AddVendor(output_zip)
306  banner("userdata")
307  AddUserdata(output_zip)
308  banner("cache")
309  AddCache(output_zip)
310
311  common.ZipClose(output_zip)
312
313def main(argv):
314  def option_handler(o, _):
315    if o in ("-a", "--add_missing"):
316      OPTIONS.add_missing = True
317    elif o in ("-r", "--rebuild_recovery",):
318      OPTIONS.rebuild_recovery = True
319    else:
320      return False
321    return True
322
323  args = common.ParseOptions(
324      argv, __doc__, extra_opts="ar",
325      extra_long_opts=["add_missing", "rebuild_recovery"],
326      extra_option_handler=option_handler)
327
328
329  if len(args) != 1:
330    common.Usage(__doc__)
331    sys.exit(1)
332
333  AddImagesToTargetFiles(args[0])
334  print "done."
335
336if __name__ == '__main__':
337  try:
338    common.CloseInheritedPipes()
339    main(sys.argv[1:])
340  except common.ExternalError as e:
341    print
342    print "   ERROR: %s" % (e,)
343    print
344    sys.exit(1)
345  finally:
346    common.Cleanup()
347