File: check_spec_links.py

package info (click to toggle)
openxr-sdk-source 1.0.14~dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,564 kB
  • sloc: python: 16,103; cpp: 12,052; ansic: 8,813; xml: 3,480; sh: 410; makefile: 338; ruby: 247
file content (159 lines) | stat: -rwxr-xr-x 5,747 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/env python3
#
# Copyright (c) 2018-2019 Collabora, Ltd.
#
# SPDX-License-Identifier: Apache-2.0
#
# Author(s):    Ryan Pavlik <ryan.pavlik@collabora.com>
#
# Purpose:      This file performs some basic checks of the custom macros
#               used in the AsciiDoctor source for the spec, especially
#               related to the validity of the entities linked-to.

from pathlib import Path

from reg import Registry
from spec_tools.entity_db import EntityDatabase
from spec_tools.macro_checker import MacroChecker
from spec_tools.macro_checker_file import BlockType, MacroCheckerFile
from spec_tools.main import checkerMain
from spec_tools.shared import MessageId

###
# "Configuration" constants

EXTRA_DEFINES = ('XRAPI_ATTR', 'XRAPI_CALL', 'XRAPI_PTR', 'XR_NO_STDINT_H')

# These are marked with the code: macro
SYSTEM_TYPES = set(('void', 'char', 'float', 'size_t', 'uintptr_t',
                    'int8_t', 'uint8_t',
                    'int32_t', 'uint32_t',
                    'int64_t', 'uint64_t'))

ROOT = Path(__file__).resolve().parent.parent.parent
DEFAULT_DISABLED_MESSAGES = set((MessageId.REFPAGE_MISSING,))

CWD = Path('.').resolve()


class XREntityDatabase(EntityDatabase):
    """OpenXR-specific subclass of EntityDatabase."""

    def makeRegistry(self):
        root = Path(__file__).resolve().parent.parent.parent
        registryFile = str(root / 'specification/registry/xr.xml')
        registry = Registry()
        registry.loadFile(registryFile)
        return registry

    def getNamePrefix(self):
        return "xr"

    def getPlatformRequires(self):
        return "openxr_platform_defines"

    def getSystemTypes(self):
        return SYSTEM_TYPES

    def populateMacros(self):
        # TODO: Where should flags actually go?
        # #Not mentioned in the style guide.
        # TODO: What about flag wildcards? There are a few such uses...

        self.addMacro('basetype', ('basetypes',), link=True)

    def populateEntities(self):
        # These are not mentioned in the XML
        for name in EXTRA_DEFINES:
            self.addEntity(name, 'dlink', category='configdefines',
                           generates=False)

    def handleType(self, name, info, requires):
        """Extend superclass implementation for OpenXR bitmasks."""
        cat = info.elem.get('category')

        if cat == 'bitmask':
            # OpenXR uses elink, not tlink, for flags.
            self.addEntity(
                name,
                'elink',
                elem=info.elem,
                category='flags')
        else:
            super().handleType(name, info, requires)


class XRMacroCheckerFile(MacroCheckerFile):
    """OpenXR-specific subclass of MacroCheckerFile."""

    def processBlockOpen(self, block_type, context=None, delimiter=None):
        """Do any block-type-specific processing and push the new block.

        Extends the superclass to warn if a ref-page-like block is opened
        without a ref page tag.

        Must call self.pushBlock().

        Called by self.processBlockDelimiter().
        """
        # OpenXR only uses '--' blocks for ref pages.
        if block_type == BlockType.REF_PAGE_LIKE and \
                not self.prev_line_ref_page_tag:

            self.error(MessageId.REFPAGE_BLOCK,
                       ["Found a line containing only -- outside of a reference page block, not preceded by a reference page tag,",
                        "Pretending there was one and opening refpage block for unknown entity anyway, for more readable messages."],
                       group=None)

            self.current_ref_page = '?missing-refpage-tag?'
            self.prev_line_ref_page_tag = None
            self.in_ref_page = True
            # Manually pushing the block here, to force a refpage block.
            # Not going to superclass method in this case.
            self.pushBlock(block_type, refpage=self.current_ref_page,
                           context=context, delimiter=delimiter)
            return

        # Everything else about block opening is standard.
        super().processBlockOpen(block_type, context=context,
                                 delimiter=delimiter)

    def handleIncludeMissingRefPage(self, entity, generated_type):
        """Report a message about an include outside of a ref-page block."""
        # TODO is it reasonable that we just stuck all the Flags includes in
        # the appendix?
        if entity.endswith('Flags'):
            # OK, it's a flag, no worries.
            return

        super().handleIncludeMissingRefPage(entity, generated_type)

    def computeExpectedRefPageFromInclude(self, entity):
        """Compute the expected ref page entity based on an include entity name."""
        # TODO use registry associations between FlagBits and Flags to do this
        # better?
        if entity.endswith('FlagBits'):
            return entity.replace('FlagBits', 'Flags')
        return entity


def makeMacroChecker(enabled_messages):
    """Create a correctly-configured MacroChecker instance."""
    entity_db = XREntityDatabase()
    return MacroChecker(enabled_messages, entity_db, XRMacroCheckerFile, ROOT)


if __name__ == '__main__':
    available_messages = set(MessageId)
    # LEGACY messages are Vulkan-only.
    available_messages.remove(MessageId.LEGACY)

    default_enabled_messages = available_messages.difference(
        DEFAULT_DISABLED_MESSAGES)

    all_docs = sorted((str(fn)
                       for fn in (ROOT / 'specification/sources/').glob('**/*.adoc')
                       if "styleguide" not in str(fn)))

    checkerMain(default_enabled_messages, makeMacroChecker,
                all_docs, available_messages=available_messages)