pico-imx6: Fix bootmenu handling
[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 import re
5
6 class 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
34 class 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
72 class 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
99 class 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