xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 1 | # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | """Module for ChromeOS & Android build related logic in suite scheduler.""" |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 6 | # pylint: disable=g-bad-import-order |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 7 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 8 | from distutils import version |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 9 | import collections |
Xixuan Wu | 244e0ec | 2018-05-23 14:49:55 -0700 | [diff] [blame] | 10 | import json |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 11 | import logging |
| 12 | import re |
| 13 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 14 | import apiclient |
| 15 | |
Xixuan Wu | f856ff1 | 2019-05-21 14:09:38 -0700 | [diff] [blame] | 16 | BuildInfo = collections.namedtuple( |
| 17 | 'BuildInfo', |
| 18 | [ |
| 19 | 'board', |
| 20 | 'model', |
| 21 | 'milestone', |
| 22 | 'platform', |
| 23 | 'build_config', |
| 24 | ]) |
| 25 | |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 26 | # Bare branches |
| 27 | BARE_BRANCHES = ['factory', 'firmware'] |
| 28 | |
| 29 | # Definition of os types. |
| 30 | OS_TYPE_CROS = 'cros' |
| 31 | OS_TYPE_BRILLO = 'brillo' |
| 32 | OS_TYPE_ANDROID = 'android' |
| 33 | OS_TYPES = [OS_TYPE_CROS, OS_TYPE_BRILLO, OS_TYPE_ANDROID] |
| 34 | OS_TYPES_LAUNCH_CONTROL = [OS_TYPE_BRILLO, OS_TYPE_ANDROID] |
| 35 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 36 | # Launch control build's target's information |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 37 | LaunchControlBuildTargetInfo = collections.namedtuple( |
| 38 | 'LaunchControlBuildTargetInfo', |
| 39 | [ |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 40 | 'target', |
| 41 | 'type', |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 42 | ]) |
| 43 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 44 | # ChromeOS build config's information |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 45 | CrOSBuildConfigInfo = collections.namedtuple( |
| 46 | 'CrOSBuildConfigInfo', |
| 47 | [ |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 48 | 'board', |
| 49 | 'type', |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 50 | ]) |
| 51 | |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 52 | # The default build type for fetching latest build. |
Xixuan Wu | 7899db5 | 2018-05-29 14:19:06 -0700 | [diff] [blame] | 53 | _DEFAULT_BUILD_SUFFIX = '-release' |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 54 | |
| 55 | # The default setting of board for fetching latest build. |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 56 | _DEFAULT_HEAD = 'master' |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 57 | |
| 58 | # The path for storing the latest build. |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 59 | _GS_LATEST_HEAD_PATTERN = '%(board)s%(suffix)s/%(name)s' |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 60 | |
| 61 | # The gs bucket to fetch the latest build. |
| 62 | _GS_BUCKET = 'chromeos-image-archive' |
| 63 | |
| 64 | # The file in Google Storage to fetch the latest build. |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 65 | _LATEST_HEAD = 'LATEST-master' |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 66 | |
Xixuan Wu | 244e0ec | 2018-05-23 14:49:55 -0700 | [diff] [blame] | 67 | # The bucket for ChromeOS build release boards. Maintained by GoldenEye. |
| 68 | _GE_RELEASE_BUILD_BUCKET = 'chromeos-build-release-console' |
| 69 | |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 70 | # Special android target to board map. |
| 71 | _ANDROID_TARGET_TO_BOARD_MAP = { |
| 72 | 'seed_l8150': 'gm4g_sprout', |
| 73 | 'bat_land': 'bat' |
| 74 | } |
| 75 | |
| 76 | # CrOS build name patter |
| 77 | _CROS_BUILD_PATTERN = '%(board)s-%(build_type)s/R%(milestone)s-%(manifest)s' |
| 78 | |
| 79 | # Android build name pattern |
| 80 | _ANDROID_BUILD_PATTERN = '%(branch)s/%(target)s/%(build_id)s' |
| 81 | |
| 82 | # The pattern for Launch Control target |
| 83 | _LAUNCH_CONTROL_TARGET_PATTERN = r'(?P<build_target>.+)-(?P<build_type>[^-]+)' |
| 84 | |
| 85 | # The pattern for CrOS build config |
| 86 | _CROS_BUILD_CONFIG_PATTERN = r'-([^-]+)(?:-group)?' |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 87 | |
| 88 | |
| 89 | class NoBuildError(Exception): |
| 90 | """Raised when failing to get the required build from Google Storage.""" |
| 91 | |
| 92 | |
| 93 | class BuildType(object): |
| 94 | """Representing the type of test source build. |
| 95 | |
| 96 | This is used to identify the test source build for testing. |
| 97 | """ |
| 98 | FIRMWARE_RW = 'firmware_rw' |
| 99 | FIRMWARE_RO = 'firmware_ro' |
| 100 | CROS = 'cros' |
| 101 | |
| 102 | |
| 103 | class BuildVersionKey(object): |
| 104 | """Keys referring to the builds to install in run_suites.""" |
| 105 | |
| 106 | CROS_VERSION = 'cros_version' |
| 107 | ANDROID_BUILD_VERSION = 'android_version' |
| 108 | TESTBED_BUILD_VERSION = 'testbed_version' |
| 109 | FW_RW_VERSION = 'fwrw_version' |
| 110 | FW_RO_VERSION = 'fwro_version' |
| 111 | |
| 112 | |
| 113 | class AndroidBuild(collections.namedtuple( |
| 114 | '_AndroidBuildBase', ['branch', 'target', 'build_id']), object): |
| 115 | """Class for constructing android build string.""" |
| 116 | |
| 117 | def __str__(self): |
| 118 | return _ANDROID_BUILD_PATTERN % {'branch': self.branch, |
| 119 | 'target': self.target, |
| 120 | 'build_id': self.build_id} |
| 121 | |
| 122 | |
| 123 | class CrOSBuild(collections.namedtuple( |
| 124 | '_CrOSBuildBase', |
| 125 | ['board', 'build_type', 'milestone', 'manifest']), object): |
| 126 | """Class for constructing ChromeOS build string.""" |
| 127 | |
| 128 | def __str__(self): |
| 129 | return _CROS_BUILD_PATTERN % {'board': self.board, |
| 130 | 'build_type': self.build_type, |
| 131 | 'milestone': self.milestone, |
| 132 | 'manifest': self.manifest} |
| 133 | |
| 134 | |
| 135 | def get_latest_cros_build_from_gs(storage_client, board=None, suffix=None): |
| 136 | """Get latest build for given board from Google Storage. |
| 137 | |
| 138 | Args: |
| 139 | storage_client: a rest_client.StorageRestClient object. |
| 140 | board: the board to fetch latest build. Default is 'master'. |
| 141 | suffix: suffix represents build channel, like '-release'. |
| 142 | Default is '-paladin'. |
| 143 | |
| 144 | Returns: |
| 145 | a ChromeOS version string, e.g. '59.0.000.0'. |
| 146 | |
| 147 | Raises: |
| 148 | HttpError if error happens in interacting with Google Storage. |
| 149 | """ |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 150 | board = board if board is not None else _DEFAULT_HEAD |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 151 | suffix = suffix if suffix is not None else _DEFAULT_BUILD_SUFFIX |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 152 | file_to_check = _GS_LATEST_HEAD_PATTERN % { |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 153 | 'board': board, |
| 154 | 'suffix': suffix, |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 155 | 'name': _LATEST_HEAD} |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 156 | |
| 157 | try: |
| 158 | return storage_client.read_object(_GS_BUCKET, file_to_check) |
| 159 | except apiclient.errors.HttpError as e: |
| 160 | raise NoBuildError( |
| 161 | 'Cannot find latest build for board %s, suffix %s: %s' % |
| 162 | (board, suffix, str(e))) |
| 163 | |
| 164 | |
Xixuan Wu | 244e0ec | 2018-05-23 14:49:55 -0700 | [diff] [blame] | 165 | def get_board_family_mapping_from_gs(storage_client): |
| 166 | """Get board_family to boards mapping from Google Storage. |
| 167 | |
| 168 | Args: |
| 169 | storage_client: a rest_client.StorageRestClient object. |
| 170 | |
| 171 | Returns: |
| 172 | a dictionary of mapping between board family name to boards, e.g. |
| 173 | {'nyan': ['nyan', 'nyan_big', 'nyan_blaze', ..]} |
| 174 | |
| 175 | Raises: |
| 176 | HttpError if error happens in interacting with Google Storage. |
| 177 | """ |
| 178 | try: |
| 179 | boards = storage_client.read_object(_GE_RELEASE_BUILD_BUCKET, 'boards.json') |
| 180 | json_object = json.loads(boards) |
| 181 | board_family = {} |
| 182 | for board in json_object['boards']: |
| 183 | group = board['reference_group'] |
| 184 | if not group: |
| 185 | continue |
| 186 | |
| 187 | # This is to change boards like nyan-blaze to nyan_blaze, which is |
| 188 | # actually used in lab. |
| 189 | board_name = board['public_codename'].replace('-', '_') |
| 190 | if group not in board_family: |
| 191 | board_family[group] = [] |
| 192 | |
| 193 | board_family[group].append(board_name) |
| 194 | |
| 195 | logging.info('Successfully get following board families from GS: %r', |
| 196 | board_family.keys()) |
| 197 | return board_family |
| 198 | except apiclient.errors.HttpError as e: |
| 199 | logging.error('Cannot load boards.json in bucket %s: %s', |
| 200 | _GE_RELEASE_BUILD_BUCKET, str(e)) |
| 201 | raise |
| 202 | |
| 203 | |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 204 | def buildinfo_list_to_branch_build_dict(cros_board_list, buildinfo_list): |
| 205 | """Validate and convert a list of BuildInfo to branch build dict. |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 206 | |
| 207 | Args: |
Xixuan Wu | 6fb1627 | 2017-10-19 13:16:00 -0700 | [diff] [blame] | 208 | cros_board_list: The board list including all CrOS boards. |
Xixuan Wu | c681901 | 2019-05-23 11:34:59 -0700 | [diff] [blame] | 209 | buildinfo_list: A list of BuildInfo objects. |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 210 | |
| 211 | Returns: |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 212 | A branch build dict: |
| 213 | key: a tuple of (board, build_type, milestone), like: |
| 214 | ('wolf', 'release', '58') |
| 215 | value: the latest manifest for the given tuple, like: |
| 216 | '9242.0.0'. |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 217 | """ |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 218 | branch_build_dict = {} |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 219 | for build in buildinfo_list: |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 220 | try: |
| 221 | build_config_info = parse_cros_build_config(build.board, |
| 222 | build.build_config) |
| 223 | except ValueError as e: |
| 224 | logging.warning('Failed to parse build config: %s: %s', |
| 225 | build.build_config, e) |
| 226 | continue |
| 227 | |
| 228 | if build.board != build_config_info.board: |
| 229 | logging.warning('Non-matched build_config and board: %s, %s', |
| 230 | build.board, build.board) |
| 231 | continue |
| 232 | |
Xixuan Wu | 6fb1627 | 2017-10-19 13:16:00 -0700 | [diff] [blame] | 233 | if build.board not in cros_board_list: |
| 234 | logging.warning('%s is not a valid CrOS board.', build.board) |
| 235 | continue |
| 236 | |
Xixuan Wu | 8d2f286 | 2018-08-28 16:48:04 -0700 | [diff] [blame] | 237 | build_key = (build.board, build.model, build_config_info.type, |
| 238 | build.milestone) |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 239 | cur_manifest = branch_build_dict.get(build_key) |
| 240 | if cur_manifest is not None: |
| 241 | branch_build_dict[build_key] = max( |
| 242 | [cur_manifest, build.platform], key=version.LooseVersion) |
| 243 | else: |
| 244 | branch_build_dict[build_key] = build.platform |
| 245 | |
| 246 | return branch_build_dict |
| 247 | |
| 248 | |
Xinan Lin | 71eeeb0 | 2020-03-10 17:37:12 -0700 | [diff] [blame] | 249 | def get_cros_builds(build_client, cros_board_list, earliest_end_time, |
| 250 | latest_end_time, event_type): |
Xinan Lin | ea1efcb | 2019-12-30 23:46:42 -0800 | [diff] [blame] | 251 | """Get branch builds for ChromeOS boards inside the given time span. |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 252 | |
| 253 | Args: |
Xixuan Wu | c681901 | 2019-05-23 11:34:59 -0700 | [diff] [blame] | 254 | build_client: a rest_client.BuildBucketBigqueryClient object, to |
| 255 | connect Buildbucket Bigquery. |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 256 | cros_board_list: The board list including all CrOS boards. |
Xinan Lin | ea1efcb | 2019-12-30 23:46:42 -0800 | [diff] [blame] | 257 | earliest_end_time: a datetime.datetime object in UTC. |
| 258 | latest_end_time: a datetime.datetime object in UTC. |
Xinan Lin | 71eeeb0 | 2020-03-10 17:37:12 -0700 | [diff] [blame] | 259 | event: a string of event type. |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 260 | |
| 261 | Returns: |
| 262 | a two-tuple of branch build dicts. Each branch build dict has |
| 263 | keys and value described below. The first build_dict describes |
| 264 | successful builds and the second describes builds that failed |
| 265 | but met the relaxed success requirement (a successful 'HWTest [sanity]' |
| 266 | stage). |
| 267 | key: a tuple of (board, build_type, milestone), like: |
| 268 | ('wolf', 'release', '58') |
| 269 | value: the latest manifest for the given tuple, like: |
| 270 | '9242.0.0'. |
| 271 | """ |
Xixuan Wu | c681901 | 2019-05-23 11:34:59 -0700 | [diff] [blame] | 272 | # Buildbucket Bigquery use UTC timezone |
Xinan Lin | ea1efcb | 2019-12-30 23:46:42 -0800 | [diff] [blame] | 273 | all_branch_builds = build_client.get_passed_builds( |
Xinan Lin | 71eeeb0 | 2020-03-10 17:37:12 -0700 | [diff] [blame] | 274 | earliest_end_time, latest_end_time, event_type) |
Xinan Lin | ea1efcb | 2019-12-30 23:46:42 -0800 | [diff] [blame] | 275 | relaxed_builds = build_client.get_relaxed_passed_builds( |
Xinan Lin | 71eeeb0 | 2020-03-10 17:37:12 -0700 | [diff] [blame] | 276 | earliest_end_time, latest_end_time, event_type) |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 277 | |
| 278 | branch_builds_dict = buildinfo_list_to_branch_build_dict( |
| 279 | cros_board_list, all_branch_builds) |
| 280 | relaxed_builds_dict = buildinfo_list_to_branch_build_dict( |
Xixuan Wu | 384f913 | 2018-08-28 15:48:26 -0700 | [diff] [blame] | 281 | cros_board_list, relaxed_builds + all_branch_builds) |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 282 | |
| 283 | return branch_builds_dict, relaxed_builds_dict |
| 284 | |
| 285 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 286 | def get_latest_launch_control_build(android_client, branch, target): |
| 287 | """Get the latest launch control build from Android Build API. |
| 288 | |
| 289 | Args: |
| 290 | android_client: a rest_client.AndroidBuildRestClient object. |
| 291 | branch: the launch control branch. |
| 292 | target: the launch control target. |
| 293 | |
| 294 | Returns: |
| 295 | a string latest launch control build id. |
| 296 | |
| 297 | Raises: |
| 298 | NoBuildError if no latest launch control build is found. |
| 299 | """ |
| 300 | try: |
| 301 | latest_build_id = android_client.get_latest_build_id(branch, target) |
| 302 | if latest_build_id is None: |
| 303 | raise NoBuildError('No latest builds is found.') |
| 304 | |
| 305 | return latest_build_id |
| 306 | except apiclient.errors.HttpError as e: |
| 307 | raise NoBuildError('HttpError happened in getting the latest launch ' |
| 308 | 'control build for ' |
| 309 | '%s,%s: %s' % (branch, target, str(e))) |
| 310 | |
| 311 | |
| 312 | def get_launch_control_builds_by_branch_targets( |
| 313 | android_client, android_board_list, launch_control_branch_targets): |
| 314 | """Get latest launch_control_builds for android boards. |
| 315 | |
| 316 | For every tasks in this event, if it has settings of launch control |
| 317 | branch & target, get the latest launch control build for it. |
| 318 | |
| 319 | Args: |
| 320 | android_client: a rest_client.AndroidBuildRestClient object to |
| 321 | interact with android build API. |
| 322 | android_board_list: a list of Android boards. |
| 323 | launch_control_branch_targets: a dict of branch:targets, see property |
| 324 | launch_control_branch_targets in base_event.py. |
| 325 | |
| 326 | Returns: |
| 327 | a launch control build dict: |
| 328 | key: an android board, like 'shamu'. |
| 329 | value: a list involves the latest builds for each pair |
| 330 | (branch, target) of this board, like: |
| 331 | [u'git_nyc-mr2-release/shamu-userdebug/3844975', |
| 332 | u'git_nyc-mr1-release/shamu-userdebug/3783920'] |
| 333 | """ |
| 334 | launch_control_dict = {} |
| 335 | board_to_builds_dict = {} |
| 336 | for branch, targets in launch_control_branch_targets.iteritems(): |
| 337 | for t in targets: |
| 338 | try: |
| 339 | board = parse_launch_control_target(t).target |
| 340 | except ValueError: |
| 341 | logging.warning( |
| 342 | 'Failed to parse launch control target: %s', t) |
| 343 | continue |
| 344 | |
| 345 | if board not in android_board_list: |
| 346 | continue |
| 347 | |
| 348 | # Use dict here to reduce the times to call AndroidBuild API |
| 349 | if launch_control_dict.get((branch, t)) is None: |
| 350 | try: |
| 351 | build_id = get_latest_launch_control_build( |
| 352 | android_client, branch, t) |
| 353 | except NoBuildError as e: |
| 354 | logging.warning(e) |
| 355 | continue |
| 356 | |
| 357 | build = str(AndroidBuild(branch, t, build_id)) |
| 358 | launch_control_dict[(branch, t)] = build |
| 359 | board_to_builds_dict.setdefault(board, []).append( |
| 360 | build) |
| 361 | |
| 362 | for board, in board_to_builds_dict.iteritems(): |
| 363 | mapped_board = get_board_by_android_target(board) |
| 364 | if mapped_board != board: |
| 365 | logging.debug('Map board %s to %s', board, mapped_board) |
| 366 | if board_to_builds_dict.get(mapped_board) is None: |
| 367 | del board_to_builds_dict[board] |
| 368 | else: |
| 369 | board_to_builds_dict[board] = board_to_builds_dict[ |
| 370 | mapped_board] |
| 371 | |
| 372 | return board_to_builds_dict |
| 373 | |
| 374 | |
| 375 | def parse_launch_control_target(target): |
| 376 | """Parse the build target and type from a Launch Control target. |
| 377 | |
| 378 | The Launch Control target has the format of build_target-build_type, e.g., |
| 379 | shamu-eng or dragonboard-userdebug. This method extracts the build target |
| 380 | and type from the target name. |
| 381 | |
| 382 | Args: |
| 383 | target: Name of a Launch Control target, e.g., shamu-userdebug. |
| 384 | |
| 385 | Returns: |
| 386 | a LaunchControlBuildTargetInfo object whose value is like |
| 387 | (target='shamu', |
| 388 | type='userdebug') |
| 389 | |
| 390 | Raises: |
| 391 | ValueError: if target is not valid. |
| 392 | """ |
| 393 | match = re.match(_LAUNCH_CONTROL_TARGET_PATTERN, target) |
| 394 | if not match: |
| 395 | raise ValueError('target format is not valid') |
| 396 | |
| 397 | return LaunchControlBuildTargetInfo(match.group('build_target'), |
| 398 | match.group('build_type')) |
| 399 | |
| 400 | |
| 401 | def parse_cros_build_config(board, build_config): |
| 402 | """Parse build_type from a given builder for a given board. |
| 403 | |
| 404 | Args: |
| 405 | board: the prefix of a ChromeOS build_config, representing board. |
| 406 | build_config: a ChromeOS build_config name, like 'kevin-release'. |
| 407 | |
| 408 | Returns: |
| 409 | a CrOSBuildConfigInfo object whose value is like |
| 410 | (board='kevin', |
| 411 | type='release') |
| 412 | |
| 413 | Raises: |
| 414 | ValueError: if build_config is in invalid form. |
| 415 | """ |
| 416 | if build_config[0:len(board)] != board: |
| 417 | raise ValueError('build_config cannot be parsed: %s' % build_config) |
| 418 | |
| 419 | match = re.match(_CROS_BUILD_CONFIG_PATTERN, build_config[len(board):]) |
| 420 | if not match: |
| 421 | raise ValueError('build_config %s is not matched %s' % ( |
| 422 | build_config, _CROS_BUILD_CONFIG_PATTERN)) |
| 423 | |
| 424 | return CrOSBuildConfigInfo(board, match.groups()[0]) |
| 425 | |
| 426 | |
| 427 | def get_board_by_android_target(target): |
| 428 | """Map a android target to a android board. |
| 429 | |
| 430 | # Mapping between an android board name and a build target. This is for |
| 431 | # special case handling for certain Android board that the board name and |
| 432 | # build target name does not match. |
| 433 | # This comes from server/site_utils.py in autotest module. |
| 434 | |
| 435 | Args: |
| 436 | target: an android target. |
| 437 | |
| 438 | Returns: |
| 439 | a string android board mapped by ANDROID_TARGET_TO_BOARD_MAP. |
| 440 | """ |
| 441 | return _ANDROID_TARGET_TO_BOARD_MAP.get(target, target) |
Xixuan Wu | 588668d | 2019-01-04 10:33:31 -0800 | [diff] [blame] | 442 | |
| 443 | |
| 444 | def to_full_model(board, model): |
| 445 | """Form a full model name with given board, model. |
| 446 | |
| 447 | Args: |
| 448 | board: A string board name. |
| 449 | model: A string model name. |
| 450 | |
| 451 | Returns: |
| 452 | A string full_model name. |
| 453 | """ |
| 454 | return '%s_%s' % (board, model) |
Xixuan Wu | b4b2f41 | 2019-05-03 11:22:31 -0700 | [diff] [blame] | 455 | |
| 456 | |
| 457 | def reshape_board(board): |
| 458 | """Reshape some variants of board names to original board. |
| 459 | |
| 460 | Args: |
| 461 | board: A string board name. |
| 462 | |
| 463 | Returns: |
| 464 | A string board name after reshaping. |
| 465 | """ |
| 466 | # The hack derives from build info structure saved in CIDB/buildbucket: |
Kazuhiro Inaba | f020822 | 2020-03-05 10:33:07 +0900 | [diff] [blame] | 467 | # *-kernelnext/*-arcnext etc are saved as board name, but not the real board |
Xixuan Wu | b4b2f41 | 2019-05-03 11:22:31 -0700 | [diff] [blame] | 468 | # for DUT. |
Sean Abraham | 2a177d7 | 2021-02-01 09:38:16 -0700 | [diff] [blame] | 469 | # NOTE: please try to keep this list in sync with the corresponding list in |
| 470 | # chromite/lib/config_lib.py |
| 471 | special_suffixes = [ |
| 472 | '-arc-r', # |
| 473 | '-arc-r-userdebug', # |
| 474 | '-arcnext', # |
| 475 | '-arcvm', # |
| 476 | '-blueznext', # |
| 477 | '-borealis', # |
| 478 | '-campfire', # |
| 479 | '-cfm', # |
| 480 | '-kernelnext', # |
| 481 | '-kvm', # |
Sean Abraham | d6815ba | 2021-02-26 09:29:53 -0700 | [diff] [blame] | 482 | '-libcamera', # |
Sean Abraham | 2a177d7 | 2021-02-01 09:38:16 -0700 | [diff] [blame] | 483 | '-ndktranslation', # |
Kazuhiro Inaba | 840dac7 | 2021-02-01 16:47:05 +0900 | [diff] [blame] | 484 | '-userdebug', # |
Sean Abraham | bf297a9 | 2020-12-10 09:19:49 -0700 | [diff] [blame] | 485 | ] |
Sean Abraham | 2a177d7 | 2021-02-01 09:38:16 -0700 | [diff] [blame] | 486 | for s in special_suffixes: |
Sean Abraham | bf297a9 | 2020-12-10 09:19:49 -0700 | [diff] [blame] | 487 | if board.endswith(s): |
| 488 | board = board[:-len(s)] |
Xixuan Wu | b4b2f41 | 2019-05-03 11:22:31 -0700 | [diff] [blame] | 489 | return board |