dtoc: Support scanning of structs in header files
Drivers can have private / platform data contained in structs and these struct definitions are generally kept in header files. In order to generate build-time devices, dtoc needs to generate code that declares the data contained in those structs. This generated code must include the relevant header file, to avoid a build error. We need a way for dtoc to scan header files for struct definitions. Then, when it wants to generate code that uses a struct, it can make sure it includes the correct header file, first. Add a parser for struct information, similar to drivers. Keep a dict of the structs that were found. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
1a8b4b9d94
commit
acf5cb88b4
@ -126,6 +126,22 @@ class UclassDriver:
|
||||
return hash(self.uclass_id)
|
||||
|
||||
|
||||
class Struct:
|
||||
"""Holds information about a struct definition
|
||||
|
||||
Attributes:
|
||||
name: Struct name, e.g. 'fred' if the struct is 'struct fred'
|
||||
fname: Filename containing the struct, in a format that C files can
|
||||
include, e.g. 'asm/clk.h'
|
||||
"""
|
||||
def __init__(self, name, fname):
|
||||
self.name = name
|
||||
self.fname =fname
|
||||
|
||||
def __repr__(self):
|
||||
return ("Struct(name='%s', fname='%s')" % (self.name, self.fname))
|
||||
|
||||
|
||||
class Scanner:
|
||||
"""Scanning of the U-Boot source tree
|
||||
|
||||
@ -151,6 +167,9 @@ class Scanner:
|
||||
_uclass: Dict of uclass information
|
||||
key: uclass name, e.g. 'UCLASS_I2C'
|
||||
value: UClassDriver
|
||||
_structs: Dict of all structs found in U-Boot:
|
||||
key: Name of struct
|
||||
value: Struct object
|
||||
"""
|
||||
def __init__(self, basedir, warning_disabled, drivers_additional):
|
||||
"""Set up a new Scanner
|
||||
@ -167,6 +186,7 @@ class Scanner:
|
||||
self._of_match = {}
|
||||
self._compat_to_driver = {}
|
||||
self._uclass = {}
|
||||
self._structs = {}
|
||||
|
||||
def get_normalized_compat_name(self, node):
|
||||
"""Get a node's normalized compat name
|
||||
@ -204,6 +224,41 @@ class Scanner:
|
||||
|
||||
return compat_list_c[0], compat_list_c[1:]
|
||||
|
||||
def _parse_structs(self, fname, buff):
|
||||
"""Parse a H file to extract struct definitions contained within
|
||||
|
||||
This parses 'struct xx {' definitions to figure out what structs this
|
||||
header defines.
|
||||
|
||||
Args:
|
||||
buff (str): Contents of file
|
||||
fname (str): Filename (to use when printing errors)
|
||||
"""
|
||||
structs = {}
|
||||
|
||||
re_struct = re.compile('^struct ([a-z0-9_]+) {$')
|
||||
re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)')
|
||||
prefix = ''
|
||||
for line in buff.splitlines():
|
||||
# Handle line continuation
|
||||
if prefix:
|
||||
line = prefix + line
|
||||
prefix = ''
|
||||
if line.endswith('\\'):
|
||||
prefix = line[:-1]
|
||||
continue
|
||||
|
||||
m_struct = re_struct.match(line)
|
||||
if m_struct:
|
||||
name = m_struct.group(1)
|
||||
include_dir = os.path.join(self._basedir, 'include')
|
||||
rel_fname = os.path.relpath(fname, include_dir)
|
||||
m_asm = re_asm.match(rel_fname)
|
||||
if m_asm:
|
||||
rel_fname = 'asm/' + m_asm.group(1)
|
||||
structs[name] = Struct(name, rel_fname)
|
||||
self._structs.update(structs)
|
||||
|
||||
@classmethod
|
||||
def _get_re_for_member(cls, member):
|
||||
"""_get_re_for_member: Get a compiled regular expression
|
||||
@ -482,6 +537,29 @@ class Scanner:
|
||||
continue
|
||||
self._driver_aliases[alias[1]] = alias[0]
|
||||
|
||||
def scan_header(self, fname):
|
||||
"""Scan a header file to build a list of struct definitions
|
||||
|
||||
It updates the following members:
|
||||
_structs - updated with new Struct records for each struct found
|
||||
in the file
|
||||
|
||||
Args
|
||||
fname: header filename to scan
|
||||
"""
|
||||
with open(fname, encoding='utf-8') as inf:
|
||||
try:
|
||||
buff = inf.read()
|
||||
except UnicodeDecodeError:
|
||||
# This seems to happen on older Python versions
|
||||
print("Skipping file '%s' due to unicode error" % fname)
|
||||
return
|
||||
|
||||
# If this file has any U_BOOT_DRIVER() declarations, process it to
|
||||
# obtain driver information
|
||||
if 'struct' in buff:
|
||||
self._parse_structs(fname, buff)
|
||||
|
||||
def scan_drivers(self):
|
||||
"""Scan the driver folders to build a list of driver names and aliases
|
||||
|
||||
@ -494,9 +572,11 @@ class Scanner:
|
||||
if rel_path.startswith('build') or rel_path.startswith('.git'):
|
||||
continue
|
||||
for fname in filenames:
|
||||
if not fname.endswith('.c'):
|
||||
continue
|
||||
self.scan_driver(dirpath + '/' + fname)
|
||||
pathname = dirpath + '/' + fname
|
||||
if fname.endswith('.c'):
|
||||
self.scan_driver(pathname)
|
||||
elif fname.endswith('.h'):
|
||||
self.scan_header(pathname)
|
||||
|
||||
for fname in self._drivers_additional:
|
||||
if not isinstance(fname, str) or len(fname) == 0:
|
||||
|
@ -318,3 +318,48 @@ UCLASS_DRIVER(i2c) = {
|
||||
scan._parse_uclass_driver('file.c', buff)
|
||||
self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'",
|
||||
str(exc.exception))
|
||||
|
||||
def test_struct_scan(self):
|
||||
"""Test collection of struct info"""
|
||||
buff = '''
|
||||
/* some comment */
|
||||
struct some_struct1 {
|
||||
struct i2c_msg *msgs;
|
||||
uint nmsgs;
|
||||
};
|
||||
'''
|
||||
scan = src_scan.Scanner(None, False, None)
|
||||
scan._basedir = os.path.join(OUR_PATH, '..', '..')
|
||||
scan._parse_structs('arch/arm/include/asm/file.h', buff)
|
||||
self.assertIn('some_struct1', scan._structs)
|
||||
struc = scan._structs['some_struct1']
|
||||
self.assertEqual('some_struct1', struc.name)
|
||||
self.assertEqual('asm/file.h', struc.fname)
|
||||
|
||||
buff = '''
|
||||
/* another comment */
|
||||
struct another_struct {
|
||||
int speed_hz;
|
||||
int max_transaction_bytes;
|
||||
};
|
||||
'''
|
||||
scan._parse_structs('include/file2.h', buff)
|
||||
self.assertIn('another_struct', scan._structs)
|
||||
struc = scan._structs['another_struct']
|
||||
self.assertEqual('another_struct', struc.name)
|
||||
self.assertEqual('file2.h', struc.fname)
|
||||
|
||||
self.assertEqual(2, len(scan._structs))
|
||||
|
||||
self.assertEqual("Struct(name='another_struct', fname='file2.h')",
|
||||
str(struc))
|
||||
|
||||
def test_struct_scan_errors(self):
|
||||
"""Test scanning a header file with an invalid unicode file"""
|
||||
output = tools.GetOutputFilename('output.h')
|
||||
tools.WriteFile(output, b'struct this is a test \x81 of bad unicode')
|
||||
|
||||
scan = src_scan.Scanner(None, False, None)
|
||||
with test_util.capture_sys_output() as (stdout, _):
|
||||
scan.scan_header(output)
|
||||
self.assertIn('due to unicode error', stdout.getvalue())
|
||||
|
Loading…
Reference in New Issue
Block a user