generate_unexpire_flags.py 5.3 KB

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