blob: 02725e7cbbd10e2550f24f41fecbe73fd7941802 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (C) 2013 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import copy
import os
import sys
if sys.version_info.major == 2:
import cPickle as pickle
else:
import pickle
from blinkbuild.name_style_converter import NameStyleConverter
import make_runtime_features_utilities as util
import json5_generator
import template_expander
class BaseRuntimeFeatureWriter(json5_generator.Writer):
# |class_name| should be passed as a template input to generate the target
# class. Set this variable if the template generates a class.
class_name = None
# |file_basename| must be set by subclasses since it is used to generate
# the header guard.
file_basename = None
def __init__(self, json5_file_path, output_dir):
super(BaseRuntimeFeatureWriter, self).__init__(json5_file_path,
output_dir)
# Subclasses should add generated output files and their contents to this dict.
self._outputs = {}
assert self.file_basename
self._features = self.json5_file.name_dictionaries
origin_trial_set = util.origin_trials(self._features)
# Make sure the resulting dictionaries have all the keys we expect.
for feature in self._features:
feature['in_origin_trial'] = str(
feature['name']) in origin_trial_set
feature['data_member_name'] = self._data_member_name(
feature['name'])
# If 'status' is a dict, add the values for all the not-mentioned platforms too.
if isinstance(feature['status'], dict):
feature['status'] = self._status_with_all_platforms(
feature['status'])
# Specify the type of status
feature['status_type'] = "dict" if isinstance(
feature['status'], dict) else "str"
if feature['base_feature'] == 'none':
feature['base_feature'] = ''
elif feature['base_feature'] == '':
feature['base_feature'] = feature['name']
self._origin_trial_features = [
feature for feature in self._features if feature['in_origin_trial']
]
self._header_guard = self.make_header_guard(self._relative_output_dir +
self.file_basename + '.h')
@staticmethod
def _data_member_name(str_or_converter):
converter = NameStyleConverter(str_or_converter) if type(
str_or_converter) is str else str_or_converter
return converter.to_class_data_member(prefix='is', suffix='enabled')
def _feature_sets(self):
# Another way to think of the status levels is as "sets of features"
# which is how we're referring to them in this generator.
return self.json5_file.parameters['status']['valid_values']
def _status_with_all_platforms(self, status):
new_status = copy.deepcopy(status)
default = new_status['default'] if 'default' in new_status else ''
new_status['default'] = default
for platform in self._platforms():
if platform not in new_status:
new_status[platform] = default
return new_status
def _platforms(self):
# Remove all occurrences of 'default' from 'valid_keys'
platforms = self.json5_file.parameters['status']['valid_keys']
return [platform for platform in platforms if platform != 'default']
class RuntimeFeatureWriter(BaseRuntimeFeatureWriter):
class_name = 'RuntimeEnabledFeatures'
file_basename = 'runtime_enabled_features'
def __init__(self, json5_file_path, output_dir):
super(RuntimeFeatureWriter, self).__init__(json5_file_path, output_dir)
self._outputs = {
(self.file_basename + '.h'):
self.generate_header,
(self.file_basename + '.cc'):
self.generate_implementation,
('exported/web_runtime_features_base.cc'):
self.generate_web_implementation,
}
# Write features to file for bindings generation
self._write_features_to_pickle_file(output_dir)
self._overridable_features = util.overridable_features(self._features)
overridable_set = set()
for feature in self._overridable_features:
overridable_set.add(str(feature['name']))
for feature in self._features:
feature['is_overridable_feature'] = str(
feature['name']) in overridable_set
def _write_features_to_pickle_file(self, platform_output_dir):
# TODO(yashard): Get the file path from args instead of hardcoding it.
file_name = os.path.join(platform_output_dir, '..', 'build', 'scripts',
'runtime_enabled_features.pickle')
features_map = {}
for feature in self._features:
features_map[str(feature['name'])] = {
'in_origin_trial': feature['in_origin_trial']
}
if os.path.isfile(file_name):
with open(os.path.abspath(file_name)) as pickle_file:
# pylint: disable=broad-except
try:
if pickle.load(pickle_file) == features_map:
return
except Exception:
# If trouble unpickling, overwrite
pass
with open(os.path.abspath(file_name), 'wb') as pickle_file:
pickle.dump(features_map, pickle_file)
def _template_inputs(self):
return {
'features': self._features,
'feature_sets': self._feature_sets(),
'platforms': self._platforms(),
'input_files': self._input_files,
'origin_trial_controlled_features': self._origin_trial_features,
'header_guard': self._header_guard,
}
@template_expander.use_jinja('templates/' + file_basename + '.h.tmpl')
def generate_header(self):
return self._template_inputs()
@template_expander.use_jinja('templates/' + file_basename + '.cc.tmpl')
def generate_implementation(self):
return self._template_inputs()
@template_expander.use_jinja('templates/web_runtime_features_base.cc.tmpl')
def generate_web_implementation(self):
return self._template_inputs()
class RuntimeFeatureTestHelpersWriter(BaseRuntimeFeatureWriter):
class_name = 'ScopedRuntimeEnabledFeatureForTest'
file_basename = 'runtime_enabled_features_test_helpers'
def __init__(self, json5_file_path, output_dir):
super(RuntimeFeatureTestHelpersWriter, self).__init__(
json5_file_path, output_dir)
self._outputs = {
('testing/' + self.file_basename + '.h'): self.generate_header
}
def _template_inputs(self):
return {
'features': self._features,
'input_files': self._input_files,
'header_guard': self._header_guard,
}
@template_expander.use_jinja('templates/' + file_basename + '.h.tmpl')
def generate_header(self):
return self._template_inputs()
if __name__ == '__main__':
json5_generator.Maker(RuntimeFeatureWriter).main()
json5_generator.Maker(RuntimeFeatureTestHelpersWriter).main()