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