From c52bd22539f3bb009f73cc1608b155813f7e48a0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 11 Jul 2022 19:04:03 -0600 Subject: [PATCH] buildman: Split out Boards into its own file Use a separate file for the Boards class so that its name matches the module name. Fix up the function names to match the pylint style and fix some other warnings. Signed-off-by: Simon Glass --- tools/buildman/board.py | 281 +--------------------------------- tools/buildman/boards.py | 290 ++++++++++++++++++++++++++++++++++++ tools/buildman/control.py | 4 +- tools/buildman/func_test.py | 11 +- tools/buildman/test.py | 3 +- 5 files changed, 301 insertions(+), 288 deletions(-) create mode 100644 tools/buildman/boards.py diff --git a/tools/buildman/board.py b/tools/buildman/board.py index ebb9d6f67d..3268b39e35 100644 --- a/tools/buildman/board.py +++ b/tools/buildman/board.py @@ -1,74 +1,8 @@ # SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2012 The Chromium OS Authors. -from collections import OrderedDict -import re -class Expr: - """A single regular expression for matching boards to build""" - - def __init__(self, expr): - """Set up a new Expr object. - - Args: - expr: String cotaining regular expression to store - """ - self._expr = expr - self._re = re.compile(expr) - - def matches(self, props): - """Check if any of the properties match the regular expression. - - Args: - props: List of properties to check - Returns: - True if any of the properties match the regular expression - """ - for prop in props: - if self._re.match(prop): - return True - return False - - def __str__(self): - return self._expr - -class Term: - """A list of expressions each of which must match with properties. - - This provides a list of 'AND' expressions, meaning that each must - match the board properties for that board to be built. - """ - def __init__(self): - self._expr_list = [] - self._board_count = 0 - - def add_expr(self, expr): - """Add an Expr object to the list to check. - - Args: - expr: New Expr object to add to the list of those that must - match for a board to be built. - """ - self._expr_list.append(Expr(expr)) - - def __str__(self): - """Return some sort of useful string describing the term""" - return '&'.join([str(expr) for expr in self._expr_list]) - - def matches(self, props): - """Check if any of the properties match this term - - Each of the expressions in the term is checked. All must match. - - Args: - props: List of properties to check - Returns: - True if all of the expressions in the Term match, else False - """ - for expr in self._expr_list: - if not expr.matches(props): - return False - return True +"""A single board which can be selected and built""" class Board: """A particular board that we can build""" @@ -95,216 +29,3 @@ class Board: self.props = [self.target, self.arch, self.cpu, self.board_name, self.vendor, self.soc, self.options] self.build_it = False - - -class Boards: - """Manage a list of boards.""" - def __init__(self): - # Use a simple list here, sinc OrderedDict requires Python 2.7 - self._boards = [] - - def add_board(self, brd): - """Add a new board to the list. - - The board's target member must not already exist in the board list. - - Args: - brd: board to add - """ - self._boards.append(brd) - - def read_boards(self, fname): - """Read a list of boards from a board file. - - Create a Board object for each and add it to our _boards list. - - Args: - fname: Filename of boards.cfg file - """ - with open(fname, 'r', encoding='utf-8') as fd: - for line in fd: - if line[0] == '#': - continue - fields = line.split() - if not fields: - continue - for upto in range(len(fields)): - if fields[upto] == '-': - fields[upto] = '' - while len(fields) < 8: - fields.append('') - if len(fields) > 8: - fields = fields[:8] - - brd = Board(*fields) - self.add_board(brd) - - - def get_list(self): - """Return a list of available boards. - - Returns: - List of Board objects - """ - return self._boards - - def get_dict(self): - """Build a dictionary containing all the boards. - - Returns: - Dictionary: - key is board.target - value is board - """ - board_dict = OrderedDict() - for brd in self._boards: - board_dict[brd.target] = brd - return board_dict - - def get_selected_dict(self): - """Return a dictionary containing the selected boards - - Returns: - List of Board objects that are marked selected - """ - board_dict = OrderedDict() - for brd in self._boards: - if brd.build_it: - board_dict[brd.target] = brd - return board_dict - - def get_selected(self): - """Return a list of selected boards - - Returns: - List of Board objects that are marked selected - """ - return [brd for brd in self._boards if brd.build_it] - - def get_selected_names(self): - """Return a list of selected boards - - Returns: - List of board names that are marked selected - """ - return [brd.target for brd in self._boards if brd.build_it] - - def _build_terms(self, args): - """Convert command line arguments to a list of terms. - - This deals with parsing of the arguments. It handles the '&' - operator, which joins several expressions into a single Term. - - For example: - ['arm & freescale sandbox', 'tegra'] - - will produce 3 Terms containing expressions as follows: - arm, freescale - sandbox - tegra - - The first Term has two expressions, both of which must match for - a board to be selected. - - Args: - args: List of command line arguments - Returns: - A list of Term objects - """ - syms = [] - for arg in args: - for word in arg.split(): - sym_build = [] - for term in word.split('&'): - if term: - sym_build.append(term) - sym_build.append('&') - syms += sym_build[:-1] - terms = [] - term = None - oper = None - for sym in syms: - if sym == '&': - oper = sym - elif oper: - term.add_expr(sym) - oper = None - else: - if term: - terms.append(term) - term = Term() - term.add_expr(sym) - if term: - terms.append(term) - return terms - - def select_boards(self, args, exclude=[], brds=None): - """Mark boards selected based on args - - Normally either boards (an explicit list of boards) or args (a list of - terms to match against) is used. It is possible to specify both, in - which case they are additive. - - If brds and args are both empty, all boards are selected. - - Args: - args: List of strings specifying boards to include, either named, - or by their target, architecture, cpu, vendor or soc. If - empty, all boards are selected. - exclude: List of boards to exclude, regardless of 'args' - brds: List of boards to build - - Returns: - Tuple - Dictionary which holds the list of boards which were selected - due to each argument, arranged by argument. - List of errors found - """ - result = OrderedDict() - warnings = [] - terms = self._build_terms(args) - - result['all'] = [] - for term in terms: - result[str(term)] = [] - - exclude_list = [] - for expr in exclude: - exclude_list.append(Expr(expr)) - - found = [] - for brd in self._boards: - matching_term = None - build_it = False - if terms: - match = False - for term in terms: - if term.matches(brd.props): - matching_term = str(term) - build_it = True - break - elif brds: - if brd.target in brds: - build_it = True - found.append(brd.target) - else: - build_it = True - - # Check that it is not specifically excluded - for expr in exclude_list: - if expr.matches(brd.props): - build_it = False - break - - if build_it: - brd.build_it = True - if matching_term: - result[matching_term].append(brd.target) - result['all'].append(brd.target) - - if brds: - remaining = set(brds) - set(found) - if remaining: - warnings.append('Boards not found: %s\n' % ', '.join(remaining)) - - return result, warnings diff --git a/tools/buildman/boards.py b/tools/buildman/boards.py new file mode 100644 index 0000000000..ec143f9e0f --- /dev/null +++ b/tools/buildman/boards.py @@ -0,0 +1,290 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2012 The Chromium OS Authors. + +"""Maintains a list of boards and allows them to be selected""" + +from collections import OrderedDict +import re + +from buildman import board + + +class Expr: + """A single regular expression for matching boards to build""" + + def __init__(self, expr): + """Set up a new Expr object. + + Args: + expr: String cotaining regular expression to store + """ + self._expr = expr + self._re = re.compile(expr) + + def matches(self, props): + """Check if any of the properties match the regular expression. + + Args: + props: List of properties to check + Returns: + True if any of the properties match the regular expression + """ + for prop in props: + if self._re.match(prop): + return True + return False + + def __str__(self): + return self._expr + +class Term: + """A list of expressions each of which must match with properties. + + This provides a list of 'AND' expressions, meaning that each must + match the board properties for that board to be built. + """ + def __init__(self): + self._expr_list = [] + self._board_count = 0 + + def add_expr(self, expr): + """Add an Expr object to the list to check. + + Args: + expr: New Expr object to add to the list of those that must + match for a board to be built. + """ + self._expr_list.append(Expr(expr)) + + def __str__(self): + """Return some sort of useful string describing the term""" + return '&'.join([str(expr) for expr in self._expr_list]) + + def matches(self, props): + """Check if any of the properties match this term + + Each of the expressions in the term is checked. All must match. + + Args: + props: List of properties to check + Returns: + True if all of the expressions in the Term match, else False + """ + for expr in self._expr_list: + if not expr.matches(props): + return False + return True + + +class Boards: + """Manage a list of boards.""" + def __init__(self): + # Use a simple list here, sinc OrderedDict requires Python 2.7 + self._boards = [] + + def add_board(self, brd): + """Add a new board to the list. + + The board's target member must not already exist in the board list. + + Args: + brd: board to add + """ + self._boards.append(brd) + + def read_boards(self, fname): + """Read a list of boards from a board file. + + Create a Board object for each and add it to our _boards list. + + Args: + fname: Filename of boards.cfg file + """ + with open(fname, 'r', encoding='utf-8') as inf: + for line in inf: + if line[0] == '#': + continue + fields = line.split() + if not fields: + continue + for upto, field in enumerate(fields): + if field == '-': + fields[upto] = '' + while len(fields) < 8: + fields.append('') + if len(fields) > 8: + fields = fields[:8] + + brd = board.Board(*fields) + self.add_board(brd) + + + def get_list(self): + """Return a list of available boards. + + Returns: + List of Board objects + """ + return self._boards + + def get_dict(self): + """Build a dictionary containing all the boards. + + Returns: + Dictionary: + key is board.target + value is board + """ + board_dict = OrderedDict() + for brd in self._boards: + board_dict[brd.target] = brd + return board_dict + + def get_selected_dict(self): + """Return a dictionary containing the selected boards + + Returns: + List of Board objects that are marked selected + """ + board_dict = OrderedDict() + for brd in self._boards: + if brd.build_it: + board_dict[brd.target] = brd + return board_dict + + def get_selected(self): + """Return a list of selected boards + + Returns: + List of Board objects that are marked selected + """ + return [brd for brd in self._boards if brd.build_it] + + def get_selected_names(self): + """Return a list of selected boards + + Returns: + List of board names that are marked selected + """ + return [brd.target for brd in self._boards if brd.build_it] + + @classmethod + def _build_terms(cls, args): + """Convert command line arguments to a list of terms. + + This deals with parsing of the arguments. It handles the '&' + operator, which joins several expressions into a single Term. + + For example: + ['arm & freescale sandbox', 'tegra'] + + will produce 3 Terms containing expressions as follows: + arm, freescale + sandbox + tegra + + The first Term has two expressions, both of which must match for + a board to be selected. + + Args: + args: List of command line arguments + Returns: + A list of Term objects + """ + syms = [] + for arg in args: + for word in arg.split(): + sym_build = [] + for term in word.split('&'): + if term: + sym_build.append(term) + sym_build.append('&') + syms += sym_build[:-1] + terms = [] + term = None + oper = None + for sym in syms: + if sym == '&': + oper = sym + elif oper: + term.add_expr(sym) + oper = None + else: + if term: + terms.append(term) + term = Term() + term.add_expr(sym) + if term: + terms.append(term) + return terms + + def select_boards(self, args, exclude=None, brds=None): + """Mark boards selected based on args + + Normally either boards (an explicit list of boards) or args (a list of + terms to match against) is used. It is possible to specify both, in + which case they are additive. + + If brds and args are both empty, all boards are selected. + + Args: + args: List of strings specifying boards to include, either named, + or by their target, architecture, cpu, vendor or soc. If + empty, all boards are selected. + exclude: List of boards to exclude, regardless of 'args' + brds: List of boards to build + + Returns: + Tuple + Dictionary which holds the list of boards which were selected + due to each argument, arranged by argument. + List of errors found + """ + result = OrderedDict() + warnings = [] + terms = self._build_terms(args) + + result['all'] = [] + for term in terms: + result[str(term)] = [] + + exclude_list = [] + if exclude: + for expr in exclude: + exclude_list.append(Expr(expr)) + + found = [] + for brd in self._boards: + matching_term = None + build_it = False + if terms: + for term in terms: + if term.matches(brd.props): + matching_term = str(term) + build_it = True + break + elif brds: + if brd.target in brds: + build_it = True + found.append(brd.target) + else: + build_it = True + + # Check that it is not specifically excluded + for expr in exclude_list: + if expr.matches(brd.props): + build_it = False + break + + if build_it: + brd.build_it = True + if matching_term: + result[matching_term].append(brd.target) + result['all'].append(brd.target) + + if brds: + remaining = set(brds) - set(found) + if remaining: + warnings.append(f"Boards not found: {', '.join(remaining)}\n") + + return result, warnings diff --git a/tools/buildman/control.py b/tools/buildman/control.py index a5c1c2e51c..8d3e781d51 100644 --- a/tools/buildman/control.py +++ b/tools/buildman/control.py @@ -8,7 +8,7 @@ import shutil import subprocess import sys -from buildman import board +from buildman import boards from buildman import bsettings from buildman import cfgutil from buildman import toolchain @@ -197,7 +197,7 @@ def DoBuildman(options, args, toolchains=None, make_func=None, brds=None, if status != 0: sys.exit("Failed to generate boards.cfg") - brds = board.Boards() + brds = boards.Boards() brds.read_boards(board_file) exclude = [] diff --git a/tools/buildman/func_test.py b/tools/buildman/func_test.py index 6806ea7fbe..f12e996634 100644 --- a/tools/buildman/func_test.py +++ b/tools/buildman/func_test.py @@ -9,6 +9,7 @@ import tempfile import unittest from buildman import board +from buildman import boards from buildman import bsettings from buildman import cmdline from buildman import control @@ -187,7 +188,7 @@ class TestFunctional(unittest.TestCase): self.setupToolchains() self._toolchains.Add('arm-gcc', test=False) self._toolchains.Add('powerpc-gcc', test=False) - self._boards = board.Boards() + self._boards = boards.Boards() for brd in BOARDS: self._boards.add_board(board.Board(*brd)) @@ -442,7 +443,7 @@ class TestFunctional(unittest.TestCase): def testNoBoards(self): """Test that buildman aborts when there are no boards""" - self._boards = board.Boards() + self._boards = boards.Boards() with self.assertRaises(SystemExit): self._RunControl() @@ -580,7 +581,7 @@ class TestFunctional(unittest.TestCase): def testWorkInOutput(self): """Test the -w option which should write directly to the output dir""" - board_list = board.Boards() + board_list = boards.Boards() board_list.add_board(board.Board(*BOARDS[0])) self._RunControl('-o', self._output_dir, '-w', clean_dir=False, brds=board_list) @@ -599,14 +600,14 @@ class TestFunctional(unittest.TestCase): self.assertFalse( os.path.exists(os.path.join(self._output_dir, 'u-boot'))) - board_list = board.Boards() + board_list = boards.Boards() board_list.add_board(board.Board(*BOARDS[0])) with self.assertRaises(SystemExit) as e: self._RunControl('-b', self._test_branch, '-o', self._output_dir, '-w', clean_dir=False, brds=board_list) self.assertIn("single commit", str(e.exception)) - board_list = board.Boards() + board_list = boards.Boards() board_list.add_board(board.Board(*BOARDS[0])) with self.assertRaises(SystemExit) as e: self._RunControl('-w', clean_dir=False) diff --git a/tools/buildman/test.py b/tools/buildman/test.py index d7306fb4df..daf5467503 100644 --- a/tools/buildman/test.py +++ b/tools/buildman/test.py @@ -10,6 +10,7 @@ import time import unittest from buildman import board +from buildman import boards from buildman import bsettings from buildman import builder from buildman import cfgutil @@ -131,7 +132,7 @@ class TestBuild(unittest.TestCase): self.commits.append(comm) # Set up boards to build - self.brds = board.Boards() + self.brds = boards.Boards() for brd in BOARDS: self.brds.add_board(board.Board(*brd)) self.brds.select_boards([])