1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2012 The Chromium OS Authors. 3 4import re 5 6class 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 72class 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 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] = '' 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 310