1# Copyright (c) 2012 The Chromium OS Authors. 2# 3# SPDX-License-Identifier: GPL-2.0+ 4# 5 6import re 7 8class Expr: 9 """A single regular expression for matching boards to build""" 10 11 def __init__(self, expr): 12 """Set up a new Expr object. 13 14 Args: 15 expr: String cotaining regular expression to store 16 """ 17 self._expr = expr 18 self._re = re.compile(expr) 19 20 def Matches(self, props): 21 """Check if any of the properties match the regular expression. 22 23 Args: 24 props: List of properties to check 25 Returns: 26 True if any of the properties match the regular expression 27 """ 28 for prop in props: 29 if self._re.match(prop): 30 return True 31 return False 32 33 def __str__(self): 34 return self._expr 35 36class Term: 37 """A list of expressions each of which must match with properties. 38 39 This provides a list of 'AND' expressions, meaning that each must 40 match the board properties for that board to be built. 41 """ 42 def __init__(self): 43 self._expr_list = [] 44 self._board_count = 0 45 46 def AddExpr(self, expr): 47 """Add an Expr object to the list to check. 48 49 Args: 50 expr: New Expr object to add to the list of those that must 51 match for a board to be built. 52 """ 53 self._expr_list.append(Expr(expr)) 54 55 def __str__(self): 56 """Return some sort of useful string describing the term""" 57 return '&'.join([str(expr) for expr in self._expr_list]) 58 59 def Matches(self, props): 60 """Check if any of the properties match this term 61 62 Each of the expressions in the term is checked. All must match. 63 64 Args: 65 props: List of properties to check 66 Returns: 67 True if all of the expressions in the Term match, else False 68 """ 69 for expr in self._expr_list: 70 if not expr.Matches(props): 71 return False 72 return True 73 74class Board: 75 """A particular board that we can build""" 76 def __init__(self, status, arch, cpu, soc, vendor, board_name, target, options): 77 """Create a new board type. 78 79 Args: 80 status: define whether the board is 'Active' or 'Orphaned' 81 arch: Architecture name (e.g. arm) 82 cpu: Cpu name (e.g. arm1136) 83 soc: Name of SOC, or '' if none (e.g. mx31) 84 vendor: Name of vendor (e.g. armltd) 85 board_name: Name of board (e.g. integrator) 86 target: Target name (use make <target>_defconfig to configure) 87 options: board-specific options (e.g. integratorcp:CM1136) 88 """ 89 self.target = target 90 self.arch = arch 91 self.cpu = cpu 92 self.board_name = board_name 93 self.vendor = vendor 94 self.soc = soc 95 self.options = options 96 self.props = [self.target, self.arch, self.cpu, self.board_name, 97 self.vendor, self.soc, self.options] 98 self.build_it = False 99 100 101class Boards: 102 """Manage a list of boards.""" 103 def __init__(self): 104 # Use a simple list here, sinc OrderedDict requires Python 2.7 105 self._boards = [] 106 107 def AddBoard(self, board): 108 """Add a new board to the list. 109 110 The board's target member must not already exist in the board list. 111 112 Args: 113 board: board to add 114 """ 115 self._boards.append(board) 116 117 def ReadBoards(self, fname): 118 """Read a list of boards from a board file. 119 120 Create a board object for each and add it to our _boards list. 121 122 Args: 123 fname: Filename of boards.cfg file 124 """ 125 with open(fname, 'r') as fd: 126 for line in fd: 127 if line[0] == '#': 128 continue 129 fields = line.split() 130 if not fields: 131 continue 132 for upto in range(len(fields)): 133 if fields[upto] == '-': 134 fields[upto] = '' 135 while len(fields) < 8: 136 fields.append('') 137 if len(fields) > 8: 138 fields = fields[:8] 139 140 board = Board(*fields) 141 self.AddBoard(board) 142 143 144 def GetList(self): 145 """Return a list of available boards. 146 147 Returns: 148 List of Board objects 149 """ 150 return self._boards 151 152 def GetDict(self): 153 """Build a dictionary containing all the boards. 154 155 Returns: 156 Dictionary: 157 key is board.target 158 value is board 159 """ 160 board_dict = {} 161 for board in self._boards: 162 board_dict[board.target] = board 163 return board_dict 164 165 def GetSelectedDict(self): 166 """Return a dictionary containing the selected boards 167 168 Returns: 169 List of Board objects that are marked selected 170 """ 171 board_dict = {} 172 for board in self._boards: 173 if board.build_it: 174 board_dict[board.target] = board 175 return board_dict 176 177 def GetSelected(self): 178 """Return a list of selected boards 179 180 Returns: 181 List of Board objects that are marked selected 182 """ 183 return [board for board in self._boards if board.build_it] 184 185 def GetSelectedNames(self): 186 """Return a list of selected boards 187 188 Returns: 189 List of board names that are marked selected 190 """ 191 return [board.target for board in self._boards if board.build_it] 192 193 def _BuildTerms(self, args): 194 """Convert command line arguments to a list of terms. 195 196 This deals with parsing of the arguments. It handles the '&' 197 operator, which joins several expressions into a single Term. 198 199 For example: 200 ['arm & freescale sandbox', 'tegra'] 201 202 will produce 3 Terms containing expressions as follows: 203 arm, freescale 204 sandbox 205 tegra 206 207 The first Term has two expressions, both of which must match for 208 a board to be selected. 209 210 Args: 211 args: List of command line arguments 212 Returns: 213 A list of Term objects 214 """ 215 syms = [] 216 for arg in args: 217 for word in arg.split(): 218 sym_build = [] 219 for term in word.split('&'): 220 if term: 221 sym_build.append(term) 222 sym_build.append('&') 223 syms += sym_build[:-1] 224 terms = [] 225 term = None 226 oper = None 227 for sym in syms: 228 if sym == '&': 229 oper = sym 230 elif oper: 231 term.AddExpr(sym) 232 oper = None 233 else: 234 if term: 235 terms.append(term) 236 term = Term() 237 term.AddExpr(sym) 238 if term: 239 terms.append(term) 240 return terms 241 242 def SelectBoards(self, args, exclude=[]): 243 """Mark boards selected based on args 244 245 Args: 246 args: List of strings specifying boards to include, either named, 247 or by their target, architecture, cpu, vendor or soc. If 248 empty, all boards are selected. 249 exclude: List of boards to exclude, regardless of 'args' 250 251 Returns: 252 Dictionary which holds the number of boards which were selected 253 due to each argument, arranged by argument. 254 """ 255 result = {} 256 terms = self._BuildTerms(args) 257 258 result['all'] = 0 259 for term in terms: 260 result[str(term)] = 0 261 262 exclude_list = [] 263 for expr in exclude: 264 exclude_list.append(Expr(expr)) 265 266 for board in self._boards: 267 matching_term = None 268 build_it = False 269 if terms: 270 match = False 271 for term in terms: 272 if term.Matches(board.props): 273 matching_term = str(term) 274 build_it = True 275 break 276 else: 277 build_it = True 278 279 # Check that it is not specifically excluded 280 for expr in exclude_list: 281 if expr.Matches(board.props): 282 build_it = False 283 break 284 285 if build_it: 286 board.build_it = True 287 if matching_term: 288 result[matching_term] += 1 289 result['all'] += 1 290 291 return result 292