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.h'), 19 'frontend_features.json': ('Frontend', 'FrontendFeatures.h'), 20 'gl_features.json': ('OpenGL', 'FeaturesGL.h'), 21 'mtl_features.json': ('Metal', 'FeaturesMtl.h'), 22 'vk_features.json': ('Vulkan', 'FeaturesVk.h'), 23} 24feature_list_header_file = '../../util/angle_features_autogen.h' 25feature_list_source_file = '../../util/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_{NAME}_H_ 33#define ANGLE_PLATFORM_{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_{NAME}_H_ 54""" 55 56template_feature = u"""FeatureInfo {var_name} = {{ 57 "{display_name}", FeatureCategory::{category}, 58 {description}, 59 &members, {issue} 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_UTILS_ANGLE_FEATURES_AUTOGEN_H_ 73#define ANGLE_SRC_TESTS_TEST_UTILS_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_UTILS_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 return re.sub('_(.)', lambda m: m.group(1).upper(), json_name) 131 132 133def main(): 134 if len(sys.argv) == 2 and sys.argv[1] == 'inputs': 135 print(','.join(list(feature_files.keys()))) 136 return 137 if len(sys.argv) == 2 and sys.argv[1] == 'outputs': 138 print(','.join([header for (_, header) in feature_files.values()]) + ',' + 139 feature_list_header_file + ',' + feature_list_source_file) 140 return 141 142 name_map = {} 143 144 for src_file, (category_prefix, header_file) in feature_files.items(): 145 with open(src_file) as fin: 146 src = json.loads(fin.read()) 147 148 features_json = src['features'] 149 features = [] 150 151 # Go over the list of features and write the header file that declares the features struct 152 for feature_json in features_json: 153 json_name = feature_json['name'] 154 var_name = make_camel_case(json_name) 155 # Use the same (camelCase) name for display as well 156 display_name = var_name 157 issue = feature_json.get('issue', None) 158 feature = template_feature.format( 159 var_name=var_name, 160 display_name=display_name, 161 category=category_prefix + feature_json['category'], 162 description='\n'.join('"' + line + '"' for line in feature_json['description']), 163 issue='' if issue is None else '"' + issue + '"') 164 165 features.append(feature) 166 167 # Keep track of the feature names. Sometimes the same feature name is present in 168 # multiple backends. That's ok for the purposes of feature overriding. 169 name_map[var_name] = display_name 170 171 description = '\n'.join(['// ' + line for line in src['description']]) 172 name = header_file[:-2] 173 174 header = template_header.format( 175 script_name=os.path.basename(__file__), 176 input_file_name=src_file, 177 description=description.replace(src_file, header_file), 178 name=name, 179 NAME=name.upper(), 180 features='\n'.join(features)) 181 182 with open(header_file, 'w') as fout: 183 fout.write(header) 184 fout.close() 185 186 # Generate helpers for use by tests to override a feature or not. 187 feature_enums = [] 188 feature_strings = [] 189 for var_name, display_name in sorted(name_map.items(), key=lambda item: item[0].lower()): 190 VarName = var_name[0].upper() + var_name[1:] 191 192 feature_enums.append(template_feature_enum.format(VarName=VarName)) 193 194 feature_strings.append( 195 template_feature_string.format(VarName=VarName, display_name=display_name)) 196 197 with open(feature_list_header_file, 'w') as fout: 198 fout.write( 199 template_feature_list_header.format( 200 script_name=os.path.basename(__file__), 201 input_file_name='*_features.json', 202 features='\n'.join(feature_enums))) 203 fout.close() 204 205 with open(feature_list_source_file, 'w') as fout: 206 fout.write( 207 template_feature_list_source.format( 208 script_name=os.path.basename(__file__), 209 input_file_name='*_features.json', 210 features='\n'.join(feature_strings))) 211 fout.close() 212 213 214if __name__ == '__main__': 215 sys.exit(main()) 216