diff options
Diffstat (limited to 'bestflags/flags.py')
-rw-r--r-- | bestflags/flags.py | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/bestflags/flags.py b/bestflags/flags.py new file mode 100644 index 00000000..b316421e --- /dev/null +++ b/bestflags/flags.py @@ -0,0 +1,197 @@ +# 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()]) |