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
|
#!/usr/bin/env python3
# Copyright 2020 The Chromium Authors
# 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
// 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 += f'BASE_FEATURE(k{feature[1]},\n'
body += f' "{feature[1]}",\n'
body += f' base::FEATURE_DISABLED_BY_DEFAULT);\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 += f'BASE_DECLARE_FEATURE(kUnexpireFlagsM{m});\n'
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()
|