summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorEd Bartosh <ed.bartosh@linux.intel.com>2017-01-30 23:54:29 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2017-02-02 17:37:44 +0000
commitee10c2f1bb26f86aad141c2b622d9365af431cd7 (patch)
tree22b397351509129e7e11456e7802589062127d86 /scripts
parentdba8cf377c14665fa759779f9b1af094377cf0c6 (diff)
downloadpoky-ee10c2f1bb26f86aad141c2b622d9365af431cd7.tar.gz
wic: moved content of direct.py to direct_plugin
This move simplifies directory structure and makes further refactoring easier. The code from direct.py was used only in direct_plugin, so it's safe to move it there. [YOCTO #10619] (From OE-Core rev: a8f5ebb26183faa9af6eb72f4dabfcf83aa1e8d4) Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/lib/wic/imager/__init__.py0
-rw-r--r--scripts/lib/wic/imager/direct.py402
-rw-r--r--scripts/lib/wic/plugins/imager/direct_plugin.py396
3 files changed, 380 insertions, 418 deletions
diff --git a/scripts/lib/wic/imager/__init__.py b/scripts/lib/wic/imager/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/scripts/lib/wic/imager/__init__.py
+++ /dev/null
diff --git a/scripts/lib/wic/imager/direct.py b/scripts/lib/wic/imager/direct.py
deleted file mode 100644
index ff06b504ce..0000000000
--- a/scripts/lib/wic/imager/direct.py
+++ /dev/null
@@ -1,402 +0,0 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# Copyright (c) 2013, Intel Corporation.
5# All rights reserved.
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# DESCRIPTION
21# This implements the 'direct' image creator class for 'wic'
22#
23# AUTHORS
24# Tom Zanussi <tom.zanussi (at] linux.intel.com>
25#
26
27import os
28import shutil
29import uuid
30import tempfile
31
32from wic import msger
33from wic.utils.oe.misc import get_bitbake_var
34from wic.utils.partitionedfs import Image
35from wic.utils.errors import CreatorError, ImageError
36from wic.plugin import pluginmgr
37from wic.utils.oe.misc import exec_cmd, exec_native_cmd
38
39disk_methods = {
40 "do_install_disk":None,
41}
42
43class DiskImage():
44 """
45 A Disk backed by a file.
46 """
47 def __init__(self, device, size):
48 self.size = size
49 self.device = device
50 self.created = False
51
52 def exists(self):
53 return os.path.exists(self.device)
54
55 def create(self):
56 if self.created:
57 return
58 # create sparse disk image
59 with open(self.device, 'w') as sparse:
60 os.ftruncate(sparse.fileno(), self.size)
61
62 self.created = True
63
64class DirectImageCreator:
65 """
66 Installs a system into a file containing a partitioned disk image.
67
68 DirectImageCreator is an advanced ImageCreator subclass; an image
69 file is formatted with a partition table, each partition created
70 from a rootfs or other OpenEmbedded build artifact and dd'ed into
71 the virtual disk. The disk image can subsequently be dd'ed onto
72 media and used on actual hardware.
73 """
74
75 def __init__(self, image_name, ksobj, oe_builddir, image_output_dir,
76 rootfs_dir, bootimg_dir, kernel_dir, native_sysroot,
77 compressor, bmap=False):
78 """
79 Initialize a DirectImageCreator instance.
80
81 This method takes the same arguments as ImageCreator.__init__()
82 """
83 self.name = image_name
84 self.outdir = image_output_dir
85 self.workdir = tempfile.mktemp(prefix='wic')
86 self.ks = ksobj
87
88 self.__image = None
89 self.__disks = {}
90 self.__disk_format = "direct"
91 self._disk_names = []
92 self.ptable_format = self.ks.bootloader.ptable
93
94 self.oe_builddir = oe_builddir
95 self.rootfs_dir = rootfs_dir
96 self.bootimg_dir = bootimg_dir
97 self.kernel_dir = kernel_dir
98 self.native_sysroot = native_sysroot
99 self.compressor = compressor
100 self.bmap = bmap
101
102 def _get_part_num(self, num, parts):
103 """calculate the real partition number, accounting for partitions not
104 in the partition table and logical partitions
105 """
106 realnum = 0
107 for pnum, part in enumerate(parts, 1):
108 if not part.no_table:
109 realnum += 1
110 if pnum == num:
111 if part.no_table:
112 return 0
113 if self.ptable_format == 'msdos' and realnum > 3:
114 # account for logical partition numbering, ex. sda5..
115 return realnum + 1
116 return realnum
117
118 def _write_fstab(self, image_rootfs):
119 """overriden to generate fstab (temporarily) in rootfs. This is called
120 from _create, make sure it doesn't get called from
121 BaseImage.create()
122 """
123 if not image_rootfs:
124 return
125
126 fstab_path = image_rootfs + "/etc/fstab"
127 if not os.path.isfile(fstab_path):
128 return
129
130 with open(fstab_path) as fstab:
131 fstab_lines = fstab.readlines()
132
133 if self._update_fstab(fstab_lines, self._get_parts()):
134 shutil.copyfile(fstab_path, fstab_path + ".orig")
135
136 with open(fstab_path, "w") as fstab:
137 fstab.writelines(fstab_lines)
138
139 return fstab_path
140
141 def _update_fstab(self, fstab_lines, parts):
142 """Assume partition order same as in wks"""
143 updated = False
144 for num, part in enumerate(parts, 1):
145 pnum = self._get_part_num(num, parts)
146 if not pnum or not part.mountpoint \
147 or part.mountpoint in ("/", "/boot"):
148 continue
149
150 # mmc device partitions are named mmcblk0p1, mmcblk0p2..
151 prefix = 'p' if part.disk.startswith('mmcblk') else ''
152 device_name = "/dev/%s%s%d" % (part.disk, prefix, pnum)
153
154 opts = part.fsopts if part.fsopts else "defaults"
155 line = "\t".join([device_name, part.mountpoint, part.fstype,
156 opts, "0", "0"]) + "\n"
157
158 fstab_lines.append(line)
159 updated = True
160
161 return updated
162
163 def set_bootimg_dir(self, bootimg_dir):
164 """
165 Accessor for bootimg_dir, the actual location used for the source
166 of the bootimg. Should be set by source plugins (only if they
167 change the default bootimg source) so the correct info gets
168 displayed for print_outimage_info().
169 """
170 self.bootimg_dir = bootimg_dir
171
172 def _get_parts(self):
173 if not self.ks:
174 raise CreatorError("Failed to get partition info, "
175 "please check your kickstart setting.")
176
177 # Set a default partition if no partition is given out
178 if not self.ks.partitions:
179 partstr = "part / --size 1900 --ondisk sda --fstype=ext3"
180 args = partstr.split()
181 part = self.ks.parse(args[1:])
182 if part not in self.ks.partitions:
183 self.ks.partitions.append(part)
184
185 # partitions list from kickstart file
186 return self.ks.partitions
187
188 def _full_name(self, name, extention):
189 """ Construct full file name for a file we generate. """
190 return "%s-%s.%s" % (self.name, name, extention)
191
192 def _full_path(self, path, name, extention):
193 """ Construct full file path to a file we generate. """
194 return os.path.join(path, self._full_name(name, extention))
195
196 def get_default_source_plugin(self):
197 """
198 The default source plugin i.e. the plugin that's consulted for
199 overall image generation tasks outside of any particular
200 partition. For convenience, we just hang it off the
201 bootloader handler since it's the one non-partition object in
202 any setup. By default the default plugin is set to the same
203 plugin as the /boot partition; since we hang it off the
204 bootloader object, the default can be explicitly set using the
205 --source bootloader param.
206 """
207 return self.ks.bootloader.source
208
209 #
210 # Actual implemention
211 #
212 def create(self):
213 """
214 For 'wic', we already have our build artifacts - we just create
215 filesystems from the artifacts directly and combine them into
216 a partitioned image.
217 """
218 parts = self._get_parts()
219
220 self._image = Image(self.native_sysroot)
221
222 disk_ids = {}
223 for num, part in enumerate(parts, 1):
224 # as a convenience, set source to the boot partition source
225 # instead of forcing it to be set via bootloader --source
226 if not self.ks.bootloader.source and part.mountpoint == "/boot":
227 self.ks.bootloader.source = part.source
228
229 # generate parition UUIDs
230 if not part.uuid and part.use_uuid:
231 if self.ptable_format == 'gpt':
232 part.uuid = str(uuid.uuid4())
233 else: # msdos partition table
234 if part.disk not in disk_ids:
235 disk_ids[part.disk] = int.from_bytes(os.urandom(4), 'little')
236 disk_id = disk_ids[part.disk]
237 part.uuid = '%0x-%02d' % (disk_id, self._get_part_num(num, parts))
238
239 fstab_path = self._write_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
240
241 for part in parts:
242 # get rootfs size from bitbake variable if it's not set in .ks file
243 if not part.size:
244 # and if rootfs name is specified for the partition
245 image_name = self.rootfs_dir.get(part.rootfs_dir)
246 if image_name and os.path.sep not in image_name:
247 # Bitbake variable ROOTFS_SIZE is calculated in
248 # Image._get_rootfs_size method from meta/lib/oe/image.py
249 # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT,
250 # IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE
251 rsize_bb = get_bitbake_var('ROOTFS_SIZE', image_name)
252 if rsize_bb:
253 part.size = int(round(float(rsize_bb)))
254 # need to create the filesystems in order to get their
255 # sizes before we can add them and do the layout.
256 # Image.create() actually calls __format_disks() to create
257 # the disk images and carve out the partitions, then
258 # self.assemble() calls Image.assemble() which calls
259 # __write_partitition() for each partition to dd the fs
260 # into the partitions.
261 part.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir,
262 self.bootimg_dir, self.kernel_dir, self.native_sysroot)
263
264
265 self._image.add_partition(part.disk_size,
266 part.disk,
267 part.mountpoint,
268 part.source_file,
269 part.fstype,
270 part.label,
271 fsopts=part.fsopts,
272 boot=part.active,
273 align=part.align,
274 no_table=part.no_table,
275 part_type=part.part_type,
276 uuid=part.uuid,
277 system_id=part.system_id)
278
279 if fstab_path:
280 shutil.move(fstab_path + ".orig", fstab_path)
281
282 self._image.layout_partitions(self.ptable_format)
283
284 for disk_name, disk in self._image.disks.items():
285 full_path = self._full_path(self.workdir, disk_name, "direct")
286 msger.debug("Adding disk %s as %s with size %s bytes" \
287 % (disk_name, full_path, disk['min_size']))
288 disk_obj = DiskImage(full_path, disk['min_size'])
289 #self._disks[disk_name] = disk_obj
290 self._image.add_disk(disk_name, disk_obj, disk_ids.get(disk_name))
291
292 self._image.create()
293
294 def assemble(self):
295 """
296 Assemble partitions into disk image(s)
297 """
298 for disk_name, disk in self._image.disks.items():
299 full_path = self._full_path(self.workdir, disk_name, "direct")
300 msger.debug("Assembling disk %s as %s with size %s bytes" \
301 % (disk_name, full_path, disk['min_size']))
302 self._image.assemble(full_path)
303
304 def finalize(self):
305 """
306 Finalize the disk image.
307
308 For example, prepare the image to be bootable by e.g.
309 creating and installing a bootloader configuration.
310 """
311 source_plugin = self.get_default_source_plugin()
312 if source_plugin:
313 self._source_methods = pluginmgr.get_source_plugin_methods(source_plugin, disk_methods)
314 for disk_name, disk in self._image.disks.items():
315 self._source_methods["do_install_disk"](disk, disk_name, self,
316 self.workdir,
317 self.oe_builddir,
318 self.bootimg_dir,
319 self.kernel_dir,
320 self.native_sysroot)
321
322 for disk_name, disk in self._image.disks.items():
323 full_path = self._full_path(self.workdir, disk_name, "direct")
324 # Generate .bmap
325 if self.bmap:
326 msger.debug("Generating bmap file for %s" % disk_name)
327 exec_native_cmd("bmaptool create %s -o %s.bmap" % (full_path, full_path),
328 self.native_sysroot)
329 # Compress the image
330 if self.compressor:
331 msger.debug("Compressing disk %s with %s" % (disk_name, self.compressor))
332 exec_cmd("%s %s" % (self.compressor, full_path))
333
334 def print_outimage_info(self):
335 """
336 Print the image(s) and artifacts used, for the user.
337 """
338 msg = "The new image(s) can be found here:\n"
339
340 parts = self._get_parts()
341
342 for disk_name in self._image.disks:
343 extension = "direct" + {"gzip": ".gz",
344 "bzip2": ".bz2",
345 "xz": ".xz",
346 "": ""}.get(self.compressor)
347 full_path = self._full_path(self.outdir, disk_name, extension)
348 msg += ' %s\n\n' % full_path
349
350 msg += 'The following build artifacts were used to create the image(s):\n'
351 for part in parts:
352 if part.rootfs_dir is None:
353 continue
354 if part.mountpoint == '/':
355 suffix = ':'
356 else:
357 suffix = '["%s"]:' % (part.mountpoint or part.label)
358 msg += ' ROOTFS_DIR%s%s\n' % (suffix.ljust(20), part.rootfs_dir)
359
360 msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
361 msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
362 msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
363
364 msger.info(msg)
365
366 @property
367 def rootdev(self):
368 """
369 Get root device name to use as a 'root' parameter
370 in kernel command line.
371
372 Assume partition order same as in wks
373 """
374 parts = self._get_parts()
375 for num, part in enumerate(parts, 1):
376 if part.mountpoint == "/":
377 if part.uuid:
378 return "PARTUUID=%s" % part.uuid
379 else:
380 suffix = 'p' if part.disk.startswith('mmcblk') else ''
381 pnum = self._get_part_num(num, parts)
382 return "/dev/%s%s%-d" % (part.disk, suffix, pnum)
383
384 def cleanup(self):
385 if self._image:
386 try:
387 self._image.cleanup()
388 except ImageError as err:
389 msger.warning("%s" % err)
390
391 # Move results to the output dir
392 if not os.path.exists(self.outdir):
393 os.makedirs(self.outdir)
394
395 for fname in os.listdir(self.workdir):
396 path = os.path.join(self.workdir, fname)
397 if os.path.isfile(path):
398 shutil.move(path, os.path.join(self.outdir, fname))
399
400 # remove work directory
401 shutil.rmtree(self.workdir, ignore_errors=True)
402
diff --git a/scripts/lib/wic/plugins/imager/direct_plugin.py b/scripts/lib/wic/plugins/imager/direct_plugin.py
index e839d2feae..91a9792814 100644
--- a/scripts/lib/wic/plugins/imager/direct_plugin.py
+++ b/scripts/lib/wic/plugins/imager/direct_plugin.py
@@ -23,16 +23,23 @@
23# AUTHORS 23# AUTHORS
24# Tom Zanussi <tom.zanussi (at] linux.intel.com> 24# Tom Zanussi <tom.zanussi (at] linux.intel.com>
25# 25#
26import os
27import shutil
28import uuid
29import tempfile
30import time
26 31
27from time import strftime 32from time import strftime
28
29from os.path import basename, splitext 33from os.path import basename, splitext
30from wic.utils import errors
31from wic.ksparser import KickStart, KickStartError
32from wic import msger
33 34
34import wic.imager.direct as direct 35from wic import msger
36from wic.ksparser import KickStart, KickStartError
37from wic.plugin import pluginmgr
35from wic.pluginbase import ImagerPlugin 38from wic.pluginbase import ImagerPlugin
39from wic.utils import errors
40from wic.utils.errors import CreatorError, ImageError
41from wic.utils.oe.misc import get_bitbake_var, exec_cmd, exec_native_cmd
42from wic.utils.partitionedfs import Image
36 43
37class DirectPlugin(ImagerPlugin): 44class DirectPlugin(ImagerPlugin):
38 """ 45 """
@@ -87,16 +94,10 @@ class DirectPlugin(ImagerPlugin):
87 strftime("%Y%m%d%H%M")) 94 strftime("%Y%m%d%H%M"))
88 krootfs_dir = cls.__rootfs_dir_to_dict(rootfs_dir) 95 krootfs_dir = cls.__rootfs_dir_to_dict(rootfs_dir)
89 96
90 creator = direct.DirectImageCreator(image_name, 97 creator = DirectImageCreator(image_name, ksobj, oe_builddir,
91 ksobj, 98 image_output_dir, krootfs_dir,
92 oe_builddir, 99 bootimg_dir, kernel_dir, native_sysroot,
93 image_output_dir, 100 compressor, opts.bmap)
94 krootfs_dir,
95 bootimg_dir,
96 kernel_dir,
97 native_sysroot,
98 compressor,
99 opts.bmap)
100 try: 101 try:
101 creator.create() 102 creator.create()
102 creator.assemble() 103 creator.assemble()
@@ -108,4 +109,367 @@ class DirectPlugin(ImagerPlugin):
108 finally: 109 finally:
109 creator.cleanup() 110 creator.cleanup()
110 111
111 return 0 112disk_methods = {
113 "do_install_disk":None,
114}
115
116class DiskImage():
117 """
118 A Disk backed by a file.
119 """
120 def __init__(self, device, size):
121 self.size = size
122 self.device = device
123 self.created = False
124
125 def exists(self):
126 return os.path.exists(self.device)
127
128 def create(self):
129 if self.created:
130 return
131 # create sparse disk image
132 with open(self.device, 'w') as sparse:
133 os.ftruncate(sparse.fileno(), self.size)
134
135 self.created = True
136
137class DirectImageCreator:
138 """
139 Installs a system into a file containing a partitioned disk image.
140
141 DirectImageCreator is an advanced ImageCreator subclass; an image
142 file is formatted with a partition table, each partition created
143 from a rootfs or other OpenEmbedded build artifact and dd'ed into
144 the virtual disk. The disk image can subsequently be dd'ed onto
145 media and used on actual hardware.
146 """
147
148 def __init__(self, image_name, ksobj, oe_builddir, image_output_dir,
149 rootfs_dir, bootimg_dir, kernel_dir, native_sysroot,
150 compressor, bmap=False):
151 """
152 Initialize a DirectImageCreator instance.
153
154 This method takes the same arguments as ImageCreator.__init__()
155 """
156 self.name = image_name
157 self.outdir = image_output_dir
158 self.workdir = tempfile.mktemp(prefix='wic')
159 self.ks = ksobj
160
161 self.__image = None
162 self.__disks = {}
163 self.__disk_format = "direct"
164 self._disk_names = []
165 self.ptable_format = self.ks.bootloader.ptable
166
167 self.oe_builddir = oe_builddir
168 self.rootfs_dir = rootfs_dir
169 self.bootimg_dir = bootimg_dir
170 self.kernel_dir = kernel_dir
171 self.native_sysroot = native_sysroot
172 self.compressor = compressor
173 self.bmap = bmap
174
175 def _get_part_num(self, num, parts):
176 """calculate the real partition number, accounting for partitions not
177 in the partition table and logical partitions
178 """
179 realnum = 0
180 for pnum, part in enumerate(parts, 1):
181 if not part.no_table:
182 realnum += 1
183 if pnum == num:
184 if part.no_table:
185 return 0
186 if self.ptable_format == 'msdos' and realnum > 3:
187 # account for logical partition numbering, ex. sda5..
188 return realnum + 1
189 return realnum
190
191 def _write_fstab(self, image_rootfs):
192 """overriden to generate fstab (temporarily) in rootfs. This is called
193 from _create, make sure it doesn't get called from
194 BaseImage.create()
195 """
196 if not image_rootfs:
197 return
198
199 fstab_path = image_rootfs + "/etc/fstab"
200 if not os.path.isfile(fstab_path):
201 return
202
203 with open(fstab_path) as fstab:
204 fstab_lines = fstab.readlines()
205
206 if self._update_fstab(fstab_lines, self._get_parts()):
207 shutil.copyfile(fstab_path, fstab_path + ".orig")
208
209 with open(fstab_path, "w") as fstab:
210 fstab.writelines(fstab_lines)
211
212 return fstab_path
213
214 def _update_fstab(self, fstab_lines, parts):
215 """Assume partition order same as in wks"""
216 updated = False
217 for num, part in enumerate(parts, 1):
218 pnum = self._get_part_num(num, parts)
219 if not pnum or not part.mountpoint \
220 or part.mountpoint in ("/", "/boot"):
221 continue
222
223 # mmc device partitions are named mmcblk0p1, mmcblk0p2..
224 prefix = 'p' if part.disk.startswith('mmcblk') else ''
225 device_name = "/dev/%s%s%d" % (part.disk, prefix, pnum)
226
227 opts = part.fsopts if part.fsopts else "defaults"
228 line = "\t".join([device_name, part.mountpoint, part.fstype,
229 opts, "0", "0"]) + "\n"
230
231 fstab_lines.append(line)
232 updated = True
233
234 return updated
235
236 def set_bootimg_dir(self, bootimg_dir):
237 """
238 Accessor for bootimg_dir, the actual location used for the source
239 of the bootimg. Should be set by source plugins (only if they
240 change the default bootimg source) so the correct info gets
241 displayed for print_outimage_info().
242 """
243 self.bootimg_dir = bootimg_dir
244
245 def _get_parts(self):
246 if not self.ks:
247 raise CreatorError("Failed to get partition info, "
248 "please check your kickstart setting.")
249
250 # Set a default partition if no partition is given out
251 if not self.ks.partitions:
252 partstr = "part / --size 1900 --ondisk sda --fstype=ext3"
253 args = partstr.split()
254 part = self.ks.parse(args[1:])
255 if part not in self.ks.partitions:
256 self.ks.partitions.append(part)
257
258 # partitions list from kickstart file
259 return self.ks.partitions
260
261 def _full_name(self, name, extention):
262 """ Construct full file name for a file we generate. """
263 return "%s-%s.%s" % (self.name, name, extention)
264
265 def _full_path(self, path, name, extention):
266 """ Construct full file path to a file we generate. """
267 return os.path.join(path, self._full_name(name, extention))
268
269 def get_default_source_plugin(self):
270 """
271 The default source plugin i.e. the plugin that's consulted for
272 overall image generation tasks outside of any particular
273 partition. For convenience, we just hang it off the
274 bootloader handler since it's the one non-partition object in
275 any setup. By default the default plugin is set to the same
276 plugin as the /boot partition; since we hang it off the
277 bootloader object, the default can be explicitly set using the
278 --source bootloader param.
279 """
280 return self.ks.bootloader.source
281
282 #
283 # Actual implemention
284 #
285 def create(self):
286 """
287 For 'wic', we already have our build artifacts - we just create
288 filesystems from the artifacts directly and combine them into
289 a partitioned image.
290 """
291 parts = self._get_parts()
292
293 self._image = Image(self.native_sysroot)
294
295 disk_ids = {}
296 for num, part in enumerate(parts, 1):
297 # as a convenience, set source to the boot partition source
298 # instead of forcing it to be set via bootloader --source
299 if not self.ks.bootloader.source and part.mountpoint == "/boot":
300 self.ks.bootloader.source = part.source
301
302 # generate parition UUIDs
303 if not part.uuid and part.use_uuid:
304 if self.ptable_format == 'gpt':
305 part.uuid = str(uuid.uuid4())
306 else: # msdos partition table
307 if part.disk not in disk_ids:
308 disk_ids[part.disk] = int.from_bytes(os.urandom(4), 'little')
309 disk_id = disk_ids[part.disk]
310 part.uuid = '%0x-%02d' % (disk_id, self._get_part_num(num, parts))
311
312 fstab_path = self._write_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
313
314 for part in parts:
315 # get rootfs size from bitbake variable if it's not set in .ks file
316 if not part.size:
317 # and if rootfs name is specified for the partition
318 image_name = self.rootfs_dir.get(part.rootfs_dir)
319 if image_name and os.path.sep not in image_name:
320 # Bitbake variable ROOTFS_SIZE is calculated in
321 # Image._get_rootfs_size method from meta/lib/oe/image.py
322 # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT,
323 # IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE
324 rsize_bb = get_bitbake_var('ROOTFS_SIZE', image_name)
325 if rsize_bb:
326 part.size = int(round(float(rsize_bb)))
327 # need to create the filesystems in order to get their
328 # sizes before we can add them and do the layout.
329 # Image.create() actually calls __format_disks() to create
330 # the disk images and carve out the partitions, then
331 # self.assemble() calls Image.assemble() which calls
332 # __write_partitition() for each partition to dd the fs
333 # into the partitions.
334 part.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir,
335 self.bootimg_dir, self.kernel_dir, self.native_sysroot)
336
337
338 self._image.add_partition(part.disk_size,
339 part.disk,
340 part.mountpoint,
341 part.source_file,
342 part.fstype,
343 part.label,
344 fsopts=part.fsopts,
345 boot=part.active,
346 align=part.align,
347 no_table=part.no_table,
348 part_type=part.part_type,
349 uuid=part.uuid,
350 system_id=part.system_id)
351
352 if fstab_path:
353 shutil.move(fstab_path + ".orig", fstab_path)
354
355 self._image.layout_partitions(self.ptable_format)
356
357 for disk_name, disk in self._image.disks.items():
358 full_path = self._full_path(self.workdir, disk_name, "direct")
359 msger.debug("Adding disk %s as %s with size %s bytes" \
360 % (disk_name, full_path, disk['min_size']))
361 disk_obj = DiskImage(full_path, disk['min_size'])
362 #self._disks[disk_name] = disk_obj
363 self._image.add_disk(disk_name, disk_obj, disk_ids.get(disk_name))
364
365 self._image.create()
366
367 def assemble(self):
368 """
369 Assemble partitions into disk image(s)
370 """
371 for disk_name, disk in self._image.disks.items():
372 full_path = self._full_path(self.workdir, disk_name, "direct")
373 msger.debug("Assembling disk %s as %s with size %s bytes" \
374 % (disk_name, full_path, disk['min_size']))
375 self._image.assemble(full_path)
376
377 def finalize(self):
378 """
379 Finalize the disk image.
380
381 For example, prepare the image to be bootable by e.g.
382 creating and installing a bootloader configuration.
383 """
384 source_plugin = self.get_default_source_plugin()
385 if source_plugin:
386 self._source_methods = pluginmgr.get_source_plugin_methods(source_plugin, disk_methods)
387 for disk_name, disk in self._image.disks.items():
388 self._source_methods["do_install_disk"](disk, disk_name, self,
389 self.workdir,
390 self.oe_builddir,
391 self.bootimg_dir,
392 self.kernel_dir,
393 self.native_sysroot)
394
395 for disk_name, disk in self._image.disks.items():
396 full_path = self._full_path(self.workdir, disk_name, "direct")
397 # Generate .bmap
398 if self.bmap:
399 msger.debug("Generating bmap file for %s" % disk_name)
400 exec_native_cmd("bmaptool create %s -o %s.bmap" % (full_path, full_path),
401 self.native_sysroot)
402 # Compress the image
403 if self.compressor:
404 msger.debug("Compressing disk %s with %s" % (disk_name, self.compressor))
405 exec_cmd("%s %s" % (self.compressor, full_path))
406
407 def print_outimage_info(self):
408 """
409 Print the image(s) and artifacts used, for the user.
410 """
411 msg = "The new image(s) can be found here:\n"
412
413 parts = self._get_parts()
414
415 for disk_name in self._image.disks:
416 extension = "direct" + {"gzip": ".gz",
417 "bzip2": ".bz2",
418 "xz": ".xz",
419 "": ""}.get(self.compressor)
420 full_path = self._full_path(self.outdir, disk_name, extension)
421 msg += ' %s\n\n' % full_path
422
423 msg += 'The following build artifacts were used to create the image(s):\n'
424 for part in parts:
425 if part.rootfs_dir is None:
426 continue
427 if part.mountpoint == '/':
428 suffix = ':'
429 else:
430 suffix = '["%s"]:' % (part.mountpoint or part.label)
431 msg += ' ROOTFS_DIR%s%s\n' % (suffix.ljust(20), part.rootfs_dir)
432
433 msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
434 msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
435 msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
436
437 msger.info(msg)
438
439 @property
440 def rootdev(self):
441 """
442 Get root device name to use as a 'root' parameter
443 in kernel command line.
444
445 Assume partition order same as in wks
446 """
447 parts = self._get_parts()
448 for num, part in enumerate(parts, 1):
449 if part.mountpoint == "/":
450 if part.uuid:
451 return "PARTUUID=%s" % part.uuid
452 else:
453 suffix = 'p' if part.disk.startswith('mmcblk') else ''
454 pnum = self._get_part_num(num, parts)
455 return "/dev/%s%s%-d" % (part.disk, suffix, pnum)
456
457 def cleanup(self):
458 if self._image:
459 try:
460 self._image.cleanup()
461 except ImageError as err:
462 msger.warning("%s" % err)
463
464 # Move results to the output dir
465 if not os.path.exists(self.outdir):
466 os.makedirs(self.outdir)
467
468 for fname in os.listdir(self.workdir):
469 path = os.path.join(self.workdir, fname)
470 if os.path.isfile(path):
471 shutil.move(path, os.path.join(self.outdir, fname))
472
473 # remove work directory
474 shutil.rmtree(self.workdir, ignore_errors=True)
475