| # Copyright 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| |
| """ Utilities for dealing with builder names. This module obtains its attributes |
| dynamically from builder_name_schema.json. """ |
| |
| |
| import json |
| import os |
| |
| |
| # All of these global variables are filled in by _LoadSchema(). |
| |
| # The full schema. |
| BUILDER_NAME_SCHEMA = None |
| |
| # Character which separates parts of a builder name. |
| BUILDER_NAME_SEP = None |
| |
| # Builder roles. |
| BUILDER_ROLE_BUILD = 'Build' |
| BUILDER_ROLE_BUILDSTATS = 'BuildStats' |
| BUILDER_ROLE_HOUSEKEEPER = 'Housekeeper' |
| BUILDER_ROLE_INFRA = 'Infra' |
| BUILDER_ROLE_PERF = 'Perf' |
| BUILDER_ROLE_TEST = 'Test' |
| BUILDER_ROLE_FM = 'FM' |
| BUILDER_ROLE_UPLOAD = 'Upload' |
| BUILDER_ROLES = (BUILDER_ROLE_BUILD, |
| BUILDER_ROLE_BUILDSTATS, |
| BUILDER_ROLE_HOUSEKEEPER, |
| BUILDER_ROLE_INFRA, |
| BUILDER_ROLE_PERF, |
| BUILDER_ROLE_TEST, |
| BUILDER_ROLE_FM, |
| BUILDER_ROLE_UPLOAD) |
| |
| |
| def _LoadSchema(): |
| """ Load the builder naming schema from the JSON file. """ |
| |
| def _UnicodeToStr(obj): |
| """ Convert all unicode strings in obj to Python strings. """ |
| if isinstance(obj, unicode): |
| return str(obj) |
| elif isinstance(obj, dict): |
| return dict(map(_UnicodeToStr, obj.iteritems())) |
| elif isinstance(obj, list): |
| return list(map(_UnicodeToStr, obj)) |
| elif isinstance(obj, tuple): |
| return tuple(map(_UnicodeToStr, obj)) |
| |
| builder_name_json_filename = os.path.join( |
| os.path.dirname(__file__), 'builder_name_schema.json') |
| builder_name_schema_json = json.load(open(builder_name_json_filename)) |
| |
| global BUILDER_NAME_SCHEMA |
| BUILDER_NAME_SCHEMA = _UnicodeToStr( |
| builder_name_schema_json['builder_name_schema']) |
| |
| global BUILDER_NAME_SEP |
| BUILDER_NAME_SEP = _UnicodeToStr( |
| builder_name_schema_json['builder_name_sep']) |
| |
| # Since the builder roles are dictionary keys, just assert that the global |
| # variables above account for all of them. |
| assert len(BUILDER_ROLES) == len(BUILDER_NAME_SCHEMA) |
| for role in BUILDER_ROLES: |
| assert role in BUILDER_NAME_SCHEMA |
| |
| |
| _LoadSchema() |
| |
| |
| def MakeBuilderName(**parts): |
| for v in parts.itervalues(): |
| if BUILDER_NAME_SEP in v: |
| raise ValueError('Parts cannot contain "%s"' % BUILDER_NAME_SEP) |
| |
| rv_parts = [] |
| |
| def process(depth, parts): |
| role_key = 'role' |
| if depth != 0: |
| role_key = 'sub-role-%d' % depth |
| role = parts.get(role_key) |
| if not role: |
| raise ValueError('Invalid parts; missing key %s' % role_key) |
| s = BUILDER_NAME_SCHEMA.get(role) |
| if not s: |
| raise ValueError('Invalid parts; unknown role %s' % role) |
| rv_parts.append(role) |
| del parts[role_key] |
| |
| for key in s.get('keys', []): |
| value = parts.get(key) |
| if not value: |
| raise ValueError('Invalid parts; missing %s' % key) |
| rv_parts.append(value) |
| del parts[key] |
| |
| recurse_roles = s.get('recurse_roles', []) |
| if len(recurse_roles) > 0: |
| sub_role_key = 'sub-role-%d' % (depth+1) |
| sub_role = parts.get(sub_role_key) |
| if not sub_role: |
| raise ValueError('Invalid parts; missing %s' % sub_role_key) |
| |
| found = False |
| for recurse_role in recurse_roles: |
| if recurse_role == sub_role: |
| found = True |
| parts = process(depth+1, parts) |
| break |
| if not found: |
| raise ValueError('Invalid parts; unknown sub-role %s' % sub_role) |
| |
| for key in s.get('optional_keys', []): |
| if parts.get(key): |
| rv_parts.append(parts[key]) |
| del parts[key] |
| |
| if len(parts) > 0: |
| raise ValueError('Invalid parts; too many parts: %s' % parts) |
| |
| return parts |
| |
| process(0, parts) |
| |
| return BUILDER_NAME_SEP.join(rv_parts) |
| |
| |
| def DictForBuilderName(builder_name): |
| """Makes a dictionary containing details about the builder from its name.""" |
| split = builder_name.split(BUILDER_NAME_SEP) |
| |
| def pop_front(items): |
| try: |
| return items.pop(0), items |
| except: |
| raise ValueError( |
| 'Invalid builder name: %s (not enough parts)' % builder_name) |
| |
| result = {} |
| |
| def _parse(depth, role, parts): |
| schema = BUILDER_NAME_SCHEMA.get(role) |
| if not schema: |
| raise ValueError('Invalid builder name: %s' % builder_name) |
| if depth == 0: |
| result['role'] = role |
| else: |
| result['sub-role-%d' % depth] = role |
| for key in schema.get('keys', []): |
| value, parts = pop_front(parts) |
| result[key] = value |
| for sub_role in schema.get('recurse_roles', []): |
| if len(parts) > 0 and sub_role == parts[0]: |
| parts = _parse(depth+1, parts[0], parts[1:]) |
| for key in schema.get('optional_keys', []): |
| if parts: |
| value, parts = pop_front(parts) |
| result[key] = value |
| if parts: |
| raise ValueError('Invalid builder name: %s' % builder_name) |
| return parts |
| |
| _parse(0, split[0], split[1:]) |
| |
| return result |