xref: /aosp_15_r20/external/toolchain-utils/bestflags/flags_util.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
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"""Utility functions to explore the neighbor flags.
5
6Part of the Chrome build flags optimization.
7"""
8
9__author__ = "[email protected] (Yuheng Long)"
10
11import flags
12from flags import Flag
13
14
15def ClimbNext(flags_dict, climb_spec):
16    """Get the flags that are different from |flags_dict| by |climb_spec|.
17
18    Given a set of flags, |flags_dict|, return a new set of flags that are
19    adjacent along the flag spec |climb_spec|.
20
21    An example flags_dict is {foo=[1-9]:foo=5, bar=[1-5]:bar=2} and climb_spec is
22    bar=[1-5]. This method changes the flag that contains the spec bar=[1-5]. The
23    results are its neighbors dictionaries, i.e., {foo=[1-9]:foo=5,
24    bar=[1-5]:bar=1} and {foo=[1-9]:foo=5, bar=[1-5]:bar=3}.
25
26    Args:
27      flags_dict: The dictionary containing the original flags whose neighbors are
28        to be explored.
29      climb_spec: The spec in the flags_dict is to be changed. The spec is a
30        definition in the little language, a string with escaped sequences of the
31        form [<start>-<end>] where start and end is an positive integer for a
32        fillable value. An example of a spec is "foo[0-9]".
33
34    Returns:
35      List of dictionaries of neighbor flags.
36    """
37
38    # This method searches for a pattern [start-end] in the spec. If the spec
39    # contains this pattern, it is a numeric flag. Otherwise it is a boolean flag.
40    # For example, -finline-limit=[1-1000] is a numeric flag and -falign-jumps is
41    # a boolean flag.
42    numeric_flag_match = flags.Search(climb_spec)
43
44    # If the flags do not contain the spec.
45    if climb_spec not in flags_dict:
46        results = flags_dict.copy()
47
48        if numeric_flag_match:
49            # Numeric flags.
50            results[climb_spec] = Flag(
51                climb_spec, int(numeric_flag_match.group("start"))
52            )
53        else:
54            # Boolean flags.
55            results[climb_spec] = Flag(climb_spec)
56
57        return [results]
58
59    # The flags contain the spec.
60    if not numeric_flag_match:
61        # Boolean flags.
62        results = flags_dict.copy()
63
64        # Turn off the flag. A flag is turned off if it is not presented in the
65        # flags_dict.
66        del results[climb_spec]
67        return [results]
68
69    # Numeric flags.
70    flag = flags_dict[climb_spec]
71
72    # The value of the flag having spec.
73    value = flag.GetValue()
74    results = []
75
76    if value + 1 < int(numeric_flag_match.group("end")):
77        # If the value is not the end value, explore the value that is 1 larger than
78        # the current value.
79        neighbor = flags_dict.copy()
80        neighbor[climb_spec] = Flag(climb_spec, value + 1)
81        results.append(neighbor)
82
83    if value > int(numeric_flag_match.group("start")):
84        # If the value is not the start value, explore the value that is 1 lesser
85        # than the current value.
86        neighbor = flags_dict.copy()
87        neighbor[climb_spec] = Flag(climb_spec, value - 1)
88        results.append(neighbor)
89    else:
90        # Delete the value, i.e., turn off the flag. A flag is turned off if it is
91        # not presented in the flags_dict.
92        neighbor = flags_dict.copy()
93        del neighbor[climb_spec]
94        results.append(neighbor)
95
96    return results
97