1#!/usr/bin/python3 2# 3# Copyright (c) 2018-2019 Collabora, Ltd. 4# 5# SPDX-License-Identifier: Apache-2.0 6# 7# Author(s): Ryan Pavlik <ryan.pavlik@collabora.com> 8# 9# Purpose: This file performs some basic checks of the custom macros 10# used in the AsciiDoctor source for the spec, especially 11# related to the validity of the entities linked-to. 12 13from pathlib import Path 14 15from reg import Registry 16from spec_tools.entity_db import EntityDatabase 17from spec_tools.macro_checker import MacroChecker 18from spec_tools.macro_checker_file import MacroCheckerFile 19from spec_tools.main import checkerMain 20from spec_tools.shared import (AUTO_FIX_STRING, EXTENSION_CATEGORY, MessageId, 21 MessageType) 22 23### 24# "Configuration" constants 25 26FREEFORM_CATEGORY = 'freeform' 27 28# defines mentioned in spec but not needed in registry 29EXTRA_DEFINES = ( 30 'VKAPI_ATTR', 31 'VKAPI_CALL', 32 'VKAPI_PTR', 33 'VK_NO_STDDEF_H', 34 'VK_NO_STDINT_H', 35 ) 36 37# Extra freeform refpages in addition to EXTRA_DEFINES 38EXTRA_REFPAGES = ( 39 'VK_VERSION_1_0', 40 'VK_VERSION_1_1', 41 'VK_VERSION_1_2', 42 'VK_VERSION_1_3', 43 'WSIheaders', 44 'provisional-headers', 45 ) 46 47# These are marked with the code: macro 48SYSTEM_TYPES = set(('void', 'char', 'float', 'size_t', 'uintptr_t', 49 'int8_t', 'uint8_t', 50 'int32_t', 'uint32_t', 51 'int64_t', 'uint64_t')) 52 53ROOT = Path(__file__).resolve().parent.parent 54DEFAULT_DISABLED_MESSAGES = set(( 55 MessageId.LEGACY, 56 MessageId.REFPAGE_MISSING, 57 MessageId.MISSING_MACRO, 58 MessageId.EXTENSION, 59 # TODO *text macro checking actually needs fixing for Vulkan 60 MessageId.MISUSED_TEXT, 61 MessageId.MISSING_TEXT 62)) 63 64CWD = Path('.').resolve() 65 66 67class VulkanEntityDatabase(EntityDatabase): 68 """Vulkan-specific subclass of EntityDatabase.""" 69 70 def __init__(self, *args, **kwargs): 71 super().__init__(*args, **kwargs) 72 self._conditionally_recognized = set(('fname', 'sname')) 73 74 def makeRegistry(self): 75 registryFile = str(ROOT / 'xml/vk.xml') 76 registry = Registry() 77 registry.loadFile(registryFile) 78 return registry 79 80 def getNamePrefix(self): 81 return "vk" 82 83 def getPlatformRequires(self): 84 return 'vk_platform' 85 86 def getSystemTypes(self): 87 return SYSTEM_TYPES 88 89 def populateMacros(self): 90 self.addMacros('t', ['link', 'name'], ['funcpointers', 'flags']) 91 92 def populateEntities(self): 93 # These are not mentioned in the XML 94 for name in EXTRA_DEFINES: 95 self.addEntity(name, 'dlink', 96 category=FREEFORM_CATEGORY, generates=False) 97 for name in EXTRA_REFPAGES: 98 self.addEntity(name, 'code', 99 category=FREEFORM_CATEGORY, generates=False) 100 101 def shouldBeRecognized(self, macro, entity_name): 102 """Determine, based on the macro and the name provided, if we should expect to recognize the entity.""" 103 if super().shouldBeRecognized(macro, entity_name): 104 return True 105 106 # The *name: macros in Vulkan should also be recognized if the entity name matches the pattern. 107 if macro in self._conditionally_recognized and self.likelyRecognizedEntity(entity_name): 108 return True 109 return False 110 111 112class VulkanMacroCheckerFile(MacroCheckerFile): 113 """Vulkan-specific subclass of MacroCheckerFile.""" 114 115 def perform_entity_check(self, type): 116 """Returns True if an entity check should be performed on this 117 refpage type. 118 119 Overrides base class definition for Vulkan, since we have refpage 120 types which do not correspond to entities in the API.""" 121 122 return type != 'builtins' and type != 'spirv' 123 124 def handleWrongMacro(self, msg, data): 125 """Report an appropriate message when we found that the macro used is incorrect. 126 127 May be overridden depending on each API's behavior regarding macro misuse: 128 e.g. in some cases, it may be considered a MessageId.LEGACY warning rather than 129 a MessageId.WRONG_MACRO or MessageId.EXTENSION. 130 """ 131 message_type = MessageType.WARNING 132 message_id = MessageId.WRONG_MACRO 133 group = 'macro' 134 135 if data.category == EXTENSION_CATEGORY: 136 # Ah, this is an extension 137 msg.append( 138 'This is apparently an extension name, which should be marked up as a link.') 139 message_id = MessageId.EXTENSION 140 group = None # replace the whole thing 141 else: 142 # Non-extension, we found the macro though. 143 if data.macro[0] == self.macro[0] and data.macro[1:] == 'link' and self.macro[1:] == 'name': 144 # First letter matches, old is 'name', new is 'link': 145 # This is legacy markup 146 msg.append( 147 'This is legacy markup that has not been updated yet.') 148 message_id = MessageId.LEGACY 149 else: 150 # Not legacy, just wrong. 151 message_type = MessageType.ERROR 152 153 msg.append(AUTO_FIX_STRING) 154 self.diag(message_type, message_id, msg, 155 group=group, replacement=self.makeMacroMarkup(data=data), fix=self.makeFix(data=data)) 156 157 def allowEnumXrefs(self): 158 """Returns True if enums can be specified in the 'xrefs' attribute 159 of a refpage. 160 161 Overrides base class behavior. OpenXR does not allow this. 162 """ 163 return True 164 165def makeMacroChecker(enabled_messages): 166 """Create a correctly-configured MacroChecker instance.""" 167 entity_db = VulkanEntityDatabase() 168 return MacroChecker(enabled_messages, entity_db, VulkanMacroCheckerFile, ROOT) 169 170 171if __name__ == '__main__': 172 default_enabled_messages = set(MessageId).difference( 173 DEFAULT_DISABLED_MESSAGES) 174 175 all_docs = [str(fn) 176 for fn in sorted((ROOT / 'chapters/').glob('**/[A-Za-z]*.adoc'))] 177 all_docs.extend([str(fn) 178 for fn in sorted((ROOT / 'appendices/').glob('**/[A-Za-z]*.adoc'))]) 179 180 checkerMain(default_enabled_messages, makeMacroChecker, 181 all_docs) 182