
From: Simon Glass <sjg@chromium.org> We wish to support .buildman files in the config/ directory which can associate boards with config fragments. Add a parser for this. Signed-off-by: Simon Glass <sjg@chromium.org> --- tools/buildman/boards.py | 89 ++++++++++++++++++++++++++++++++++++- tools/buildman/func_test.py | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 1 deletion(-) diff --git a/tools/buildman/boards.py b/tools/buildman/boards.py index 2fe43c3fc89..99d9c5fb9bd 100644 --- a/tools/buildman/boards.py +++ b/tools/buildman/boards.py @@ -5,7 +5,7 @@ """Maintains a list of boards and allows them to be selected""" -from collections import OrderedDict +from collections import OrderedDict, namedtuple import errno import fnmatch import glob @@ -35,6 +35,8 @@ COMMENT_BLOCK = f'''# ''' +Extended = namedtuple('Extended', 'name,desc,fragments,targets') + def try_remove(fname): """Remove a file ignoring 'No such file or directory' error. @@ -903,3 +905,88 @@ class Boards: print(warn, file=sys.stderr) self.format_and_output(params_list, output) return not warnings + + +class ExtendedParser: + """Parser for extended-board (.buildman) files""" + def __init__(self): + self.extended = [] + self.name = None + self.fragments = [] + self.targets = [] + self.in_targets = False + self.desc = None + + def start(self): + """Start a new extended board""" + self.name = None + self.fragments = [] + self.targets = [] + self.in_targets = False + self.desc = None + + def finish(self): + """Finish any pending extended board""" + if self.name: + self.extended.append(Extended(self.name, self.desc, self.fragments, + self.targets)) + self.start() + + @staticmethod + def parse_file(fname): + """Parse a file and return the result""" + return ExtendedParser.parse_data(fname, + tools.read_file(fname, binary=False)) + + @staticmethod + def parse_data(fname, data): + """Parse a file and return the result""" + parser = ExtendedParser() + parser.parse(fname, data) + return parser.extended + + def parse(self, fname, data): + """Parse the file""" + self.start() + for seq, line in enumerate(data.splitlines()): + linenum = seq + 1 + if not line.strip() or line[0] == '#': + continue + if line[0] == ' ': + if not self.in_targets: + raise ValueError(f'{fname}:{linenum}: Unexpected indent') + if '=' in line: + pair = line.split('=') + if len(pair) != 2: + raise ValueError(f'{fname}:{linenum}: Invalid CONFIG syntax') + first, rest = pair + cfg = first.strip() + value = rest.strip() + self.targets.append([cfg, value]) + else: + target = line.strip() + if ' ' in target: + raise ValueError(f'{fname}:{linenum}: Invalid target regex') + self.targets.append(['regex', line.strip()]) + else: + pair = line.split(':') + if len(pair) != 2: + raise ValueError(f'{fname}:{linenum}: Invalid tag') + tag, rest = pair + value = rest.strip() + if tag == 'name': + self.finish() + if ' ' in value: + raise ValueError(f'{fname}:{linenum}: Invalid name') + self.name = value + elif tag == 'desc': + self.desc = value + elif tag == 'fragment': + self.fragments.append(value) + elif tag == 'targets': + self.in_targets = True + else: + raise ValueError(f"{fname}:{linenum}: Unknown tag '{tag}'") + + self.finish() + return self.extended diff --git a/tools/buildman/func_test.py b/tools/buildman/func_test.py index 7e837fe075e..968765b713c 100644 --- a/tools/buildman/func_test.py +++ b/tools/buildman/func_test.py @@ -14,6 +14,7 @@ import unittest from buildman import board from buildman import boards +from buildman.boards import Extended from buildman import bsettings from buildman import cmdline from buildman import control @@ -1177,3 +1178,91 @@ CONFIG_SOC="fred" # It should appear at the end of the build line self.assertEqual(b'u-boot.dtb', lines[1].split()[-1]) + + def test_extended(self): + """Test parsing of extended (.buildman) files""" + data = ''' +name: acpi_boards +desc: Build RISC-V QEMU builds with ACPI +fragment: acpi +targets: + qemu_riscv* + +name: am62x_dfu +desc: Build Android variant of 'k3' boards, with DFU +fragment: am62x_r5_usbdfu +fragment: am62x_a53_android +targets: + CONFIG_SYS_SOC="k3" +''' + fname = os.path.join(self._base_dir, 'try.buildman') + tools.write_file(fname, data.encode('utf-8')) + result = boards.ExtendedParser.parse_file(fname) + self.maxDiff = None + self.assertEqual([ + Extended(name='acpi_boards', + desc='Build RISC-V QEMU builds with ACPI', + fragments=['acpi'], + targets=[ + ['regex', 'qemu_riscv*']]), + Extended(name='am62x_dfu', + desc="Build Android variant of 'k3' boards, with DFU", + fragments=['am62x_r5_usbdfu', 'am62x_a53_android'], + targets=[ + ['CONFIG_SYS_SOC', '"k3"']] + )], result) + + def test_extended_bad_indent(self): + """Test unexpected indentation""" + with self.assertRaises(ValueError) as exc: + boards.ExtendedParser.parse_data('mary', ' name: fred') + self.assertEqual('mary:1: Unexpected indent', + str(exc.exception)) + + def test_extended_invalid_config(self): + """Test a bad CONFIG_xxx= line""" + with self.assertRaises(ValueError) as exc: + boards.ExtendedParser.parse_data('anna', ''' +name: joan +targets: + CONFIG_SOMETHING=val=" +''') + self.assertEqual('anna:4: Invalid CONFIG syntax', + str(exc.exception)) + + def test_extended_invalid_target(self): + """Test a bad target regex""" + with self.assertRaises(ValueError) as exc: + boards.ExtendedParser.parse_data('john', ''' +name: fred +targets: + qemu* *riscv +''') + self.assertEqual('john:4: Invalid target regex', + str(exc.exception)) + + def test_extended_invalid_tag(self): + """Test a bad tag""" + with self.assertRaises(ValueError) as exc: + boards.ExtendedParser.parse_data('hannah', ''' +name: frank :was here +''') + self.assertEqual('hannah:2: Invalid tag', + str(exc.exception)) + + def test_extended_unknown_tag(self): + """Test a unknown tag""" + with self.assertRaises(ValueError) as exc: + boards.ExtendedParser.parse_data('susan', ''' +name: allan +something: me +''') + self.assertEqual("susan:3: Unknown tag 'something'", + str(exc.exception)) + + def test_extended_bad_name(self): + """Test a name with an invalid char""" + with self.assertRaises(ValueError) as exc: + boards.ExtendedParser.parse_data('bert', 'name: katie was here') + self.assertEqual('bert:1: Invalid name', + str(exc.exception)) -- 2.43.0