• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2#
3# Copyright (c) 2013-2019 The Khronos Group Inc.
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
17import argparse
18import pdb
19import re
20import sys
21import time
22import xml.etree.ElementTree as etree
23
24from cgenerator import CGeneratorOptions, COutputGenerator
25from docgenerator import DocGeneratorOptions, DocOutputGenerator
26from extensionmetadocgenerator import (ExtensionMetaDocGeneratorOptions,
27                                       ExtensionMetaDocOutputGenerator)
28from generator import write
29from hostsyncgenerator import HostSynchronizationOutputGenerator
30from pygenerator import PyOutputGenerator
31from reg import Registry
32from validitygenerator import ValidityOutputGenerator
33from vkconventions import VulkanConventions
34
35# Simple timer functions
36startTime = None
37
38
39def startTimer(timeit):
40    global startTime
41    if timeit:
42        startTime = time.process_time()
43
44def endTimer(timeit, msg):
45    global startTime
46    if timeit:
47        endTime = time.process_time()
48        write(msg, endTime - startTime, file=sys.stderr)
49        startTime = None
50
51
52def makeREstring(strings, default=None, strings_are_regex=False):
53    """Turn a list of strings into a regexp string matching exactly those strings."""
54    if strings or default is None:
55        if not strings_are_regex:
56            strings = (re.escape(s) for s in strings)
57        return '^(' + '|'.join(strings) + ')$'
58    return default
59
60# Returns a directory of [ generator function, generator options ] indexed
61# by specified short names. The generator options incorporate the following
62# parameters:
63#
64# args is an parsed argument object; see below for the fields that are used.
65def makeGenOpts(args):
66    global genOpts
67    genOpts = {}
68
69    # Default class of extensions to include, or None
70    defaultExtensions = args.defaultExtensions
71
72    # Additional extensions to include (list of extensions)
73    extensions = args.extension
74
75    # Extensions to remove (list of extensions)
76    removeExtensions = args.removeExtensions
77
78    # Extensions to emit (list of extensions)
79    emitExtensions = args.emitExtensions
80
81    # Features to include (list of features)
82    features = args.feature
83
84    # Whether to disable inclusion protect in headers
85    protect = args.protect
86
87    # Output target directory
88    directory = args.directory
89
90    # Descriptive names for various regexp patterns used to select
91    # versions and extensions
92    allFeatures = allExtensions = r'.*'
93
94    # Turn lists of names/patterns into matching regular expressions
95    addExtensionsPat     = makeREstring(extensions, None)
96    removeExtensionsPat  = makeREstring(removeExtensions, None)
97    emitExtensionsPat    = makeREstring(emitExtensions, allExtensions)
98    featuresPat          = makeREstring(features, allFeatures)
99
100    # Copyright text prefixing all headers (list of strings).
101    prefixStrings = [
102        '/*',
103        '** Copyright (c) 2015-2019 The Khronos Group Inc.',
104        '**',
105        '** Licensed under the Apache License, Version 2.0 (the "License");',
106        '** you may not use this file except in compliance with the License.',
107        '** You may obtain a copy of the License at',
108        '**',
109        '**     http://www.apache.org/licenses/LICENSE-2.0',
110        '**',
111        '** Unless required by applicable law or agreed to in writing, software',
112        '** distributed under the License is distributed on an "AS IS" BASIS,',
113        '** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
114        '** See the License for the specific language governing permissions and',
115        '** limitations under the License.',
116        '*/',
117        ''
118    ]
119
120    # Text specific to Vulkan headers
121    vkPrefixStrings = [
122        '/*',
123        '** This header is generated from the Khronos Vulkan XML API Registry.',
124        '**',
125        '*/',
126        ''
127    ]
128
129    # Defaults for generating re-inclusion protection wrappers (or not)
130    protectFile = protect
131
132    # An API style conventions object
133    conventions = VulkanConventions()
134
135    # API include files for spec and ref pages
136    # Overwrites include subdirectories in spec source tree
137    # The generated include files do not include the calling convention
138    # macros (apientry etc.), unlike the header files.
139    # Because the 1.0 core branch includes ref pages for extensions,
140    # all the extension interfaces need to be generated, even though
141    # none are used by the core spec itself.
142    genOpts['apiinc'] = [
143          DocOutputGenerator,
144          DocGeneratorOptions(
145            conventions       = conventions,
146            filename          = 'timeMarker',
147            directory         = directory,
148            apiname           = 'vulkan',
149            profile           = None,
150            versions          = featuresPat,
151            emitversions      = featuresPat,
152            defaultExtensions = None,
153            addExtensions     = addExtensionsPat,
154            removeExtensions  = removeExtensionsPat,
155            emitExtensions    = emitExtensionsPat,
156            prefixText        = prefixStrings + vkPrefixStrings,
157            apicall           = '',
158            apientry          = '',
159            apientryp         = '*',
160            alignFuncParam    = 48,
161            expandEnumerants  = False)
162        ]
163
164    # API names to validate man/api spec includes & links
165    genOpts['vkapi.py'] = [
166          PyOutputGenerator,
167          DocGeneratorOptions(
168            conventions       = conventions,
169            filename          = 'vkapi.py',
170            directory         = directory,
171            apiname           = 'vulkan',
172            profile           = None,
173            versions          = featuresPat,
174            emitversions      = featuresPat,
175            defaultExtensions = None,
176            addExtensions     = addExtensionsPat,
177            removeExtensions  = removeExtensionsPat,
178            emitExtensions    = emitExtensionsPat)
179        ]
180
181    # API validity files for spec
182    genOpts['validinc'] = [
183          ValidityOutputGenerator,
184          DocGeneratorOptions(
185            conventions       = conventions,
186            filename          = 'timeMarker',
187            directory         = directory,
188            apiname           = 'vulkan',
189            profile           = None,
190            versions          = featuresPat,
191            emitversions      = featuresPat,
192            defaultExtensions = None,
193            addExtensions     = addExtensionsPat,
194            removeExtensions  = removeExtensionsPat,
195            emitExtensions    = emitExtensionsPat)
196        ]
197
198    # API host sync table files for spec
199    genOpts['hostsyncinc'] = [
200          HostSynchronizationOutputGenerator,
201          DocGeneratorOptions(
202            conventions       = conventions,
203            filename          = 'timeMarker',
204            directory         = directory,
205            apiname           = 'vulkan',
206            profile           = None,
207            versions          = featuresPat,
208            emitversions      = featuresPat,
209            defaultExtensions = None,
210            addExtensions     = addExtensionsPat,
211            removeExtensions  = removeExtensionsPat,
212            emitExtensions    = emitExtensionsPat)
213        ]
214
215    # Extension metainformation for spec extension appendices
216    genOpts['extinc'] = [
217          ExtensionMetaDocOutputGenerator,
218          ExtensionMetaDocGeneratorOptions(
219            conventions       = conventions,
220            filename          = 'timeMarker',
221            directory         = directory,
222            apiname           = 'vulkan',
223            profile           = None,
224            versions          = featuresPat,
225            emitversions      = None,
226            defaultExtensions = defaultExtensions,
227            addExtensions     = None,
228            removeExtensions  = None,
229            emitExtensions    = emitExtensionsPat)
230        ]
231
232    # Platform extensions, in their own header files
233    # Each element of the platforms[] array defines information for
234    # generating a single platform:
235    #   [0] is the generated header file name
236    #   [1] is the set of platform extensions to generate
237    #   [2] is additional extensions whose interfaces should be considered,
238    #   but suppressed in the output, to avoid duplicate definitions of
239    #   dependent types like VkDisplayKHR and VkSurfaceKHR which come from
240    #   non-platform extensions.
241
242    # Track all platform extensions, for exclusion from vulkan_core.h
243    allPlatformExtensions = []
244
245    # Extensions suppressed for all platforms.
246    # Covers common WSI extension types.
247    commonSuppressExtensions = [ 'VK_KHR_display', 'VK_KHR_swapchain' ]
248
249    platforms = [
250        [ 'vulkan_android.h',     [ 'VK_KHR_android_surface',
251                                    'VK_ANDROID_external_memory_android_hardware_buffer'
252                                                                  ], commonSuppressExtensions ],
253        [ 'vulkan_fuchsia.h',     [ 'VK_FUCHSIA_imagepipe_surface'], commonSuppressExtensions ],
254        [ 'vulkan_ggp.h',         [ 'VK_GGP_stream_descriptor_surface',
255                                    'VK_GGP_frame_token'          ], commonSuppressExtensions ],
256        [ 'vulkan_ios.h',         [ 'VK_MVK_ios_surface'          ], commonSuppressExtensions ],
257        [ 'vulkan_macos.h',       [ 'VK_MVK_macos_surface'        ], commonSuppressExtensions ],
258        [ 'vulkan_vi.h',          [ 'VK_NN_vi_surface'            ], commonSuppressExtensions ],
259        [ 'vulkan_wayland.h',     [ 'VK_KHR_wayland_surface'      ], commonSuppressExtensions ],
260        [ 'vulkan_win32.h',       [ 'VK_.*_win32(|_.*)', 'VK_EXT_full_screen_exclusive' ],
261                                                                     commonSuppressExtensions +
262                                                                     [ 'VK_KHR_external_semaphore',
263                                                                       'VK_KHR_external_memory_capabilities',
264                                                                       'VK_KHR_external_fence',
265                                                                       'VK_KHR_external_fence_capabilities',
266                                                                       'VK_KHR_get_surface_capabilities2',
267                                                                       'VK_NV_external_memory_capabilities',
268                                                                     ] ],
269        [ 'vulkan_xcb.h',         [ 'VK_KHR_xcb_surface'          ], commonSuppressExtensions ],
270        [ 'vulkan_xlib.h',        [ 'VK_KHR_xlib_surface'         ], commonSuppressExtensions ],
271        [ 'vulkan_xlib_xrandr.h', [ 'VK_EXT_acquire_xlib_display' ], commonSuppressExtensions ],
272        [ 'vulkan_metal.h',       [ 'VK_EXT_metal_surface'        ], commonSuppressExtensions ],
273    ]
274
275    for platform in platforms:
276        headername = platform[0]
277
278        allPlatformExtensions += platform[1]
279
280        addPlatformExtensionsRE = makeREstring(
281            platform[1] + platform[2], strings_are_regex=True)
282        emitPlatformExtensionsRE = makeREstring(
283            platform[1], strings_are_regex=True)
284
285        opts = CGeneratorOptions(
286            conventions       = conventions,
287            filename          = headername,
288            directory         = directory,
289            apiname           = 'vulkan',
290            profile           = None,
291            versions          = featuresPat,
292            emitversions      = None,
293            defaultExtensions = None,
294            addExtensions     = addPlatformExtensionsRE,
295            removeExtensions  = None,
296            emitExtensions    = emitPlatformExtensionsRE,
297            prefixText        = prefixStrings + vkPrefixStrings,
298            genFuncPointers   = True,
299            protectFile       = protectFile,
300            protectFeature    = False,
301            protectProto      = '#ifndef',
302            protectProtoStr   = 'VK_NO_PROTOTYPES',
303            apicall           = 'VKAPI_ATTR ',
304            apientry          = 'VKAPI_CALL ',
305            apientryp         = 'VKAPI_PTR *',
306            alignFuncParam    = 48,
307            genEnumBeginEndRange = True)
308
309        genOpts[headername] = [ COutputGenerator, opts ]
310
311    # Header for core API + extensions.
312    # To generate just the core API,
313    # change to 'defaultExtensions = None' below.
314    #
315    # By default this adds all enabled, non-platform extensions.
316    # It removes all platform extensions (from the platform headers options
317    # constructed above) as well as any explicitly specified removals.
318
319    removeExtensionsPat = makeREstring(
320        allPlatformExtensions + removeExtensions, None, strings_are_regex=True)
321
322    genOpts['vulkan_core.h'] = [
323          COutputGenerator,
324          CGeneratorOptions(
325            conventions       = conventions,
326            filename          = 'vulkan_core.h',
327            directory         = directory,
328            apiname           = 'vulkan',
329            profile           = None,
330            versions          = featuresPat,
331            emitversions      = featuresPat,
332            defaultExtensions = defaultExtensions,
333            addExtensions     = None,
334            removeExtensions  = removeExtensionsPat,
335            emitExtensions    = emitExtensionsPat,
336            prefixText        = prefixStrings + vkPrefixStrings,
337            genFuncPointers   = True,
338            protectFile       = protectFile,
339            protectFeature    = False,
340            protectProto      = '#ifndef',
341            protectProtoStr   = 'VK_NO_PROTOTYPES',
342            apicall           = 'VKAPI_ATTR ',
343            apientry          = 'VKAPI_CALL ',
344            apientryp         = 'VKAPI_PTR *',
345            alignFuncParam    = 48,
346            genEnumBeginEndRange = True)
347        ]
348
349    # Unused - vulkan10.h target.
350    # It is possible to generate a header with just the Vulkan 1.0 +
351    # extension interfaces defined, but since the promoted KHR extensions
352    # are now defined in terms of the 1.1 interfaces, such a header is very
353    # similar to vulkan_core.h.
354    genOpts['vulkan10.h'] = [
355          COutputGenerator,
356          CGeneratorOptions(
357            conventions       = conventions,
358            filename          = 'vulkan10.h',
359            directory         = directory,
360            apiname           = 'vulkan',
361            profile           = None,
362            versions          = 'VK_VERSION_1_0',
363            emitversions      = 'VK_VERSION_1_0',
364            defaultExtensions = defaultExtensions,
365            addExtensions     = None,
366            removeExtensions  = removeExtensionsPat,
367            emitExtensions    = emitExtensionsPat,
368            prefixText        = prefixStrings + vkPrefixStrings,
369            genFuncPointers   = True,
370            protectFile       = protectFile,
371            protectFeature    = False,
372            protectProto      = '#ifndef',
373            protectProtoStr   = 'VK_NO_PROTOTYPES',
374            apicall           = 'VKAPI_ATTR ',
375            apientry          = 'VKAPI_CALL ',
376            apientryp         = 'VKAPI_PTR *',
377            alignFuncParam    = 48)
378        ]
379
380    genOpts['alias.h'] = [
381          COutputGenerator,
382          CGeneratorOptions(
383            conventions       = conventions,
384            filename          = 'alias.h',
385            directory         = directory,
386            apiname           = 'vulkan',
387            profile           = None,
388            versions          = featuresPat,
389            emitversions      = featuresPat,
390            defaultExtensions = defaultExtensions,
391            addExtensions     = None,
392            removeExtensions  = removeExtensionsPat,
393            emitExtensions    = emitExtensionsPat,
394            prefixText        = None,
395            genFuncPointers   = False,
396            protectFile       = False,
397            protectFeature    = False,
398            protectProto      = '',
399            protectProtoStr   = '',
400            apicall           = '',
401            apientry          = '',
402            apientryp         = '',
403            alignFuncParam    = 36)
404        ]
405
406# Generate a target based on the options in the matching genOpts{} object.
407# This is encapsulated in a function so it can be profiled and/or timed.
408# The args parameter is an parsed argument object containing the following
409# fields that are used:
410#   target - target to generate
411#   directory - directory to generate it in
412#   protect - True if re-inclusion wrappers should be created
413#   extensions - list of additional extensions to include in generated
414#   interfaces
415def genTarget(args):
416    # Create generator options with specified parameters
417    makeGenOpts(args)
418
419    if args.target in genOpts:
420        createGenerator = genOpts[args.target][0]
421        options = genOpts[args.target][1]
422
423        if not args.quiet:
424            write('* Building', options.filename, file=sys.stderr)
425            write('* options.versions          =', options.versions, file=sys.stderr)
426            write('* options.emitversions      =', options.emitversions, file=sys.stderr)
427            write('* options.defaultExtensions =', options.defaultExtensions, file=sys.stderr)
428            write('* options.addExtensions     =', options.addExtensions, file=sys.stderr)
429            write('* options.removeExtensions  =', options.removeExtensions, file=sys.stderr)
430            write('* options.emitExtensions    =', options.emitExtensions, file=sys.stderr)
431
432        startTimer(args.time)
433        gen = createGenerator(errFile=errWarn,
434                              warnFile=errWarn,
435                              diagFile=diag)
436        reg.setGenerator(gen)
437        reg.apiGen(options)
438
439        if not args.quiet:
440            write('* Generated', options.filename, file=sys.stderr)
441        endTimer(args.time, '* Time to generate ' + options.filename + ' =')
442    else:
443        write('No generator options for unknown target:',
444              args.target, file=sys.stderr)
445
446
447# -feature name
448# -extension name
449# For both, "name" may be a single name, or a space-separated list
450# of names, or a regular expression.
451if __name__ == '__main__':
452    parser = argparse.ArgumentParser()
453
454    parser.add_argument('-defaultExtensions', action='store',
455                        default='vulkan',
456                        help='Specify a single class of extensions to add to targets')
457    parser.add_argument('-extension', action='append',
458                        default=[],
459                        help='Specify an extension or extensions to add to targets')
460    parser.add_argument('-removeExtensions', action='append',
461                        default=[],
462                        help='Specify an extension or extensions to remove from targets')
463    parser.add_argument('-emitExtensions', action='append',
464                        default=[],
465                        help='Specify an extension or extensions to emit in targets')
466    parser.add_argument('-feature', action='append',
467                        default=[],
468                        help='Specify a core API feature name or names to add to targets')
469    parser.add_argument('-debug', action='store_true',
470                        help='Enable debugging')
471    parser.add_argument('-dump', action='store_true',
472                        help='Enable dump to stderr')
473    parser.add_argument('-diagfile', action='store',
474                        default=None,
475                        help='Write diagnostics to specified file')
476    parser.add_argument('-errfile', action='store',
477                        default=None,
478                        help='Write errors and warnings to specified file instead of stderr')
479    parser.add_argument('-noprotect', dest='protect', action='store_false',
480                        help='Disable inclusion protection in output headers')
481    parser.add_argument('-profile', action='store_true',
482                        help='Enable profiling')
483    parser.add_argument('-registry', action='store',
484                        default='vk.xml',
485                        help='Use specified registry file instead of vk.xml')
486    parser.add_argument('-time', action='store_true',
487                        help='Enable timing')
488    parser.add_argument('-validate', action='store_true',
489                        help='Enable group validation')
490    parser.add_argument('-o', action='store', dest='directory',
491                        default='.',
492                        help='Create target and related files in specified directory')
493    parser.add_argument('target', metavar='target', nargs='?',
494                        help='Specify target')
495    parser.add_argument('-quiet', action='store_true', default=True,
496                        help='Suppress script output during normal execution.')
497    parser.add_argument('-verbose', action='store_false', dest='quiet', default=True,
498                        help='Enable script output during normal execution.')
499
500    args = parser.parse_args()
501
502    # This splits arguments which are space-separated lists
503    args.feature = [name for arg in args.feature for name in arg.split()]
504    args.extension = [name for arg in args.extension for name in arg.split()]
505
506    # Load & parse registry
507    reg = Registry()
508
509    startTimer(args.time)
510    tree = etree.parse(args.registry)
511    endTimer(args.time, '* Time to make ElementTree =')
512
513    if args.debug:
514        pdb.run('reg.loadElementTree(tree)')
515    else:
516        startTimer(args.time)
517        reg.loadElementTree(tree)
518        endTimer(args.time, '* Time to parse ElementTree =')
519
520    if args.validate:
521        reg.validateGroups()
522
523    if args.dump:
524        write('* Dumping registry to regdump.txt', file=sys.stderr)
525        reg.dumpReg(filehandle = open('regdump.txt', 'w', encoding='utf-8'))
526
527    # create error/warning & diagnostic files
528    if args.errfile:
529        errWarn = open(args.errfile, 'w', encoding='utf-8')
530    else:
531        errWarn = sys.stderr
532
533    if args.diagfile:
534        diag = open(args.diagfile, 'w', encoding='utf-8')
535    else:
536        diag = None
537
538    if args.debug:
539        pdb.run('genTarget(args)')
540    elif args.profile:
541        import cProfile
542        import pstats
543        cProfile.run('genTarget(args)', 'profile.txt')
544        p = pstats.Stats('profile.txt')
545        p.strip_dirs().sort_stats('time').print_stats(50)
546    else:
547        genTarget(args)
548