OSDN Git Service

DO NOT MERGE: Only use a fixed timestamp when packaging.
[android-x86/build.git] / tools / releasetools / add_img_to_target_files.py
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 datetime
32 import errno
33 import os
34 import tempfile
35 import zipfile
36
37 import build_image
38 import common
39
40 OPTIONS = common.OPTIONS
41
42 OPTIONS.add_missing = False
43 OPTIONS.rebuild_recovery = False
44 OPTIONS.replace_verity_public_key = False
45 OPTIONS.replace_verity_private_key = False
46 OPTIONS.verity_signer_path = None
47
48 def 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
74 def 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
80 def 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
96 def 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
102 def 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   # Use a fixed timestamp (01/01/2009) when packaging the image.
127   # Bug: 24377993
128   epoch = datetime.datetime.fromtimestamp(0)
129   timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
130   image_props["timestamp"] = int(timestamp)
131
132   if what == "system":
133     fs_config_prefix = ""
134   else:
135     fs_config_prefix = what + "_"
136
137   fs_config = os.path.join(
138       input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
139   if not os.path.exists(fs_config):
140     fs_config = None
141
142   fc_config = os.path.join(input_dir, "BOOT/RAMDISK/file_contexts")
143   if not os.path.exists(fc_config):
144     fc_config = None
145
146   # Override values loaded from info_dict.
147   if fs_config:
148     image_props["fs_config"] = fs_config
149   if fc_config:
150     image_props["selinux_fc"] = fc_config
151   if block_list:
152     image_props["block_list"] = block_list
153   if image_props.get("system_root_image") == "true":
154     image_props["ramdisk_dir"] = os.path.join(input_dir, "BOOT/RAMDISK")
155     image_props["ramdisk_fs_config"] = os.path.join(
156         input_dir, "META/boot_filesystem_config.txt")
157
158   succ = build_image.BuildImage(os.path.join(input_dir, what),
159                                 image_props, img)
160   assert succ, "build " + what + ".img image failed"
161
162   return img
163
164
165 def AddUserdata(output_zip, prefix="IMAGES/"):
166   """Create an empty userdata image and store it in output_zip."""
167
168   prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "userdata.img")
169   if os.path.exists(prebuilt_path):
170     print "userdata.img already exists in %s, no need to rebuild..." % (prefix,)
171     return
172
173   image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict,
174                                                     "data")
175   # We only allow yaffs to have a 0/missing partition_size.
176   # Extfs, f2fs must have a size. Skip userdata.img if no size.
177   if (not image_props.get("fs_type", "").startswith("yaffs") and
178       not image_props.get("partition_size")):
179     return
180
181   print "creating userdata.img..."
182
183   # Use a fixed timestamp (01/01/2009) when packaging the image.
184   # Bug: 24377993
185   epoch = datetime.datetime.fromtimestamp(0)
186   timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
187   image_props["timestamp"] = int(timestamp)
188
189   # The name of the directory it is making an image out of matters to
190   # mkyaffs2image.  So we create a temp dir, and within it we create an
191   # empty dir named "data", and build the image from that.
192   temp_dir = tempfile.mkdtemp()
193   user_dir = os.path.join(temp_dir, "data")
194   os.mkdir(user_dir)
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   os.rmdir(user_dir)
207   os.rmdir(temp_dir)
208
209
210 def AddCache(output_zip, prefix="IMAGES/"):
211   """Create an empty cache image and store it in output_zip."""
212
213   prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "cache.img")
214   if os.path.exists(prebuilt_path):
215     print "cache.img already exists in %s, no need to rebuild..." % (prefix,)
216     return
217
218   image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict,
219                                                     "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
253 def 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)
269   if "selinux_fc" in OPTIONS.info_dict:
270     OPTIONS.info_dict["selinux_fc"] = os.path.join(
271         OPTIONS.input_tmp, "BOOT", "RAMDISK", "file_contexts")
272
273   common.ZipClose(input_zip)
274   output_zip = zipfile.ZipFile(filename, "a",
275                                compression=zipfile.ZIP_DEFLATED)
276
277   def banner(s):
278     print "\n\n++++ " + s + " ++++\n\n"
279
280   banner("boot")
281   prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
282   boot_image = None
283   if os.path.exists(prebuilt_path):
284     print "boot.img already exists in IMAGES/, no need to rebuild..."
285     if OPTIONS.rebuild_recovery:
286       boot_image = common.GetBootableImage(
287           "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
288   else:
289     boot_image = common.GetBootableImage(
290         "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
291     if boot_image:
292       boot_image.AddToZip(output_zip)
293
294   banner("recovery")
295   recovery_image = None
296   prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img")
297   if os.path.exists(prebuilt_path):
298     print "recovery.img already exists in IMAGES/, no need to rebuild..."
299     if OPTIONS.rebuild_recovery:
300       recovery_image = common.GetBootableImage(
301           "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
302   else:
303     recovery_image = common.GetBootableImage(
304         "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
305     if recovery_image:
306       recovery_image.AddToZip(output_zip)
307
308   banner("system")
309   AddSystem(output_zip, recovery_img=recovery_image, boot_img=boot_image)
310   if has_vendor:
311     banner("vendor")
312     AddVendor(output_zip)
313   banner("userdata")
314   AddUserdata(output_zip)
315   banner("cache")
316   AddCache(output_zip)
317
318   common.ZipClose(output_zip)
319
320 def main(argv):
321   def option_handler(o, a):
322     if o in ("-a", "--add_missing"):
323       OPTIONS.add_missing = True
324     elif o in ("-r", "--rebuild_recovery",):
325       OPTIONS.rebuild_recovery = True
326     elif o == "--replace_verity_private_key":
327       OPTIONS.replace_verity_private_key = (True, a)
328     elif o == "--replace_verity_public_key":
329       OPTIONS.replace_verity_public_key = (True, a)
330     elif o == "--verity_signer_path":
331       OPTIONS.verity_signer_path = a
332     else:
333       return False
334     return True
335
336   args = common.ParseOptions(
337       argv, __doc__, extra_opts="ar",
338       extra_long_opts=["add_missing", "rebuild_recovery",
339                        "replace_verity_public_key=",
340                        "replace_verity_private_key=",
341                        "verity_signer_path="],
342       extra_option_handler=option_handler)
343
344
345   if len(args) != 1:
346     common.Usage(__doc__)
347     sys.exit(1)
348
349   AddImagesToTargetFiles(args[0])
350   print "done."
351
352 if __name__ == '__main__':
353   try:
354     common.CloseInheritedPipes()
355     main(sys.argv[1:])
356   except common.ExternalError as e:
357     print
358     print "   ERROR: %s" % (e,)
359     print
360     sys.exit(1)
361   finally:
362     common.Cleanup()