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 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
|
#!/usr/bin/env python
# Copyright 2015 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import os.path
import sys
import optparse
_script_path = os.path.realpath(__file__)
sys.path.insert(0, os.path.normpath(_script_path + "/../../json_comment_eater"))
try:
import json_comment_eater
finally:
sys.path.pop(0)
sys.path.insert(0, os.path.normpath(_script_path + "/../../json_to_struct"))
try:
import json_to_struct
finally:
sys.path.pop(0)
sys.path.insert(
0,
os.path.normpath(_script_path + "/../../../components/variations/service"))
try:
import generate_ui_string_overrider
finally:
sys.path.pop(0)
_platforms = [
'android',
'android_weblayer',
'android_webview',
'chromeos',
'chromeos_lacros',
'fuchsia',
'ios',
'linux',
'mac',
'windows',
]
_form_factors = [
'desktop',
'phone',
'tablet',
]
# Convert a platform argument to the matching Platform enum value in
# components/variations/proto/study.proto.
def _PlatformEnumValue(platform):
assert platform in _platforms
return 'Study::PLATFORM_' + platform.upper()
def _FormFactorEnumValue(form_factor):
assert form_factor in _form_factors
return 'Study::' + form_factor.upper()
def _Load(filename):
"""Loads a JSON file into a Python object and return this object."""
with open(filename, 'r') as handle:
result = json.loads(json_comment_eater.Nom(handle.read()))
return result
def _LoadFieldTrialConfig(filename, platforms):
"""Loads a field trial config JSON and converts it into a format that can be
used by json_to_struct.
"""
return _FieldTrialConfigToDescription(_Load(filename), platforms)
def _ConvertOverrideUIStrings(override_ui_strings):
"""Converts override_ui_strings to formatted dicts."""
overrides = []
for ui_string, override in override_ui_strings.items():
overrides.append({
'name_hash': generate_ui_string_overrider.HashName(ui_string),
'value': override
})
return overrides
def _CreateExperiment(experiment_data, platforms, form_factors,
is_low_end_device):
"""Creates an experiment dictionary with all necessary information.
Args:
experiment_data: An experiment json config.
platforms: A list of platforms for this trial. This should be
a subset of |_platforms|.
form_factors: A list of form factors for this trial. This should be
a subset of |_form_factors|.
is_low_end_device: An optional parameter. This can either be True or
False. None if not specified.
Returns:
An experiment dict.
"""
experiment = {
'name': experiment_data['name'],
'platforms': [_PlatformEnumValue(p) for p in platforms],
'form_factors': [_FormFactorEnumValue(f) for f in form_factors],
}
if is_low_end_device is not None:
experiment['is_low_end_device'] = str(is_low_end_device).lower()
forcing_flags_data = experiment_data.get('forcing_flag')
if forcing_flags_data:
experiment['forcing_flag'] = forcing_flags_data
min_os_version_data = experiment_data.get('min_os_version')
if min_os_version_data:
experiment['min_os_version'] = min_os_version_data
disable_benchmark_data = experiment_data.get('disable_benchmarking')
if disable_benchmark_data:
experiment['disable_benchmarking'] = disable_benchmark_data
hardware_classes_data = experiment_data.get('hardware_classes')
if hardware_classes_data:
experiment['hardware_classes'] = hardware_classes_data
exclude_hardware_classes_data = experiment_data.get(
'exclude_hardware_classes')
if exclude_hardware_classes_data:
experiment['exclude_hardware_classes'] = exclude_hardware_classes_data
params_data = experiment_data.get('params')
if (params_data):
experiment['params'] = [{'key': param, 'value': params_data[param]}
for param in sorted(params_data.keys())];
enable_features_data = experiment_data.get('enable_features')
if enable_features_data:
experiment['enable_features'] = enable_features_data
disable_features_data = experiment_data.get('disable_features')
if disable_features_data:
experiment['disable_features'] = disable_features_data
override_ui_strings = experiment_data.get('override_ui_strings')
if override_ui_strings:
experiment['override_ui_string'] = _ConvertOverrideUIStrings(
override_ui_strings)
return experiment
def _CreateTrial(study_name, experiment_configs, platforms):
"""Returns the applicable experiments for |study_name| and |platforms|.
This iterates through all of the experiment_configs for |study_name|
and picks out the applicable experiments based off of the valid platforms
and device type settings if specified.
"""
experiments = []
for config in experiment_configs:
platform_intersection = [p for p in platforms if p in config['platforms']]
if platform_intersection:
experiments += [
_CreateExperiment(e, platform_intersection,
config.get('form_factors', []),
config.get('is_low_end_device'))
for e in config['experiments']
]
return {
'name': study_name,
'experiments': experiments,
}
def _GenerateTrials(config, platforms):
for study_name in sorted(config.keys()):
study = _CreateTrial(study_name, config[study_name], platforms)
# To avoid converting studies with empty experiments (e.g. the study doesn't
# apply to the target platforms), this generator only yields studies that
# have non-empty experiments.
if study['experiments']:
yield study
def ConfigToStudies(config, platforms):
"""Returns the applicable studies from config for the platforms."""
return [study for study in _GenerateTrials(config, platforms)]
def _FieldTrialConfigToDescription(config, platforms):
return {
'elements': {
'kFieldTrialConfig': {
'studies': ConfigToStudies(config, platforms)
}
}
}
def main(arguments):
parser = optparse.OptionParser(
description='Generates a struct from a JSON description.',
usage='usage: %prog [option] -s schema -p platform description')
parser.add_option('-b', '--destbase',
help='base directory of generated files.')
parser.add_option('-d', '--destdir',
help='directory to output generated files, relative to destbase.')
parser.add_option('-n', '--namespace',
help='C++ namespace for generated files. e.g search_providers.')
parser.add_option('-p', '--platform', action='append', choices=_platforms,
help='target platform for the field trial, mandatory.')
parser.add_option('-s', '--schema', help='path to the schema file, '
'mandatory.')
parser.add_option('-o', '--output', help='output filename, '
'mandatory.')
parser.add_option('-j',
'--java',
action='store_true',
help='specify this flag to generate a java class.')
parser.add_option('-y', '--year',
help='year to put in the copy-right.')
(opts, args) = parser.parse_args(args=arguments)
if not opts.schema:
parser.error('You must specify a --schema.')
if not opts.platform:
parser.error('You must specify at least 1 --platform.')
description_filename = os.path.normpath(args[0])
shortroot = opts.output
if opts.destdir:
output_root = os.path.join(os.path.normpath(opts.destdir), shortroot)
else:
output_root = shortroot
if opts.destbase:
basepath = os.path.normpath(opts.destbase)
else:
basepath = ''
schema = _Load(opts.schema)
description = _LoadFieldTrialConfig(description_filename, opts.platform)
json_to_struct.GenerateStruct(
basepath, output_root, opts.namespace, schema, description,
os.path.split(description_filename)[1], os.path.split(opts.schema)[1],
opts.year)
# TODO(peilinwang) filter the schema by platform, form_factor, etc.
if opts.java:
json_to_struct.GenerateClass(basepath, output_root, opts.namespace, schema,
description,
os.path.split(description_filename)[1],
os.path.split(opts.schema)[1], opts.year)
if __name__ == '__main__':
main(sys.argv[1:])
|