1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
|
#!/usr/bin/env python
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates extra flags needed to allow temporarily reverting flag expiry.
This program generates three files:
* A C++ source file, containing definitions of base::Features that unexpire
flags that expired in recent milestones, along with a definition of a
definition of a function `flags::ExpiryEnabledForMilestone`
* A C++ header file, containing declarations of those base::Features
* A C++ source fragment, containing definitions of flags_ui::FeatureEntry
structures for flags corresponding to those base::Features
Which milestones are recent is sourced from //chrome/VERSION in the source tree.
"""
import os
import sys
ROOT_PATH = os.path.join(os.path.dirname(__file__), '..', '..')
def get_chromium_version():
"""Parses the chromium version out of //chrome/VERSION."""
with open(os.path.join(ROOT_PATH, 'chrome', 'VERSION')) as f:
for line in f.readlines():
key, value = line.strip().split('=')
if key == 'MAJOR':
return int(value)
return None
def recent_mstones(mstone):
"""Returns the list of milestones considered 'recent' for the given mstone.
Flag unexpiry is available only for flags that expired at recent mstones."""
return [mstone - 1, mstone]
def file_header(prog_name):
"""Returns the header to use on generated files."""
return """// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This is a generated file. Do not edit it! It was generated by:
// {prog_name}
""".format(prog_name=prog_name)
def gen_features_impl(prog_name, mstone):
"""Generates the definitions for the unexpiry features and the expiry-check
function.
This function generates the contents of a complete C++ source file,
which defines base::Features for unexpiration of flags from recent milestones,
as well as a function ExpiryEnabledForMilestone().
"""
body = file_header(prog_name)
body += """
#include "base/feature_list.h"
#include "chrome/browser/unexpire_flags_gen.h"
namespace flags {
"""
features = [(m, 'UnexpireFlagsM' + str(m)) for m in recent_mstones(mstone)]
for feature in features:
body += 'const base::Feature k{f} {{\n'.format(f=feature[1])
body += ' "{f}",\n'.format(f=feature[1])
body += ' base::FEATURE_DISABLED_BY_DEFAULT\n'
body += '};\n\n'
body += """// Returns the unexpire feature for the given mstone, if any.
const base::Feature* GetUnexpireFeatureForMilestone(int milestone) {
switch (milestone) {
"""
for feature in features:
body += ' case {m}: return &k{f};\n'.format(m=feature[0], f=feature[1])
body += """ default: return nullptr;
}
}
} // namespace flags
"""
return body
def gen_features_header(prog_name, mstone):
"""Generate a header file declaring features and the expiry predicate.
This header declares the features and function described in
gen_features_impl().
"""
body = file_header(prog_name)
body += """
#ifndef GEN_CHROME_BROWSER_UNEXPIRE_FLAGS_GEN_H_
#define GEN_CHROME_BROWSER_UNEXPIRE_FLAGS_GEN_H_
namespace flags {
"""
for m in recent_mstones(mstone):
body += 'extern const base::Feature kUnexpireFlagsM{m};\n'.format(m=m)
body += """
// Returns the base::Feature used to decide whether flag expiration is enabled
// for a given milestone, if there is such a feature. If not, returns nullptr.
const base::Feature* GetUnexpireFeatureForMilestone(int milestone);
} // namespace flags
#endif // GEN_CHROME_BROWSER_UNEXPIRE_FLAGS_GEN_H_
"""
return body
def gen_flags_fragment(prog_name, mstone):
"""Generates a .inc file containing flag definitions.
This creates a C++ source fragment defining flags, which are bound to the
features described in gen_features_impl().
"""
# Note: The exact format of the flag name (temporary-unexpire-flags-m{m}) is
# depended on by a hack in UnexpiredMilestonesFromStorage(). See
# https://crbug.com/1101828 for more details.
fragment = """
{{"temporary-unexpire-flags-m{m}",
"Temporarily unexpire M{m} flags.",
"Temporarily unexpire flags that expired as of M{m}. These flags will be"
" removed soon.",
kOsAll | flags_ui::kFlagInfrastructure,
FEATURE_VALUE_TYPE(flags::kUnexpireFlagsM{m})}},
"""
return '\n'.join([fragment.format(m=m) for m in recent_mstones(mstone)])
def update_file_if_stale(filename, data):
"""Writes data to filename if data is different from file's contents on disk.
"""
try:
disk_data = open(filename, 'r').read()
if disk_data == data:
return
except IOError:
pass
open(filename, 'w').write(data)
def main():
mstone = get_chromium_version()
if not mstone:
raise ValueError('Can\'t find or understand //chrome/VERSION')
progname = sys.argv[0]
# Note the mstone - 1 here: the listed expiration mstone is the last mstone in
# which that flag is present, not the first mstone in which it is not present.
update_file_if_stale(sys.argv[1], gen_features_impl(progname, mstone - 1))
update_file_if_stale(sys.argv[2], gen_features_header(progname, mstone - 1))
update_file_if_stale(sys.argv[3], gen_flags_fragment(progname, mstone - 1))
if __name__ == '__main__':
main()
|