1#! /usr/bin/python3 2 3# Copyright 2022 The ANGLE Project Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6# 7# gen_features.py: 8# Code generation for ANGLE features. 9# NOTE: don't run this script directly. Run scripts/run_code_generation.py. 10 11from collections import namedtuple 12import json 13import os 14import re 15import sys 16 17feature_files = { 18 'd3d_features.json': ('D3D', 'FeaturesD3D'), 19 'frontend_features.json': ('Frontend', 'FrontendFeatures'), 20 'gl_features.json': ('OpenGL', 'FeaturesGL'), 21 'mtl_features.json': ('Metal', 'FeaturesMtl'), 22 'vk_features.json': ('Vulkan', 'FeaturesVk'), 23} 24feature_list_header_file = '../../util/autogen/angle_features_autogen.h' 25feature_list_source_file = '../../util/autogen/angle_features_autogen.cpp' 26 27template_header = u"""// GENERATED FILE - DO NOT EDIT. 28// Generated by {script_name} using data from {input_file_name}. 29// 30{description} 31 32#ifndef ANGLE_PLATFORM_AUTOGEN_{NAME}_H_ 33#define ANGLE_PLATFORM_AUTOGEN_{NAME}_H_ 34 35#include "platform/Feature.h" 36 37namespace angle 38{{ 39 40struct {name} : FeatureSetBase 41{{ 42 {name}(); 43 ~{name}(); 44 45{features} 46}}; 47 48inline {name}::{name}() = default; 49inline {name}::~{name}() = default; 50 51}} // namespace angle 52 53#endif // ANGLE_PLATFORM_AUTOGEN_{NAME}_H_ 54""" 55 56template_feature = u""" FeatureInfo {var_name} = {{ 57 "{display_name}", 58 FeatureCategory::{category}, 59 &members, 60 }}; 61""" 62 63template_feature_list_header = u"""// GENERATED FILE - DO NOT EDIT. 64// Generated by {script_name} using data from {input_file_name}. 65// 66// Copyright 2022 The ANGLE Project Authors. All rights reserved. 67// Use of this source code is governed by a BSD-style license that can be 68// found in the LICENSE file. 69// 70// angle_features_autogen.h: List of ANGLE features to help enable/disable them in tests. 71 72#ifndef ANGLE_SRC_TESTS_TEST_UTIL_AUTOGEN_ANGLE_FEATURES_AUTOGEN_H_ 73#define ANGLE_SRC_TESTS_TEST_UTIL_AUTOGEN_ANGLE_FEATURES_AUTOGEN_H_ 74 75#include "../util_export.h" 76 77namespace angle 78{{ 79enum class Feature 80{{ 81{features} 82 83 InvalidEnum, 84 EnumCount = InvalidEnum, 85}}; 86 87ANGLE_UTIL_EXPORT extern const char *GetFeatureName(Feature feature); 88 89}} // namespace angle 90 91#endif // ANGLE_SRC_TESTS_TEST_UTIL_AUTOGEN_ANGLE_FEATURES_AUTOGEN_H_ 92""" 93 94template_feature_enum = u"""{VarName},""" 95 96template_feature_list_source = u"""// GENERATED FILE - DO NOT EDIT. 97// Generated by {script_name} using data from {input_file_name}. 98// 99// Copyright 2022 The ANGLE Project Authors. All rights reserved. 100// Use of this source code is governed by a BSD-style license that can be 101// found in the LICENSE file. 102// 103// angle_features_autogen.cpp: List of ANGLE features to help enable/disable them in tests. 104 105#include "angle_features_autogen.h" 106 107#include "common/PackedEnums.h" 108 109namespace angle 110{{ 111namespace 112{{ 113constexpr PackedEnumMap<Feature, const char *> kFeatureNames = {{{{ 114{features} 115}}}}; 116}} // anonymous namespace 117 118const char *GetFeatureName(Feature feature) 119{{ 120 return kFeatureNames[feature]; 121}} 122 123}} // namespace angle 124""" 125 126template_feature_string = u""" {{Feature::{VarName}, "{display_name}"}},""" 127 128 129def make_camel_case(json_name): 130 assert '_' in json_name, 'feature names in the json file are expected to be in snake_case' 131 return re.sub('_(.)', lambda m: m.group(1).upper(), json_name) 132 133 134def make_header_name(class_name): 135 return class_name + '_autogen.h' 136 137 138def header_path(class_name): 139 return 'autogen/' + make_header_name(class_name) 140 141 142def write_or_verify_file(filename, content, verify_only): 143 if verify_only: 144 try: 145 with open(filename) as f: 146 # Note: .gitattributes "* text=auto" handles LF <-> CRLF on Windows 147 return f.read() == content 148 except FileNotFoundError: 149 return False 150 else: 151 with open(filename, 'w') as fout: 152 fout.write(content) 153 return True 154 155 156def main(): 157 if len(sys.argv) == 2 and sys.argv[1] == 'inputs': 158 print(','.join(list(feature_files.keys()))) 159 return 160 if len(sys.argv) == 2 and sys.argv[1] == 'outputs': 161 print(','.join([header_path(class_name) for (_, class_name) in feature_files.values()]) + 162 ',' + feature_list_header_file + ',' + feature_list_source_file) 163 return 164 165 # --verify-only enables dirty checks without relying on checked in hashes. 166 # Compares the content of the existing file with the generated content. 167 verify_only = '--verify-only' in sys.argv 168 169 name_map = {} 170 171 for src_file, (category_prefix, class_name) in feature_files.items(): 172 with open(src_file) as fin: 173 src = json.loads(fin.read()) 174 175 features_json = src['features'] 176 features = [] 177 178 # Go over the list of features and write the header file that declares the features struct 179 for feature_json in features_json: 180 json_name = feature_json['name'] 181 var_name = make_camel_case(json_name) 182 # Use the same (camelCase) name for display as well 183 display_name = var_name 184 feature = template_feature.format( 185 var_name=var_name, 186 display_name=display_name, 187 category=category_prefix + feature_json['category']) 188 189 features.append(feature) 190 191 # Keep track of the feature names. Sometimes the same feature name is present in 192 # multiple backends. That's ok for the purposes of feature overriding. 193 name_map[var_name] = display_name 194 195 description = '\n'.join( 196 ['//' + (' ' + line if line else '') for line in src['description']]) 197 header_file = make_header_name(class_name) 198 199 header = template_header.format( 200 script_name=os.path.basename(__file__), 201 input_file_name=src_file, 202 description=description.replace(src_file, header_file), 203 name=class_name, 204 NAME=class_name.upper(), 205 features='\n'.join(features)) 206 207 if not write_or_verify_file(header_path(class_name), header, verify_only): 208 return 1 209 210 # Generate helpers for use by tests to override a feature or not. 211 feature_enums = [] 212 feature_strings = [] 213 for var_name, display_name in sorted(name_map.items(), key=lambda item: item[0].lower()): 214 VarName = var_name[0].upper() + var_name[1:] 215 216 feature_enums.append(template_feature_enum.format(VarName=VarName)) 217 218 feature_strings.append( 219 template_feature_string.format(VarName=VarName, display_name=display_name)) 220 221 if not write_or_verify_file( 222 feature_list_header_file, 223 template_feature_list_header.format( 224 script_name=os.path.basename(__file__), 225 input_file_name='*_features.json', 226 features='\n'.join(' ' + v for v in feature_enums)), verify_only): 227 return 1 228 229 if not write_or_verify_file( 230 feature_list_source_file, 231 template_feature_list_source.format( 232 script_name=os.path.basename(__file__), 233 input_file_name='*_features.json', 234 features='\n'.join(feature_strings)), verify_only): 235 return 1 236 237 238if __name__ == '__main__': 239 sys.exit(main()) 240