check_spec_links.py
1 #!/usr/bin/python3 2 # 3 # Copyright (c) 2018-2019 Collabora, Ltd. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 # 17 # Author(s): Ryan Pavlik <ryan.pavlik@collabora.com> 18 # 19 # Purpose: This file performs some basic checks of the custom macros 20 # used in the AsciiDoctor source for the spec, especially 21 # related to the validity of the entities linked-to. 22 23 from pathlib import Path 24 25 from reg import Registry 26 from spec_tools.entity_db import EntityDatabase 27 from spec_tools.macro_checker import MacroChecker 28 from spec_tools.macro_checker_file import MacroCheckerFile 29 from spec_tools.main import checkerMain 30 from spec_tools.shared import (AUTO_FIX_STRING, EXTENSION_CATEGORY, MessageId, 31 MessageType) 32 33 ### 34 # "Configuration" constants 35 36 EXTRA_DEFINES = tuple() # TODO - defines mentioned in spec but not needed in registry 37 38 # These are marked with the code: macro 39 SYSTEM_TYPES = set(('void', 'char', 'float', 'size_t', 'uintptr_t', 40 'int8_t', 'uint8_t', 41 'int32_t', 'uint32_t', 42 'int64_t', 'uint64_t')) 43 44 ROOT = Path(__file__).resolve().parent.parent 45 DEFAULT_DISABLED_MESSAGES = set(( 46 MessageId.LEGACY, 47 MessageId.REFPAGE_MISSING, 48 MessageId.MISSING_MACRO, 49 MessageId.EXTENSION, 50 # TODO *text macro checking actually needs fixing for Vulkan 51 MessageId.MISUSED_TEXT, 52 MessageId.MISSING_TEXT 53 )) 54 55 CWD = Path('.').resolve() 56 57 58 class VulkanEntityDatabase(EntityDatabase): 59 """Vulkan-specific subclass of EntityDatabase.""" 60 61 def makeRegistry(self): 62 registryFile = str(ROOT / 'xml/vk.xml') 63 registry = Registry() 64 registry.loadFile(registryFile) 65 return registry 66 67 def getNamePrefix(self): 68 return "vk" 69 70 def getPlatformRequires(self): 71 return 'vk_platform' 72 73 def getSystemTypes(self): 74 return SYSTEM_TYPES 75 76 def populateMacros(self): 77 self.addMacros('t', ['link', 'name'], ['funcpointers', 'flags']) 78 79 def populateEntities(self): 80 # These are not mentioned in the XML 81 for name in EXTRA_DEFINES: 82 self.addEntity(name, 'dlink', category='configdefines') 83 84 85 class VulkanMacroCheckerFile(MacroCheckerFile): 86 """Vulkan-specific subclass of MacroCheckerFile.""" 87 88 def handleWrongMacro(self, msg, data): 89 """Report an appropriate message when we found that the macro used is incorrect. 90 91 May be overridden depending on each API's behavior regarding macro misuse: 92 e.g. in some cases, it may be considered a MessageId.LEGACY warning rather than 93 a MessageId.WRONG_MACRO or MessageId.EXTENSION. 94 """ 95 message_type = MessageType.WARNING 96 message_id = MessageId.WRONG_MACRO 97 group = 'macro' 98 99 if data.category == EXTENSION_CATEGORY: 100 # Ah, this is an extension 101 msg.append( 102 'This is apparently an extension name, which should be marked up as a link.') 103 message_id = MessageId.EXTENSION 104 group = None # replace the whole thing 105 else: 106 # Non-extension, we found the macro though. 107 if data.macro[0] == self.macro[0] and data.macro[1:] == 'link' and self.macro[1:] == 'name': 108 # First letter matches, old is 'name', new is 'link': 109 # This is legacy markup 110 msg.append( 111 'This is legacy markup that has not been updated yet.') 112 message_id = MessageId.LEGACY 113 else: 114 # Not legacy, just wrong. 115 message_type = MessageType.ERROR 116 117 msg.append(AUTO_FIX_STRING) 118 self.diag(message_type, message_id, msg, 119 group=group, replacement=self.makeMacroMarkup(data=data), fix=self.makeFix(data=data)) 120 121 122 def makeMacroChecker(enabled_messages): 123 """Create a correctly-configured MacroChecker instance.""" 124 entity_db = VulkanEntityDatabase() 125 return MacroChecker(enabled_messages, entity_db, VulkanMacroCheckerFile, ROOT) 126 127 128 if __name__ == '__main__': 129 default_enabled_messages = set(MessageId).difference( 130 DEFAULT_DISABLED_MESSAGES) 131 132 all_docs = [str(fn) 133 for fn in sorted((ROOT / 'chapters/').glob('**/*.txt'))] 134 all_docs.extend([str(fn) 135 for fn in sorted((ROOT / 'appendices/').glob('**/*.txt'))]) 136 137 checkerMain(default_enabled_messages, makeMacroChecker, 138 all_docs)