binman: Allow creation of entry documentation

Binman supports quite a number of different entries now. The operation of
these is not always obvious but at present the source code is the only
reference for understanding how an entry works.

Add a way to create documentation (from the source code) which can be put
in a new 'README.entries' file.

Signed-off-by: Simon Glass <sjg@chromium.org>
lime2-spi
Simon Glass 6 years ago
parent 3fb397bba0
commit fd8d1f7962
  1. 22
      tools/binman/binman.py
  2. 2
      tools/binman/cmdline.py
  3. 4
      tools/binman/control.py
  4. 93
      tools/binman/entry.py
  5. 4
      tools/binman/etype/u_boot_dtb_with_ucode.py
  6. 15
      tools/binman/ftest.py

@ -77,9 +77,20 @@ def RunTests(debug, args):
return 1
return 0
def GetEntryModules(include_testing=True):
"""Get a set of entry class implementations
Returns:
Set of paths to entry class filenames
"""
glob_list = glob.glob(os.path.join(our_path, 'etype/*.py'))
return set([os.path.splitext(os.path.basename(item))[0]
for item in glob_list
if include_testing or '_testing' not in item])
def RunTestCoverage():
"""Run the tests and check that we get 100% coverage"""
glob_list = glob.glob(os.path.join(our_path, 'etype/*.py'))
glob_list = GetEntryModules(False)
all_set = set([os.path.splitext(os.path.basename(item))[0]
for item in glob_list if '_testing' not in item])
test_util.RunTestCoverage('tools/binman/binman.py', None,
@ -107,13 +118,8 @@ def RunBinman(options, args):
elif options.test_coverage:
RunTestCoverage()
elif options.full_help:
pager = os.getenv('PAGER')
if not pager:
pager = 'more'
fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
'README')
command.Run(pager, fname)
elif options.entry_docs:
control.WriteEntryDocs(GetEntryModules())
else:
try:

@ -28,6 +28,8 @@ def ParseArgs(argv):
help='Configuration file (.dtb) to use')
parser.add_option('-D', '--debug', action='store_true',
help='Enabling debugging (provides a full traceback on error)')
parser.add_option('-E', '--entry-docs', action='store_true',
help='Write out entry documentation (see README.entries)')
parser.add_option('-I', '--indir', action='append',
help='Add a path to a directory to use for input files')
parser.add_option('-H', '--full-help', action='store_true',

@ -92,6 +92,10 @@ def SetEntryArgs(args):
def GetEntryArg(name):
return entry_args.get(name)
def WriteEntryDocs(modules, test_missing=None):
from entry import Entry
Entry.WriteDocs(modules, test_missing)
def Binman(options, args):
"""The main control code for binman

@ -78,20 +78,18 @@ class Entry(object):
self.ReadNode()
@staticmethod
def Create(section, node, etype=None):
"""Create a new entry for a node.
def Lookup(section, node_path, etype):
"""Look up the entry class for a node.
Args:
section: Section object containing this node
node: Node object containing information about the entry to create
etype: Entry type to use, or None to work it out (used for tests)
section: Section object containing this node
node_node: Path name of Node object containing information about
the entry to create (used for errors)
etype: Entry type to use
Returns:
A new Entry object of the correct type (a subclass of Entry)
The entry class object if found, else None
"""
if not etype:
etype = fdt_util.GetString(node, 'type', node.name)
# Convert something like 'u-boot@0' to 'u_boot' since we are only
# interested in the type.
module_name = etype.replace('-', '_')
@ -110,15 +108,34 @@ class Entry(object):
module = importlib.import_module(module_name)
else:
module = __import__(module_name)
except ImportError:
raise ValueError("Unknown entry type '%s' in node '%s'" %
(etype, node.path))
except ImportError as e:
raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
(etype, node_path, module_name, e))
finally:
sys.path = old_path
modules[module_name] = module
# Look up the expected class name
return getattr(module, 'Entry_%s' % module_name)
@staticmethod
def Create(section, node, etype=None):
"""Create a new entry for a node.
Args:
section: Section object containing this node
node: Node object containing information about the entry to
create
etype: Entry type to use, or None to work it out (used for tests)
Returns:
A new Entry object of the correct type (a subclass of Entry)
"""
if not etype:
etype = fdt_util.GetString(node, 'type', node.name)
obj = Entry.Lookup(section, node.path, etype)
# Call its constructor to get the object we want.
obj = getattr(module, 'Entry_%s' % module_name)
return obj(section, etype, node)
def ReadNode(self):
@ -376,3 +393,53 @@ class Entry(object):
else:
value = fdt_util.GetDatatype(self._node, name, datatype)
return value
@staticmethod
def WriteDocs(modules, test_missing=None):
"""Write out documentation about the various entry types to stdout
Args:
modules: List of modules to include
test_missing: Used for testing. This is a module to report
as missing
"""
print('''Binman Entry Documentation
===========================
This file describes the entry types supported by binman. These entry types can
be placed in an image one by one to build up a final firmware image. It is
fairly easy to create new entry types. Just add a new file to the 'etype'
directory. You can use the existing entries as examples.
Note that some entries are subclasses of others, using and extending their
features to produce new behaviours.
''')
modules = sorted(modules)
# Don't show the test entry
if '_testing' in modules:
modules.remove('_testing')
missing = []
for name in modules:
module = Entry.Lookup(name, name, name)
docs = getattr(module, '__doc__')
if test_missing == name:
docs = None
if docs:
lines = docs.splitlines()
first_line = lines[0]
rest = [line[4:] for line in lines[1:]]
hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
print(hdr)
print('-' * len(hdr))
print('\n'.join(rest))
print()
print()
else:
missing.append(name)
if missing:
raise ValueError('Documentation is missing for modules: %s' %
', '.join(missing))

@ -6,7 +6,6 @@
#
import control
import fdt
from entry import Entry
from blob import Entry_blob
import tools
@ -38,6 +37,9 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
return 'u-boot.dtb'
def ProcessFdt(self, fdt):
# So the module can be loaded without it
import fdt
# If the section does not need microcode, there is nothing to do
ucode_dest_entry = self.section.FindEntryType(
'u-boot-spl-with-ucode-ptr')

@ -21,6 +21,7 @@ import control
import elf
import fdt
import fdt_util
import test_util
import tools
import tout
@ -1177,6 +1178,20 @@ class TestFunctional(unittest.TestCase):
TEXT_DATA3 + 'some text')
self.assertEqual(expected, data)
def testEntryDocs(self):
"""Test for creation of entry documentation"""
with test_util.capture_sys_output() as (stdout, stderr):
control.WriteEntryDocs(binman.GetEntryModules())
self.assertTrue(len(stdout.getvalue()) > 0)
def testEntryDocsMissing(self):
"""Test handling of missing entry documentation"""
with self.assertRaises(ValueError) as e:
with test_util.capture_sys_output() as (stdout, stderr):
control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
self.assertIn('Documentation is missing for modules: u_boot',
str(e.exception))
if __name__ == "__main__":
unittest.main()

Loading…
Cancel
Save