| # Copyright 2013 The ChromiumOS Authors |
| # 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__ = "[email protected] (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()]) |