nested inside their sections.
+Passing command-line arguments to entries
+-----------------------------------------
+
+Sometimes it is useful to pass binman the value of an entry property from the
+command line. For example some entries need access to files and it is not
+always convenient to put these filenames in the image definition (device tree).
+
+The-a option supports this:
+
+ -a<prop>=<value>
+
+where
+
+ <prop> is the property to set
+ <value> is the value to set it to
+
+Not all properties can be provided this way. Only some entries support it,
+typically for filenames.
+
+
Code coverage
-------------
args is a list of string arguments
"""
parser = OptionParser()
+ parser.add_option('-a', '--entry-arg', type='string', action='append',
+ help='Set argument value arg=value')
parser.add_option('-b', '--board', type='string',
help='Board name to build')
parser.add_option('-B', '--build-dir', type='string', default='b',
from collections import OrderedDict
import os
+import re
import sys
import tools
# 'u-boot-spl.dtb')
fdt_files = {}
+# Arguments passed to binman to provide arguments to entries
+entry_args = {}
+
def _ReadImageDesc(binman_node):
"""Read the image descriptions from the /binman node
def GetFdtPath(fname):
return fdt_files[fname]._fname
+def SetEntryArgs(args):
+ global entry_args
+
+ entry_args = {}
+ if args:
+ for arg in args:
+ m = re.match('([^=]*)=(.*)', arg)
+ if not m:
+ raise ValueError("Invalid entry arguemnt '%s'" % arg)
+ entry_args[m.group(1)] = m.group(2)
+
+def GetEntryArg(name):
+ return entry_args.get(name)
+
def Binman(options, args):
"""The main control code for binman
try:
tools.SetInputDirs(options.indir)
tools.PrepareOutputDir(options.outdir, options.preserve)
+ SetEntryArgs(options.entry_arg)
# Get the device tree ready by compiling it and copying the compiled
# output into a file in our output directly. Then scan it for use
from __future__ import print_function
+from collections import namedtuple
+
# importlib was introduced in Python 2.7 but there was a report of it not
# working in 2.7.12, so we work around this:
# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
have_importlib = False
import fdt_util
+import control
import os
import sys
import tools
our_path = os.path.dirname(os.path.realpath(__file__))
+
+# An argument which can be passed to entries on the command line, in lieu of
+# device-tree properties.
+EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
+
+
class Entry(object):
"""An Entry in the section
"""Convenience function to raise an error referencing a node"""
raise ValueError("Node '%s': %s" % (self._node.path, msg))
+ def GetEntryArgsOrProps(self, props, required=False):
+ """Return the values of a set of properties
+
+ Args:
+ props: List of EntryArg objects
+
+ Raises:
+ ValueError if a property is not found
+ """
+ values = []
+ missing = []
+ for prop in props:
+ python_prop = prop.name.replace('-', '_')
+ if hasattr(self, python_prop):
+ value = getattr(self, python_prop)
+ else:
+ value = None
+ if value is None:
+ value = self.GetArg(prop.name, prop.datatype)
+ if value is None and required:
+ missing.append(prop.name)
+ values.append(value)
+ if missing:
+ self.Raise('Missing required properties/entry args: %s' %
+ (', '.join(missing)))
+ return values
+
def GetPath(self):
"""Get the path of a node
indent: Curent indent level of map (0=none, 1=one level, etc.)
"""
self.WriteMapLine(fd, indent, self.name, self.offset, self.size)
+
+ def GetArg(self, name, datatype=str):
+ """Get the value of an entry argument or device-tree-node property
+
+ Some node properties can be provided as arguments to binman. First check
+ the entry arguments, and fall back to the device tree if not found
+
+ Args:
+ name: Argument name
+ datatype: Data type (str or int)
+
+ Returns:
+ Value of argument as a string or int, or None if no value
+
+ Raises:
+ ValueError if the argument cannot be converted to in
+ """
+ value = control.GetEntryArg(name)
+ if value is not None:
+ if datatype == int:
+ try:
+ value = int(value)
+ except ValueError:
+ self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
+ (name, value))
+ elif datatype == str:
+ pass
+ else:
+ raise ValueError("GetArg() internal error: Unknown data type '%s'" %
+ datatype)
+ else:
+ value = fdt_util.GetDatatype(self._node, name, datatype)
+ return value
# Entry-type module for testing purposes. Not used in real images.
#
-from entry import Entry
+from collections import OrderedDict
+
+from entry import Entry, EntryArg
import fdt_util
import tools
self.process_fdt_ready = False
self.never_complete_process_fdt = fdt_util.GetBool(self._node,
'never-complete-process-fdt')
+ self.require_args = fdt_util.GetBool(self._node, 'require-args')
+
+ # This should be picked up by GetEntryArgsOrProps()
+ self.test_existing_prop = 'existing'
+ self.force_bad_datatype = fdt_util.GetBool(self._node,
+ 'force-bad-datatype')
+ (self.test_str_fdt, self.test_str_arg, self.test_int_fdt,
+ self.test_int_arg, existing) = self.GetEntryArgsOrProps([
+ EntryArg('test-str-fdt', str),
+ EntryArg('test-str-arg', str),
+ EntryArg('test-int-fdt', int),
+ EntryArg('test-int-arg', int),
+ EntryArg('test-existing-prop', str)], self.require_args)
+ if self.force_bad_datatype:
+ self.GetEntryArgsOrProps([EntryArg('test-bad-datatype-arg', bool)])
def ObtainContents(self):
if self.return_unknown_contents:
# options.verbosity = tout.DEBUG
return control.Binman(options, args)
- def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False):
+ def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
+ entry_args=None):
"""Run binman with a given test file
Args:
args.append('-m')
if update_dtb:
args.append('-up')
+ if entry_args:
+ for arg, value in entry_args.iteritems():
+ args.append('-a%s=%s' % (arg, value))
return self._DoBinman(*args)
def _SetupDtb(self, fname, outfile='u-boot.dtb'):
return data
def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
- update_dtb=False):
+ update_dtb=False, entry_args=None):
"""Run binman and return the resulting image
This runs binman with a given test file and then reads the resulting
dtb_data = self._SetupDtb(fname)
try:
- retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb)
+ retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
+ entry_args=entry_args)
self.assertEqual(0, retcode)
out_dtb_fname = control.GetFdtPath('u-boot.dtb')
self.assertIn('Could not complete processing of Fdt: remaining '
'[<_testing.Entry__testing', str(e.exception))
+ def testEntryArgs(self):
+ """Test passing arguments to entries from the command line"""
+ entry_args = {
+ 'test-str-arg': 'test1',
+ 'test-int-arg': '456',
+ }
+ self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
+ self.assertIn('image', control.images)
+ entry = control.images['image'].GetEntries()['_testing']
+ self.assertEqual('test0', entry.test_str_fdt)
+ self.assertEqual('test1', entry.test_str_arg)
+ self.assertEqual(123, entry.test_int_fdt)
+ self.assertEqual(456, entry.test_int_arg)
+
+ def testEntryArgsMissing(self):
+ """Test missing arguments and properties"""
+ entry_args = {
+ 'test-int-arg': '456',
+ }
+ self._DoReadFileDtb('63_entry_args_missing.dts', entry_args=entry_args)
+ entry = control.images['image'].GetEntries()['_testing']
+ self.assertEqual('test0', entry.test_str_fdt)
+ self.assertEqual(None, entry.test_str_arg)
+ self.assertEqual(None, entry.test_int_fdt)
+ self.assertEqual(456, entry.test_int_arg)
+
+ def testEntryArgsRequired(self):
+ """Test missing arguments and properties"""
+ entry_args = {
+ 'test-int-arg': '456',
+ }
+ with self.assertRaises(ValueError) as e:
+ self._DoReadFileDtb('64_entry_args_required.dts')
+ self.assertIn("Node '/binman/_testing': Missing required "
+ 'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
+ str(e.exception))
+
+ def testEntryArgsInvalidFormat(self):
+ """Test that an invalid entry-argument format is detected"""
+ args = ['-d', self.TestFile('64_entry_args_required.dts'), '-ano-value']
+ with self.assertRaises(ValueError) as e:
+ self._DoBinman(*args)
+ self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
+
+ def testEntryArgsInvalidInteger(self):
+ """Test that an invalid entry-argument integer is detected"""
+ entry_args = {
+ 'test-int-arg': 'abc',
+ }
+ with self.assertRaises(ValueError) as e:
+ self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
+ self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
+ "'test-int-arg' (value 'abc') to integer",
+ str(e.exception))
+
+ def testEntryArgsInvalidDatatype(self):
+ """Test that an invalid entry-argument datatype is detected
+
+ This test could be written in entry_test.py except that it needs
+ access to control.entry_args, which seems more than that module should
+ be able to see.
+ """
+ entry_args = {
+ 'test-bad-datatype-arg': '12',
+ }
+ with self.assertRaises(ValueError) as e:
+ self._DoReadFileDtb('65_entry_args_unknown_datatype.dts',
+ entry_args=entry_args)
+ self.assertIn('GetArg() internal error: Unknown data type ',
+ str(e.exception))
+
+
if __name__ == "__main__":
unittest.main()
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ _testing {
+ test-str-fdt = "test0";
+ test-int-fdt = <123>;
+ };
+ };
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ _testing {
+ test-str-fdt = "test0";
+ };
+ };
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ _testing {
+ require-args;
+ test-str-fdt = "test0";
+ };
+ };
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ _testing {
+ test-str-fdt = "test0";
+ test-int-fdt = <123>;
+ force-bad-datatype;
+ };
+ };
+};
if propname in node.props:
return True
return default
+
+def GetDatatype(node, propname, datatype):
+ """Get a value of a given type from a property
+
+ Args:
+ node: Node object to read from
+ propname: property name to read
+ datatype: Type to read (str or int)
+
+ Returns:
+ value read, or None if none
+
+ Raises:
+ ValueError if datatype is not str or int
+ """
+ if datatype == str:
+ return GetString(node, propname)
+ elif datatype == int:
+ return GetInt(node, propname)
+ raise ValueError("fdt_util internal error: Unknown data type '%s'" %
+ datatype)
self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
+ def testGetDataType(self):
+ self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
+ self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
+ str))
+ with self.assertRaises(ValueError) as e:
+ self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
+ bool))
+
def testFdtCellsToCpu(self):
val = self.node.props['intarray'].value
self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))