aboutsummaryrefslogtreecommitdiff
path: root/bestflags/flags.py
diff options
context:
space:
mode:
Diffstat (limited to 'bestflags/flags.py')
-rw-r--r--bestflags/flags.py197
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()])