blob: 2a1d021574c1396098dafd41689e62311aa9f289 [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
Stephen Warren8426d8b2013-10-10 10:00:20 -06004import re
5
Simon Glass6131bea2014-08-09 15:33:08 -06006class 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
Simon Glassfc3fe1c2013-04-03 11:07:16 +000072class Board:
73 """A particular board that we can build"""
Andreas Bießmann03c1bb22013-09-19 10:08:45 +020074 def __init__(self, status, arch, cpu, soc, vendor, board_name, target, options):
Simon Glassfc3fe1c2013-04-03 11:07:16 +000075 """Create a new board type.
76
77 Args:
Andreas Bießmann03c1bb22013-09-19 10:08:45 +020078 status: define whether the board is 'Active' or 'Orphaned'
Simon Glassfc3fe1c2013-04-03 11:07:16 +000079 arch: Architecture name (e.g. arm)
80 cpu: Cpu name (e.g. arm1136)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000081 soc: Name of SOC, or '' if none (e.g. mx31)
Andreas Bießmann03c1bb22013-09-19 10:08:45 +020082 vendor: Name of vendor (e.g. armltd)
83 board_name: Name of board (e.g. integrator)
Masahiro Yamada73f30b92014-07-30 14:08:22 +090084 target: Target name (use make <target>_defconfig to configure)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000085 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
Simon Glassfc3fe1c2013-04-03 11:07:16 +000093 self.options = options
Tom Rinie0f24062016-11-04 22:59:45 -040094 self.props = [self.target, self.arch, self.cpu, self.board_name,
95 self.vendor, self.soc, self.options]
Simon Glassfc3fe1c2013-04-03 11:07:16 +000096 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] = ''
Andreas Bießmann03c1bb22013-09-19 10:08:45 +0200133 while len(fields) < 8:
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000134 fields.append('')
Andreas Bießmann03c1bb22013-09-19 10:08:45 +0200135 if len(fields) > 8:
136 fields = fields[:8]
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000137
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
Simon Glass6131bea2014-08-09 15:33:08 -0600191 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
Simon Glass06890362018-06-11 23:26:46 -0600240 def SelectBoards(self, args, exclude=[], boards=None):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000241 """Mark boards selected based on args
242
Simon Glass06890362018-06-11 23:26:46 -0600243 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
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000249 Args:
Simon Glass3cf4ae62014-08-28 09:43:41 -0600250 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'
Simon Glass06890362018-06-11 23:26:46 -0600254 boards: List of boards to build
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000255
256 Returns:
Simon Glass06890362018-06-11 23:26:46 -0600257 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
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000261 """
262 result = {}
Simon Glass06890362018-06-11 23:26:46 -0600263 warnings = []
Simon Glass6131bea2014-08-09 15:33:08 -0600264 terms = self._BuildTerms(args)
265
Simon Glass8d7523c2017-01-23 05:38:56 -0700266 result['all'] = []
Simon Glass6131bea2014-08-09 15:33:08 -0600267 for term in terms:
Simon Glass8d7523c2017-01-23 05:38:56 -0700268 result[str(term)] = []
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000269
Simon Glass3cf4ae62014-08-28 09:43:41 -0600270 exclude_list = []
271 for expr in exclude:
272 exclude_list.append(Expr(expr))
273
Simon Glass06890362018-06-11 23:26:46 -0600274 found = []
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000275 for board in self._boards:
Simon Glass3cf4ae62014-08-28 09:43:41 -0600276 matching_term = None
277 build_it = False
Simon Glass6131bea2014-08-09 15:33:08 -0600278 if terms:
279 match = False
280 for term in terms:
281 if term.Matches(board.props):
Simon Glass3cf4ae62014-08-28 09:43:41 -0600282 matching_term = str(term)
283 build_it = True
Simon Glass6131bea2014-08-09 15:33:08 -0600284 break
Simon Glass06890362018-06-11 23:26:46 -0600285 elif boards:
286 if board.target in boards:
287 build_it = True
288 found.append(board.target)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000289 else:
Simon Glass3cf4ae62014-08-28 09:43:41 -0600290 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:
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000299 board.build_it = True
Simon Glass3cf4ae62014-08-28 09:43:41 -0600300 if matching_term:
Simon Glass8d7523c2017-01-23 05:38:56 -0700301 result[matching_term].append(board.target)
302 result['all'].append(board.target)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000303
Simon Glass06890362018-06-11 23:26:46 -0600304 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