diff options
Diffstat (limited to 'toolkit/components/nimbus/generate/generate_feature_manifest.py')
-rw-r--r-- | toolkit/components/nimbus/generate/generate_feature_manifest.py | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/toolkit/components/nimbus/generate/generate_feature_manifest.py b/toolkit/components/nimbus/generate/generate_feature_manifest.py new file mode 100644 index 0000000000..d4a3b71434 --- /dev/null +++ b/toolkit/components/nimbus/generate/generate_feature_manifest.py @@ -0,0 +1,158 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +import json +import sys +from pathlib import Path + +import jsonschema +import yaml + +HEADER_LINE = ( + "// This file was generated by generate_feature_manifest.py from FeatureManifest.yaml." + " DO NOT EDIT.\n" +) + +FEATURE_MANIFEST_SCHEMA = Path("schemas", "ExperimentFeatureManifest.schema.json") + +NIMBUS_FALLBACK_PREFS = ( + "constexpr std::pair<nsLiteralCString, nsLiteralCString>" + "NIMBUS_FALLBACK_PREFS[]{{{}}};" +) + +EXPORTED_SYMBOLS = 'const EXPORTED_SYMBOLS = ["{}"];' + + +def write_fm_cpp_headers(fd): + fd.write(HEADER_LINE) + + +def write_fm_js_headers(fd): + fd.write("\n".join([HEADER_LINE, EXPORTED_SYMBOLS.format("FeatureManifest")])) + + +def validate_feature_manifest(schema_path, manifest_path, manifest): + with open(schema_path, "r") as f: + schema = json.load(f) + + set_prefs = {} + fallback_prefs = {} + + for feature, feature_def in manifest.items(): + try: + jsonschema.validate(feature_def, schema) + + for variable, variable_def in feature_def.get("variables", {}).items(): + set_pref = variable_def.get("setPref") + if set_pref is not None: + if set_pref in set_prefs: + other_feature = set_prefs[set_pref][0] + other_variable = set_prefs[set_pref][1] + print("Multiple variables cannot declare the same setPref") + print( + f"{feature} variable {variable} wants to set pref {set_pref}" + ) + print( + f"{other_feature} variable {other_variable} wants to set pref " + f"{set_pref}" + ) + raise Exception("Set prefs are exclusive") + + set_prefs[set_pref] = (feature, variable) + + fallback_pref = variable_def.get("fallbackPref") + if fallback_pref is not None: + fallback_prefs[fallback_pref] = (feature, variable) + + conflicts = [ + ( + "setPref", + fallback_pref, + "fallbackPref", + set_prefs.get(fallback_pref), + ), + ("fallbackPref", set_pref, "setPref", fallback_prefs.get(set_pref)), + ] + + for kind, pref, other_kind, conflict in conflicts: + if conflict is not None: + print( + "The same pref cannot be specified in setPref and fallbackPref" + ) + print( + f"{feature} variable {variable} has specified {kind} {pref}" + ) + print( + f"{conflict[0]} variable {conflict[1]} has specified {other_kind} " + f"{pref}" + ) + raise Exception("Set prefs and fallback prefs cannot overlap") + + except Exception as e: + print("Error while validating FeatureManifest.yaml") + print(f"On key: {feature}") + print(f"Input file: {manifest_path}") + raise e + + +def generate_feature_manifest(fd, input_file): + write_fm_js_headers(fd) + + try: + with open(input_file, "r", encoding="utf-8") as f: + manifest = yaml.safe_load(f) + + validate_feature_manifest( + Path(input_file).parent / FEATURE_MANIFEST_SCHEMA, input_file, manifest + ) + + fd.write(f"const FeatureManifest = {json.dumps(manifest)};") + except (IOError) as e: + print(f"{input_file}: error:\n {e}\n") + sys.exit(1) + + +def platform_feature_manifest_array(features): + entries = [] + for (feature, featureData) in features.items(): + # Features have to be tagged isEarlyStartup to be accessible + # to Nimbus platform API + if not featureData.get("isEarlyStartup", False): + continue + entries.extend( + '{{ "{}_{}"_ns, "{}"_ns }}'.format( + feature, variable, variableData["fallbackPref"] + ) + for (variable, variableData) in featureData.get("variables", {}).items() + if variableData.get("fallbackPref", False) + ) + return NIMBUS_FALLBACK_PREFS.format(", ".join(entries)) + + +def generate_platform_feature_manifest(fd, input_file): + write_fm_cpp_headers(fd) + + def file_structure(data): + return "\n".join( + [ + "#ifndef mozilla_NimbusFeaturesManifest_h", + "#define mozilla_NimbusFeaturesManifest_h", + "#include <utility>", + '#include "mozilla/Maybe.h"', + '#include "nsStringFwd.h"', + "namespace mozilla {", + platform_feature_manifest_array(data), + '#include "./lib/NimbusFeatureManifest.inc.h"', + "} // namespace mozilla", + "#endif // mozilla_NimbusFeaturesManifest_h", + ] + ) + + try: + with open(input_file, "r", encoding="utf-8") as yaml_input: + data = yaml.safe_load(yaml_input) + fd.write(file_structure(data)) + except (IOError) as e: + print("{}: error:\n {}\n".format(input_file, e)) + sys.exit(1) |