1e5c31af7Sopenharmony_ci#!/usr/bin/python3 2e5c31af7Sopenharmony_ci# 3e5c31af7Sopenharmony_ci# Copyright (c) 2019 Collabora, Ltd. 4e5c31af7Sopenharmony_ci# 5e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0 6e5c31af7Sopenharmony_ci# 7e5c31af7Sopenharmony_ci# Author(s): Ryan Pavlik <ryan.pavlik@collabora.com> 8e5c31af7Sopenharmony_ci# 9e5c31af7Sopenharmony_ci# Purpose: This script checks some "business logic" in the XML registry. 10e5c31af7Sopenharmony_ci 11e5c31af7Sopenharmony_ciimport argparse 12e5c31af7Sopenharmony_ciimport re 13e5c31af7Sopenharmony_ciimport sys 14e5c31af7Sopenharmony_cifrom pathlib import Path 15e5c31af7Sopenharmony_cifrom collections import defaultdict, deque, namedtuple 16e5c31af7Sopenharmony_ci 17e5c31af7Sopenharmony_cifrom check_spec_links import VulkanEntityDatabase as OrigEntityDatabase 18e5c31af7Sopenharmony_cifrom reg import Registry 19e5c31af7Sopenharmony_cifrom spec_tools.consistency_tools import XMLChecker 20e5c31af7Sopenharmony_cifrom spec_tools.util import findNamedElem, getElemName, getElemType 21e5c31af7Sopenharmony_cifrom apiconventions import APIConventions 22e5c31af7Sopenharmony_cifrom parse_dependency import dependencyNames 23e5c31af7Sopenharmony_ci 24e5c31af7Sopenharmony_ci# These are extensions which do not follow the usual naming conventions, 25e5c31af7Sopenharmony_ci# specifying the alternate convention they follow 26e5c31af7Sopenharmony_ciEXTENSION_ENUM_NAME_SPELLING_CHANGE = { 27e5c31af7Sopenharmony_ci 'VK_EXT_swapchain_colorspace': 'VK_EXT_SWAPCHAIN_COLOR_SPACE', 28e5c31af7Sopenharmony_ci} 29e5c31af7Sopenharmony_ci 30e5c31af7Sopenharmony_ci# These are extensions whose names *look* like they end in version numbers, 31e5c31af7Sopenharmony_ci# but do not 32e5c31af7Sopenharmony_ciEXTENSION_NAME_VERSION_EXCEPTIONS = ( 33e5c31af7Sopenharmony_ci 'VK_AMD_gpu_shader_int16', 34e5c31af7Sopenharmony_ci 'VK_EXT_index_type_uint8', 35e5c31af7Sopenharmony_ci 'VK_EXT_shader_image_atomic_int64', 36e5c31af7Sopenharmony_ci 'VK_KHR_video_decode_h264', 37e5c31af7Sopenharmony_ci 'VK_KHR_video_decode_h265', 38e5c31af7Sopenharmony_ci 'VK_KHR_video_encode_h264', 39e5c31af7Sopenharmony_ci 'VK_KHR_video_encode_h265', 40e5c31af7Sopenharmony_ci 'VK_KHR_external_fence_win32', 41e5c31af7Sopenharmony_ci 'VK_KHR_external_memory_win32', 42e5c31af7Sopenharmony_ci 'VK_KHR_external_semaphore_win32', 43e5c31af7Sopenharmony_ci 'VK_KHR_shader_atomic_int64', 44e5c31af7Sopenharmony_ci 'VK_KHR_shader_float16_int8', 45e5c31af7Sopenharmony_ci 'VK_KHR_spirv_1_4', 46e5c31af7Sopenharmony_ci 'VK_NV_external_memory_win32', 47e5c31af7Sopenharmony_ci 'VK_RESERVED_do_not_use_146', 48e5c31af7Sopenharmony_ci 'VK_RESERVED_do_not_use_94', 49e5c31af7Sopenharmony_ci) 50e5c31af7Sopenharmony_ci 51e5c31af7Sopenharmony_ci# These are APIs which can be required by an extension despite not having 52e5c31af7Sopenharmony_ci# suffixes matching the vendor ID of that extension. 53e5c31af7Sopenharmony_ci# Most are external types. 54e5c31af7Sopenharmony_ci# We could make this an (extension name, api name) set to be more specific. 55e5c31af7Sopenharmony_ciEXTENSION_API_NAME_EXCEPTIONS = { 56e5c31af7Sopenharmony_ci 'AHardwareBuffer', 57e5c31af7Sopenharmony_ci 'ANativeWindow', 58e5c31af7Sopenharmony_ci 'CAMetalLayer', 59e5c31af7Sopenharmony_ci 'IOSurfaceRef', 60e5c31af7Sopenharmony_ci 'MTLBuffer_id', 61e5c31af7Sopenharmony_ci 'MTLCommandQueue_id', 62e5c31af7Sopenharmony_ci 'MTLDevice_id', 63e5c31af7Sopenharmony_ci 'MTLSharedEvent_id', 64e5c31af7Sopenharmony_ci 'MTLTexture_id', 65e5c31af7Sopenharmony_ci 'VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE', 66e5c31af7Sopenharmony_ci 'VkFlags64', 67e5c31af7Sopenharmony_ci 'VkPipelineCacheCreateFlagBits', 68e5c31af7Sopenharmony_ci 'VkPipelineColorBlendStateCreateFlagBits', 69e5c31af7Sopenharmony_ci 'VkPipelineDepthStencilStateCreateFlagBits', 70e5c31af7Sopenharmony_ci 'VkPipelineLayoutCreateFlagBits', 71e5c31af7Sopenharmony_ci} 72e5c31af7Sopenharmony_ci 73e5c31af7Sopenharmony_ci# These are APIs which contain _RESERVED_ intentionally 74e5c31af7Sopenharmony_ciEXTENSION_NAME_RESERVED_EXCEPTIONS = { 75e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_PRIVATE_VENDOR_INFO_RESERVED_OFFSET_0_NV' 76e5c31af7Sopenharmony_ci} 77e5c31af7Sopenharmony_ci 78e5c31af7Sopenharmony_ci# Exceptions to pointer parameter naming rules 79e5c31af7Sopenharmony_ci# Keyed by (entity name, type, name). 80e5c31af7Sopenharmony_ciCHECK_PARAM_POINTER_NAME_EXCEPTIONS = { 81e5c31af7Sopenharmony_ci ('vkGetDrmDisplayEXT', 'VkDisplayKHR', 'display') : None, 82e5c31af7Sopenharmony_ci} 83e5c31af7Sopenharmony_ci 84e5c31af7Sopenharmony_ci# Exceptions to pNext member requiring an optional attribute 85e5c31af7Sopenharmony_ciCHECK_MEMBER_PNEXT_OPTIONAL_EXCEPTIONS = ( 86e5c31af7Sopenharmony_ci 'VkVideoEncodeInfoKHR', 87e5c31af7Sopenharmony_ci 'VkVideoEncodeRateControlLayerInfoKHR', 88e5c31af7Sopenharmony_ci) 89e5c31af7Sopenharmony_ci 90e5c31af7Sopenharmony_ci# Exceptions to VK_INCOMPLETE being required for, and only applicable to, array 91e5c31af7Sopenharmony_ci# enumeration functions 92e5c31af7Sopenharmony_ciCHECK_ARRAY_ENUMERATION_RETURN_CODE_EXCEPTIONS = ( 93e5c31af7Sopenharmony_ci 'vkGetDeviceFaultInfoEXT', 94e5c31af7Sopenharmony_ci 'vkEnumerateDeviceLayerProperties', 95e5c31af7Sopenharmony_ci) 96e5c31af7Sopenharmony_ci 97e5c31af7Sopenharmony_ci# Exceptions to unknown structure type constants. 98e5c31af7Sopenharmony_ci# This is most likely an error in this script, not the XML. 99e5c31af7Sopenharmony_ci# It does not understand Vulkan SC (alternate 'api') types. 100e5c31af7Sopenharmony_ciCHECK_TYPE_STYPE_EXCEPTIONS = ( 101e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_PERFORMANCE_QUERY_RESERVATION_INFO_KHR', 102e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_PIPELINE_POOL_SIZE', 103e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_FAULT_DATA', 104e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_SC_1_0_FEATURES', 105e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_DEVICE_OBJECT_RESERVATION_CREATE_INFO', 106e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_PIPELINE_OFFLINE_CREATE_INFO', 107e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_FAULT_CALLBACK_INFO', 108e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_RESERVATION_CREATE_INFO', 109e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_DEVICE_SEMAPHORE_SCI_SYNC_POOL_RESERVATION_CREATE_INFO_NV', 110e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_CONSUMPTION', 111e5c31af7Sopenharmony_ci 'VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_SC_1_0_PROPERTIES', 112e5c31af7Sopenharmony_ci) 113e5c31af7Sopenharmony_ci 114e5c31af7Sopenharmony_cidef get_extension_commands(reg): 115e5c31af7Sopenharmony_ci extension_cmds = set() 116e5c31af7Sopenharmony_ci for ext in reg.extensions: 117e5c31af7Sopenharmony_ci for cmd in ext.findall('./require/command[@name]'): 118e5c31af7Sopenharmony_ci extension_cmds.add(cmd.get('name')) 119e5c31af7Sopenharmony_ci return extension_cmds 120e5c31af7Sopenharmony_ci 121e5c31af7Sopenharmony_ci 122e5c31af7Sopenharmony_cidef get_enum_value_names(reg, enum_type): 123e5c31af7Sopenharmony_ci names = set() 124e5c31af7Sopenharmony_ci result_elem = reg.groupdict[enum_type].elem 125e5c31af7Sopenharmony_ci for val in result_elem.findall('./enum[@name]'): 126e5c31af7Sopenharmony_ci names.add(val.get('name')) 127e5c31af7Sopenharmony_ci return names 128e5c31af7Sopenharmony_ci 129e5c31af7Sopenharmony_ci 130e5c31af7Sopenharmony_ci# Regular expression matching an extension name ending in a (possible) version number 131e5c31af7Sopenharmony_ciEXTNAME_RE = re.compile(r'(?P<base>(\w+[A-Za-z]))(?P<version>\d+)') 132e5c31af7Sopenharmony_ci 133e5c31af7Sopenharmony_ciDESTROY_PREFIX = 'vkDestroy' 134e5c31af7Sopenharmony_ciTYPEENUM = 'VkStructureType' 135e5c31af7Sopenharmony_ci 136e5c31af7Sopenharmony_ciSPECIFICATION_DIR = Path(__file__).parent.parent 137e5c31af7Sopenharmony_ciREVISION_RE = re.compile(r' *[*] Revision (?P<num>[1-9][0-9]*),.*') 138e5c31af7Sopenharmony_ci 139e5c31af7Sopenharmony_ci 140e5c31af7Sopenharmony_cidef get_extension_source(extname): 141e5c31af7Sopenharmony_ci fn = f'{extname}.adoc' 142e5c31af7Sopenharmony_ci return str(SPECIFICATION_DIR / 'appendices' / fn) 143e5c31af7Sopenharmony_ci 144e5c31af7Sopenharmony_ci 145e5c31af7Sopenharmony_ciclass EntityDatabase(OrigEntityDatabase): 146e5c31af7Sopenharmony_ci 147e5c31af7Sopenharmony_ci def __init__(self, args): 148e5c31af7Sopenharmony_ci """Retain command-line arguments for later use in makeRegistry""" 149e5c31af7Sopenharmony_ci self.args = args 150e5c31af7Sopenharmony_ci 151e5c31af7Sopenharmony_ci super().__init__() 152e5c31af7Sopenharmony_ci 153e5c31af7Sopenharmony_ci # Override base class method to not exclude 'disabled' extensions 154e5c31af7Sopenharmony_ci def getExclusionSet(self): 155e5c31af7Sopenharmony_ci """Return a set of "support=" attribute strings that should not be included in the database. 156e5c31af7Sopenharmony_ci 157e5c31af7Sopenharmony_ci Called only during construction.""" 158e5c31af7Sopenharmony_ci 159e5c31af7Sopenharmony_ci return set(()) 160e5c31af7Sopenharmony_ci 161e5c31af7Sopenharmony_ci def makeRegistry(self): 162e5c31af7Sopenharmony_ci try: 163e5c31af7Sopenharmony_ci import lxml.etree as etree 164e5c31af7Sopenharmony_ci HAS_LXML = True 165e5c31af7Sopenharmony_ci except ImportError: 166e5c31af7Sopenharmony_ci HAS_LXML = False 167e5c31af7Sopenharmony_ci if not HAS_LXML: 168e5c31af7Sopenharmony_ci return super().makeRegistry() 169e5c31af7Sopenharmony_ci 170e5c31af7Sopenharmony_ci if len(self.args.files) > 0: 171e5c31af7Sopenharmony_ci registryFile = self.args.files[0] 172e5c31af7Sopenharmony_ci else: 173e5c31af7Sopenharmony_ci registryFile = str(SPECIFICATION_DIR / 'xml/vk.xml') 174e5c31af7Sopenharmony_ci 175e5c31af7Sopenharmony_ci registry = Registry() 176e5c31af7Sopenharmony_ci registry.filename = registryFile 177e5c31af7Sopenharmony_ci registry.loadElementTree(etree.parse(registryFile)) 178e5c31af7Sopenharmony_ci return registry 179e5c31af7Sopenharmony_ci 180e5c31af7Sopenharmony_ci 181e5c31af7Sopenharmony_ciclass Checker(XMLChecker): 182e5c31af7Sopenharmony_ci def __init__(self, args): 183e5c31af7Sopenharmony_ci manual_types_to_codes = { 184e5c31af7Sopenharmony_ci # These are hard-coded "manual" return codes: 185e5c31af7Sopenharmony_ci # the codes of the value (string, list, or tuple) 186e5c31af7Sopenharmony_ci # are available for a command if-and-only-if 187e5c31af7Sopenharmony_ci # the key type is passed as an input. 188e5c31af7Sopenharmony_ci 'VkFormat': 'VK_ERROR_FORMAT_NOT_SUPPORTED' 189e5c31af7Sopenharmony_ci } 190e5c31af7Sopenharmony_ci forward_only = { 191e5c31af7Sopenharmony_ci # Like the above, but these are only valid in the 192e5c31af7Sopenharmony_ci # "type implies return code" direction 193e5c31af7Sopenharmony_ci } 194e5c31af7Sopenharmony_ci reverse_only = { 195e5c31af7Sopenharmony_ci # like the above, but these are only valid in the 196e5c31af7Sopenharmony_ci # "return code implies type or its descendant" direction 197e5c31af7Sopenharmony_ci # "XrDuration": "XR_TIMEOUT_EXPIRED" 198e5c31af7Sopenharmony_ci } 199e5c31af7Sopenharmony_ci # Some return codes are related in that only one of a set 200e5c31af7Sopenharmony_ci # may be returned by a command 201e5c31af7Sopenharmony_ci # (eg. XR_ERROR_SESSION_RUNNING and XR_ERROR_SESSION_NOT_RUNNING) 202e5c31af7Sopenharmony_ci self.exclusive_return_code_sets = tuple( 203e5c31af7Sopenharmony_ci # set(("XR_ERROR_SESSION_NOT_RUNNING", "XR_ERROR_SESSION_RUNNING")), 204e5c31af7Sopenharmony_ci ) 205e5c31af7Sopenharmony_ci 206e5c31af7Sopenharmony_ci # This is used to report collisions. 207e5c31af7Sopenharmony_ci conventions = APIConventions() 208e5c31af7Sopenharmony_ci db = EntityDatabase(args) 209e5c31af7Sopenharmony_ci 210e5c31af7Sopenharmony_ci self.extension_cmds = get_extension_commands(db.registry) 211e5c31af7Sopenharmony_ci self.return_codes = get_enum_value_names(db.registry, 'VkResult') 212e5c31af7Sopenharmony_ci self.structure_types = get_enum_value_names(db.registry, TYPEENUM) 213e5c31af7Sopenharmony_ci 214e5c31af7Sopenharmony_ci # Dict of entity name to a list of messages to suppress. (Exclude any context data and "Warning:"/"Error:") 215e5c31af7Sopenharmony_ci # Keys are entity names, values are tuples or lists of message text to suppress. 216e5c31af7Sopenharmony_ci suppressions = {} 217e5c31af7Sopenharmony_ci 218e5c31af7Sopenharmony_ci # Structures explicitly allowed to have 'limittype' attributes 219e5c31af7Sopenharmony_ci self.allowedStructs = set(( 220e5c31af7Sopenharmony_ci 'VkFormatProperties', 221e5c31af7Sopenharmony_ci 'VkFormatProperties2', 222e5c31af7Sopenharmony_ci 'VkPhysicalDeviceProperties', 223e5c31af7Sopenharmony_ci 'VkPhysicalDeviceProperties2', 224e5c31af7Sopenharmony_ci 'VkPhysicalDeviceLimits', 225e5c31af7Sopenharmony_ci 'VkQueueFamilyProperties', 226e5c31af7Sopenharmony_ci 'VkQueueFamilyProperties2', 227e5c31af7Sopenharmony_ci 'VkSparseImageFormatProperties', 228e5c31af7Sopenharmony_ci 'VkSparseImageFormatProperties2', 229e5c31af7Sopenharmony_ci )) 230e5c31af7Sopenharmony_ci 231e5c31af7Sopenharmony_ci # Substructures of allowed structures. This can be found by looking 232e5c31af7Sopenharmony_ci # at tags, but there are so few cases that it is hardwired for now. 233e5c31af7Sopenharmony_ci self.nestedStructs = set(( 234e5c31af7Sopenharmony_ci 'VkPhysicalDeviceLimits', 235e5c31af7Sopenharmony_ci 'VkPhysicalDeviceSparseProperties', 236e5c31af7Sopenharmony_ci 'VkPhysicalDeviceProperties', 237e5c31af7Sopenharmony_ci 'VkQueueFamilyProperties', 238e5c31af7Sopenharmony_ci 'VkSparseImageFormatProperties', 239e5c31af7Sopenharmony_ci )) 240e5c31af7Sopenharmony_ci 241e5c31af7Sopenharmony_ci # Structures all of whose (non pNext/sType) members are required to 242e5c31af7Sopenharmony_ci # have 'limittype' attributes, as are their descendants 243e5c31af7Sopenharmony_ci self.requiredStructs = set(( 244e5c31af7Sopenharmony_ci 'VkPhysicalDeviceProperties', 245e5c31af7Sopenharmony_ci 'VkPhysicalDeviceProperties2', 246e5c31af7Sopenharmony_ci 'VkPhysicalDeviceLimits', 247e5c31af7Sopenharmony_ci 'VkSparseImageFormatProperties', 248e5c31af7Sopenharmony_ci 'VkSparseImageFormatProperties2', 249e5c31af7Sopenharmony_ci )) 250e5c31af7Sopenharmony_ci 251e5c31af7Sopenharmony_ci # Structures which have already have their limittype attributes validated 252e5c31af7Sopenharmony_ci self.validatedLimittype = set() 253e5c31af7Sopenharmony_ci 254e5c31af7Sopenharmony_ci # Initialize superclass 255e5c31af7Sopenharmony_ci super().__init__(entity_db=db, conventions=conventions, 256e5c31af7Sopenharmony_ci manual_types_to_codes=manual_types_to_codes, 257e5c31af7Sopenharmony_ci forward_only_types_to_codes=forward_only, 258e5c31af7Sopenharmony_ci reverse_only_types_to_codes=reverse_only, 259e5c31af7Sopenharmony_ci suppressions=suppressions, 260e5c31af7Sopenharmony_ci display_warnings=args.warn) 261e5c31af7Sopenharmony_ci 262e5c31af7Sopenharmony_ci def check(self): 263e5c31af7Sopenharmony_ci """Extends base class behavior with additional checks""" 264e5c31af7Sopenharmony_ci 265e5c31af7Sopenharmony_ci # This test is not run on a per-structure basis, but loops over 266e5c31af7Sopenharmony_ci # specific structures 267e5c31af7Sopenharmony_ci self.check_type_required_limittype() 268e5c31af7Sopenharmony_ci 269e5c31af7Sopenharmony_ci super().check() 270e5c31af7Sopenharmony_ci 271e5c31af7Sopenharmony_ci def check_command(self, name, info): 272e5c31af7Sopenharmony_ci """Extends base class behavior with additional checks""" 273e5c31af7Sopenharmony_ci 274e5c31af7Sopenharmony_ci if name[0:5] == 'vkCmd': 275e5c31af7Sopenharmony_ci if info.elem.get('tasks') is None: 276e5c31af7Sopenharmony_ci self.record_error(f'{name} is a vkCmd* command, but is missing a "tasks" attribute') 277e5c31af7Sopenharmony_ci 278e5c31af7Sopenharmony_ci super().check_command(name, info) 279e5c31af7Sopenharmony_ci 280e5c31af7Sopenharmony_ci def check_command_return_codes_basic(self, name, info, 281e5c31af7Sopenharmony_ci successcodes, errorcodes): 282e5c31af7Sopenharmony_ci """Check a command's return codes for consistency. 283e5c31af7Sopenharmony_ci 284e5c31af7Sopenharmony_ci Called on every command.""" 285e5c31af7Sopenharmony_ci # Check that all extension commands can return the code associated 286e5c31af7Sopenharmony_ci # with trying to use an extension that was not enabled. 287e5c31af7Sopenharmony_ci # if name in self.extension_cmds and UNSUPPORTED not in errorcodes: 288e5c31af7Sopenharmony_ci # self.record_error('Missing expected return code', 289e5c31af7Sopenharmony_ci # UNSUPPORTED, 290e5c31af7Sopenharmony_ci # 'implied due to being an extension command') 291e5c31af7Sopenharmony_ci 292e5c31af7Sopenharmony_ci codes = successcodes.union(errorcodes) 293e5c31af7Sopenharmony_ci 294e5c31af7Sopenharmony_ci # Check that all return codes are recognized. 295e5c31af7Sopenharmony_ci unrecognized = codes - self.return_codes 296e5c31af7Sopenharmony_ci if unrecognized: 297e5c31af7Sopenharmony_ci self.record_error('Unrecognized return code(s):', 298e5c31af7Sopenharmony_ci unrecognized) 299e5c31af7Sopenharmony_ci 300e5c31af7Sopenharmony_ci elem = info.elem 301e5c31af7Sopenharmony_ci params = [(getElemName(elt), elt) for elt in elem.findall('param')] 302e5c31af7Sopenharmony_ci 303e5c31af7Sopenharmony_ci def is_count_output(name, elt): 304e5c31af7Sopenharmony_ci # Must end with Count or Size, 305e5c31af7Sopenharmony_ci # not be const, 306e5c31af7Sopenharmony_ci # and be a pointer (detected by naming convention) 307e5c31af7Sopenharmony_ci return (name.endswith('Count') or name.endswith('Size')) \ 308e5c31af7Sopenharmony_ci and (elt.tail is None or 'const' not in elt.tail) \ 309e5c31af7Sopenharmony_ci and (name.startswith('p')) 310e5c31af7Sopenharmony_ci 311e5c31af7Sopenharmony_ci countParams = [elt 312e5c31af7Sopenharmony_ci for name, elt in params 313e5c31af7Sopenharmony_ci if is_count_output(name, elt)] 314e5c31af7Sopenharmony_ci if countParams: 315e5c31af7Sopenharmony_ci assert(len(countParams) == 1) 316e5c31af7Sopenharmony_ci if 'VK_INCOMPLETE' not in successcodes: 317e5c31af7Sopenharmony_ci message = "Apparent enumeration of an array without VK_INCOMPLETE in successcodes for command {}.".format(name) 318e5c31af7Sopenharmony_ci if name in CHECK_ARRAY_ENUMERATION_RETURN_CODE_EXCEPTIONS: 319e5c31af7Sopenharmony_ci self.record_warning('(Allowed exception)', message) 320e5c31af7Sopenharmony_ci else: 321e5c31af7Sopenharmony_ci self.record_error(message) 322e5c31af7Sopenharmony_ci 323e5c31af7Sopenharmony_ci elif 'VK_INCOMPLETE' in successcodes: 324e5c31af7Sopenharmony_ci message = "VK_INCOMPLETE in successcodes of command {} that is apparently not an array enumeration.".format(name) 325e5c31af7Sopenharmony_ci if name in CHECK_ARRAY_ENUMERATION_RETURN_CODE_EXCEPTIONS: 326e5c31af7Sopenharmony_ci self.record_warning('(Allowed exception)', message) 327e5c31af7Sopenharmony_ci else: 328e5c31af7Sopenharmony_ci self.record_error(message) 329e5c31af7Sopenharmony_ci 330e5c31af7Sopenharmony_ci def check_param(self, param): 331e5c31af7Sopenharmony_ci """Check a member of a struct or a param of a function. 332e5c31af7Sopenharmony_ci 333e5c31af7Sopenharmony_ci Called from check_params.""" 334e5c31af7Sopenharmony_ci super().check_param(param) 335e5c31af7Sopenharmony_ci 336e5c31af7Sopenharmony_ci if not self.is_api_type(param): 337e5c31af7Sopenharmony_ci return 338e5c31af7Sopenharmony_ci 339e5c31af7Sopenharmony_ci param_text = ''.join(param.itertext()) 340e5c31af7Sopenharmony_ci param_name = getElemName(param) 341e5c31af7Sopenharmony_ci 342e5c31af7Sopenharmony_ci # Make sure the number of leading 'p' matches the pointer count. 343e5c31af7Sopenharmony_ci pointercount = param.find('type').tail 344e5c31af7Sopenharmony_ci if pointercount: 345e5c31af7Sopenharmony_ci pointercount = pointercount.count('*') 346e5c31af7Sopenharmony_ci if pointercount: 347e5c31af7Sopenharmony_ci prefix = 'p' * pointercount 348e5c31af7Sopenharmony_ci if not param_name.startswith(prefix): 349e5c31af7Sopenharmony_ci param_type = param.find('type').text 350e5c31af7Sopenharmony_ci message = "Apparently incorrect pointer-related name prefix for {} - expected it to start with '{}'".format( 351e5c31af7Sopenharmony_ci param_text, prefix) 352e5c31af7Sopenharmony_ci if (self.entity, param_type, param_name) in CHECK_PARAM_POINTER_NAME_EXCEPTIONS: 353e5c31af7Sopenharmony_ci self.record_warning('(Allowed exception)', message, elem=param) 354e5c31af7Sopenharmony_ci else: 355e5c31af7Sopenharmony_ci self.record_error(message, elem=param) 356e5c31af7Sopenharmony_ci 357e5c31af7Sopenharmony_ci # Make sure no members have optional="false" attributes 358e5c31af7Sopenharmony_ci optional = param.get('optional') 359e5c31af7Sopenharmony_ci if optional == 'false': 360e5c31af7Sopenharmony_ci message = f'{self.entity}.{param_name} member has disallowed \'optional="false"\' attribute (remove this attribute)' 361e5c31af7Sopenharmony_ci self.record_error(message, elem=param) 362e5c31af7Sopenharmony_ci 363e5c31af7Sopenharmony_ci # Make sure pNext members have optional="true" attributes 364e5c31af7Sopenharmony_ci if param_name == self.conventions.nextpointer_member_name: 365e5c31af7Sopenharmony_ci optional = param.get('optional') 366e5c31af7Sopenharmony_ci if optional is None or optional != 'true': 367e5c31af7Sopenharmony_ci message = f'{self.entity}.pNext member is missing \'optional="true"\' attribute' 368e5c31af7Sopenharmony_ci if self.entity in CHECK_MEMBER_PNEXT_OPTIONAL_EXCEPTIONS: 369e5c31af7Sopenharmony_ci self.record_warning('(Allowed exception)', message, elem=param) 370e5c31af7Sopenharmony_ci else: 371e5c31af7Sopenharmony_ci self.record_error(message, elem=param) 372e5c31af7Sopenharmony_ci 373e5c31af7Sopenharmony_ci def check_type_stype(self, name, info, type_elts): 374e5c31af7Sopenharmony_ci """Check a struct type's sType member""" 375e5c31af7Sopenharmony_ci if len(type_elts) > 1: 376e5c31af7Sopenharmony_ci self.record_error( 377e5c31af7Sopenharmony_ci 'Have more than one member of type', TYPEENUM) 378e5c31af7Sopenharmony_ci else: 379e5c31af7Sopenharmony_ci type_elt = type_elts[0] 380e5c31af7Sopenharmony_ci val = type_elt.get('values') 381e5c31af7Sopenharmony_ci if val and val not in self.structure_types: 382e5c31af7Sopenharmony_ci message = f'{self.entity} has unknown structure type constant {val}' 383e5c31af7Sopenharmony_ci if val in CHECK_TYPE_STYPE_EXCEPTIONS: 384e5c31af7Sopenharmony_ci self.record_warning('(Allowed exception)', message) 385e5c31af7Sopenharmony_ci else: 386e5c31af7Sopenharmony_ci self.record_error(message) 387e5c31af7Sopenharmony_ci 388e5c31af7Sopenharmony_ci def check_type_pnext(self, name, info): 389e5c31af7Sopenharmony_ci """Check a struct type's pNext member, if present""" 390e5c31af7Sopenharmony_ci 391e5c31af7Sopenharmony_ci next_name = self.conventions.nextpointer_member_name 392e5c31af7Sopenharmony_ci next_member = findNamedElem(info.elem.findall('member'), next_name) 393e5c31af7Sopenharmony_ci if next_member is not None: 394e5c31af7Sopenharmony_ci # Ensure that the 'optional' attribute is set to 'true' 395e5c31af7Sopenharmony_ci optional = next_member.get('optional') 396e5c31af7Sopenharmony_ci if optional is None or optional != 'true': 397e5c31af7Sopenharmony_ci message = f'{name}.{next_name} member is missing \'optional="true"\' attribute' 398e5c31af7Sopenharmony_ci if name in CHECK_MEMBER_PNEXT_OPTIONAL_EXCEPTIONS: 399e5c31af7Sopenharmony_ci self.record_warning('(Allowed exception)', message) 400e5c31af7Sopenharmony_ci else: 401e5c31af7Sopenharmony_ci self.record_error(message) 402e5c31af7Sopenharmony_ci 403e5c31af7Sopenharmony_ci def __isLimittypeStruct(self, name, info, allowedStructs): 404e5c31af7Sopenharmony_ci """Check if a type element is a structure allowed to have 'limittype' attributes 405e5c31af7Sopenharmony_ci name - name of a structure 406e5c31af7Sopenharmony_ci info - corresponding TypeInfo object 407e5c31af7Sopenharmony_ci allowedStructs - set of struct names explicitly allowed""" 408e5c31af7Sopenharmony_ci 409e5c31af7Sopenharmony_ci # Is this an explicitly allowed struct? 410e5c31af7Sopenharmony_ci if name in allowedStructs: 411e5c31af7Sopenharmony_ci return True 412e5c31af7Sopenharmony_ci 413e5c31af7Sopenharmony_ci # Is this a struct extending an explicitly allowed struct? 414e5c31af7Sopenharmony_ci extends = info.elem.get('structextends') 415e5c31af7Sopenharmony_ci if extends is not None: 416e5c31af7Sopenharmony_ci # See if any name in the structextends attribute is an allowed 417e5c31af7Sopenharmony_ci # struct 418e5c31af7Sopenharmony_ci if len(set(extends.split(',')) & allowedStructs) > 0: 419e5c31af7Sopenharmony_ci return True 420e5c31af7Sopenharmony_ci 421e5c31af7Sopenharmony_ci return False 422e5c31af7Sopenharmony_ci 423e5c31af7Sopenharmony_ci def __validateStructLimittypes(self, name, info, requiredLimittype): 424e5c31af7Sopenharmony_ci """Validate 'limittype' attributes for a single struct. 425e5c31af7Sopenharmony_ci info - TypeInfo for a struct <type> 426e5c31af7Sopenharmony_ci requiredLimittype - True if members *must* have a limittype""" 427e5c31af7Sopenharmony_ci 428e5c31af7Sopenharmony_ci # Do not re-check structures 429e5c31af7Sopenharmony_ci if name in self.validatedLimittype: 430e5c31af7Sopenharmony_ci return {} 431e5c31af7Sopenharmony_ci self.validatedLimittype.add(name) 432e5c31af7Sopenharmony_ci 433e5c31af7Sopenharmony_ci limittypeDiags = namedtuple('limittypeDiags', ['missing', 'invalid']) 434e5c31af7Sopenharmony_ci badFields = defaultdict(lambda : limittypeDiags(missing=[], invalid=[])) 435e5c31af7Sopenharmony_ci validLimittypes = { 'min', 'max', 'not', 'pot', 'mul', 'bits', 'bitmask', 'range', 'struct', 'exact', 'noauto' } 436e5c31af7Sopenharmony_ci for member in info.getMembers(): 437e5c31af7Sopenharmony_ci memberName = member.findtext('name') 438e5c31af7Sopenharmony_ci if memberName in ['sType', 'pNext']: 439e5c31af7Sopenharmony_ci continue 440e5c31af7Sopenharmony_ci limittype = member.get('limittype') 441e5c31af7Sopenharmony_ci if limittype is None: 442e5c31af7Sopenharmony_ci # Do not tag this as missing if it is not required 443e5c31af7Sopenharmony_ci if requiredLimittype: 444e5c31af7Sopenharmony_ci badFields[info.elem.get('name')].missing.append(memberName) 445e5c31af7Sopenharmony_ci elif limittype == 'struct': 446e5c31af7Sopenharmony_ci typeName = member.findtext('type') 447e5c31af7Sopenharmony_ci memberType = self.reg.typedict[typeName] 448e5c31af7Sopenharmony_ci badFields.update(self.__validateStructLimittypes(typeName, memberType, requiredLimittype)) 449e5c31af7Sopenharmony_ci else: 450e5c31af7Sopenharmony_ci for value in limittype.split(','): 451e5c31af7Sopenharmony_ci if value not in validLimittypes: 452e5c31af7Sopenharmony_ci badFields[info.elem.get('name')].invalid.append(memberName) 453e5c31af7Sopenharmony_ci 454e5c31af7Sopenharmony_ci return badFields 455e5c31af7Sopenharmony_ci 456e5c31af7Sopenharmony_ci def check_type_disallowed_limittype(self, name, info): 457e5c31af7Sopenharmony_ci """Check if a struct type's members cannot have the 'limittype' attribute""" 458e5c31af7Sopenharmony_ci 459e5c31af7Sopenharmony_ci # If not allowed to have limittypes, verify this for each member 460e5c31af7Sopenharmony_ci if not self.__isLimittypeStruct(name, info, self.allowedStructs.union(self.nestedStructs)): 461e5c31af7Sopenharmony_ci for member in info.getMembers(): 462e5c31af7Sopenharmony_ci if member.get('limittype') is not None: 463e5c31af7Sopenharmony_ci memname = member.findtext('name') 464e5c31af7Sopenharmony_ci self.record_error(f'{name} member {memname} has disallowed limittype attribute') 465e5c31af7Sopenharmony_ci 466e5c31af7Sopenharmony_ci def check_type_optional_value(self, name, info): 467e5c31af7Sopenharmony_ci """Check if a struct type's members have disallowed 'optional' attribute values""" 468e5c31af7Sopenharmony_ci 469e5c31af7Sopenharmony_ci for member in info.getMembers(): 470e5c31af7Sopenharmony_ci # Make sure no members have optional="false" attributes 471e5c31af7Sopenharmony_ci optional = member.get('optional') 472e5c31af7Sopenharmony_ci if optional == 'false': 473e5c31af7Sopenharmony_ci memname = member.findtext('name') 474e5c31af7Sopenharmony_ci message = f'{name} member {memname} has disallowed \'optional="false"\' attribute (remove this attribute)' 475e5c31af7Sopenharmony_ci self.record_error(message, elem=member) 476e5c31af7Sopenharmony_ci 477e5c31af7Sopenharmony_ci def check_type_required_limittype(self): 478e5c31af7Sopenharmony_ci """Check struct type members which must have the 'limittype' attribute 479e5c31af7Sopenharmony_ci 480e5c31af7Sopenharmony_ci Called from check.""" 481e5c31af7Sopenharmony_ci 482e5c31af7Sopenharmony_ci for name in self.allowedStructs: 483e5c31af7Sopenharmony_ci # Assume that only extending structs of structs explicitly 484e5c31af7Sopenharmony_ci # requiring limittypes also require them 485e5c31af7Sopenharmony_ci requiredLimittype = (name in self.requiredStructs) 486e5c31af7Sopenharmony_ci 487e5c31af7Sopenharmony_ci info = self.reg.typedict[name] 488e5c31af7Sopenharmony_ci 489e5c31af7Sopenharmony_ci self.set_error_context(entity=name, elem=info.elem) 490e5c31af7Sopenharmony_ci 491e5c31af7Sopenharmony_ci badFields = self.__validateStructLimittypes(name, info, requiredLimittype) 492e5c31af7Sopenharmony_ci for extendingStructName in self.reg.validextensionstructs[name]: 493e5c31af7Sopenharmony_ci extendingStruct = self.reg.typedict[extendingStructName] 494e5c31af7Sopenharmony_ci badFields.update(self.__validateStructLimittypes(extendingStructName, extendingStruct, requiredLimittype)) 495e5c31af7Sopenharmony_ci 496e5c31af7Sopenharmony_ci if badFields: 497e5c31af7Sopenharmony_ci for key in sorted(badFields.keys()): 498e5c31af7Sopenharmony_ci diags = badFields[key] 499e5c31af7Sopenharmony_ci if diags.missing: 500e5c31af7Sopenharmony_ci self.record_error(f'{name} missing limittype for members {", ".join(badFields[key].missing)}') 501e5c31af7Sopenharmony_ci if diags.invalid: 502e5c31af7Sopenharmony_ci self.record_error(f'{name} has invalid limittype for members {", ".join(badFields[key].invalid)}') 503e5c31af7Sopenharmony_ci 504e5c31af7Sopenharmony_ci def check_type(self, name, info, category): 505e5c31af7Sopenharmony_ci """Check a type's XML data for consistency. 506e5c31af7Sopenharmony_ci 507e5c31af7Sopenharmony_ci Called from check.""" 508e5c31af7Sopenharmony_ci 509e5c31af7Sopenharmony_ci if category == 'struct': 510e5c31af7Sopenharmony_ci type_elts = [elt 511e5c31af7Sopenharmony_ci for elt in info.elem.findall('member') 512e5c31af7Sopenharmony_ci if getElemType(elt) == TYPEENUM] 513e5c31af7Sopenharmony_ci 514e5c31af7Sopenharmony_ci if type_elts: 515e5c31af7Sopenharmony_ci self.check_type_stype(name, info, type_elts) 516e5c31af7Sopenharmony_ci self.check_type_pnext(name, info) 517e5c31af7Sopenharmony_ci 518e5c31af7Sopenharmony_ci # Check for disallowed limittypes on all structures 519e5c31af7Sopenharmony_ci self.check_type_disallowed_limittype(name, info) 520e5c31af7Sopenharmony_ci 521e5c31af7Sopenharmony_ci # Check for disallowed 'optional' values 522e5c31af7Sopenharmony_ci self.check_type_optional_value(name, info) 523e5c31af7Sopenharmony_ci elif category == 'bitmask': 524e5c31af7Sopenharmony_ci if 'Flags' in name: 525e5c31af7Sopenharmony_ci expected_require = name.replace('Flags', 'FlagBits') 526e5c31af7Sopenharmony_ci require = info.elem.get('require') 527e5c31af7Sopenharmony_ci if require is not None and expected_require != require: 528e5c31af7Sopenharmony_ci self.record_error('Unexpected require attribute value:', 529e5c31af7Sopenharmony_ci 'got', require, 530e5c31af7Sopenharmony_ci 'but expected', expected_require) 531e5c31af7Sopenharmony_ci super().check_type(name, info, category) 532e5c31af7Sopenharmony_ci 533e5c31af7Sopenharmony_ci def check_suffixes(self, name, info, supported, name_exceptions): 534e5c31af7Sopenharmony_ci """Check suffixes of new APIs required by an extension, which should 535e5c31af7Sopenharmony_ci match the author ID of the extension. 536e5c31af7Sopenharmony_ci 537e5c31af7Sopenharmony_ci Called from check_extension. 538e5c31af7Sopenharmony_ci 539e5c31af7Sopenharmony_ci name - extension name 540e5c31af7Sopenharmony_ci info - extdict entry for name 541e5c31af7Sopenharmony_ci supported - True if extension supported by API being checked 542e5c31af7Sopenharmony_ci name_exceptions - set of API names to not check, in addition to 543e5c31af7Sopenharmony_ci the global exception list.""" 544e5c31af7Sopenharmony_ci 545e5c31af7Sopenharmony_ci def has_suffix(apiname, author): 546e5c31af7Sopenharmony_ci return apiname[-len(author):] == author 547e5c31af7Sopenharmony_ci 548e5c31af7Sopenharmony_ci def has_any_suffixes(apiname, authors): 549e5c31af7Sopenharmony_ci for author in authors: 550e5c31af7Sopenharmony_ci if has_suffix(apiname, author): 551e5c31af7Sopenharmony_ci return True 552e5c31af7Sopenharmony_ci return False 553e5c31af7Sopenharmony_ci 554e5c31af7Sopenharmony_ci def check_names(elems, author, alt_authors, name_exceptions): 555e5c31af7Sopenharmony_ci """Check names in a list of elements for consistency 556e5c31af7Sopenharmony_ci 557e5c31af7Sopenharmony_ci elems - list of elements to check 558e5c31af7Sopenharmony_ci author - author ID of the <extension> tag 559e5c31af7Sopenharmony_ci alt_authors - set of other allowed author IDs 560e5c31af7Sopenharmony_ci name_exceptions - additional set of allowed exceptions""" 561e5c31af7Sopenharmony_ci 562e5c31af7Sopenharmony_ci for elem in elems: 563e5c31af7Sopenharmony_ci apiname = elem.get('name', 'NO NAME ATTRIBUTE') 564e5c31af7Sopenharmony_ci suffix = apiname[-len(author):] 565e5c31af7Sopenharmony_ci 566e5c31af7Sopenharmony_ci if (not has_suffix(apiname, author) and 567e5c31af7Sopenharmony_ci apiname not in EXTENSION_API_NAME_EXCEPTIONS and 568e5c31af7Sopenharmony_ci apiname not in name_exceptions): 569e5c31af7Sopenharmony_ci 570e5c31af7Sopenharmony_ci msg = f'Extension {name} <{elem.tag}> {apiname} does not have expected suffix {author}' 571e5c31af7Sopenharmony_ci 572e5c31af7Sopenharmony_ci # Explicit 'aliased' deprecations not matching the 573e5c31af7Sopenharmony_ci # naming rules are allowed, but warned. 574e5c31af7Sopenharmony_ci if has_any_suffixes(apiname, alt_authors): 575e5c31af7Sopenharmony_ci self.record_warning('Allowed alternate author ID:', msg) 576e5c31af7Sopenharmony_ci elif not supported: 577e5c31af7Sopenharmony_ci self.record_warning('Allowed inconsistency for disabled extension:', msg) 578e5c31af7Sopenharmony_ci elif elem.get('deprecated') == 'aliased': 579e5c31af7Sopenharmony_ci self.record_warning('Allowed aliasing deprecation:', msg) 580e5c31af7Sopenharmony_ci else: 581e5c31af7Sopenharmony_ci msg += '\n\ 582e5c31af7Sopenharmony_ciThis may be due to an extension interaction not having the correct <require depends="">\n\ 583e5c31af7Sopenharmony_ciOther exceptions can be added to xml_consistency.py:EXTENSION_API_NAME_EXCEPTIONS' 584e5c31af7Sopenharmony_ci self.record_error(msg) 585e5c31af7Sopenharmony_ci 586e5c31af7Sopenharmony_ci elem = info.elem 587e5c31af7Sopenharmony_ci 588e5c31af7Sopenharmony_ci self.set_error_context(entity=name, elem=elem) 589e5c31af7Sopenharmony_ci 590e5c31af7Sopenharmony_ci # Extract author ID from the extension name. 591e5c31af7Sopenharmony_ci author = name.split('_')[1] 592e5c31af7Sopenharmony_ci 593e5c31af7Sopenharmony_ci # Loop over each <require> tag checking the API name suffixes in 594e5c31af7Sopenharmony_ci # that tag for consistency. 595e5c31af7Sopenharmony_ci # Names in tags whose 'depends' attribute includes extensions with 596e5c31af7Sopenharmony_ci # different author IDs may be suffixed with those IDs. 597e5c31af7Sopenharmony_ci for req_elem in elem.findall('./require'): 598e5c31af7Sopenharmony_ci depends = req_elem.get('depends', '') 599e5c31af7Sopenharmony_ci alt_authors = set() 600e5c31af7Sopenharmony_ci if len(depends) > 0: 601e5c31af7Sopenharmony_ci for name in dependencyNames(depends): 602e5c31af7Sopenharmony_ci # Skip core versions 603e5c31af7Sopenharmony_ci if name[0:11] != 'VK_VERSION_': 604e5c31af7Sopenharmony_ci # Extract author ID from extension name 605e5c31af7Sopenharmony_ci id = name.split('_')[1] 606e5c31af7Sopenharmony_ci alt_authors.add(id) 607e5c31af7Sopenharmony_ci 608e5c31af7Sopenharmony_ci check_names(req_elem.findall('./command'), author, alt_authors, name_exceptions) 609e5c31af7Sopenharmony_ci check_names(req_elem.findall('./type'), author, alt_authors, name_exceptions) 610e5c31af7Sopenharmony_ci check_names(req_elem.findall('./enum'), author, alt_authors, name_exceptions) 611e5c31af7Sopenharmony_ci 612e5c31af7Sopenharmony_ci def check_extension(self, name, info, supported): 613e5c31af7Sopenharmony_ci """Check an extension's XML data for consistency. 614e5c31af7Sopenharmony_ci 615e5c31af7Sopenharmony_ci Called from check.""" 616e5c31af7Sopenharmony_ci 617e5c31af7Sopenharmony_ci elem = info.elem 618e5c31af7Sopenharmony_ci enums = elem.findall('./require/enum[@name]') 619e5c31af7Sopenharmony_ci 620e5c31af7Sopenharmony_ci # If extension name is not on the exception list and matches the 621e5c31af7Sopenharmony_ci # versioned-extension pattern, map the extension name to the version 622e5c31af7Sopenharmony_ci # name with the version as a separate word. Otherwise just map it to 623e5c31af7Sopenharmony_ci # the upper-case version of the extension name. 624e5c31af7Sopenharmony_ci 625e5c31af7Sopenharmony_ci matches = EXTNAME_RE.fullmatch(name) 626e5c31af7Sopenharmony_ci ext_versioned_name = False 627e5c31af7Sopenharmony_ci if name in EXTENSION_ENUM_NAME_SPELLING_CHANGE: 628e5c31af7Sopenharmony_ci ext_enum_name = EXTENSION_ENUM_NAME_SPELLING_CHANGE.get(name) 629e5c31af7Sopenharmony_ci elif matches is None or name in EXTENSION_NAME_VERSION_EXCEPTIONS: 630e5c31af7Sopenharmony_ci # This is the usual case, either a name that does not look 631e5c31af7Sopenharmony_ci # versioned, or one that does but is on the exception list. 632e5c31af7Sopenharmony_ci ext_enum_name = name.upper() 633e5c31af7Sopenharmony_ci else: 634e5c31af7Sopenharmony_ci # This is a versioned extension name. 635e5c31af7Sopenharmony_ci # Treat the version number as a separate word. 636e5c31af7Sopenharmony_ci base = matches.group('base') 637e5c31af7Sopenharmony_ci version = matches.group('version') 638e5c31af7Sopenharmony_ci ext_enum_name = base.upper() + '_' + version 639e5c31af7Sopenharmony_ci # Keep track of this case 640e5c31af7Sopenharmony_ci ext_versioned_name = True 641e5c31af7Sopenharmony_ci 642e5c31af7Sopenharmony_ci # Look for the expected SPEC_VERSION token name 643e5c31af7Sopenharmony_ci version_name = f'{ext_enum_name}_SPEC_VERSION' 644e5c31af7Sopenharmony_ci version_elem = findNamedElem(enums, version_name) 645e5c31af7Sopenharmony_ci 646e5c31af7Sopenharmony_ci if version_elem is None: 647e5c31af7Sopenharmony_ci # Did not find a SPEC_VERSION enum matching the extension name 648e5c31af7Sopenharmony_ci if ext_versioned_name: 649e5c31af7Sopenharmony_ci suffix = '\n\ 650e5c31af7Sopenharmony_ci Make sure that trailing version numbers in extension names are treated\n\ 651e5c31af7Sopenharmony_ci as separate words in extension enumerant names. If this is an extension\n\ 652e5c31af7Sopenharmony_ci whose name ends in a number which is not a version, such as "...h264"\n\ 653e5c31af7Sopenharmony_ci or "...int16", add it to EXTENSION_NAME_VERSION_EXCEPTIONS in\n\ 654e5c31af7Sopenharmony_ci scripts/xml_consistency.py.' 655e5c31af7Sopenharmony_ci else: 656e5c31af7Sopenharmony_ci suffix = '' 657e5c31af7Sopenharmony_ci self.record_error(f'Missing version enum {version_name}{suffix}') 658e5c31af7Sopenharmony_ci elif supported: 659e5c31af7Sopenharmony_ci # Skip unsupported / disabled extensions for these checks 660e5c31af7Sopenharmony_ci 661e5c31af7Sopenharmony_ci fn = get_extension_source(name) 662e5c31af7Sopenharmony_ci revisions = [] 663e5c31af7Sopenharmony_ci with open(fn, 'r', encoding='utf-8') as fp: 664e5c31af7Sopenharmony_ci for line in fp: 665e5c31af7Sopenharmony_ci line = line.rstrip() 666e5c31af7Sopenharmony_ci match = REVISION_RE.match(line) 667e5c31af7Sopenharmony_ci if match: 668e5c31af7Sopenharmony_ci revisions.append(int(match.group('num'))) 669e5c31af7Sopenharmony_ci ver_from_xml = version_elem.get('value') 670e5c31af7Sopenharmony_ci if revisions: 671e5c31af7Sopenharmony_ci ver_from_text = str(max(revisions)) 672e5c31af7Sopenharmony_ci if ver_from_xml != ver_from_text: 673e5c31af7Sopenharmony_ci self.record_error('Version enum mismatch: spec text indicates', ver_from_text, 674e5c31af7Sopenharmony_ci 'but XML says', ver_from_xml) 675e5c31af7Sopenharmony_ci else: 676e5c31af7Sopenharmony_ci if ver_from_xml == '1': 677e5c31af7Sopenharmony_ci self.record_warning( 678e5c31af7Sopenharmony_ci "Cannot find version history in spec text - make sure it has lines starting exactly like ' * Revision 1, ....'", 679e5c31af7Sopenharmony_ci filename=fn) 680e5c31af7Sopenharmony_ci else: 681e5c31af7Sopenharmony_ci self.record_warning("Cannot find version history in spec text, but XML reports a non-1 version number", ver_from_xml, 682e5c31af7Sopenharmony_ci " - make sure the spec text has lines starting exactly like ' * Revision 1, ....'", 683e5c31af7Sopenharmony_ci filename=fn) 684e5c31af7Sopenharmony_ci 685e5c31af7Sopenharmony_ci for enum in enums: 686e5c31af7Sopenharmony_ci enum_name = enum.get('name') 687e5c31af7Sopenharmony_ci if '_RESERVED_' in enum_name and enum_name not in EXTENSION_NAME_RESERVED_EXCEPTIONS: 688e5c31af7Sopenharmony_ci self.record_error(enum_name, 'should not contain _RESERVED_ for a supported extension.\n\ 689e5c31af7Sopenharmony_ciIf this is intentional, add it to EXTENSION_NAME_RESERVED_EXCEPTIONS in scripts/xml_consistency.py.') 690e5c31af7Sopenharmony_ci 691e5c31af7Sopenharmony_ci name_define = f'{ext_enum_name}_EXTENSION_NAME' 692e5c31af7Sopenharmony_ci name_elem = findNamedElem(enums, name_define) 693e5c31af7Sopenharmony_ci if name_elem is None: 694e5c31af7Sopenharmony_ci self.record_error('Missing name enum', name_define) 695e5c31af7Sopenharmony_ci else: 696e5c31af7Sopenharmony_ci # Note: etree handles the XML entities here and turns " back into " 697e5c31af7Sopenharmony_ci expected_name = f'"{name}"' 698e5c31af7Sopenharmony_ci name_val = name_elem.get('value') 699e5c31af7Sopenharmony_ci if name_val != expected_name: 700e5c31af7Sopenharmony_ci self.record_error('Incorrect name enum: expected', expected_name, 701e5c31af7Sopenharmony_ci 'got', name_val) 702e5c31af7Sopenharmony_ci 703e5c31af7Sopenharmony_ci self.check_suffixes(name, info, supported, { version_name, name_define }) 704e5c31af7Sopenharmony_ci 705e5c31af7Sopenharmony_ci # More general checks 706e5c31af7Sopenharmony_ci super().check_extension(name, info, supported) 707e5c31af7Sopenharmony_ci 708e5c31af7Sopenharmony_ci def check_format(self): 709e5c31af7Sopenharmony_ci """Check an extension's XML data for consistency. 710e5c31af7Sopenharmony_ci 711e5c31af7Sopenharmony_ci Called from check.""" 712e5c31af7Sopenharmony_ci 713e5c31af7Sopenharmony_ci astc3d_formats = [ 714e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT', 715e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT', 716e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT', 717e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT', 718e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT', 719e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT', 720e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT', 721e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT', 722e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT', 723e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT', 724e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT', 725e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT', 726e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT', 727e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT', 728e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT', 729e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT', 730e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT', 731e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT', 732e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT', 733e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT', 734e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT', 735e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT', 736e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT', 737e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT', 738e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT', 739e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT', 740e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT', 741e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT', 742e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT', 743e5c31af7Sopenharmony_ci 'VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT' 744e5c31af7Sopenharmony_ci ] 745e5c31af7Sopenharmony_ci 746e5c31af7Sopenharmony_ci # Need to build list of formats from rest of <enums> 747e5c31af7Sopenharmony_ci enum_formats = [] 748e5c31af7Sopenharmony_ci for enum in self.reg.groupdict['VkFormat'].elem: 749e5c31af7Sopenharmony_ci if enum.get('alias') is None and enum.get('name') != 'VK_FORMAT_UNDEFINED': 750e5c31af7Sopenharmony_ci enum_formats.append(enum.get('name')) 751e5c31af7Sopenharmony_ci 752e5c31af7Sopenharmony_ci found_formats = [] 753e5c31af7Sopenharmony_ci for name, info in self.reg.formatsdict.items(): 754e5c31af7Sopenharmony_ci found_formats.append(name) 755e5c31af7Sopenharmony_ci self.set_error_context(entity=name, elem=info.elem) 756e5c31af7Sopenharmony_ci 757e5c31af7Sopenharmony_ci if name not in enum_formats: 758e5c31af7Sopenharmony_ci self.record_error('The <format> has no matching <enum> for', name) 759e5c31af7Sopenharmony_ci 760e5c31af7Sopenharmony_ci # Check never just 1 plane 761e5c31af7Sopenharmony_ci plane_elems = info.elem.findall('plane') 762e5c31af7Sopenharmony_ci if len(plane_elems) == 1: 763e5c31af7Sopenharmony_ci self.record_error('The <format> has only 1 <plane> for', name) 764e5c31af7Sopenharmony_ci 765e5c31af7Sopenharmony_ci valid_chroma = ['420', '422', '444'] 766e5c31af7Sopenharmony_ci if info.elem.get('chroma') and info.elem.get('chroma') not in valid_chroma: 767e5c31af7Sopenharmony_ci self.record_error('The <format> has chroma is not a valid value for', name) 768e5c31af7Sopenharmony_ci 769e5c31af7Sopenharmony_ci # The formatsgenerator.py assumes only 1 <spirvimageformat> tag. 770e5c31af7Sopenharmony_ci # If this changes in the future, remove this warning and update generator script 771e5c31af7Sopenharmony_ci spirv_image_format = info.elem.findall('spirvimageformat') 772e5c31af7Sopenharmony_ci if len(spirv_image_format) > 1: 773e5c31af7Sopenharmony_ci self.record_error('More than 1 <spirvimageformat> but formatsgenerator.py is not updated, for format', name) 774e5c31af7Sopenharmony_ci 775e5c31af7Sopenharmony_ci # Re-loop to check the other way if the <format> is missing 776e5c31af7Sopenharmony_ci for enum in self.reg.groupdict['VkFormat'].elem: 777e5c31af7Sopenharmony_ci name = enum.get('name') 778e5c31af7Sopenharmony_ci if enum.get('alias') is None and name != 'VK_FORMAT_UNDEFINED': 779e5c31af7Sopenharmony_ci if name not in found_formats and name not in astc3d_formats: 780e5c31af7Sopenharmony_ci self.set_error_context(entity=name, elem=enum) 781e5c31af7Sopenharmony_ci self.record_error('The <enum> has no matching <format> for ', name) 782e5c31af7Sopenharmony_ci 783e5c31af7Sopenharmony_ci super().check_format() 784e5c31af7Sopenharmony_ci 785e5c31af7Sopenharmony_ci # This should be called from check() but as a first pass, do it here 786e5c31af7Sopenharmony_ci # Check for invalid version names in e.g. 787e5c31af7Sopenharmony_ci # <enable version="VK_VERSION_1_2"/> 788e5c31af7Sopenharmony_ci # Could also consistency check struct / extension tags here 789e5c31af7Sopenharmony_ci for capname in self.reg.spirvcapdict: 790e5c31af7Sopenharmony_ci for elem in self.reg.spirvcapdict[capname].elem.findall('enable'): 791e5c31af7Sopenharmony_ci version = elem.get('version') 792e5c31af7Sopenharmony_ci if version is not None and version not in self.reg.apidict: 793e5c31af7Sopenharmony_ci self.set_error_context(entity=capname, elem=elem) 794e5c31af7Sopenharmony_ci self.record_error(f'<spirvcapability> {capname} enabled by a nonexistent version {version}') 795e5c31af7Sopenharmony_ci 796e5c31af7Sopenharmony_ciif __name__ == '__main__': 797e5c31af7Sopenharmony_ci 798e5c31af7Sopenharmony_ci parser = argparse.ArgumentParser() 799e5c31af7Sopenharmony_ci parser.add_argument('-warn', action='store_true', 800e5c31af7Sopenharmony_ci help='Enable display of warning messages') 801e5c31af7Sopenharmony_ci parser.add_argument('files', metavar='filename', nargs='*', 802e5c31af7Sopenharmony_ci help='XML filename to check') 803e5c31af7Sopenharmony_ci 804e5c31af7Sopenharmony_ci args = parser.parse_args() 805e5c31af7Sopenharmony_ci 806e5c31af7Sopenharmony_ci ckr = Checker(args) 807e5c31af7Sopenharmony_ci ckr.check() 808e5c31af7Sopenharmony_ci 809e5c31af7Sopenharmony_ci if ckr.fail: 810e5c31af7Sopenharmony_ci sys.exit(1) 811