• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 The Chromium 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
6""" Utilities for dealing with builder names. This module obtains its attributes
7dynamically from builder_name_schema.json. """
8
9
10import json
11import os
12
13
14# All of these global variables are filled in by _LoadSchema().
15
16# The full schema.
17BUILDER_NAME_SCHEMA = None
18
19# Character which separates parts of a builder name.
20BUILDER_NAME_SEP = None
21
22# Builder roles.
23BUILDER_ROLE_BUILD = 'Build'
24BUILDER_ROLE_BUILDSTATS = 'BuildStats'
25BUILDER_ROLE_CANARY = 'Canary'
26BUILDER_ROLE_CODESIZE = 'CodeSize'
27BUILDER_ROLE_HOUSEKEEPER = 'Housekeeper'
28BUILDER_ROLE_INFRA = 'Infra'
29BUILDER_ROLE_PERF = 'Perf'
30BUILDER_ROLE_TEST = 'Test'
31BUILDER_ROLE_FM = 'FM'
32BUILDER_ROLE_UPLOAD = 'Upload'
33BUILDER_ROLES = (BUILDER_ROLE_BUILD,
34                 BUILDER_ROLE_BUILDSTATS,
35                 BUILDER_ROLE_CANARY,
36                 BUILDER_ROLE_CODESIZE,
37                 BUILDER_ROLE_HOUSEKEEPER,
38                 BUILDER_ROLE_INFRA,
39                 BUILDER_ROLE_PERF,
40                 BUILDER_ROLE_TEST,
41                 BUILDER_ROLE_FM,
42                 BUILDER_ROLE_UPLOAD)
43
44
45def _LoadSchema():
46  """ Load the builder naming schema from the JSON file. """
47
48  def ToStr(obj):
49    """ Convert all unicode strings in obj to Python strings. """
50    if isinstance(obj, str):
51      return obj  # pragma: nocover
52    elif isinstance(obj, dict):
53      return dict(map(ToStr, obj.items()))
54    elif isinstance(obj, list):
55      return list(map(ToStr, obj))
56    elif isinstance(obj, tuple):
57      return tuple(map(ToStr, obj))
58    else:
59      return obj.decode('utf-8')
60
61  builder_name_json_filename = os.path.join(
62      os.path.dirname(__file__), 'builder_name_schema.json')
63  builder_name_schema_json = json.load(open(builder_name_json_filename))
64
65  global BUILDER_NAME_SCHEMA
66  BUILDER_NAME_SCHEMA = ToStr(
67      builder_name_schema_json['builder_name_schema'])
68
69  global BUILDER_NAME_SEP
70  BUILDER_NAME_SEP = ToStr(
71      builder_name_schema_json['builder_name_sep'])
72
73  # Since the builder roles are dictionary keys, just assert that the global
74  # variables above account for all of them.
75  assert len(BUILDER_ROLES) == len(BUILDER_NAME_SCHEMA)
76  for role in BUILDER_ROLES:
77    assert role in BUILDER_NAME_SCHEMA
78
79
80_LoadSchema()
81
82
83def MakeBuilderName(**parts):
84  for v in parts.values():
85    if BUILDER_NAME_SEP in v:
86      raise ValueError('Parts cannot contain "%s"' % BUILDER_NAME_SEP)
87
88  rv_parts = []
89
90  def process(depth, parts):
91    role_key = 'role'
92    if depth != 0:
93      role_key = 'sub-role-%d' % depth
94    role = parts.get(role_key)
95    if not role:
96      raise ValueError('Invalid parts; missing key %s' % role_key)
97    s = BUILDER_NAME_SCHEMA.get(role)
98    if not s:
99      raise ValueError('Invalid parts; unknown role %s' % role)
100    rv_parts.append(role)
101    del parts[role_key]
102
103    for key in s.get('keys', []):
104      value = parts.get(key)
105      if not value:
106        raise ValueError('Invalid parts; missing %s' % key)
107      rv_parts.append(value)
108      del parts[key]
109
110    recurse_roles = s.get('recurse_roles', [])
111    if len(recurse_roles) > 0:
112      sub_role_key = 'sub-role-%d' % (depth+1)
113      sub_role = parts.get(sub_role_key)
114      if not sub_role:
115        raise ValueError('Invalid parts; missing %s' % sub_role_key)
116
117      found = False
118      for recurse_role in recurse_roles:
119        if recurse_role == sub_role:
120          found = True
121          parts = process(depth+1, parts)
122          break
123      if not found:
124        raise ValueError('Invalid parts; unknown sub-role %s' % sub_role)
125
126    for key in s.get('optional_keys', []):
127      if parts.get(key):
128        rv_parts.append(parts[key])
129        del parts[key]
130
131    if len(parts) > 0:
132      raise ValueError('Invalid parts; too many parts: %s' % parts)
133
134    return parts
135
136  process(0, parts)
137
138  return BUILDER_NAME_SEP.join(rv_parts)
139
140
141def DictForBuilderName(builder_name):
142  """Makes a dictionary containing details about the builder from its name."""
143  split = builder_name.split(BUILDER_NAME_SEP)
144
145  def pop_front(items):
146    try:
147      return items.pop(0), items
148    except:
149      raise ValueError(
150          'Invalid builder name: %s (not enough parts)' % builder_name)
151
152  result = {}
153
154  def _parse(depth, role, parts):
155    schema = BUILDER_NAME_SCHEMA.get(role)
156    if not schema:
157      raise ValueError('Invalid builder name: %s' % builder_name)
158    if depth == 0:
159      result['role'] = str(role)
160    else:
161      result['sub-role-%d' % depth] = str(role)
162    for key in schema.get('keys', []):
163      value, parts = pop_front(parts)
164      result[key] = str(value)
165    for sub_role in schema.get('recurse_roles', []):
166      if len(parts) > 0 and sub_role == parts[0]:
167        parts = _parse(depth+1, parts[0], parts[1:])
168    for key in schema.get('optional_keys', []):
169      if parts:
170        value, parts = pop_front(parts)
171        result[key] = str(value)
172    if parts:
173      raise ValueError('Invalid builder name: %s' % builder_name)
174    return parts
175
176  _parse(0, split[0], split[1:])
177
178  return result
179