blob: 8cccc2ed5f08e698470575a71b4fdc9e05904c6e [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassbf7fd502016-11-25 20:15:51 -07002# Copyright (c) 2016 Google, Inc
3#
Simon Glassbf7fd502016-11-25 20:15:51 -07004# Base class for all entries
5#
6
Simon Glass3b0c3822018-06-01 09:38:20 -06007from __future__ import print_function
8
Simon Glass53af22a2018-07-17 13:25:32 -06009from collections import namedtuple
10
Simon Glassbf7fd502016-11-25 20:15:51 -070011# importlib was introduced in Python 2.7 but there was a report of it not
12# working in 2.7.12, so we work around this:
13# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
14try:
15 import importlib
16 have_importlib = True
17except:
18 have_importlib = False
19
Simon Glassbadf0ec2018-06-01 09:38:15 -060020import os
21import sys
Simon Glassc55a50f2018-09-14 04:57:19 -060022
23import fdt_util
24import state
Simon Glassbf7fd502016-11-25 20:15:51 -070025import tools
26
27modules = {}
28
Simon Glassbadf0ec2018-06-01 09:38:15 -060029our_path = os.path.dirname(os.path.realpath(__file__))
30
Simon Glass53af22a2018-07-17 13:25:32 -060031
32# An argument which can be passed to entries on the command line, in lieu of
33# device-tree properties.
34EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
35
36
Simon Glassbf7fd502016-11-25 20:15:51 -070037class Entry(object):
Simon Glass25ac0e62018-06-01 09:38:14 -060038 """An Entry in the section
Simon Glassbf7fd502016-11-25 20:15:51 -070039
40 An entry corresponds to a single node in the device-tree description
Simon Glass25ac0e62018-06-01 09:38:14 -060041 of the section. Each entry ends up being a part of the final section.
Simon Glassbf7fd502016-11-25 20:15:51 -070042 Entries can be placed either right next to each other, or with padding
43 between them. The type of the entry determines the data that is in it.
44
45 This class is not used by itself. All entry objects are subclasses of
46 Entry.
47
48 Attributes:
Simon Glass8122f392018-07-17 13:25:28 -060049 section: Section object containing this entry
Simon Glassbf7fd502016-11-25 20:15:51 -070050 node: The node that created this entry
Simon Glass3ab95982018-08-01 15:22:37 -060051 offset: Offset of entry within the section, None if not known yet (in
52 which case it will be calculated by Pack())
Simon Glassbf7fd502016-11-25 20:15:51 -070053 size: Entry size in bytes, None if not known
Simon Glass8287ee82019-07-08 14:25:30 -060054 uncomp_size: Size of uncompressed data in bytes, if the entry is
55 compressed, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070056 contents_size: Size of contents in bytes, 0 by default
Simon Glass3ab95982018-08-01 15:22:37 -060057 align: Entry start offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070058 align_size: Entry size alignment, or None
Simon Glass3ab95982018-08-01 15:22:37 -060059 align_end: Entry end offset alignment, or None
Simon Glassbf7fd502016-11-25 20:15:51 -070060 pad_before: Number of pad bytes before the contents, 0 if none
61 pad_after: Number of pad bytes after the contents, 0 if none
62 data: Contents of entry (string of bytes)
Simon Glass8287ee82019-07-08 14:25:30 -060063 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
Simon Glassbf7fd502016-11-25 20:15:51 -070064 """
Simon Glassc8d48ef2018-06-01 09:38:21 -060065 def __init__(self, section, etype, node, read_node=True, name_prefix=''):
Simon Glass25ac0e62018-06-01 09:38:14 -060066 self.section = section
Simon Glassbf7fd502016-11-25 20:15:51 -070067 self.etype = etype
68 self._node = node
Simon Glassc8d48ef2018-06-01 09:38:21 -060069 self.name = node and (name_prefix + node.name) or 'none'
Simon Glass3ab95982018-08-01 15:22:37 -060070 self.offset = None
Simon Glassbf7fd502016-11-25 20:15:51 -070071 self.size = None
Simon Glass8287ee82019-07-08 14:25:30 -060072 self.uncomp_size = None
Simon Glass24d0d3c2018-07-17 13:25:47 -060073 self.data = None
Simon Glassbf7fd502016-11-25 20:15:51 -070074 self.contents_size = 0
75 self.align = None
76 self.align_size = None
77 self.align_end = None
78 self.pad_before = 0
79 self.pad_after = 0
Simon Glass3ab95982018-08-01 15:22:37 -060080 self.offset_unset = False
Simon Glassdbf6be92018-08-01 15:22:42 -060081 self.image_pos = None
Simon Glassba64a0b2018-09-14 04:57:29 -060082 self._expand_size = False
Simon Glass8287ee82019-07-08 14:25:30 -060083 self.compress = 'none'
Simon Glassbf7fd502016-11-25 20:15:51 -070084 if read_node:
85 self.ReadNode()
86
87 @staticmethod
Simon Glassfd8d1f72018-07-17 13:25:36 -060088 def Lookup(section, node_path, etype):
89 """Look up the entry class for a node.
Simon Glassbf7fd502016-11-25 20:15:51 -070090
91 Args:
Simon Glassfd8d1f72018-07-17 13:25:36 -060092 section: Section object containing this node
93 node_node: Path name of Node object containing information about
94 the entry to create (used for errors)
95 etype: Entry type to use
Simon Glassbf7fd502016-11-25 20:15:51 -070096
97 Returns:
Simon Glassfd8d1f72018-07-17 13:25:36 -060098 The entry class object if found, else None
Simon Glassbf7fd502016-11-25 20:15:51 -070099 """
Simon Glassdd57c132018-06-01 09:38:11 -0600100 # Convert something like 'u-boot@0' to 'u_boot' since we are only
101 # interested in the type.
Simon Glassbf7fd502016-11-25 20:15:51 -0700102 module_name = etype.replace('-', '_')
Simon Glassdd57c132018-06-01 09:38:11 -0600103 if '@' in module_name:
104 module_name = module_name.split('@')[0]
Simon Glassbf7fd502016-11-25 20:15:51 -0700105 module = modules.get(module_name)
106
Simon Glassbadf0ec2018-06-01 09:38:15 -0600107 # Also allow entry-type modules to be brought in from the etype directory.
108
Simon Glassbf7fd502016-11-25 20:15:51 -0700109 # Import the module if we have not already done so.
110 if not module:
Simon Glassbadf0ec2018-06-01 09:38:15 -0600111 old_path = sys.path
112 sys.path.insert(0, os.path.join(our_path, 'etype'))
Simon Glassbf7fd502016-11-25 20:15:51 -0700113 try:
114 if have_importlib:
115 module = importlib.import_module(module_name)
116 else:
117 module = __import__(module_name)
Simon Glassfd8d1f72018-07-17 13:25:36 -0600118 except ImportError as e:
119 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
120 (etype, node_path, module_name, e))
Simon Glassbadf0ec2018-06-01 09:38:15 -0600121 finally:
122 sys.path = old_path
Simon Glassbf7fd502016-11-25 20:15:51 -0700123 modules[module_name] = module
124
Simon Glassfd8d1f72018-07-17 13:25:36 -0600125 # Look up the expected class name
126 return getattr(module, 'Entry_%s' % module_name)
127
128 @staticmethod
129 def Create(section, node, etype=None):
130 """Create a new entry for a node.
131
132 Args:
133 section: Section object containing this node
134 node: Node object containing information about the entry to
135 create
136 etype: Entry type to use, or None to work it out (used for tests)
137
138 Returns:
139 A new Entry object of the correct type (a subclass of Entry)
140 """
141 if not etype:
142 etype = fdt_util.GetString(node, 'type', node.name)
143 obj = Entry.Lookup(section, node.path, etype)
144
Simon Glassbf7fd502016-11-25 20:15:51 -0700145 # Call its constructor to get the object we want.
Simon Glass25ac0e62018-06-01 09:38:14 -0600146 return obj(section, etype, node)
Simon Glassbf7fd502016-11-25 20:15:51 -0700147
148 def ReadNode(self):
149 """Read entry information from the node
150
151 This reads all the fields we recognise from the node, ready for use.
152 """
Simon Glass15a587c2018-07-17 13:25:51 -0600153 if 'pos' in self._node.props:
154 self.Raise("Please use 'offset' instead of 'pos'")
Simon Glass3ab95982018-08-01 15:22:37 -0600155 self.offset = fdt_util.GetInt(self._node, 'offset')
Simon Glassbf7fd502016-11-25 20:15:51 -0700156 self.size = fdt_util.GetInt(self._node, 'size')
157 self.align = fdt_util.GetInt(self._node, 'align')
158 if tools.NotPowerOfTwo(self.align):
159 raise ValueError("Node '%s': Alignment %s must be a power of two" %
160 (self._node.path, self.align))
161 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
162 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
163 self.align_size = fdt_util.GetInt(self._node, 'align-size')
164 if tools.NotPowerOfTwo(self.align_size):
165 raise ValueError("Node '%s': Alignment size %s must be a power "
166 "of two" % (self._node.path, self.align_size))
167 self.align_end = fdt_util.GetInt(self._node, 'align-end')
Simon Glass3ab95982018-08-01 15:22:37 -0600168 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
Simon Glassba64a0b2018-09-14 04:57:29 -0600169 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
Simon Glassbf7fd502016-11-25 20:15:51 -0700170
Simon Glass6c234bf2018-09-14 04:57:18 -0600171 def GetDefaultFilename(self):
172 return None
173
Simon Glass539aece2018-09-14 04:57:22 -0600174 def GetFdtSet(self):
175 """Get the set of device trees used by this entry
176
177 Returns:
178 Set containing the filename from this entry, if it is a .dtb, else
179 an empty set
180 """
181 fname = self.GetDefaultFilename()
182 # It would be better to use isinstance(self, Entry_blob_dtb) here but
183 # we cannot access Entry_blob_dtb
184 if fname and fname.endswith('.dtb'):
Simon Glassd141f6c2019-05-14 15:53:39 -0600185 return set([fname])
186 return set()
Simon Glass539aece2018-09-14 04:57:22 -0600187
Simon Glass0a98b282018-09-14 04:57:28 -0600188 def ExpandEntries(self):
189 pass
190
Simon Glass078ab1a2018-07-06 10:27:41 -0600191 def AddMissingProperties(self):
192 """Add new properties to the device tree as needed for this entry"""
Simon Glassdbf6be92018-08-01 15:22:42 -0600193 for prop in ['offset', 'size', 'image-pos']:
Simon Glass078ab1a2018-07-06 10:27:41 -0600194 if not prop in self._node.props:
Simon Glassf46621d2018-09-14 04:57:21 -0600195 state.AddZeroProp(self._node, prop)
Simon Glass8287ee82019-07-08 14:25:30 -0600196 if self.compress != 'none':
197 state.AddZeroProp(self._node, 'uncomp-size')
Simon Glasse0e5df92018-09-14 04:57:31 -0600198 err = state.CheckAddHashProp(self._node)
199 if err:
200 self.Raise(err)
Simon Glass078ab1a2018-07-06 10:27:41 -0600201
202 def SetCalculatedProperties(self):
203 """Set the value of device-tree properties calculated by binman"""
Simon Glassf46621d2018-09-14 04:57:21 -0600204 state.SetInt(self._node, 'offset', self.offset)
205 state.SetInt(self._node, 'size', self.size)
Simon Glassf8f8df62018-09-14 04:57:34 -0600206 state.SetInt(self._node, 'image-pos',
207 self.image_pos - self.section.GetRootSkipAtStart())
Simon Glass8287ee82019-07-08 14:25:30 -0600208 if self.uncomp_size is not None:
209 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
Simon Glasse0e5df92018-09-14 04:57:31 -0600210 state.CheckSetHashValue(self._node, self.GetData)
Simon Glass078ab1a2018-07-06 10:27:41 -0600211
Simon Glassecab8972018-07-06 10:27:40 -0600212 def ProcessFdt(self, fdt):
Simon Glass6ed45ba2018-09-14 04:57:24 -0600213 """Allow entries to adjust the device tree
214
215 Some entries need to adjust the device tree for their purposes. This
216 may involve adding or deleting properties.
217
218 Returns:
219 True if processing is complete
220 False if processing could not be completed due to a dependency.
221 This will cause the entry to be retried after others have been
222 called
223 """
Simon Glassecab8972018-07-06 10:27:40 -0600224 return True
225
Simon Glassc8d48ef2018-06-01 09:38:21 -0600226 def SetPrefix(self, prefix):
227 """Set the name prefix for a node
228
229 Args:
230 prefix: Prefix to set, or '' to not use a prefix
231 """
232 if prefix:
233 self.name = prefix + self.name
234
Simon Glass5c890232018-07-06 10:27:19 -0600235 def SetContents(self, data):
236 """Set the contents of an entry
237
238 This sets both the data and content_size properties
239
240 Args:
241 data: Data to set to the contents (string)
242 """
243 self.data = data
244 self.contents_size = len(self.data)
245
246 def ProcessContentsUpdate(self, data):
247 """Update the contens of an entry, after the size is fixed
248
249 This checks that the new data is the same size as the old.
250
251 Args:
252 data: Data to set to the contents (string)
253
254 Raises:
255 ValueError if the new data size is not the same as the old
256 """
257 if len(data) != self.contents_size:
258 self.Raise('Cannot update entry size from %d to %d' %
259 (len(data), self.contents_size))
260 self.SetContents(data)
261
Simon Glassbf7fd502016-11-25 20:15:51 -0700262 def ObtainContents(self):
263 """Figure out the contents of an entry.
264
265 Returns:
266 True if the contents were found, False if another call is needed
267 after the other entries are processed.
268 """
269 # No contents by default: subclasses can implement this
270 return True
271
Simon Glass3ab95982018-08-01 15:22:37 -0600272 def Pack(self, offset):
Simon Glass25ac0e62018-06-01 09:38:14 -0600273 """Figure out how to pack the entry into the section
Simon Glassbf7fd502016-11-25 20:15:51 -0700274
275 Most of the time the entries are not fully specified. There may be
276 an alignment but no size. In that case we take the size from the
277 contents of the entry.
278
Simon Glass3ab95982018-08-01 15:22:37 -0600279 If an entry has no hard-coded offset, it will be placed at @offset.
Simon Glassbf7fd502016-11-25 20:15:51 -0700280
Simon Glass3ab95982018-08-01 15:22:37 -0600281 Once this function is complete, both the offset and size of the
Simon Glassbf7fd502016-11-25 20:15:51 -0700282 entry will be know.
283
284 Args:
Simon Glass3ab95982018-08-01 15:22:37 -0600285 Current section offset pointer
Simon Glassbf7fd502016-11-25 20:15:51 -0700286
287 Returns:
Simon Glass3ab95982018-08-01 15:22:37 -0600288 New section offset pointer (after this entry)
Simon Glassbf7fd502016-11-25 20:15:51 -0700289 """
Simon Glass3ab95982018-08-01 15:22:37 -0600290 if self.offset is None:
291 if self.offset_unset:
292 self.Raise('No offset set with offset-unset: should another '
293 'entry provide this correct offset?')
294 self.offset = tools.Align(offset, self.align)
Simon Glassbf7fd502016-11-25 20:15:51 -0700295 needed = self.pad_before + self.contents_size + self.pad_after
296 needed = tools.Align(needed, self.align_size)
297 size = self.size
298 if not size:
299 size = needed
Simon Glass3ab95982018-08-01 15:22:37 -0600300 new_offset = self.offset + size
301 aligned_offset = tools.Align(new_offset, self.align_end)
302 if aligned_offset != new_offset:
303 size = aligned_offset - self.offset
304 new_offset = aligned_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700305
306 if not self.size:
307 self.size = size
308
309 if self.size < needed:
310 self.Raise("Entry contents size is %#x (%d) but entry size is "
311 "%#x (%d)" % (needed, needed, self.size, self.size))
312 # Check that the alignment is correct. It could be wrong if the
Simon Glass3ab95982018-08-01 15:22:37 -0600313 # and offset or size values were provided (i.e. not calculated), but
Simon Glassbf7fd502016-11-25 20:15:51 -0700314 # conflict with the provided alignment values
315 if self.size != tools.Align(self.size, self.align_size):
316 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
317 (self.size, self.size, self.align_size, self.align_size))
Simon Glass3ab95982018-08-01 15:22:37 -0600318 if self.offset != tools.Align(self.offset, self.align):
319 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
320 (self.offset, self.offset, self.align, self.align))
Simon Glassbf7fd502016-11-25 20:15:51 -0700321
Simon Glass3ab95982018-08-01 15:22:37 -0600322 return new_offset
Simon Glassbf7fd502016-11-25 20:15:51 -0700323
324 def Raise(self, msg):
325 """Convenience function to raise an error referencing a node"""
326 raise ValueError("Node '%s': %s" % (self._node.path, msg))
327
Simon Glass53af22a2018-07-17 13:25:32 -0600328 def GetEntryArgsOrProps(self, props, required=False):
329 """Return the values of a set of properties
330
331 Args:
332 props: List of EntryArg objects
333
334 Raises:
335 ValueError if a property is not found
336 """
337 values = []
338 missing = []
339 for prop in props:
340 python_prop = prop.name.replace('-', '_')
341 if hasattr(self, python_prop):
342 value = getattr(self, python_prop)
343 else:
344 value = None
345 if value is None:
346 value = self.GetArg(prop.name, prop.datatype)
347 if value is None and required:
348 missing.append(prop.name)
349 values.append(value)
350 if missing:
351 self.Raise('Missing required properties/entry args: %s' %
352 (', '.join(missing)))
353 return values
354
Simon Glassbf7fd502016-11-25 20:15:51 -0700355 def GetPath(self):
356 """Get the path of a node
357
358 Returns:
359 Full path of the node for this entry
360 """
361 return self._node.path
362
363 def GetData(self):
364 return self.data
365
Simon Glass3ab95982018-08-01 15:22:37 -0600366 def GetOffsets(self):
Simon Glassed7dd5e2019-07-08 13:18:30 -0600367 """Get the offsets for siblings
368
369 Some entry types can contain information about the position or size of
370 other entries. An example of this is the Intel Flash Descriptor, which
371 knows where the Intel Management Engine section should go.
372
373 If this entry knows about the position of other entries, it can specify
374 this by returning values here
375
376 Returns:
377 Dict:
378 key: Entry type
379 value: List containing position and size of the given entry
Simon Glasscf549042019-07-08 13:18:39 -0600380 type. Either can be None if not known
Simon Glassed7dd5e2019-07-08 13:18:30 -0600381 """
Simon Glassbf7fd502016-11-25 20:15:51 -0700382 return {}
383
Simon Glasscf549042019-07-08 13:18:39 -0600384 def SetOffsetSize(self, offset, size):
385 """Set the offset and/or size of an entry
386
387 Args:
388 offset: New offset, or None to leave alone
389 size: New size, or None to leave alone
390 """
391 if offset is not None:
392 self.offset = offset
393 if size is not None:
394 self.size = size
Simon Glassbf7fd502016-11-25 20:15:51 -0700395
Simon Glassdbf6be92018-08-01 15:22:42 -0600396 def SetImagePos(self, image_pos):
397 """Set the position in the image
398
399 Args:
400 image_pos: Position of this entry in the image
401 """
402 self.image_pos = image_pos + self.offset
403
Simon Glassbf7fd502016-11-25 20:15:51 -0700404 def ProcessContents(self):
405 pass
Simon Glass19790632017-11-13 18:55:01 -0700406
Simon Glassf55382b2018-06-01 09:38:13 -0600407 def WriteSymbols(self, section):
Simon Glass19790632017-11-13 18:55:01 -0700408 """Write symbol values into binary files for access at run time
409
410 Args:
Simon Glassf55382b2018-06-01 09:38:13 -0600411 section: Section containing the entry
Simon Glass19790632017-11-13 18:55:01 -0700412 """
413 pass
Simon Glass18546952018-06-01 09:38:16 -0600414
Simon Glass3ab95982018-08-01 15:22:37 -0600415 def CheckOffset(self):
416 """Check that the entry offsets are correct
Simon Glass18546952018-06-01 09:38:16 -0600417
Simon Glass3ab95982018-08-01 15:22:37 -0600418 This is used for entries which have extra offset requirements (other
Simon Glass18546952018-06-01 09:38:16 -0600419 than having to be fully inside their section). Sub-classes can implement
420 this function and raise if there is a problem.
421 """
422 pass
Simon Glass3b0c3822018-06-01 09:38:20 -0600423
Simon Glass8122f392018-07-17 13:25:28 -0600424 @staticmethod
Simon Glass163ed6c2018-09-14 04:57:36 -0600425 def GetStr(value):
426 if value is None:
427 return '<none> '
428 return '%08x' % value
429
430 @staticmethod
Simon Glass1be70d22018-07-17 13:25:49 -0600431 def WriteMapLine(fd, indent, name, offset, size, image_pos):
Simon Glass163ed6c2018-09-14 04:57:36 -0600432 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
433 Entry.GetStr(offset), Entry.GetStr(size),
434 name), file=fd)
Simon Glass8122f392018-07-17 13:25:28 -0600435
Simon Glass3b0c3822018-06-01 09:38:20 -0600436 def WriteMap(self, fd, indent):
437 """Write a map of the entry to a .map file
438
439 Args:
440 fd: File to write the map to
441 indent: Curent indent level of map (0=none, 1=one level, etc.)
442 """
Simon Glass1be70d22018-07-17 13:25:49 -0600443 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
444 self.image_pos)
Simon Glass53af22a2018-07-17 13:25:32 -0600445
Simon Glass11e36cc2018-07-17 13:25:38 -0600446 def GetEntries(self):
447 """Return a list of entries contained by this entry
448
449 Returns:
450 List of entries, or None if none. A normal entry has no entries
451 within it so will return None
452 """
453 return None
454
Simon Glass53af22a2018-07-17 13:25:32 -0600455 def GetArg(self, name, datatype=str):
456 """Get the value of an entry argument or device-tree-node property
457
458 Some node properties can be provided as arguments to binman. First check
459 the entry arguments, and fall back to the device tree if not found
460
461 Args:
462 name: Argument name
463 datatype: Data type (str or int)
464
465 Returns:
466 Value of argument as a string or int, or None if no value
467
468 Raises:
469 ValueError if the argument cannot be converted to in
470 """
Simon Glassc55a50f2018-09-14 04:57:19 -0600471 value = state.GetEntryArg(name)
Simon Glass53af22a2018-07-17 13:25:32 -0600472 if value is not None:
473 if datatype == int:
474 try:
475 value = int(value)
476 except ValueError:
477 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
478 (name, value))
479 elif datatype == str:
480 pass
481 else:
482 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
483 datatype)
484 else:
485 value = fdt_util.GetDatatype(self._node, name, datatype)
486 return value
Simon Glassfd8d1f72018-07-17 13:25:36 -0600487
488 @staticmethod
489 def WriteDocs(modules, test_missing=None):
490 """Write out documentation about the various entry types to stdout
491
492 Args:
493 modules: List of modules to include
494 test_missing: Used for testing. This is a module to report
495 as missing
496 """
497 print('''Binman Entry Documentation
498===========================
499
500This file describes the entry types supported by binman. These entry types can
501be placed in an image one by one to build up a final firmware image. It is
502fairly easy to create new entry types. Just add a new file to the 'etype'
503directory. You can use the existing entries as examples.
504
505Note that some entries are subclasses of others, using and extending their
506features to produce new behaviours.
507
508
509''')
510 modules = sorted(modules)
511
512 # Don't show the test entry
513 if '_testing' in modules:
514 modules.remove('_testing')
515 missing = []
516 for name in modules:
517 module = Entry.Lookup(name, name, name)
518 docs = getattr(module, '__doc__')
519 if test_missing == name:
520 docs = None
521 if docs:
522 lines = docs.splitlines()
523 first_line = lines[0]
524 rest = [line[4:] for line in lines[1:]]
525 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
526 print(hdr)
527 print('-' * len(hdr))
528 print('\n'.join(rest))
529 print()
530 print()
531 else:
532 missing.append(name)
533
534 if missing:
535 raise ValueError('Documentation is missing for modules: %s' %
536 ', '.join(missing))
Simon Glassa326b492018-09-14 04:57:11 -0600537
538 def GetUniqueName(self):
539 """Get a unique name for a node
540
541 Returns:
542 String containing a unique name for a node, consisting of the name
543 of all ancestors (starting from within the 'binman' node) separated
544 by a dot ('.'). This can be useful for generating unique filesnames
545 in the output directory.
546 """
547 name = self.name
548 node = self._node
549 while node.parent:
550 node = node.parent
551 if node.name == 'binman':
552 break
553 name = '%s.%s' % (node.name, name)
554 return name
Simon Glassba64a0b2018-09-14 04:57:29 -0600555
556 def ExpandToLimit(self, limit):
557 """Expand an entry so that it ends at the given offset limit"""
558 if self.offset + self.size < limit:
559 self.size = limit - self.offset
560 # Request the contents again, since changing the size requires that
561 # the data grows. This should not fail, but check it to be sure.
562 if not self.ObtainContents():
563 self.Raise('Cannot obtain contents when expanding entry')
Simon Glassfa1c9372019-07-08 13:18:38 -0600564
565 def HasSibling(self, name):
566 """Check if there is a sibling of a given name
567
568 Returns:
569 True if there is an entry with this name in the the same section,
570 else False
571 """
572 return name in self.section.GetEntries()
Simon Glasscf228942019-07-08 14:25:28 -0600573
574 def GetSiblingImagePos(self, name):
575 """Return the image position of the given sibling
576
577 Returns:
578 Image position of sibling, or None if the sibling has no position,
579 or False if there is no such sibling
580 """
581 if not self.HasSibling(name):
582 return False
583 return self.section.GetEntries()[name].image_pos