1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2012 The Chromium OS Authors. 3 4from collections import OrderedDict 5import re 6 7class 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 73class 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 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 """ 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 311