• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2013 The ChromiumOS Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""Manage bundles of flags used for the optimizing of ChromeOS.
5
6Part of the Chrome build flags optimization.
7
8The content of this module is adapted from the Trakhelp JVM project. This module
9contains the basic Class Flag and the Class FlagSet. The core abstractions are:
10
11The class Flag, which takes a domain specific language describing how to fill
12the flags with values.
13
14The class FlagSet, which contains a number of flags and can create new FlagSets
15by mixing with other FlagSets.
16
17The Flag DSL works by replacing value ranges in [x-y] with numbers in the range
18x through y.
19
20Examples:
21  "foo[0-9]bar" will expand to e.g. "foo5bar".
22"""
23
24__author__ = "yuhenglong@google.com (Yuheng Long)"
25
26import random
27import re
28
29
30#
31# This matches a [...] group in the internal representation for a flag
32# specification, and is used in "filling out" flags - placing values inside
33# the flag_spec.  The internal flag_spec format is like "foo[0]", with
34# values filled out like 5; this would be transformed by
35# FormattedForUse() into "foo5".
36_FLAG_FILLOUT_VALUE_RE = re.compile(r"\[([^\]]*)\]")
37
38# This matches a numeric flag flag=[start-end].
39rx = re.compile(r"\[(?P<start>\d+)-(?P<end>\d+)\]")
40
41
42# Search the numeric flag pattern.
43def Search(spec):
44    return rx.search(spec)
45
46
47class NoSuchFileError(Exception):
48    """Define an Exception class for user providing invalid input file."""
49
50    pass
51
52
53def ReadConf(file_name):
54    """Parse the configuration file.
55
56    The configuration contains one flag specification in each line.
57
58    Args:
59      file_name: The name of the configuration file.
60
61    Returns:
62      A list of specs in the configuration file.
63
64    Raises:
65      NoSuchFileError: The caller should provide a valid configuration file.
66    """
67
68    with open(file_name, "r") as input_file:
69        lines = input_file.readlines()
70
71        return sorted([line.strip() for line in lines if line.strip()])
72
73    raise NoSuchFileError()
74
75
76class Flag(object):
77    """A class representing a particular command line flag argument.
78
79    The Flag consists of two parts: The spec and the value.
80    The spec is a definition of the following form: a string with escaped
81    sequences of the form [<start>-<end>] where start and end is an positive
82    integer for a fillable value.
83
84    An example of a spec is "foo[0-9]".
85    There are two kinds of flags, boolean flag and numeric flags. Boolean flags
86    can either be turned on or off, which numeric flags can have different
87    positive integer values. For example, -finline-limit=[1-1000] is a numeric
88    flag and -ftree-vectorize is a boolean flag.
89
90    A (boolean/numeric) flag is not turned on if it is not selected in the
91    FlagSet.
92    """
93
94    def __init__(self, spec, value=-1):
95        self._spec = spec
96
97        # If the value is not specified, generate a random value to use.
98        if value == -1:
99            # If creating a boolean flag, the value will be 0.
100            value = 0
101
102            # Parse the spec's expression for the flag value's numeric range.
103            numeric_flag_match = Search(spec)
104
105            # If this is a numeric flag, a value is chosen within start and end, start
106            # inclusive and end exclusive.
107            if numeric_flag_match:
108                start = int(numeric_flag_match.group("start"))
109                end = int(numeric_flag_match.group("end"))
110
111                assert start < end
112                value = random.randint(start, end)
113
114        self._value = value
115
116    def __eq__(self, other):
117        if isinstance(other, Flag):
118            return (
119                self._spec == other.GetSpec()
120                and self._value == other.GetValue()
121            )
122        return False
123
124    def __hash__(self):
125        return hash(self._spec) + self._value
126
127    def GetValue(self):
128        """Get the value for this flag.
129
130        Returns:
131         The value.
132        """
133
134        return self._value
135
136    def GetSpec(self):
137        """Get the spec for this flag.
138
139        Returns:
140         The spec.
141        """
142
143        return self._spec
144
145    def FormattedForUse(self):
146        """Calculate the combination of flag_spec and values.
147
148        For e.g. the flag_spec 'foo[0-9]' and the value equals to 5, this will
149        return 'foo5'. The filled out version of the flag is the text string you use
150        when you actually want to pass the flag to some binary.
151
152        Returns:
153          A string that represent the filled out flag, e.g. the flag with the
154          FlagSpec '-X[0-9]Y' and value equals to 5 would return '-X5Y'.
155        """
156
157        return _FLAG_FILLOUT_VALUE_RE.sub(str(self._value), self._spec)
158
159
160class FlagSet(object):
161    """A dictionary of Flag objects.
162
163    The flags dictionary stores the spec and flag pair.
164    """
165
166    def __init__(self, flag_array):
167        # Store the flags as a dictionary mapping of spec -> flag object
168        self._flags = dict([(flag.GetSpec(), flag) for flag in flag_array])
169
170    def __eq__(self, other):
171        return isinstance(other, FlagSet) and self._flags == other.GetFlags()
172
173    def __hash__(self):
174        return sum([hash(flag) for flag in self._flags.values()])
175
176    def __getitem__(self, flag_spec):
177        """Get flag with a particular flag_spec.
178
179        Args:
180          flag_spec: The flag_spec to find.
181
182        Returns:
183          A flag.
184        """
185
186        return self._flags[flag_spec]
187
188    def __contains__(self, flag_spec):
189        return self._flags.has_key(flag_spec)
190
191    def GetFlags(self):
192        return self._flags
193
194    def FormattedForUse(self):
195        """Format this for use in an application.
196
197        Returns:
198          A list of flags, sorted alphabetically and filled in with the values
199          for each flag.
200        """
201
202        return sorted([f.FormattedForUse() for f in self._flags.values()])
203