xref: /aosp_15_r20/external/toolchain-utils/bestflags/flags.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
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