/ scripts / check_spec_links.py
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)