1# Copyright (c) Meta Platforms, Inc. and affiliates. 2# All rights reserved. 3# 4# This source code is licensed under the BSD-style license found in the 5# LICENSE file in the root directory of this source tree. 6 7import os 8import sys 9from typing import Any, List 10 11import pkg_resources 12import yaml 13 14 15def _to_c_bool(s: Any): 16 if s in [True, False]: 17 return str(s).lower() 18 return s 19 20 21def generate_header(d: dict): 22 """Generates a supported features header file""" 23 ini_path = os.path.join( 24 os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))), 25 "supported_features_header.ini", 26 ) 27 if os.path.isfile(ini_path): 28 header_file = open(ini_path, encoding="utf-8").read() 29 else: 30 header_file = pkg_resources.resource_string( 31 __package__, "supported_features_header.ini" 32 ).decode("utf-8") 33 34 return header_file.replace("$header_entries", "".join(generate_header_entry(d))) 35 36 37def generate_header_entry(d: dict): 38 for entry in d: 39 namespace = entry["namespace"] 40 for feature, properties in entry.items(): 41 if feature == "namespace": 42 # we handled namespace previously 43 continue 44 yield generate_header_entry_text(namespace, feature, properties) 45 46 47def generate_header_entry_text(namespace: str, feature: str, properties: dict): 48 if namespace == "global": 49 full_name = feature 50 else: 51 full_name = "_".join([namespace, feature]) 52 if "default" in properties: 53 default = _to_c_bool(properties["default"]) 54 default = f" = {default}" 55 else: 56 default = "" 57 if "docstring" in properties: 58 docstring = properties["docstring"] 59 else: 60 docstring = "TODO: add docstring for this entry" 61 t = properties["type"] 62 entry = f"{t} {full_name}{default};\n" 63 return f""" 64 // {docstring} 65 {entry} 66""" 67 68 69def generate_definition(d: dict): 70 ini_path = os.path.join( 71 os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))), 72 "supported_features_definition.ini", 73 ) 74 if os.path.isfile(ini_path): 75 definition_file = open(ini_path, encoding="utf-8").read() 76 else: 77 definition_file = pkg_resources.resource_string( 78 __package__, "supported_features_definition.ini" 79 ).decode("utf-8") 80 81 return definition_file.replace( 82 "$definition_entries", "".join(generate_definition_entry(d)) 83 ) 84 85 86def generate_definition_entry(d: dict): 87 if not d: 88 return [] # noqa: B901 89 for entry in d: 90 namespace = entry["namespace"] 91 for feature, value in entry.items(): 92 if feature == "namespace": 93 # we handled namespace previously 94 continue 95 yield generate_definition_entry_text(namespace, feature, value) 96 97 98def generate_definition_entry_text(namespace: str, feature: str, value: Any): 99 if namespace == "global": 100 full_name = feature 101 else: 102 full_name = "_".join([namespace, feature]) 103 value = _to_c_bool(value) 104 return f""" 105 .{full_name} = {value}, 106""" 107 108 109def main(args: List[Any]) -> None: 110 """ 111 This binary generates the supported_features.h from supported_features.yaml 112 from this (//executorch/kernels/test) directory. Then for a specific kernel, 113 we need to supply the overridden supported_features_def.yaml for the kernel. 114 """ 115 with open(args[0]) as f: 116 y = yaml.full_load(f) 117 if "supported_features_def" in args[0]: 118 print(generate_definition(y)) 119 else: 120 print(generate_header(y)) 121 122 123def invoke_main() -> None: 124 if len(sys.argv) != 2: 125 print( 126 "Usage: gen_supported_features.py <path-to>/supported_features{_def}.yaml" 127 ) 128 exit(1) 129 main(sys.argv[1:]) 130 131 132if __name__ == "__main__": 133 invoke_main() # pragma: no cover 134