mirror of
https://xff.cz/git/u-boot/
synced 2025-09-02 09:12:08 +02:00
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:
@@ -77,9 +77,20 @@ def RunTests(debug, args):
|
|||||||
return 1
|
return 1
|
||||||
return 0
|
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():
|
def RunTestCoverage():
|
||||||
"""Run the tests and check that we get 100% coverage"""
|
"""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]
|
all_set = set([os.path.splitext(os.path.basename(item))[0]
|
||||||
for item in glob_list if '_testing' not in item])
|
for item in glob_list if '_testing' not in item])
|
||||||
test_util.RunTestCoverage('tools/binman/binman.py', None,
|
test_util.RunTestCoverage('tools/binman/binman.py', None,
|
||||||
@@ -107,13 +118,8 @@ def RunBinman(options, args):
|
|||||||
elif options.test_coverage:
|
elif options.test_coverage:
|
||||||
RunTestCoverage()
|
RunTestCoverage()
|
||||||
|
|
||||||
elif options.full_help:
|
elif options.entry_docs:
|
||||||
pager = os.getenv('PAGER')
|
control.WriteEntryDocs(GetEntryModules())
|
||||||
if not pager:
|
|
||||||
pager = 'more'
|
|
||||||
fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
|
|
||||||
'README')
|
|
||||||
command.Run(pager, fname)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@@ -28,6 +28,8 @@ def ParseArgs(argv):
|
|||||||
help='Configuration file (.dtb) to use')
|
help='Configuration file (.dtb) to use')
|
||||||
parser.add_option('-D', '--debug', action='store_true',
|
parser.add_option('-D', '--debug', action='store_true',
|
||||||
help='Enabling debugging (provides a full traceback on error)')
|
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',
|
parser.add_option('-I', '--indir', action='append',
|
||||||
help='Add a path to a directory to use for input files')
|
help='Add a path to a directory to use for input files')
|
||||||
parser.add_option('-H', '--full-help', action='store_true',
|
parser.add_option('-H', '--full-help', action='store_true',
|
||||||
|
@@ -92,6 +92,10 @@ def SetEntryArgs(args):
|
|||||||
def GetEntryArg(name):
|
def GetEntryArg(name):
|
||||||
return entry_args.get(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):
|
def Binman(options, args):
|
||||||
"""The main control code for binman
|
"""The main control code for binman
|
||||||
|
|
||||||
|
@@ -78,20 +78,18 @@ class Entry(object):
|
|||||||
self.ReadNode()
|
self.ReadNode()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def Create(section, node, etype=None):
|
def Lookup(section, node_path, etype):
|
||||||
"""Create a new entry for a node.
|
"""Look up the entry class for a node.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
section: Section object containing this node
|
section: Section object containing this node
|
||||||
node: Node object containing information about the entry to create
|
node_node: Path name of Node object containing information about
|
||||||
etype: Entry type to use, or None to work it out (used for tests)
|
the entry to create (used for errors)
|
||||||
|
etype: Entry type to use
|
||||||
|
|
||||||
Returns:
|
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
|
# Convert something like 'u-boot@0' to 'u_boot' since we are only
|
||||||
# interested in the type.
|
# interested in the type.
|
||||||
module_name = etype.replace('-', '_')
|
module_name = etype.replace('-', '_')
|
||||||
@@ -110,15 +108,34 @@ class Entry(object):
|
|||||||
module = importlib.import_module(module_name)
|
module = importlib.import_module(module_name)
|
||||||
else:
|
else:
|
||||||
module = __import__(module_name)
|
module = __import__(module_name)
|
||||||
except ImportError:
|
except ImportError as e:
|
||||||
raise ValueError("Unknown entry type '%s' in node '%s'" %
|
raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
|
||||||
(etype, node.path))
|
(etype, node_path, module_name, e))
|
||||||
finally:
|
finally:
|
||||||
sys.path = old_path
|
sys.path = old_path
|
||||||
modules[module_name] = module
|
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.
|
# Call its constructor to get the object we want.
|
||||||
obj = getattr(module, 'Entry_%s' % module_name)
|
|
||||||
return obj(section, etype, node)
|
return obj(section, etype, node)
|
||||||
|
|
||||||
def ReadNode(self):
|
def ReadNode(self):
|
||||||
@@ -376,3 +393,53 @@ class Entry(object):
|
|||||||
else:
|
else:
|
||||||
value = fdt_util.GetDatatype(self._node, name, datatype)
|
value = fdt_util.GetDatatype(self._node, name, datatype)
|
||||||
return value
|
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 control
|
||||||
import fdt
|
|
||||||
from entry import Entry
|
from entry import Entry
|
||||||
from blob import Entry_blob
|
from blob import Entry_blob
|
||||||
import tools
|
import tools
|
||||||
@@ -38,6 +37,9 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
|
|||||||
return 'u-boot.dtb'
|
return 'u-boot.dtb'
|
||||||
|
|
||||||
def ProcessFdt(self, fdt):
|
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
|
# If the section does not need microcode, there is nothing to do
|
||||||
ucode_dest_entry = self.section.FindEntryType(
|
ucode_dest_entry = self.section.FindEntryType(
|
||||||
'u-boot-spl-with-ucode-ptr')
|
'u-boot-spl-with-ucode-ptr')
|
||||||
|
@@ -21,6 +21,7 @@ import control
|
|||||||
import elf
|
import elf
|
||||||
import fdt
|
import fdt
|
||||||
import fdt_util
|
import fdt_util
|
||||||
|
import test_util
|
||||||
import tools
|
import tools
|
||||||
import tout
|
import tout
|
||||||
|
|
||||||
@@ -1177,6 +1178,20 @@ class TestFunctional(unittest.TestCase):
|
|||||||
TEXT_DATA3 + 'some text')
|
TEXT_DATA3 + 'some text')
|
||||||
self.assertEqual(expected, data)
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Reference in New Issue
Block a user