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