You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
5.4 KiB
198 lines
5.4 KiB
# Copyright (c) 2013 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.
|
|
"""Manage bundles of flags used for the optimizing of ChromeOS.
|
|
|
|
Part of the Chrome build flags optimization.
|
|
|
|
The content of this module is adapted from the Trakhelp JVM project. This module
|
|
contains the basic Class Flag and the Class FlagSet. The core abstractions are:
|
|
|
|
The class Flag, which takes a domain specific language describing how to fill
|
|
the flags with values.
|
|
|
|
The class FlagSet, which contains a number of flags and can create new FlagSets
|
|
by mixing with other FlagSets.
|
|
|
|
The Flag DSL works by replacing value ranges in [x-y] with numbers in the range
|
|
x through y.
|
|
|
|
Examples:
|
|
"foo[0-9]bar" will expand to e.g. "foo5bar".
|
|
"""
|
|
|
|
__author__ = 'yuhenglong@google.com (Yuheng Long)'
|
|
|
|
import random
|
|
import re
|
|
|
|
#
|
|
# This matches a [...] group in the internal representation for a flag
|
|
# specification, and is used in "filling out" flags - placing values inside
|
|
# the flag_spec. The internal flag_spec format is like "foo[0]", with
|
|
# values filled out like 5; this would be transformed by
|
|
# FormattedForUse() into "foo5".
|
|
_FLAG_FILLOUT_VALUE_RE = re.compile(r'\[([^\]]*)\]')
|
|
|
|
# This matches a numeric flag flag=[start-end].
|
|
rx = re.compile(r'\[(?P<start>\d+)-(?P<end>\d+)\]')
|
|
|
|
|
|
# Search the numeric flag pattern.
|
|
def Search(spec):
|
|
return rx.search(spec)
|
|
|
|
|
|
class NoSuchFileError(Exception):
|
|
"""Define an Exception class for user providing invalid input file."""
|
|
pass
|
|
|
|
|
|
def ReadConf(file_name):
|
|
"""Parse the configuration file.
|
|
|
|
The configuration contains one flag specification in each line.
|
|
|
|
Args:
|
|
file_name: The name of the configuration file.
|
|
|
|
Returns:
|
|
A list of specs in the configuration file.
|
|
|
|
Raises:
|
|
NoSuchFileError: The caller should provide a valid configuration file.
|
|
"""
|
|
|
|
with open(file_name, 'r') as input_file:
|
|
lines = input_file.readlines()
|
|
|
|
return sorted([line.strip() for line in lines if line.strip()])
|
|
|
|
raise NoSuchFileError()
|
|
|
|
|
|
class Flag(object):
|
|
"""A class representing a particular command line flag argument.
|
|
|
|
The Flag consists of two parts: The spec and the value.
|
|
The spec is a definition of the following form: a string with escaped
|
|
sequences of the form [<start>-<end>] where start and end is an positive
|
|
integer for a fillable value.
|
|
|
|
An example of a spec is "foo[0-9]".
|
|
There are two kinds of flags, boolean flag and numeric flags. Boolean flags
|
|
can either be turned on or off, which numeric flags can have different
|
|
positive integer values. For example, -finline-limit=[1-1000] is a numeric
|
|
flag and -ftree-vectorize is a boolean flag.
|
|
|
|
A (boolean/numeric) flag is not turned on if it is not selected in the
|
|
FlagSet.
|
|
"""
|
|
|
|
def __init__(self, spec, value=-1):
|
|
self._spec = spec
|
|
|
|
# If the value is not specified, generate a random value to use.
|
|
if value == -1:
|
|
# If creating a boolean flag, the value will be 0.
|
|
value = 0
|
|
|
|
# Parse the spec's expression for the flag value's numeric range.
|
|
numeric_flag_match = Search(spec)
|
|
|
|
# If this is a numeric flag, a value is chosen within start and end, start
|
|
# inclusive and end exclusive.
|
|
if numeric_flag_match:
|
|
start = int(numeric_flag_match.group('start'))
|
|
end = int(numeric_flag_match.group('end'))
|
|
|
|
assert start < end
|
|
value = random.randint(start, end)
|
|
|
|
self._value = value
|
|
|
|
def __eq__(self, other):
|
|
if isinstance(other, Flag):
|
|
return self._spec == other.GetSpec() and self._value == other.GetValue()
|
|
return False
|
|
|
|
def __hash__(self):
|
|
return hash(self._spec) + self._value
|
|
|
|
def GetValue(self):
|
|
"""Get the value for this flag.
|
|
|
|
Returns:
|
|
The value.
|
|
"""
|
|
|
|
return self._value
|
|
|
|
def GetSpec(self):
|
|
"""Get the spec for this flag.
|
|
|
|
Returns:
|
|
The spec.
|
|
"""
|
|
|
|
return self._spec
|
|
|
|
def FormattedForUse(self):
|
|
"""Calculate the combination of flag_spec and values.
|
|
|
|
For e.g. the flag_spec 'foo[0-9]' and the value equals to 5, this will
|
|
return 'foo5'. The filled out version of the flag is the text string you use
|
|
when you actually want to pass the flag to some binary.
|
|
|
|
Returns:
|
|
A string that represent the filled out flag, e.g. the flag with the
|
|
FlagSpec '-X[0-9]Y' and value equals to 5 would return '-X5Y'.
|
|
"""
|
|
|
|
return _FLAG_FILLOUT_VALUE_RE.sub(str(self._value), self._spec)
|
|
|
|
|
|
class FlagSet(object):
|
|
"""A dictionary of Flag objects.
|
|
|
|
The flags dictionary stores the spec and flag pair.
|
|
"""
|
|
|
|
def __init__(self, flag_array):
|
|
# Store the flags as a dictionary mapping of spec -> flag object
|
|
self._flags = dict([(flag.GetSpec(), flag) for flag in flag_array])
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, FlagSet) and self._flags == other.GetFlags()
|
|
|
|
def __hash__(self):
|
|
return sum([hash(flag) for flag in self._flags.values()])
|
|
|
|
def __getitem__(self, flag_spec):
|
|
"""Get flag with a particular flag_spec.
|
|
|
|
Args:
|
|
flag_spec: The flag_spec to find.
|
|
|
|
Returns:
|
|
A flag.
|
|
"""
|
|
|
|
return self._flags[flag_spec]
|
|
|
|
def __contains__(self, flag_spec):
|
|
return self._flags.has_key(flag_spec)
|
|
|
|
def GetFlags(self):
|
|
return self._flags
|
|
|
|
def FormattedForUse(self):
|
|
"""Format this for use in an application.
|
|
|
|
Returns:
|
|
A list of flags, sorted alphabetically and filled in with the values
|
|
for each flag.
|
|
"""
|
|
|
|
return sorted([f.FormattedForUse() for f in self._flags.values()])
|