Something went wrong. Try again.
The open source OpenXR runtime
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388#!/usr/bin/env python3# Copyright 2025-2026, NVIDIA CORPORATION.# SPDX-License-Identifier: BSL-1.0"""Script to parse header files and generate X_macro lists for all enum valuesfound.
This script is used to generate the X_macro lists for the enums found in theheader files specified by the --inputs argument.
The script will generate the X_macro lists for the enums found in the headerfiles specified by the --inputs argument, and write them to the header filespecified by the --output argument.
The script will also verify that the found enums match the expected enumsspecified by the --enums argument.
If the --ignore-other-enums argument is provided, the script will ignore enumsfound in the input files that are not in the --enums list.
The script will also print verbose informational output if the --verboseargument is provided."""
import sysimport reimport argparsefrom pathlib import Path
# Global flag for info printing_print_info_enabled = False
def info(message): """ Print informational messages if info printing is enabled.
Args: message: The message to print """ if _print_info_enabled: print(message)
def error(message): """ Print an error message and exit with code 1.
Args: message: The error message to print """ print(f"Error: {message}", file=sys.stderr) sys.exit(1)
def verify_enum_line(line): """ Verify that the enum line is valid, this function errors on the side of rejecting unfamiliar lines rather then being more flexible.
Args: line: The line to verify
Returns: True if the line is valid, False otherwise """
# Empty line if (line == '\n'): return True # C++ style comment if (line.startswith('\t//')): return True # C style comment opener. if (line == '\t/*\n'): return True # C style doc comment opener. if (line == '\t/*!\n'): return True # C empty comment line. if (line == '\t *\n'): return True # C comment line. if (line.startswith('\t * ')): return True # C comment closer. if (line == '\t */\n'): return True # Sure we could use a regex to verify enum decls, # this is simpler for now. if (line.startswith('\tXRT_')): return True return False
def parse_enum_body(lines, start_index, enum_name, is_typedef, source_file, should_verify): """ Parse the body of an enum declaration.
Args: lines: List of all lines in the file start_index: Line index where enum body starts enum_name: Name of the enum is_typedef: Whether this is a typedef enum source_file: Path to source file (for error messages) should_verify: Whether to verify enum lines
Returns: Tuple of (enum_values, new_index) where enum_values is a list of enum value names and new_index is the line index after the enum body """ # Pattern for matching enum value names (ignore everything after the name) enum_pattern = r'^\s*([A-Z_][A-Z0-9_]*)'
enum_values = [] found_opening_brace = False i = start_index
# Error if we don't find the opening brace if not lines[i] == '{\n': error(f"Expected opening brace for enum {enum_name} in " f"{source_file} at line {i}, got '{lines[i].strip()}'") else: i += 1
# We have verified the opening brace, so we can start parsing the enum # body. We loop through the lines until we find the end of the enum. while i < len(lines): line = lines[i] i += 1
# Check for end of enum (typedef or regular) if is_typedef: # For typedef enum, look for } <name>_t; end_pattern = (r'^\s*\}\s*' + re.escape(enum_name) + r'_t\s*;') if re.match(end_pattern, line): break else: # For regular enum, look for }; if re.match(r'^\s*\}\s*;', line): break
# Verify the line (only if we should verify this enum) if should_verify and not verify_enum_line(line): error(f"Line {i} in {source_file} failed verification: " f"'{line.strip()}'")
# Extract enum value names # Look for pattern: ENUM_VALUE = <number>, or just ENUM_VALUE, match = re.match(enum_pattern, line) if match: enum_value = match.group(1) enum_values.append(enum_value)
return enum_values, i
def find_all_enums(source_file, expected_enums=None, ignore_other=False): """ Find all enum declarations in the source file.
Args: source_file: Path to the source header file expected_enums: List of expected enum names (optional) ignore_other: If True, don't verify lines for enums not in expected_enums
Returns: List of tuples: (enum_name, list of enum values, is_typedef) """ with open(source_file, 'r') as f: lines = f.readlines()
enums = [] i = 0
# Look for pattern: typedef enum <ident> typedef_decl_pattern = r'^\s*typedef\s+enum\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*$'
# Look for pattern: enum <ident> enum_decl_pattern = r'^\s*enum\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*$'
while i < len(lines): line = lines[i]
# Check for typedef enum declarations typedef_match = re.match(typedef_decl_pattern, line) # Check for regular enum declarations enum_match = re.match(enum_decl_pattern, line)
if typedef_match: enum_name = typedef_match.group(1) is_typedef = True i += 1 elif enum_match: enum_name = enum_match.group(1) is_typedef = False i += 1 else: i += 1 continue
# Determine if we should verify lines for this enum should_verify = True if ignore_other and expected_enums is not None: should_verify = enum_name in expected_enums
# Call the helper function to parse the enum body, # also advances the line index to the end of the enum. enum_values, i = parse_enum_body( lines, i, enum_name, is_typedef, source_file, should_verify )
# Collect what we have found so far. if enum_values: enums.append((enum_name, enum_values, is_typedef)) info(f"Found enum {enum_name} with {len(enum_values)} " f"values in {source_file}")
return enums
def verify_enums(found_enums, expected_enums, ignore_other): """ Verify that the found enums match the expected enums.
Args: found_enums: List of tuples (enum_name, enum_values, is_typedef) expected_enums: List of expected enum names ignore_other: If True, ignore unexpected enums instead of erroring """ if expected_enums is None: # No expectations defined, so any enums are acceptable return
found_names = set(enum_name for enum_name, _, _ in found_enums) expected_names = set(expected_enums)
missing_enums = expected_names - found_names unexpected_enums = found_names - expected_names
# Always error on missing enums if missing_enums: msg = "Enum list mismatch!\n" msg += f" Expected enums: {sorted(expected_names)}\n" msg += f" Found enums: {sorted(found_names)}\n" msg += f" Missing enums: {sorted(missing_enums)}" error(msg)
# Error on unexpected enums only if not ignoring them if unexpected_enums and not ignore_other: msg = "Enum list mismatch!\n" msg += f" Expected enums: {sorted(expected_names)}\n" msg += f" Found enums: {sorted(found_names)}\n" msg += f" Unexpected enums: {sorted(unexpected_enums)}" error(msg)
def generate_xmacro_header(all_enums, output_file): """ Generate the X_macro include file (without headers, just the macro definitions).
Args: all_enums: List of tuples (enum_name, enum_values, is_typedef) output_file: Path to the output include file """ # Create the output directory if it doesn't exist output_path = Path(output_file) output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_file, 'w') as f: f.write("// DO NOT EDIT - Auto-generated by " "generate_macro_lists.py\n") f.write("\n")
for enum_name, enum_values, is_typedef in all_enums: # Convert enum name to uppercase for the macro name macro_name = enum_name.upper() + "_LIST"
f.write(f"#define {macro_name}(_) \\\n")
for i, value in enumerate(enum_values): if i < len(enum_values) - 1: f.write(f"\t_({value}) \\\n") else: # Last entry doesn't need a backslash f.write(f"\t_({value})\n")
# Add a blank line between enum macros (except for the # last one) if (enum_name, enum_values, is_typedef) != all_enums[-1]: f.write("\n")
def main(): global _print_info_enabled
parser = argparse.ArgumentParser( description='Generate X_macro lists for all enums found in ' 'header files.' ) parser.add_argument( '--inputs', nargs='+', required=True, metavar='FILE', help='Input header file(s) to scan for enum declarations' ) parser.add_argument( '--enums', nargs='+', required=True, metavar='NAME', help='List of expected enum names' ) parser.add_argument( '-o', '--output', required=True, metavar='FILE', help='Output file for generated X_macro lists' ) parser.add_argument( '--verbose', '-v', action='store_true', help='Enable verbose informational output' ) parser.add_argument( '--ignore-other-enums', action='store_true', help='Ignore enums found in input files that are not in ' 'the --enums list' )
args = parser.parse_args()
# Set global info printing flag _print_info_enabled = args.verbose
try: all_enums = []
# Scan all input files for enums for source_file in args.inputs: if not Path(source_file).exists(): error(f"File not found: {source_file}")
enums = find_all_enums(source_file, args.enums, args.ignore_other_enums) all_enums.extend(enums)
if not all_enums: error("No enums found in the provided header files")
# Verify that found enums match expected enums verify_enums(all_enums, args.enums, args.ignore_other_enums)
# Filter to only include expected enums if ignore flag is set if args.ignore_other_enums: expected_set = set(args.enums) filtered_enums = [ enum_tuple for enum_tuple in all_enums if enum_tuple[0] in expected_set ] info(f"Found {len(all_enums)} enum(s) total, " f"using {len(filtered_enums)}") all_enums = filtered_enums else: info(f"Found {len(all_enums)} enum(s) total")
generate_xmacro_header(all_enums, args.output) info(f"Generated {args.output}")
except Exception as e: print(f"Error: {e}", file=sys.stderr) import traceback traceback.print_exc() sys.exit(1)
if __name__ == '__main__': main()