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