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>
This commit is contained in:
parent
3fb397bba0
commit
fd8d1f7962
@ -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…
Reference in New Issue
Block a user