• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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