1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
4 # Base class for all entries
7 from collections import namedtuple
13 from dtoc import fdt_util
14 from patman import tools
15 from patman.tools import ToHex, ToHexSize
16 from patman import tout
21 # An argument which can be passed to entries on the command line, in lieu of
22 # device-tree properties.
23 EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
25 # Information about an entry for use when displaying summaries
26 EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
27 'image_pos', 'uncomp_size', 'offset',
31 """An Entry in the section
33 An entry corresponds to a single node in the device-tree description
34 of the section. Each entry ends up being a part of the final section.
35 Entries can be placed either right next to each other, or with padding
36 between them. The type of the entry determines the data that is in it.
38 This class is not used by itself. All entry objects are subclasses of
42 section: Section object containing this entry
43 node: The node that created this entry
44 offset: Offset of entry within the section, None if not known yet (in
45 which case it will be calculated by Pack())
46 size: Entry size in bytes, None if not known
47 pre_reset_size: size as it was before ResetForPack(). This allows us to
48 keep track of the size we started with and detect size changes
49 uncomp_size: Size of uncompressed data in bytes, if the entry is
51 contents_size: Size of contents in bytes, 0 by default
52 align: Entry start offset alignment relative to the start of the
53 containing section, or None
54 align_size: Entry size alignment, or None
55 align_end: Entry end offset alignment relative to the start of the
56 containing section, or None
57 pad_before: Number of pad bytes before the contents when it is placed
58 in the containing section, 0 if none. The pad bytes become part of
60 pad_after: Number of pad bytes after the contents when it is placed in
61 the containing section, 0 if none. The pad bytes become part of
63 data: Contents of entry (string of bytes). This does not include
64 padding created by pad_before or pad_after. If the entry is
65 compressed, this contains the compressed data.
66 uncomp_data: Original uncompressed data, if this entry is compressed,
68 compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
69 orig_offset: Original offset value read from node
70 orig_size: Original size value read from node
71 missing: True if this entry is missing its contents
72 allow_missing: Allow children of this entry to be missing (used by
73 subclasses such as Entry_section)
74 allow_fake: Allow creating a dummy fake file if the blob file is not
75 available. This is mainly used for testing.
76 external: True if this entry contains an external binary blob
78 def __init__(self, section, etype, node, name_prefix=''):
79 # Put this here to allow entry-docs and help to work without libfdt
81 from binman import state
83 self.section = section
86 self.name = node and (name_prefix + node.name) or 'none'
89 self.pre_reset_size = None
90 self.uncomp_size = None
92 self.uncomp_data = None
93 self.contents_size = 0
95 self.align_size = None
99 self.offset_unset = False
100 self.image_pos = None
101 self.expand_size = False
102 self.compress = 'none'
105 self.external = False
106 self.allow_missing = False
107 self.allow_fake = False
110 def FindEntryClass(etype, expanded):
111 """Look up the entry class for a node.
114 node_node: Path name of Node object containing information about
115 the entry to create (used for errors)
116 etype: Entry type to use
117 expanded: Use the expanded version of etype
120 The entry class object if found, else None if not found and expanded
121 is True, else a tuple:
122 module name that could not be found
125 # Convert something like 'u-boot@0' to 'u_boot' since we are only
126 # interested in the type.
127 module_name = etype.replace('-', '_')
129 if '@' in module_name:
130 module_name = module_name.split('@')[0]
132 module_name += '_expanded'
133 module = modules.get(module_name)
135 # Also allow entry-type modules to be brought in from the etype directory.
137 # Import the module if we have not already done so.
140 module = importlib.import_module('binman.etype.' + module_name)
141 except ImportError as e:
144 return module_name, e
145 modules[module_name] = module
147 # Look up the expected class name
148 return getattr(module, 'Entry_%s' % module_name)
151 def Lookup(node_path, etype, expanded, missing_etype=False):
152 """Look up the entry class for a node.
155 node_node (str): Path name of Node object containing information
156 about the entry to create (used for errors)
157 etype (str): Entry type to use
158 expanded (bool): Use the expanded version of etype
159 missing_etype (bool): True to default to a blob etype if the
160 requested etype is not found
163 The entry class object if found, else None if not found and expanded
167 ValueError if expanded is False and the class is not found
169 # Convert something like 'u-boot@0' to 'u_boot' since we are only
170 # interested in the type.
171 cls = Entry.FindEntryClass(etype, expanded)
174 elif isinstance(cls, tuple):
176 cls = Entry.FindEntryClass('blob', False)
177 if isinstance(cls, tuple): # This should not fail
180 "Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
181 (etype, node_path, module_name, e))
185 def Create(section, node, etype=None, expanded=False, missing_etype=False):
186 """Create a new entry for a node.
189 section (entry_Section): Section object containing this node
190 node (Node): Node object containing information about the entry to
192 etype (str): Entry type to use, or None to work it out (used for
194 expanded (bool): Use the expanded version of etype
195 missing_etype (bool): True to default to a blob etype if the
196 requested etype is not found
199 A new Entry object of the correct type (a subclass of Entry)
202 etype = fdt_util.GetString(node, 'type', node.name)
203 obj = Entry.Lookup(node.path, etype, expanded, missing_etype)
205 # Check whether to use the expanded entry
206 new_etype = etype + '-expanded'
207 can_expand = not fdt_util.GetBool(node, 'no-expanded')
208 if can_expand and obj.UseExpanded(node, etype, new_etype):
213 obj = Entry.Lookup(node.path, etype, False, missing_etype)
215 # Call its constructor to get the object we want.
216 return obj(section, etype, node)
219 """Read entry information from the node
221 This must be called as the first thing after the Entry is created.
223 This reads all the fields we recognise from the node, ready for use.
225 if 'pos' in self._node.props:
226 self.Raise("Please use 'offset' instead of 'pos'")
227 self.offset = fdt_util.GetInt(self._node, 'offset')
228 self.size = fdt_util.GetInt(self._node, 'size')
229 self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
230 self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
231 if self.GetImage().copy_to_orig:
232 self.orig_offset = self.offset
233 self.orig_size = self.size
235 # These should not be set in input files, but are set in an FDT map,
236 # which is also read by this code.
237 self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
238 self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
240 self.align = fdt_util.GetInt(self._node, 'align')
241 if tools.NotPowerOfTwo(self.align):
242 raise ValueError("Node '%s': Alignment %s must be a power of two" %
243 (self._node.path, self.align))
244 if self.section and self.align is None:
245 self.align = self.section.align_default
246 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
247 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
248 self.align_size = fdt_util.GetInt(self._node, 'align-size')
249 if tools.NotPowerOfTwo(self.align_size):
250 self.Raise("Alignment size %s must be a power of two" %
252 self.align_end = fdt_util.GetInt(self._node, 'align-end')
253 self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
254 self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
255 self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
257 # This is only supported by blobs and sections at present
258 self.compress = fdt_util.GetString(self._node, 'compress', 'none')
260 def GetDefaultFilename(self):
264 """Get the device trees used by this entry
267 Empty dict, if this entry is not a .dtb, otherwise:
269 key: Filename from this entry (without the path)
271 Entry object for this dtb
272 Filename of file containing this dtb
276 def ExpandEntries(self):
277 """Expand out entries which produce other entries
279 Some entries generate subnodes automatically, from which sub-entries
280 are then created. This method allows those to be added to the binman
281 definition for the current image. An entry which implements this method
282 should call state.AddSubnode() to add a subnode and can add properties
283 with state.AddString(), etc.
285 An example is 'files', which produces a section containing a list of
290 def AddMissingProperties(self, have_image_pos):
291 """Add new properties to the device tree as needed for this entry
294 have_image_pos: True if this entry has an image position. This can
295 be False if its parent section is compressed, since compression
296 groups all entries together into a compressed block of data,
297 obscuring the start of each individual child entry
299 for prop in ['offset', 'size']:
300 if not prop in self._node.props:
301 state.AddZeroProp(self._node, prop)
302 if have_image_pos and 'image-pos' not in self._node.props:
303 state.AddZeroProp(self._node, 'image-pos')
304 if self.GetImage().allow_repack:
305 if self.orig_offset is not None:
306 state.AddZeroProp(self._node, 'orig-offset', True)
307 if self.orig_size is not None:
308 state.AddZeroProp(self._node, 'orig-size', True)
310 if self.compress != 'none':
311 state.AddZeroProp(self._node, 'uncomp-size')
312 err = state.CheckAddHashProp(self._node)
316 def SetCalculatedProperties(self):
317 """Set the value of device-tree properties calculated by binman"""
318 state.SetInt(self._node, 'offset', self.offset)
319 state.SetInt(self._node, 'size', self.size)
320 base = self.section.GetRootSkipAtStart() if self.section else 0
321 if self.image_pos is not None:
322 state.SetInt(self._node, 'image-pos', self.image_pos - base)
323 if self.GetImage().allow_repack:
324 if self.orig_offset is not None:
325 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
326 if self.orig_size is not None:
327 state.SetInt(self._node, 'orig-size', self.orig_size, True)
328 if self.uncomp_size is not None:
329 state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
330 state.CheckSetHashValue(self._node, self.GetData)
332 def ProcessFdt(self, fdt):
333 """Allow entries to adjust the device tree
335 Some entries need to adjust the device tree for their purposes. This
336 may involve adding or deleting properties.
339 True if processing is complete
340 False if processing could not be completed due to a dependency.
341 This will cause the entry to be retried after others have been
346 def SetPrefix(self, prefix):
347 """Set the name prefix for a node
350 prefix: Prefix to set, or '' to not use a prefix
353 self.name = prefix + self.name
355 def SetContents(self, data):
356 """Set the contents of an entry
358 This sets both the data and content_size properties
361 data: Data to set to the contents (bytes)
364 self.contents_size = len(self.data)
366 def ProcessContentsUpdate(self, data):
367 """Update the contents of an entry, after the size is fixed
369 This checks that the new data is the same size as the old. If the size
370 has changed, this triggers a re-run of the packing algorithm.
373 data: Data to set to the contents (bytes)
376 ValueError if the new data size is not the same as the old
380 if state.AllowEntryExpansion() and new_size > self.contents_size:
381 # self.data will indicate the new size needed
383 elif state.AllowEntryContraction() and new_size < self.contents_size:
386 # If not allowed to change, try to deal with it or give up
388 if new_size > self.contents_size:
389 self.Raise('Cannot update entry size from %d to %d' %
390 (self.contents_size, new_size))
392 # Don't let the data shrink. Pad it if necessary
393 if size_ok and new_size < self.contents_size:
394 data += tools.GetBytes(0, self.contents_size - new_size)
397 tout.Debug("Entry '%s' size change from %s to %s" % (
398 self._node.path, ToHex(self.contents_size),
400 self.SetContents(data)
403 def ObtainContents(self):
404 """Figure out the contents of an entry.
407 True if the contents were found, False if another call is needed
408 after the other entries are processed.
410 # No contents by default: subclasses can implement this
413 def ResetForPack(self):
414 """Reset offset/size fields so that packing can be done again"""
415 self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
416 (ToHex(self.offset), ToHex(self.orig_offset),
417 ToHex(self.size), ToHex(self.orig_size)))
418 self.pre_reset_size = self.size
419 self.offset = self.orig_offset
420 self.size = self.orig_size
422 def Pack(self, offset):
423 """Figure out how to pack the entry into the section
425 Most of the time the entries are not fully specified. There may be
426 an alignment but no size. In that case we take the size from the
427 contents of the entry.
429 If an entry has no hard-coded offset, it will be placed at @offset.
431 Once this function is complete, both the offset and size of the
435 Current section offset pointer
438 New section offset pointer (after this entry)
440 self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
441 (ToHex(self.offset), ToHex(self.size),
443 if self.offset is None:
444 if self.offset_unset:
445 self.Raise('No offset set with offset-unset: should another '
446 'entry provide this correct offset?')
447 self.offset = tools.Align(offset, self.align)
448 needed = self.pad_before + self.contents_size + self.pad_after
449 needed = tools.Align(needed, self.align_size)
453 new_offset = self.offset + size
454 aligned_offset = tools.Align(new_offset, self.align_end)
455 if aligned_offset != new_offset:
456 size = aligned_offset - self.offset
457 new_offset = aligned_offset
462 if self.size < needed:
463 self.Raise("Entry contents size is %#x (%d) but entry size is "
464 "%#x (%d)" % (needed, needed, self.size, self.size))
465 # Check that the alignment is correct. It could be wrong if the
466 # and offset or size values were provided (i.e. not calculated), but
467 # conflict with the provided alignment values
468 if self.size != tools.Align(self.size, self.align_size):
469 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
470 (self.size, self.size, self.align_size, self.align_size))
471 if self.offset != tools.Align(self.offset, self.align):
472 self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
473 (self.offset, self.offset, self.align, self.align))
474 self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
475 (self.offset, self.size, self.contents_size, new_offset))
479 def Raise(self, msg):
480 """Convenience function to raise an error referencing a node"""
481 raise ValueError("Node '%s': %s" % (self._node.path, msg))
484 """Convenience function to log info referencing a node"""
485 tag = "Info '%s'" % self._node.path
486 tout.Detail('%30s: %s' % (tag, msg))
488 def Detail(self, msg):
489 """Convenience function to log detail referencing a node"""
490 tag = "Node '%s'" % self._node.path
491 tout.Detail('%30s: %s' % (tag, msg))
493 def GetEntryArgsOrProps(self, props, required=False):
494 """Return the values of a set of properties
497 props: List of EntryArg objects
500 ValueError if a property is not found
505 python_prop = prop.name.replace('-', '_')
506 if hasattr(self, python_prop):
507 value = getattr(self, python_prop)
511 value = self.GetArg(prop.name, prop.datatype)
512 if value is None and required:
513 missing.append(prop.name)
516 self.GetImage().MissingArgs(self, missing)
520 """Get the path of a node
523 Full path of the node for this entry
525 return self._node.path
527 def GetData(self, required=True):
528 """Get the contents of an entry
531 required: True if the data must be present, False if it is OK to
535 bytes content of the entry, excluding any padding. If the entry is
536 compressed, the compressed data is returned
538 self.Detail('GetData: size %s' % ToHexSize(self.data))
541 def GetPaddedData(self, data=None):
542 """Get the data for an entry including any padding
544 Gets the entry data and uses its section's pad-byte value to add padding
545 before and after as defined by the pad-before and pad-after properties.
547 This does not consider alignment.
550 Contents of the entry along with any pad bytes before and
554 data = self.GetData()
555 return self.section.GetPaddedDataForEntry(self, data)
557 def GetOffsets(self):
558 """Get the offsets for siblings
560 Some entry types can contain information about the position or size of
561 other entries. An example of this is the Intel Flash Descriptor, which
562 knows where the Intel Management Engine section should go.
564 If this entry knows about the position of other entries, it can specify
565 this by returning values here
570 value: List containing position and size of the given entry
571 type. Either can be None if not known
575 def SetOffsetSize(self, offset, size):
576 """Set the offset and/or size of an entry
579 offset: New offset, or None to leave alone
580 size: New size, or None to leave alone
582 if offset is not None:
587 def SetImagePos(self, image_pos):
588 """Set the position in the image
591 image_pos: Position of this entry in the image
593 self.image_pos = image_pos + self.offset
595 def ProcessContents(self):
596 """Do any post-packing updates of entry contents
598 This function should call ProcessContentsUpdate() to update the entry
599 contents, if necessary, returning its return value here.
602 data: Data to set to the contents (bytes)
605 True if the new data size is OK, False if expansion is needed
608 ValueError if the new data size is not the same as the old and
609 state.AllowEntryExpansion() is False
613 def WriteSymbols(self, section):
614 """Write symbol values into binary files for access at run time
617 section: Section containing the entry
621 def CheckEntries(self):
622 """Check that the entry offsets are correct
624 This is used for entries which have extra offset requirements (other
625 than having to be fully inside their section). Sub-classes can implement
626 this function and raise if there is a problem.
634 return '%08x' % value
637 def WriteMapLine(fd, indent, name, offset, size, image_pos):
638 print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
639 Entry.GetStr(offset), Entry.GetStr(size),
642 def WriteMap(self, fd, indent):
643 """Write a map of the entry to a .map file
646 fd: File to write the map to
647 indent: Curent indent level of map (0=none, 1=one level, etc.)
649 self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
652 def GetEntries(self):
653 """Return a list of entries contained by this entry
656 List of entries, or None if none. A normal entry has no entries
657 within it so will return None
661 def GetArg(self, name, datatype=str):
662 """Get the value of an entry argument or device-tree-node property
664 Some node properties can be provided as arguments to binman. First check
665 the entry arguments, and fall back to the device tree if not found
669 datatype: Data type (str or int)
672 Value of argument as a string or int, or None if no value
675 ValueError if the argument cannot be converted to in
677 value = state.GetEntryArg(name)
678 if value is not None:
683 self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
685 elif datatype == str:
688 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
691 value = fdt_util.GetDatatype(self._node, name, datatype)
695 def WriteDocs(modules, test_missing=None):
696 """Write out documentation about the various entry types to stdout
699 modules: List of modules to include
700 test_missing: Used for testing. This is a module to report
703 print('''Binman Entry Documentation
704 ===========================
706 This file describes the entry types supported by binman. These entry types can
707 be placed in an image one by one to build up a final firmware image. It is
708 fairly easy to create new entry types. Just add a new file to the 'etype'
709 directory. You can use the existing entries as examples.
711 Note that some entries are subclasses of others, using and extending their
712 features to produce new behaviours.
716 modules = sorted(modules)
718 # Don't show the test entry
719 if '_testing' in modules:
720 modules.remove('_testing')
723 module = Entry.Lookup('WriteDocs', name, False)
724 docs = getattr(module, '__doc__')
725 if test_missing == name:
728 lines = docs.splitlines()
729 first_line = lines[0]
730 rest = [line[4:] for line in lines[1:]]
731 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
733 print('-' * len(hdr))
734 print('\n'.join(rest))
741 raise ValueError('Documentation is missing for modules: %s' %
744 def GetUniqueName(self):
745 """Get a unique name for a node
748 String containing a unique name for a node, consisting of the name
749 of all ancestors (starting from within the 'binman' node) separated
750 by a dot ('.'). This can be useful for generating unique filesnames
751 in the output directory.
757 if node.name == 'binman':
759 name = '%s.%s' % (node.name, name)
762 def ExpandToLimit(self, limit):
763 """Expand an entry so that it ends at the given offset limit"""
764 if self.offset + self.size < limit:
765 self.size = limit - self.offset
766 # Request the contents again, since changing the size requires that
767 # the data grows. This should not fail, but check it to be sure.
768 if not self.ObtainContents():
769 self.Raise('Cannot obtain contents when expanding entry')
771 def HasSibling(self, name):
772 """Check if there is a sibling of a given name
775 True if there is an entry with this name in the the same section,
778 return name in self.section.GetEntries()
780 def GetSiblingImagePos(self, name):
781 """Return the image position of the given sibling
784 Image position of sibling, or None if the sibling has no position,
785 or False if there is no such sibling
787 if not self.HasSibling(name):
789 return self.section.GetEntries()[name].image_pos
792 def AddEntryInfo(entries, indent, name, etype, size, image_pos,
793 uncomp_size, offset, entry):
794 """Add a new entry to the entries list
797 entries: List (of EntryInfo objects) to add to
798 indent: Current indent level to add to list
799 name: Entry name (string)
800 etype: Entry type (string)
801 size: Entry size in bytes (int)
802 image_pos: Position within image in bytes (int)
803 uncomp_size: Uncompressed size if the entry uses compression, else
805 offset: Entry offset within parent in bytes (int)
808 entries.append(EntryInfo(indent, name, etype, size, image_pos,
809 uncomp_size, offset, entry))
811 def ListEntries(self, entries, indent):
812 """Add files in this entry to the list of entries
814 This can be overridden by subclasses which need different behaviour.
817 entries: List (of EntryInfo objects) to add to
818 indent: Current indent level to add to list
820 self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
821 self.image_pos, self.uncomp_size, self.offset, self)
823 def ReadData(self, decomp=True, alt_format=None):
824 """Read the data for an entry from the image
826 This is used when the image has been read in and we want to extract the
827 data for a particular entry from that image.
830 decomp: True to decompress any compressed data before returning it;
831 False to return the raw, uncompressed data
836 # Use True here so that we get an uncompressed section to work from,
837 # although compressed sections are currently not supported
838 tout.Debug("ReadChildData section '%s', entry '%s'" %
839 (self.section.GetPath(), self.GetPath()))
840 data = self.section.ReadChildData(self, decomp, alt_format)
843 def ReadChildData(self, child, decomp=True, alt_format=None):
844 """Read the data for a particular child entry
846 This reads data from the parent and extracts the piece that relates to
850 child (Entry): Child entry to read data for (must be valid)
851 decomp (bool): True to decompress any compressed data before
852 returning it; False to return the raw, uncompressed data
853 alt_format (str): Alternative format to read in, or None
856 Data for the child (bytes)
860 def LoadData(self, decomp=True):
861 data = self.ReadData(decomp)
862 self.contents_size = len(data)
863 self.ProcessContentsUpdate(data)
864 self.Detail('Loaded data size %x' % len(data))
866 def GetAltFormat(self, data, alt_format):
867 """Read the data for an extry in an alternative format
869 Supported formats are list in the documentation for each entry. An
870 example is fdtmap which provides .
873 data (bytes): Data to convert (this should have been produced by the
875 alt_format (str): Format to use
881 """Get the image containing this entry
884 Image object containing this entry
886 return self.section.GetImage()
888 def WriteData(self, data, decomp=True):
889 """Write the data to an entry in the image
891 This is used when the image has been read in and we want to replace the
892 data for a particular entry in that image.
894 The image must be re-packed and written out afterwards.
897 data: Data to replace it with
898 decomp: True to compress the data if needed, False if data is
899 already compressed so should be used as is
902 True if the data did not result in a resize of this entry, False if
903 the entry must be resized
905 if self.size is not None:
906 self.contents_size = self.size
908 self.contents_size = self.pre_reset_size
909 ok = self.ProcessContentsUpdate(data)
910 self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
911 section_ok = self.section.WriteChildData(self)
912 return ok and section_ok
914 def WriteChildData(self, child):
915 """Handle writing the data in a child entry
917 This should be called on the child's parent section after the child's
918 data has been updated. It should update any data structures needed to
919 validate that the update is successful.
921 This base-class implementation does nothing, since the base Entry object
922 does not have any children.
925 child: Child Entry that was written
928 True if the section could be updated successfully, False if the
929 data is such that the section could not update
933 def GetSiblingOrder(self):
934 """Get the relative order of an entry amoung its siblings
937 'start' if this entry is first among siblings, 'end' if last,
940 entries = list(self.section.GetEntries().values())
942 if self == entries[0]:
944 elif self == entries[-1]:
948 def SetAllowMissing(self, allow_missing):
949 """Set whether a section allows missing external blobs
952 allow_missing: True if allowed, False if not allowed
954 # This is meaningless for anything other than sections
957 def SetAllowFakeBlob(self, allow_fake):
958 """Set whether a section allows to create a fake blob
961 allow_fake: True if allowed, False if not allowed
965 def CheckMissing(self, missing_list):
966 """Check if any entries in this section have missing external blobs
968 If there are missing blobs, the entries are added to the list
971 missing_list: List of Entry objects to be added to
974 missing_list.append(self)
976 def check_fake_fname(self, fname):
977 """If the file is missing and the entry allows fake blobs, fake it
979 Sets self.faked to True if faked
982 fname (str): Filename to check
985 fname (str): Filename of faked file
987 if self.allow_fake and not pathlib.Path(fname).is_file():
988 outfname = tools.GetOutputFilename(os.path.basename(fname))
989 with open(outfname, "wb") as out:
995 def CheckFakedBlobs(self, faked_blobs_list):
996 """Check if any entries in this section have faked external blobs
998 If there are faked blobs, the entries are added to the list
1001 fake_blobs_list: List of Entry objects to be added to
1003 # This is meaningless for anything other than blobs
1006 def GetAllowMissing(self):
1007 """Get whether a section allows missing external blobs
1010 True if allowed, False if not allowed
1012 return self.allow_missing
1014 def GetHelpTags(self):
1015 """Get the tags use for missing-blob help
1018 list of possible tags, most desirable first
1020 return list(filter(None, [self.missing_msg, self.name, self.etype]))
1022 def CompressData(self, indata):
1023 """Compress data according to the entry's compression method
1026 indata: Data to compress
1029 Compressed data (first word is the compressed size)
1031 self.uncomp_data = indata
1032 if self.compress != 'none':
1033 self.uncomp_size = len(indata)
1034 data = tools.Compress(indata, self.compress)
1038 def UseExpanded(cls, node, etype, new_etype):
1039 """Check whether to use an expanded entry type
1041 This is called by Entry.Create() when it finds an expanded version of
1042 an entry type (e.g. 'u-boot-expanded'). If this method returns True then
1043 it will be used (e.g. in place of 'u-boot'). If it returns False, it is
1047 node: Node object containing information about the entry to
1049 etype: Original entry type being used
1050 new_etype: New entry type proposed
1053 True to use this entry type, False to use the original one
1055 tout.Info("Node '%s': etype '%s': %s selected" %
1056 (node.path, etype, new_etype))
1059 def CheckAltFormats(self, alt_formats):
1060 """Add any alternative formats supported by this entry type
1063 alt_formats (dict): Dict to add alt_formats to:
1064 key: Name of alt format