forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			212 lines
		
	
	
	
		
			7.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			212 lines
		
	
	
	
		
			7.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # 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[]{{{}}};"
 | |
| )
 | |
| 
 | |
| # Do not add new feature IDs to this list! isEarlyStartup is being deprecated.
 | |
| # See https://bugzilla.mozilla.org/show_bug.cgi?id=1875331 for details.
 | |
| ALLOWED_ISEARLYSTARTUP_FEATURE_IDS = {
 | |
|     "abouthomecache",
 | |
|     "aboutwelcome",
 | |
|     "backgroundThreads",
 | |
|     "backgroundUpdate",
 | |
|     "bookmarks",
 | |
|     "dapTelemetry",
 | |
|     "deviceMigration",
 | |
|     "frecency",
 | |
|     "fullPageTranslation",
 | |
|     "fullPageTranslationAutomaticPopup",
 | |
|     "fxaButtonVisibility",
 | |
|     "gcParallelMarking",
 | |
|     "gleanInternalSdk",
 | |
|     "jitHintsCache",
 | |
|     "jitThresholds",
 | |
|     "jsParallelParsing",
 | |
|     "majorRelease2022",
 | |
|     "migrationWizard",
 | |
|     "newtab",
 | |
|     "nimbus-qa-2",
 | |
|     "opaqueResponseBlocking",
 | |
|     "phc",
 | |
|     "pocketNewtab",
 | |
|     "powerSaver",
 | |
|     "reportBrokenSite",
 | |
|     "saveToPocket",
 | |
|     "searchConfiguration",
 | |
|     "shellService",
 | |
|     "testFeature",
 | |
|     "updatePrompt",
 | |
|     "upgradeDialog",
 | |
|     "windowsJumpList",
 | |
| }
 | |
| 
 | |
| 
 | |
| def write_fm_headers(fd):
 | |
|     fd.write(HEADER_LINE)
 | |
| 
 | |
| 
 | |
| 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_id, feature in manifest.items():
 | |
|         try:
 | |
|             jsonschema.validate(feature, schema)
 | |
| 
 | |
|             is_early_startup = feature.get("isEarlyStartup", False)
 | |
|             allowed_is_early_startup = feature_id in ALLOWED_ISEARLYSTARTUP_FEATURE_IDS
 | |
|             if is_early_startup != allowed_is_early_startup:
 | |
|                 if is_early_startup:
 | |
|                     print(f"Feature {feature_id} is marked isEarlyStartup: true")
 | |
|                     print(
 | |
|                         "isEarlyStartup is deprecated and no new isEarlyStartup features can be added"
 | |
|                     )
 | |
|                     print(
 | |
|                         "See https://bugzilla.mozilla.org/show_bug.cgi?id=1875331 for details"
 | |
|                     )
 | |
|                     raise Exception("isEarlyStartup is deprecated")
 | |
|                 else:
 | |
|                     print(
 | |
|                         f"Feature {feature_id} is not early startup but is in the allow list."
 | |
|                     )
 | |
|                     print("Please remove it from generate_feature_manifest.py")
 | |
|                 raise Exception("isEarlyStatup is deprecated")
 | |
| 
 | |
|             for variable, variable_def in feature.get("variables", {}).items():
 | |
|                 set_pref = variable_def.get("setPref")
 | |
| 
 | |
|                 if isinstance(set_pref, dict):
 | |
|                     set_pref = set_pref.get("pref")
 | |
| 
 | |
|                 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_id} 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_id, variable)
 | |
| 
 | |
|                 fallback_pref = variable_def.get("fallbackPref")
 | |
|                 if fallback_pref is not None:
 | |
|                     fallback_prefs[fallback_pref] = (feature_id, 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_id} 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_id}")
 | |
|             print(f"Input file: {manifest_path}")
 | |
|             raise e
 | |
| 
 | |
| 
 | |
| def generate_feature_manifest(fd, input_file):
 | |
|     write_fm_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"export 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_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)
 | 
