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.
197 lines
5.4 KiB
197 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()])
|
|
|