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