diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/build/scripts/audit_runtime_enabled_features.py')
-rwxr-xr-x | chromium/third_party/WebKit/Source/build/scripts/audit_runtime_enabled_features.py | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/build/scripts/audit_runtime_enabled_features.py b/chromium/third_party/WebKit/Source/build/scripts/audit_runtime_enabled_features.py new file mode 100755 index 00000000000..fcd318f8e42 --- /dev/null +++ b/chromium/third_party/WebKit/Source/build/scripts/audit_runtime_enabled_features.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python + +import requests +import re +from in_file import InFile + +BRANCH_FORMAT = "https://src.chromium.org/blink/branches/chromium/%s/%s" +TRUNK_PATH = "Source/platform/RuntimeEnabledFeatures.in" +TRUNK_URL = "https://src.chromium.org/blink/trunk/%s" % TRUNK_PATH + + +def features_path(branch): + # RuntimeEnabledFeatures has only existed since April 2013: + if branch <= 1453: + return None + # Source/core/page/RuntimeEnabledFeatures.in existed by 1547 + # but was in an old format without status= arguments. + if branch <= 1547: + return None + if branch <= 1650: + return "Source/core/page/RuntimeEnabledFeatures.in" + # Modern location: + return TRUNK_PATH + + +def parse_features_file(features_text): + valid_values = { + 'status': ['stable', 'experimental', 'test'], + } + defaults = { + 'condition': None, + 'depends_on': [], + 'custom': False, + 'status': None, + } + + # FIXME: in_file.py manually calls str.strip so conver to str here. + features_lines = str(features_text).split("\n") + return InFile(features_lines, defaults, valid_values) + + +def stable_features(in_file): + return [feature['name'] for feature in in_file.name_dictionaries if feature['status'] == 'stable'] + + +def branch_from_version(version_string): + # Format: 31.0.1650.63, the second digit was only ever used for M4 + # no clue what it's actually intended for. + version_regexp = r"(?P<major>\d+)\.\d+\.(?P<branch>\d+)\.(?P<minor>\d+)" + match = re.match(version_regexp, version_string) + # if match == None, we'll blow up, so at least provide some debugging information: + if not match: + print version_string + return int(match.group('branch')) + + +def print_feature_diff(added_features, removed_features): + for feature in added_features: + print "+ %s" % feature + for feature in removed_features: + print "- %s" % feature + + +def historical_versions(os_string, channel): + url_pattern = "http://omahaproxy.appspot.com/history?os=%s&channel=%s" + url = url_pattern % (os_string, channel) + releases_csv = requests.get(url).text.strip("\n") + # Format: os,channel,version_string,date_string + lines = releases_csv.split('\n') + # As of June 2014, omahaproxy is now including headers: + assert(lines[0] == 'os,channel,version,timestamp') + # FIXME: We could replace this with more generic CSV parsing now that we have headers. + return [line.split(',')[2] for line in lines[1:]] + + +def feature_file_url_for_branch(branch): + path = features_path(branch) + if not path: + return None + return BRANCH_FORMAT % (branch, path) + + +def feature_file_for_branch(branch): + url = feature_file_url_for_branch(branch) + if not url: + return None + return parse_features_file(requests.get(url).text) + + +def historical_feature_tuples(os_string, channel): + feature_tuples = [] + version_strings = reversed(historical_versions(os_string, channel)) + seen_branches = set() + + for version in version_strings: + branch = branch_from_version(version) + if branch in seen_branches: + continue + seen_branches.add(branch) + + feature_file = feature_file_for_branch(branch) + if not feature_file: + continue + feature_tuple = (version, feature_file) + feature_tuples.append(feature_tuple) + return feature_tuples + + +class FeatureAuditor(object): + def __init__(self): + self.last_features = [] + + def add_version(self, version_name, feature_file): + features = stable_features(feature_file) + if self.last_features: + added_features = list(set(features) - set(self.last_features)) + removed_features = list(set(self.last_features) - set(features)) + + print "\n%s:" % version_name + print_feature_diff(added_features, removed_features) + + self.last_features = features + + +def active_feature_tuples(os_string): + feature_tuples = [] + current_releases_url = "http://omahaproxy.appspot.com/all.json" + trains = requests.get(current_releases_url).json() + train = next(train for train in trains if train['os'] == os_string) + # FIXME: This is depending on the ordering of the json, we could + # use use sorted() with true_branch, but that would put None first. + for version in reversed(train['versions']): + # FIXME: This is lame to exclude stable, the caller should + # ignore it if it doesn't want it. + if version['channel'] == 'stable': + continue # handled by historical_feature_tuples + branch = version['true_branch'] + if branch: + feature_file = feature_file_for_branch(branch) + else: + feature_file = parse_features_file(requests.get(TRUNK_URL).text) + + name = "%(version)s %(channel)s" % version + feature_tuples.append((name, feature_file)) + return feature_tuples + + +# FIXME: This only really needs feature_files. +def stale_features(tuples): + last_features = None + can_be_removed = set() + for _, feature_file in tuples: + features = stable_features(feature_file) + if last_features: + can_be_removed.update(set(features)) + removed_features = list(set(last_features) - set(features)) + can_be_removed.difference_update(set(removed_features)) + last_features = features + return sorted(can_be_removed) + + +def main(): + historical_tuples = historical_feature_tuples("win", "stable") + active_tuples = active_feature_tuples("win") + + auditor = FeatureAuditor() + for version, feature_file in historical_tuples + active_tuples: + auditor.add_version(version, feature_file) + + print "\nConsider for removal (have been stable for at least one release):" + for feature in stale_features(historical_tuples): + print feature + + +if __name__ == "__main__": + main() |