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