175 lines
6.7 KiB
Python
175 lines
6.7 KiB
Python
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""Modules for obtaining Chrome OS release info."""
|
|
|
|
|
|
import ConfigParser
|
|
import bisect
|
|
import os
|
|
|
|
|
|
_RELEASE_CONFIG_FILE = os.path.join(os.path.dirname(__file__),
|
|
'release_config.ini')
|
|
|
|
# Prefix for brachpoint definitions in the config file.
|
|
_CONF_BRANCH_SECTION = 'BRANCH'
|
|
_CONF_BRANCH_POINTS_OPT = 'branch_points'
|
|
_CONF_BRANCH_POINT_OPT_PREFIX = 'bp_'
|
|
_CONF_NEXT_BRANCH_OPT = 'next_branch'
|
|
|
|
|
|
class ReleaseError(BaseException):
|
|
"""Errors related to release and branch inference."""
|
|
pass
|
|
|
|
|
|
class ReleaseInfo(object):
|
|
"""Provides reference information about Chrome OS releases.
|
|
|
|
Currently, this class serves for mapping between releases and branches /
|
|
release milestones. The information lives in a .ini file at the current
|
|
directory, which has a single section [BRANCH] containing
|
|
|
|
branch_points: comma-separated list of release branches (e.g. R10, R11,
|
|
...)
|
|
|
|
bp_XYZ: for each branch listed above, a variable that maps to the Chrome
|
|
OS release at that branchpoint (e.g. bp_r10: 0.10.156.0). Note that .ini
|
|
file variables are case-insensitive.
|
|
|
|
next_branch: the name of the current (unforked) branch (e.g. R24)
|
|
|
|
It is also worth noting that a branch point X.Y.Z (alternatively, W.X.Y.Z)
|
|
of some branch R denotes the build number X (repsectively, W) that
|
|
constitutes the said branch. Therefore, it is only from build X+1 (W+1) and
|
|
onward that releases will be tagged with R+1.
|
|
|
|
"""
|
|
def __init__(self):
|
|
self._release_config = None
|
|
self._branchpoint_dict = None
|
|
self._next_branch = None
|
|
self._sorted_branchpoint_list = None
|
|
self._sorted_shifted_branchpoint_rel_key_list = None
|
|
|
|
def initialize(self):
|
|
"""Read release config and initialize lookup data structures."""
|
|
self._release_config = ConfigParser.ConfigParser()
|
|
try:
|
|
self._release_config.readfp(open(_RELEASE_CONFIG_FILE))
|
|
|
|
# Build branchpoint dictionary.
|
|
branchpoint_list_str = self._release_config.get(
|
|
_CONF_BRANCH_SECTION, _CONF_BRANCH_POINTS_OPT)
|
|
if branchpoint_list_str:
|
|
branchpoint_list = map(str.strip,
|
|
branchpoint_list_str.split(','))
|
|
else:
|
|
branchpoint_list = []
|
|
|
|
self._branchpoint_dict = {}
|
|
for branchpoint in branchpoint_list:
|
|
self._branchpoint_dict[branchpoint] = (
|
|
self._release_config.get(
|
|
_CONF_BRANCH_SECTION,
|
|
_CONF_BRANCH_POINT_OPT_PREFIX + branchpoint))
|
|
|
|
# Get next branch name.
|
|
self._next_branch = self._release_config.get(_CONF_BRANCH_SECTION,
|
|
_CONF_NEXT_BRANCH_OPT)
|
|
if not self._next_branch:
|
|
raise ReleaseError("missing `%s' option" %
|
|
_CONF_NEXT_BRANCH_OPT)
|
|
except IOError, e:
|
|
raise ReleaseError('failed to open release config file (%s): %s' %
|
|
(_RELEASE_CONFIG_FILE, e))
|
|
except ConfigParser.Error, e:
|
|
raise ReleaseError('failed to load release config: %s' % e)
|
|
|
|
# Infer chronologically sorted list of branchpoints.
|
|
self._sorted_branchpoint_list = self._branchpoint_dict.items()
|
|
self._sorted_branchpoint_list.append((self._next_branch, '99999.0.0'))
|
|
self._sorted_branchpoint_list.sort(
|
|
key=lambda (branch, release): self._release_key(release))
|
|
|
|
# Also store a sorted list of branchpoint release keys, for easy lookup.
|
|
self._sorted_shifted_branchpoint_rel_key_list = [
|
|
self._release_key(self._next_build_number_release(release))
|
|
for (branch, release) in self._sorted_branchpoint_list]
|
|
|
|
|
|
def _next_build_number_release(self, release):
|
|
"""Returns the release of the next build following a given release.
|
|
|
|
Given a release number 'X.Y.Z' (new scheme) or '0.X.Y.Z' (old scheme)
|
|
it will return 'X+1.0.0' or '0.X+1.0.0', respectively.
|
|
|
|
@param release: the release number in dotted notation (string)
|
|
|
|
@return The release number of the next build.
|
|
|
|
@raise ReleaseError if the release is malformed.
|
|
|
|
"""
|
|
release_components = release.split('.')
|
|
if len(release_components) == 4 and release_components[0] == '0':
|
|
prepend = '0.'
|
|
x = int(release_components[1])
|
|
elif len(release_components) != 3:
|
|
raise ReleaseError('invalid release number: %s' % release)
|
|
else:
|
|
prepend = ''
|
|
x = int(release_components[0])
|
|
|
|
return '%s%s.0.0' % (prepend, x + 1)
|
|
|
|
|
|
def _release_key(self, release):
|
|
"""Convert a Chrome OS release string into an integer key.
|
|
|
|
This translates a release string 'X.Y.Z' (new scheme) or 'W.X.Y.Z' (old
|
|
scheme where W = 0) into an integer whose value equals X * 10^7 + Y *
|
|
10^3 + Z, assuming that Y < 10^4 and Z < 10^3, and will scale safely to
|
|
any foreseeable major release number (X).
|
|
|
|
@param release: the release number in dotted notation (string)
|
|
|
|
@return A unique integer key representing the release.
|
|
|
|
@raise ReleaseError if the release is malformed.
|
|
|
|
"""
|
|
release_components = release.split('.')
|
|
if len(release_components) == 4 and release_components[0] == '0':
|
|
release_components = release_components[1:]
|
|
elif len(release_components) != 3:
|
|
raise ReleaseError('invalid release number: %s' % release)
|
|
x, y, z = [int(s) for s in release_components]
|
|
return x * 10000000 + y * 1000 + z
|
|
|
|
|
|
def get_branch_list(self):
|
|
"""Retruns chronologically sorted list of branch names."""
|
|
return [branch for (branch, release) in self._sorted_branchpoint_list]
|
|
|
|
|
|
def get_branch(self, release):
|
|
"""Returns the branch name of a given release version. """
|
|
i = bisect.bisect_left(self._sorted_shifted_branchpoint_rel_key_list,
|
|
self._release_key(release))
|
|
return self._sorted_branchpoint_list[i][0] if i else None
|
|
|
|
|
|
def get_branchpoint_release(self, branch):
|
|
"""Returns the branchpoint release of a given branch.
|
|
|
|
Returns None if given name is the next branch.
|
|
|
|
@raise KeyError if branch name not known
|
|
|
|
"""
|
|
if branch == self._next_branch:
|
|
return None
|
|
return self._branchpoint_dict[branch]
|