xref: /aosp_15_r20/external/angle/include/platform/gen_features.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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