blob: b2a3f00db3ed51c0b266024aae81bd578334eaf4 [file] [log] [blame]
Gilad Arnold84eb60c2012-11-05 23:46:10 -08001# Copyright (c) 2012 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"""Modules for obtaining Chrome OS release info."""
6
7
8import ConfigParser
9import bisect
10import os
11
12
13_RELEASE_CONFIG_FILE = os.path.join(os.getcwd(), 'release_config.ini')
14
15# Prefix for brachpoint definitions in the config file.
16_CONF_BRANCH_SECTION = 'BRANCH'
17_CONF_BRANCH_POINTS_OPT = 'branch_points'
18_CONF_BRANCH_POINT_OPT_PREFIX = 'bp_'
19_CONF_NEXT_BRANCH_OPT = 'next_branch'
20
21
22class ReleaseError(BaseException):
23 """Errors related to release and branch inference."""
24 pass
25
26
27class ReleaseInfo(object):
28 """Provides reference information about Chrome OS releases.
29
30 Currently, this class serves for mapping between releases and branches /
31 release milestones. The information lives in a .ini file at the current
32 directory, which has a single section [BRANCH] containing
33
34 branch_points: comma-separated list of release branches (e.g. R10, R11,
35 ...)
36
37 bp_XYZ: for each branch listed above, a variable that maps to the Chrome
38 OS release at that branchpoint (e.g. bp_r10: 0.10.156.0). Note that .ini
39 file variables are case-insensitive.
40
41 next_branch: the name of the current (unforked) branch (e.g. R24)
42
43 """
44 def __init__(self):
45 self._release_config = None
46 self._branchpoint_dict = None
47 self._next_branch = None
48 self._sorted_branchpoint_list = None
49 self._sorted_branchpoint_release_key_list = None
50
51 def initialize(self):
52 """Read release config and initialize lookup data structures."""
53 self._release_config = ConfigParser.ConfigParser()
54 try:
55 self._release_config.readfp(open(_RELEASE_CONFIG_FILE))
56
57 # Build branchpoint dictionary.
58 branchpoint_list_str = self._release_config.get(
59 _CONF_BRANCH_SECTION, _CONF_BRANCH_POINTS_OPT)
60 if branchpoint_list_str:
61 branchpoint_list = map(str.strip,
62 branchpoint_list_str.split(','))
63 else:
64 branchpoint_list = []
65
66 self._branchpoint_dict = {}
67 for branchpoint in branchpoint_list:
68 self._branchpoint_dict[branchpoint] = (
69 self._release_config.get(
70 _CONF_BRANCH_SECTION,
71 _CONF_BRANCH_POINT_OPT_PREFIX + branchpoint))
72
73 # Get next branch name.
74 self._next_branch = self._release_config.get(_CONF_BRANCH_SECTION,
75 _CONF_NEXT_BRANCH_OPT)
76 if not self._next_branch:
77 raise ReleaseError("missing `%s' option" %
78 _CONF_NEXT_BRANCH_OPT)
79 except IOError, e:
80 raise ReleaseError('failed to open release config file (%s): %s' %
Gilad Arnold03901082012-11-19 07:29:02 -080081 (_RELEASE_CONFIG_FILE, e))
Gilad Arnold84eb60c2012-11-05 23:46:10 -080082 except ConfigParser.Error, e:
Gilad Arnold03901082012-11-19 07:29:02 -080083 raise ReleaseError('failed to load release config: %s' % e)
Gilad Arnold84eb60c2012-11-05 23:46:10 -080084
85 # Infer chronologically sorted list of branchpoints.
86 self._sorted_branchpoint_list = self._branchpoint_dict.items()
87 self._sorted_branchpoint_list.append((self._next_branch, '99999.0.0'))
88 self._sorted_branchpoint_list.sort(
89 key=lambda (branch, release): self._release_key(release))
90
91 # Also store a sorted list of branchpoint release keys, for easy lookup.
92 self._sorted_branchpoint_release_key_list = (
93 [self._release_key(release)
94 for (branch, release) in self._sorted_branchpoint_list])
95
96
97 def _release_key(self, release):
98 """Convert a Chrome OS release string into an integer key.
99
100 This translates a release string 'X.Y.Z' (new scheme) or 'W.X.Y.Z' (old
101 scheme where W = 0) into an integer whose value equals X * 10^7 + Y *
102 10^3 + Z, assuming that Y < 10^4 and Z < 10^3, and will scale safely to
103 any foreseeable major release number (X).
104
105 @param release: the release number in dotted notation (string)
106
107 @return A unique integer key representing the release.
108
109 @raise ReleaseError if the release is malformed.
110
111 """
112 release_components = release.split('.')
113 if len(release_components) == 4 and release_components[0] == '0':
114 release_components = release_components[1:]
115 elif len(release_components) != 3:
116 raise ReleaseError('invalid release number: %s' % release)
117 x, y, z = [int(s) for s in release_components]
118 return x * 10000000 + y * 1000 + z
119
120
121 def get_branch_list(self):
122 """Retruns chronologically sorted list of branch names."""
123 return [branch for (branch, release) in self._sorted_branchpoint_list]
124
125
126 def get_branch(self, release):
127 """Returns the branch name of a given release version. """
128 i = bisect.bisect_left(self._sorted_branchpoint_release_key_list,
129 self._release_key(release))
130 return self._sorted_branchpoint_list[i][0] if i else None
131
132
133 def get_branchpoint_release(self, branch):
134 """Returns the branchpoint release of a given branch.
135
136 Returns None if given name is the next branch.
137
138 @raise KeyError if branch name not known
139
140 """
141 if branch == self._next_branch:
142 return None
143 return self._branchpoint_dict[branch]