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