blob: b316421e9c0dbe3ca48e14f5edd7a576450381a4 [file] [log] [blame]
Yuheng Longd67dcb72013-07-29 15:39:30 -07001# 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 Longd67dcb72013-07-29 15:39:30 -07004"""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# 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].
38rx = re.compile(r'\[(?P<start>\d+)-(?P<end>\d+)\]')
39
40
Yuheng Longccfaf2f2013-08-02 14:27:45 -070041# Search the numeric flag pattern.
42def Search(spec):
43 return rx.search(spec)
44
45
Yuheng Long2b514c22013-08-08 21:07:24 -070046class NoSuchFileError(Exception):
47 """Define an Exception class for user providing invalid input file."""
48 pass
49
50
Yuheng Longccfaf2f2013-08-02 14:27:45 -070051def 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 Long2b514c22013-08-08 21:07:24 -070061
62 Raises:
63 NoSuchFileError: The caller should provide a valid configuration file.
Yuheng Longccfaf2f2013-08-02 14:27:45 -070064 """
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 Long2b514c22013-08-08 21:07:24 -070070
71 raise NoSuchFileError()
Yuheng Longccfaf2f2013-08-02 14:27:45 -070072
73
Yuheng Longd67dcb72013-07-29 15:39:30 -070074class 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 Long2b514c22013-08-08 21:07:24 -070078 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 Longd67dcb72013-07-29 15:39:30 -070081
82 An example of a spec is "foo[0-9]".
Yuheng Long2b514c22013-08-08 21:07:24 -070083 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 Longd67dcb72013-07-29 15:39:30 -070090 """
91
92 def __init__(self, spec, value=-1):
93 self._spec = spec
94
Yuheng Longe896dfd2013-08-13 20:47:45 -070095 # If the value is not specified, generate a random value to use.
Yuheng Longd67dcb72013-07-29 15:39:30 -070096 if value == -1:
Yuheng Longe896dfd2013-08-13 20:47:45 -070097 # If creating a boolean flag, the value will be 0.
Yuheng Longccfaf2f2013-08-02 14:27:45 -070098 value = 0
99
Yuheng Longe896dfd2013-08-13 20:47:45 -0700100 # Parse the spec's expression for the flag value's numeric range.
101 numeric_flag_match = Search(spec)
102
Yuheng Longd67dcb72013-07-29 15:39:30 -0700103 # If this is a numeric flag, a value is chosen within start and end, start
104 # inclusive and end exclusive.
Yuheng Longe896dfd2013-08-13 20:47:45 -0700105 if numeric_flag_match:
106 start = int(numeric_flag_match.group('start'))
107 end = int(numeric_flag_match.group('end'))
Yuheng Longd67dcb72013-07-29 15:39:30 -0700108
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
155class 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()])