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