xref: /openbmc/u-boot/tools/buildman/board.py (revision 8bbfb40e)
1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2012 The Chromium OS Authors.
3
4import re
5
6class Expr:
7    """A single regular expression for matching boards to build"""
8
9    def __init__(self, expr):
10        """Set up a new Expr object.
11
12        Args:
13            expr: String cotaining regular expression to store
14        """
15        self._expr = expr
16        self._re = re.compile(expr)
17
18    def Matches(self, props):
19        """Check if any of the properties match the regular expression.
20
21        Args:
22           props: List of properties to check
23        Returns:
24           True if any of the properties match the regular expression
25        """
26        for prop in props:
27            if self._re.match(prop):
28                return True
29        return False
30
31    def __str__(self):
32        return self._expr
33
34class Term:
35    """A list of expressions each of which must match with properties.
36
37    This provides a list of 'AND' expressions, meaning that each must
38    match the board properties for that board to be built.
39    """
40    def __init__(self):
41        self._expr_list = []
42        self._board_count = 0
43
44    def AddExpr(self, expr):
45        """Add an Expr object to the list to check.
46
47        Args:
48            expr: New Expr object to add to the list of those that must
49                  match for a board to be built.
50        """
51        self._expr_list.append(Expr(expr))
52
53    def __str__(self):
54        """Return some sort of useful string describing the term"""
55        return '&'.join([str(expr) for expr in self._expr_list])
56
57    def Matches(self, props):
58        """Check if any of the properties match this term
59
60        Each of the expressions in the term is checked. All must match.
61
62        Args:
63           props: List of properties to check
64        Returns:
65           True if all of the expressions in the Term match, else False
66        """
67        for expr in self._expr_list:
68            if not expr.Matches(props):
69                return False
70        return True
71
72class Board:
73    """A particular board that we can build"""
74    def __init__(self, status, arch, cpu, soc, vendor, board_name, target, options):
75        """Create a new board type.
76
77        Args:
78            status: define whether the board is 'Active' or 'Orphaned'
79            arch: Architecture name (e.g. arm)
80            cpu: Cpu name (e.g. arm1136)
81            soc: Name of SOC, or '' if none (e.g. mx31)
82            vendor: Name of vendor (e.g. armltd)
83            board_name: Name of board (e.g. integrator)
84            target: Target name (use make <target>_defconfig to configure)
85            options: board-specific options (e.g. integratorcp:CM1136)
86        """
87        self.target = target
88        self.arch = arch
89        self.cpu = cpu
90        self.board_name = board_name
91        self.vendor = vendor
92        self.soc = soc
93        self.options = options
94        self.props = [self.target, self.arch, self.cpu, self.board_name,
95                      self.vendor, self.soc, self.options]
96        self.build_it = False
97
98
99class Boards:
100    """Manage a list of boards."""
101    def __init__(self):
102        # Use a simple list here, sinc OrderedDict requires Python 2.7
103        self._boards = []
104
105    def AddBoard(self, board):
106        """Add a new board to the list.
107
108        The board's target member must not already exist in the board list.
109
110        Args:
111            board: board to add
112        """
113        self._boards.append(board)
114
115    def ReadBoards(self, fname):
116        """Read a list of boards from a board file.
117
118        Create a board object for each and add it to our _boards list.
119
120        Args:
121            fname: Filename of boards.cfg file
122        """
123        with open(fname, 'r') as fd:
124            for line in fd:
125                if line[0] == '#':
126                    continue
127                fields = line.split()
128                if not fields:
129                    continue
130                for upto in range(len(fields)):
131                    if fields[upto] == '-':
132                        fields[upto] = ''
133                while len(fields) < 8:
134                    fields.append('')
135                if len(fields) > 8:
136                    fields = fields[:8]
137
138                board = Board(*fields)
139                self.AddBoard(board)
140
141
142    def GetList(self):
143        """Return a list of available boards.
144
145        Returns:
146            List of Board objects
147        """
148        return self._boards
149
150    def GetDict(self):
151        """Build a dictionary containing all the boards.
152
153        Returns:
154            Dictionary:
155                key is board.target
156                value is board
157        """
158        board_dict = {}
159        for board in self._boards:
160            board_dict[board.target] = board
161        return board_dict
162
163    def GetSelectedDict(self):
164        """Return a dictionary containing the selected boards
165
166        Returns:
167            List of Board objects that are marked selected
168        """
169        board_dict = {}
170        for board in self._boards:
171            if board.build_it:
172                board_dict[board.target] = board
173        return board_dict
174
175    def GetSelected(self):
176        """Return a list of selected boards
177
178        Returns:
179            List of Board objects that are marked selected
180        """
181        return [board for board in self._boards if board.build_it]
182
183    def GetSelectedNames(self):
184        """Return a list of selected boards
185
186        Returns:
187            List of board names that are marked selected
188        """
189        return [board.target for board in self._boards if board.build_it]
190
191    def _BuildTerms(self, args):
192        """Convert command line arguments to a list of terms.
193
194        This deals with parsing of the arguments. It handles the '&'
195        operator, which joins several expressions into a single Term.
196
197        For example:
198            ['arm & freescale sandbox', 'tegra']
199
200        will produce 3 Terms containing expressions as follows:
201            arm, freescale
202            sandbox
203            tegra
204
205        The first Term has two expressions, both of which must match for
206        a board to be selected.
207
208        Args:
209            args: List of command line arguments
210        Returns:
211            A list of Term objects
212        """
213        syms = []
214        for arg in args:
215            for word in arg.split():
216                sym_build = []
217                for term in word.split('&'):
218                    if term:
219                        sym_build.append(term)
220                    sym_build.append('&')
221                syms += sym_build[:-1]
222        terms = []
223        term = None
224        oper = None
225        for sym in syms:
226            if sym == '&':
227                oper = sym
228            elif oper:
229                term.AddExpr(sym)
230                oper = None
231            else:
232                if term:
233                    terms.append(term)
234                term = Term()
235                term.AddExpr(sym)
236        if term:
237            terms.append(term)
238        return terms
239
240    def SelectBoards(self, args, exclude=[], boards=None):
241        """Mark boards selected based on args
242
243        Normally either boards (an explicit list of boards) or args (a list of
244        terms to match against) is used. It is possible to specify both, in
245        which case they are additive.
246
247        If boards and args are both empty, all boards are selected.
248
249        Args:
250            args: List of strings specifying boards to include, either named,
251                  or by their target, architecture, cpu, vendor or soc. If
252                  empty, all boards are selected.
253            exclude: List of boards to exclude, regardless of 'args'
254            boards: List of boards to build
255
256        Returns:
257            Tuple
258                Dictionary which holds the list of boards which were selected
259                    due to each argument, arranged by argument.
260                List of errors found
261        """
262        result = {}
263        warnings = []
264        terms = self._BuildTerms(args)
265
266        result['all'] = []
267        for term in terms:
268            result[str(term)] = []
269
270        exclude_list = []
271        for expr in exclude:
272            exclude_list.append(Expr(expr))
273
274        found = []
275        for board in self._boards:
276            matching_term = None
277            build_it = False
278            if terms:
279                match = False
280                for term in terms:
281                    if term.Matches(board.props):
282                        matching_term = str(term)
283                        build_it = True
284                        break
285            elif boards:
286                if board.target in boards:
287                    build_it = True
288                    found.append(board.target)
289            else:
290                build_it = True
291
292            # Check that it is not specifically excluded
293            for expr in exclude_list:
294                if expr.Matches(board.props):
295                    build_it = False
296                    break
297
298            if build_it:
299                board.build_it = True
300                if matching_term:
301                    result[matching_term].append(board.target)
302                result['all'].append(board.target)
303
304        if boards:
305            remaining = set(boards) - set(found)
306            if remaining:
307                warnings.append('Boards not found: %s\n' % ', '.join(remaining))
308
309        return result, warnings
310