1# 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.
4"""Utility functions to explore the neighbor flags.
5
6Part of the Chrome build flags optimization.
7"""
8
9__author__ = 'yuhenglong@google.com (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(climb_spec,
51                                 int(numeric_flag_match.group('start')))
52    else:
53      # Boolean flags.
54      results[climb_spec] = Flag(climb_spec)
55
56    return [results]
57
58  # The flags contain the spec.
59  if not numeric_flag_match:
60    # Boolean flags.
61    results = flags_dict.copy()
62
63    # Turn off the flag. A flag is turned off if it is not presented in the
64    # flags_dict.
65    del results[climb_spec]
66    return [results]
67
68  # Numeric flags.
69  flag = flags_dict[climb_spec]
70
71  # The value of the flag having spec.
72  value = flag.GetValue()
73  results = []
74
75  if value + 1 < int(numeric_flag_match.group('end')):
76    # If the value is not the end value, explore the value that is 1 larger than
77    # the current value.
78    neighbor = flags_dict.copy()
79    neighbor[climb_spec] = Flag(climb_spec, value + 1)
80    results.append(neighbor)
81
82  if value > int(numeric_flag_match.group('start')):
83    # If the value is not the start value, explore the value that is 1 lesser
84    # than the current value.
85    neighbor = flags_dict.copy()
86    neighbor[climb_spec] = Flag(climb_spec, value - 1)
87    results.append(neighbor)
88  else:
89    # Delete the value, i.e., turn off the flag. A flag is turned off if it is
90    # not presented in the flags_dict.
91    neighbor = flags_dict.copy()
92    del neighbor[climb_spec]
93    results.append(neighbor)
94
95  return results
96