1bf215546Sopenharmony_ci#!/usr/bin/env python3 2bf215546Sopenharmony_ci""" 3bf215546Sopenharmony_ciCheck for and replace aliases with their new names from vk.xml 4bf215546Sopenharmony_ci""" 5bf215546Sopenharmony_ci 6bf215546Sopenharmony_ciimport argparse 7bf215546Sopenharmony_ciimport pathlib 8bf215546Sopenharmony_ciimport subprocess 9bf215546Sopenharmony_ciimport sys 10bf215546Sopenharmony_ciimport xml.etree.ElementTree as et 11bf215546Sopenharmony_ci 12bf215546Sopenharmony_ciTHIS_FILE = pathlib.Path(__file__) 13bf215546Sopenharmony_ciCWD = pathlib.Path.cwd() 14bf215546Sopenharmony_ci 15bf215546Sopenharmony_ciVK_XML = THIS_FILE.parent / 'vk.xml' 16bf215546Sopenharmony_ciEXCLUDE_PATHS = [ 17bf215546Sopenharmony_ci VK_XML.relative_to(CWD).as_posix(), 18bf215546Sopenharmony_ci 19bf215546Sopenharmony_ci # These files come from other repos, there's no point checking and 20bf215546Sopenharmony_ci # fixing them here as that would be overwritten in the next sync. 21bf215546Sopenharmony_ci 'src/amd/vulkan/radix_sort/', 22bf215546Sopenharmony_ci 'src/virtio/venus-protocol/', 23bf215546Sopenharmony_ci] 24bf215546Sopenharmony_ci 25bf215546Sopenharmony_ci 26bf215546Sopenharmony_cidef get_aliases(xml_file: pathlib.Path): 27bf215546Sopenharmony_ci """ 28bf215546Sopenharmony_ci Get all the aliases defined in vk.xml 29bf215546Sopenharmony_ci """ 30bf215546Sopenharmony_ci xml = et.parse(xml_file) 31bf215546Sopenharmony_ci 32bf215546Sopenharmony_ci for node in ([] 33bf215546Sopenharmony_ci + xml.findall('.//enum[@alias]') 34bf215546Sopenharmony_ci + xml.findall('.//type[@alias]') 35bf215546Sopenharmony_ci + xml.findall('.//command[@alias]') 36bf215546Sopenharmony_ci ): 37bf215546Sopenharmony_ci yield node.attrib['name'], node.attrib['alias'] 38bf215546Sopenharmony_ci 39bf215546Sopenharmony_ci 40bf215546Sopenharmony_cidef remove_prefix(string: str, prefix: str): 41bf215546Sopenharmony_ci """ 42bf215546Sopenharmony_ci Remove prefix if string starts with it, and return the full string 43bf215546Sopenharmony_ci otherwise. 44bf215546Sopenharmony_ci """ 45bf215546Sopenharmony_ci if not string.startswith(prefix): 46bf215546Sopenharmony_ci return string 47bf215546Sopenharmony_ci return string[len(prefix):] 48bf215546Sopenharmony_ci 49bf215546Sopenharmony_ci 50bf215546Sopenharmony_ci# Function from https://stackoverflow.com/a/312464 51bf215546Sopenharmony_cidef chunks(lst: list, n: int): 52bf215546Sopenharmony_ci """ 53bf215546Sopenharmony_ci Yield successive n-sized chunks from lst. 54bf215546Sopenharmony_ci """ 55bf215546Sopenharmony_ci for i in range(0, len(lst), n): 56bf215546Sopenharmony_ci yield lst[i:i + n] 57bf215546Sopenharmony_ci 58bf215546Sopenharmony_ci 59bf215546Sopenharmony_cidef main(check_only: bool): 60bf215546Sopenharmony_ci """ 61bf215546Sopenharmony_ci Entrypoint; perform the search for all the aliases, and if `check_only` 62bf215546Sopenharmony_ci is not True, replace them. 63bf215546Sopenharmony_ci """ 64bf215546Sopenharmony_ci def prepare_identifier(identifier: str) -> str: 65bf215546Sopenharmony_ci # vk_find_struct() prepends `VK_STRUCTURE_TYPE_`, so that prefix 66bf215546Sopenharmony_ci # might not appear in the code 67bf215546Sopenharmony_ci identifier = remove_prefix(identifier, 'VK_STRUCTURE_TYPE_') 68bf215546Sopenharmony_ci return identifier 69bf215546Sopenharmony_ci 70bf215546Sopenharmony_ci aliases = {} 71bf215546Sopenharmony_ci for old_name, alias_for in get_aliases(VK_XML): 72bf215546Sopenharmony_ci old_name = prepare_identifier(old_name) 73bf215546Sopenharmony_ci alias_for = prepare_identifier(alias_for) 74bf215546Sopenharmony_ci aliases[old_name] = alias_for 75bf215546Sopenharmony_ci 76bf215546Sopenharmony_ci print(f'Found {len(aliases)} aliases in {VK_XML.name}') 77bf215546Sopenharmony_ci 78bf215546Sopenharmony_ci # Some aliases have aliases 79bf215546Sopenharmony_ci recursion_needs_checking = True 80bf215546Sopenharmony_ci while recursion_needs_checking: 81bf215546Sopenharmony_ci recursion_needs_checking = False 82bf215546Sopenharmony_ci for old, new in aliases.items(): 83bf215546Sopenharmony_ci if new in aliases: 84bf215546Sopenharmony_ci aliases[old] = aliases[new] 85bf215546Sopenharmony_ci recursion_needs_checking = True 86bf215546Sopenharmony_ci 87bf215546Sopenharmony_ci # Doing the whole search in a single command breaks grep, so only 88bf215546Sopenharmony_ci # look for 500 aliases at a time. Searching them one at a time would 89bf215546Sopenharmony_ci # be extremely slow. 90bf215546Sopenharmony_ci files_with_aliases = set() 91bf215546Sopenharmony_ci for aliases_chunk in chunks([*aliases], 500): 92bf215546Sopenharmony_ci search_output = subprocess.check_output([ 93bf215546Sopenharmony_ci 'git', 94bf215546Sopenharmony_ci 'grep', 95bf215546Sopenharmony_ci '-rlP', 96bf215546Sopenharmony_ci '|'.join(aliases_chunk), 97bf215546Sopenharmony_ci 'src/' 98bf215546Sopenharmony_ci ], stderr=subprocess.DEVNULL).decode() 99bf215546Sopenharmony_ci files_with_aliases.update(search_output.splitlines()) 100bf215546Sopenharmony_ci 101bf215546Sopenharmony_ci def file_matches_path(file: str, path: str) -> bool: 102bf215546Sopenharmony_ci # if path is a folder; match any file within 103bf215546Sopenharmony_ci if path.endswith('/') and file.startswith(path): 104bf215546Sopenharmony_ci return True 105bf215546Sopenharmony_ci return file == path 106bf215546Sopenharmony_ci 107bf215546Sopenharmony_ci for excluded_path in EXCLUDE_PATHS: 108bf215546Sopenharmony_ci files_with_aliases = { 109bf215546Sopenharmony_ci file for file in files_with_aliases 110bf215546Sopenharmony_ci if not file_matches_path(file, excluded_path) 111bf215546Sopenharmony_ci } 112bf215546Sopenharmony_ci 113bf215546Sopenharmony_ci if not files_with_aliases: 114bf215546Sopenharmony_ci print('No alias found in any file.') 115bf215546Sopenharmony_ci sys.exit(0) 116bf215546Sopenharmony_ci 117bf215546Sopenharmony_ci print(f'{len(files_with_aliases)} files contain aliases:') 118bf215546Sopenharmony_ci print('\n'.join(f'- {file}' for file in files_with_aliases)) 119bf215546Sopenharmony_ci 120bf215546Sopenharmony_ci if check_only: 121bf215546Sopenharmony_ci print('You can automatically fix this by running ' 122bf215546Sopenharmony_ci f'`{THIS_FILE.relative_to(CWD)}`.') 123bf215546Sopenharmony_ci sys.exit(1) 124bf215546Sopenharmony_ci 125bf215546Sopenharmony_ci command = [ 126bf215546Sopenharmony_ci 'sed', 127bf215546Sopenharmony_ci '-i', 128bf215546Sopenharmony_ci ";".join([f's/{old}/{new}/g' for old, new in aliases.items()]), 129bf215546Sopenharmony_ci ] 130bf215546Sopenharmony_ci command += files_with_aliases 131bf215546Sopenharmony_ci subprocess.check_call(command, stderr=subprocess.DEVNULL) 132bf215546Sopenharmony_ci print('All aliases have been replaced') 133bf215546Sopenharmony_ci 134bf215546Sopenharmony_ci 135bf215546Sopenharmony_ciif __name__ == '__main__': 136bf215546Sopenharmony_ci parser = argparse.ArgumentParser() 137bf215546Sopenharmony_ci parser.add_argument('--check-only', 138bf215546Sopenharmony_ci action='store_true', 139bf215546Sopenharmony_ci help='Replace aliases found') 140bf215546Sopenharmony_ci args = parser.parse_args() 141bf215546Sopenharmony_ci main(**vars(args)) 142