blob: 447aaabea86a6be89444b7842b8ffd52d5c54705 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassc05694f2013-04-03 11:07:16 +00002# Copyright (c) 2012 The Chromium OS Authors.
Simon Glassc05694f2013-04-03 11:07:16 +00003
Simon Glassc78ed662019-10-31 07:42:53 -06004from collections import OrderedDict
Stephen Warren08447632013-10-10 10:00:20 -06005import re
6
Simon Glassaa40f9a2014-08-09 15:33:08 -06007class 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
35class 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
Simon Glassc05694f2013-04-03 11:07:16 +000073class Board:
74 """A particular board that we can build"""
Andreas Bießmannb66a1292013-09-19 10:08:45 +020075 def __init__(self, status, arch, cpu, soc, vendor, board_name, target, options):
Simon Glassc05694f2013-04-03 11:07:16 +000076 """Create a new board type.
77
78 Args:
Andreas Bießmannb66a1292013-09-19 10:08:45 +020079 status: define whether the board is 'Active' or 'Orphaned'
Simon Glassc05694f2013-04-03 11:07:16 +000080 arch: Architecture name (e.g. arm)
81 cpu: Cpu name (e.g. arm1136)
Simon Glassc05694f2013-04-03 11:07:16 +000082 soc: Name of SOC, or '' if none (e.g. mx31)
Andreas Bießmannb66a1292013-09-19 10:08:45 +020083 vendor: Name of vendor (e.g. armltd)
84 board_name: Name of board (e.g. integrator)
Masahiro Yamadae9bc8d22014-07-30 14:08:22 +090085 target: Target name (use make <target>_defconfig to configure)
Simon Glassc05694f2013-04-03 11:07:16 +000086 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
Simon Glassc05694f2013-04-03 11:07:16 +000094 self.options = options
Tom Rinib7876c82016-11-04 22:59:45 -040095 self.props = [self.target, self.arch, self.cpu, self.board_name,
96 self.vendor, self.soc, self.options]
Simon Glassc05694f2013-04-03 11:07:16 +000097 self.build_it = False
98
99
100class 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 """
Simon Glassc78ed662019-10-31 07:42:53 -0600124 with open(fname, 'r', encoding='utf-8') as fd:
Simon Glassc05694f2013-04-03 11:07:16 +0000125 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] = ''
Andreas Bießmannb66a1292013-09-19 10:08:45 +0200134 while len(fields) < 8:
Simon Glassc05694f2013-04-03 11:07:16 +0000135 fields.append('')
Andreas Bießmannb66a1292013-09-19 10:08:45 +0200136 if len(fields) > 8:
137 fields = fields[:8]
Simon Glassc05694f2013-04-03 11:07:16 +0000138
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 """
Simon Glassc78ed662019-10-31 07:42:53 -0600159 board_dict = OrderedDict()
Simon Glassc05694f2013-04-03 11:07:16 +0000160 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 """
Simon Glassc78ed662019-10-31 07:42:53 -0600170 board_dict = OrderedDict()
Simon Glassc05694f2013-04-03 11:07:16 +0000171 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
Simon Glassaa40f9a2014-08-09 15:33:08 -0600192 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
Simon Glassd9eb9f02018-06-11 23:26:46 -0600241 def SelectBoards(self, args, exclude=[], boards=None):
Simon Glassc05694f2013-04-03 11:07:16 +0000242 """Mark boards selected based on args
243
Simon Glassd9eb9f02018-06-11 23:26:46 -0600244 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
Simon Glassc05694f2013-04-03 11:07:16 +0000250 Args:
Simon Glass924c73a2014-08-28 09:43:41 -0600251 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'
Simon Glassd9eb9f02018-06-11 23:26:46 -0600255 boards: List of boards to build
Simon Glassc05694f2013-04-03 11:07:16 +0000256
257 Returns:
Simon Glassd9eb9f02018-06-11 23:26:46 -0600258 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
Simon Glassc05694f2013-04-03 11:07:16 +0000262 """
Simon Glassc78ed662019-10-31 07:42:53 -0600263 result = OrderedDict()
Simon Glassd9eb9f02018-06-11 23:26:46 -0600264 warnings = []
Simon Glassaa40f9a2014-08-09 15:33:08 -0600265 terms = self._BuildTerms(args)
266
Simon Glass6af145f2017-01-23 05:38:56 -0700267 result['all'] = []
Simon Glassaa40f9a2014-08-09 15:33:08 -0600268 for term in terms:
Simon Glass6af145f2017-01-23 05:38:56 -0700269 result[str(term)] = []
Simon Glassc05694f2013-04-03 11:07:16 +0000270
Simon Glass924c73a2014-08-28 09:43:41 -0600271 exclude_list = []
272 for expr in exclude:
273 exclude_list.append(Expr(expr))
274
Simon Glassd9eb9f02018-06-11 23:26:46 -0600275 found = []
Simon Glassc05694f2013-04-03 11:07:16 +0000276 for board in self._boards:
Simon Glass924c73a2014-08-28 09:43:41 -0600277 matching_term = None
278 build_it = False
Simon Glassaa40f9a2014-08-09 15:33:08 -0600279 if terms:
280 match = False
281 for term in terms:
282 if term.Matches(board.props):
Simon Glass924c73a2014-08-28 09:43:41 -0600283 matching_term = str(term)
284 build_it = True
Simon Glassaa40f9a2014-08-09 15:33:08 -0600285 break
Simon Glassd9eb9f02018-06-11 23:26:46 -0600286 elif boards:
287 if board.target in boards:
288 build_it = True
289 found.append(board.target)
Simon Glassc05694f2013-04-03 11:07:16 +0000290 else:
Simon Glass924c73a2014-08-28 09:43:41 -0600291 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:
Simon Glassc05694f2013-04-03 11:07:16 +0000300 board.build_it = True
Simon Glass924c73a2014-08-28 09:43:41 -0600301 if matching_term:
Simon Glass6af145f2017-01-23 05:38:56 -0700302 result[matching_term].append(board.target)
303 result['all'].append(board.target)
Simon Glassc05694f2013-04-03 11:07:16 +0000304
Simon Glassd9eb9f02018-06-11 23:26:46 -0600305 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