SCons: Streamline Vulkan buildsystem + fixups

- Renamed option to `builtin_vulkan`, since that's the name of the
  library and if we were to add new components, we'd likely use that
  same option.
- Merge `vulkan_loader/SCsub` in `vulkan/SCsub`.
- Accordingly, don't use built-in Vulkan headers when not building
  against the built-in loader library.
- Drop Vulkan registry which we don't appear to need currently.
- Style and permission fixes.
This commit is contained in:
Rémi Verschelde 2019-08-05 11:48:54 +02:00
parent 324082471d
commit 511f65214f
49 changed files with 92 additions and 42606 deletions

View file

@ -155,7 +155,7 @@ opts.Add(BoolVariable('builtin_pcre2_with_jit', "Use JIT compiler for the built-
opts.Add(BoolVariable('builtin_recast', "Use the built-in Recast library", True))
opts.Add(BoolVariable('builtin_rvo2', "Use the built-in RVO2 library", True))
opts.Add(BoolVariable('builtin_squish', "Use the built-in squish library", True))
opts.Add(BoolVariable('builtin_vulkan_loader', "Use the built-in Vulkan loader library", True))
opts.Add(BoolVariable('builtin_vulkan', "Use the built-in Vulkan loader library and headers", True))
opts.Add(BoolVariable('builtin_xatlas', "Use the built-in xatlas library", True))
opts.Add(BoolVariable('builtin_zlib', "Use the built-in zlib library", True))
opts.Add(BoolVariable('builtin_zstd', "Use the built-in Zstd library", True))

View file

@ -22,9 +22,6 @@ SConscript('alsamidi/SCsub')
SConscript('coremidi/SCsub')
SConscript('winmidi/SCsub')
if env["builtin_vulkan_loader"] and not (env["platform"]=="osx" and env['use_static_mvk']):
SConscript('vulkan_loader/SCsub')
# Graphics drivers
if (env["platform"] != "server"):
# SConscript('gles3/SCsub')

View file

@ -2,7 +2,60 @@
Import('env')
env.add_source_files(env.drivers_sources,"*.cpp")
env.add_source_files(env.drivers_sources, "*.cpp")
if env['builtin_vulkan']:
# Use bundled Vulkan headers
thirdparty_dir = "#thirdparty/vulkan"
env.Prepend(CPPPATH=[thirdparty_dir + "/include", thirdparty_dir + "/loader"])
#SConscript("shaders/SCsub")
# Build Vulkan loader library
env_thirdparty = env.Clone()
env_thirdparty.disable_warnings()
loader_sources = [
"asm_offset.c",
"cJSON.c",
"debug_utils.c",
"dev_ext_trampoline.c",
"loader.c",
"murmurhash.c",
"phys_dev_ext.c",
"trampoline.c",
"unknown_ext_chain.c",
"wsi.c",
"extension_manual.c",
]
if env['platform'] == "windows":
loader_sources.append("dirent_on_windows.c")
env_thirdparty.AppendUnique(CPPDEFINES=[
'VK_USE_PLATFORM_WIN32_KHR',
'VULKAN_NON_CMAKE_BUILD',
'WIN32_LEAN_AND_MEAN',
'API_NAME=\\"%s\\"' % 'Vulkan'
])
if not env.msvc: # Windows 7+, missing in mingw headers
env_thirdparty.AppendUnique(CPPDEFINES=[
"CM_GETIDLIST_FILTER_CLASS=0x00000200",
"CM_GETIDLIST_FILTER_PRESENT=0x00000100"
])
elif env['platform'] == "osx":
env_thirdparty.AppendUnique(CPPDEFINES=[
'VK_USE_PLATFORM_MACOS_MVK',
'VULKAN_NON_CMAKE_BUILD',
'SYSCONFDIR=\\"%s\\"' % '/etc',
'FALLBACK_DATA_DIRS=\\"%s\\"' % '/usr/local/share:/usr/share',
'FALLBACK_CONFIG_DIRS=\\"%s\\"' % '/etc/xdg'
])
elif env['platform'] == "x11":
env_thirdparty.AppendUnique(CPPDEFINES=[
'VK_USE_PLATFORM_XLIB_KHR',
'VULKAN_NON_CMAKE_BUILD',
'SYSCONFDIR=\\"%s\\"' % '/etc',
'FALLBACK_DATA_DIRS=\\"%s\\"' % '/usr/local/share:/usr/share',
'FALLBACK_CONFIG_DIRS=\\"%s\\"' % '/etc/xdg'
])
loader_sources = [thirdparty_dir + "/loader/" + file for file in loader_sources]
env_thirdparty.add_source_files(env.drivers_sources, loader_sources)

View file

@ -1,55 +0,0 @@
#!/usr/bin/env python
Import('env')
env_vlk_ldr = env.Clone()
loader_dir = "#thirdparty/vulkan/loader/"
loader_sources = [
"asm_offset.c",
"dev_ext_trampoline.c",
"phys_dev_ext.c",
"cJSON.c",
"loader.c",
"trampoline.c",
"unknown_ext_chain.c",
"wsi.c",
"debug_utils.c",
"extension_manual.c",
"murmurhash.c"
]
if (env_vlk_ldr["platform"]=="windows"):
loader_sources.append("dirent_on_windows.c")
env_vlk_ldr.AppendUnique(CPPDEFINES = [
'VK_USE_PLATFORM_WIN32_KHR',
'VULKAN_NON_CMAKE_BUILD',
'WIN32_LEAN_AND_MEAN',
'API_NAME=\\"%s\\"' % 'Vulkan'
])
if not env.msvc: #windows 7+, missing in mingw headers
env_vlk_ldr.AppendUnique(CPPDEFINES = [
"CM_GETIDLIST_FILTER_CLASS=0x00000200",
"CM_GETIDLIST_FILTER_PRESENT=0x00000100"
])
elif (env_vlk_ldr["platform"]=="osx"):
env_vlk_ldr.AppendUnique(CPPDEFINES = [
'VK_USE_PLATFORM_MACOS_MVK',
'VULKAN_NON_CMAKE_BUILD',
'SYSCONFDIR=\\"%s\\"' % '/etc',
'FALLBACK_DATA_DIRS=\\"%s\\"' % '/usr/local/share:/usr/share',
'FALLBACK_CONFIG_DIRS=\\"%s\\"' % '/etc/xdg'
])
elif (env_vlk_ldr["platform"]=="x11"):
env_vlk_ldr.AppendUnique(CPPDEFINES = [
'VK_USE_PLATFORM_XLIB_KHR',
'VULKAN_NON_CMAKE_BUILD',
'SYSCONFDIR=\\"%s\\"' % '/etc',
'FALLBACK_DATA_DIRS=\\"%s\\"' % '/usr/local/share:/usr/share',
'FALLBACK_CONFIG_DIRS=\\"%s\\"' % '/etc/xdg'
])
loader_sources = [loader_dir + file for file in loader_sources]
env_thirdparty = env_vlk_ldr.Clone()
env_thirdparty.add_source_files(env.drivers_sources, loader_sources)
env.Prepend(CPPPATH=[loader_dir])

View file

@ -154,16 +154,15 @@ def configure(env):
env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-framework', 'IOKit', '-framework', 'ForceFeedback', '-framework', 'CoreVideo', '-framework', 'AVFoundation', '-framework', 'CoreMedia'])
env.Append(LIBS=['pthread', 'z'])
env.Prepend(CPPPATH=['#thirdparty/vulkan/include/', "#thirdparty/vulkan/registry/"])
env.Append(CPPDEFINES=['VULKAN_ENABLED'])
#env.Append(CPPDEFINES=['GLES_ENABLED', 'OPENGL_ENABLED'])
env.Append(LINKFLAGS=['-framework', 'Metal', '-framework', 'QuartzCore', '-framework', 'IOSurface'])
if (env['use_static_mvk']):
env.Append(LINKFLAGS=['-framework', 'MoltenVK'])
elif not env["builtin_vulkan_loader"]:
env['builtin_vulkan'] = False
elif not env['builtin_vulkan']:
env.Append(LIBS=['vulkan'])
#env.Append(CPPDEFINES=['GLES_ENABLED', 'OPENGL_ENABLED'])
env.Append(CCFLAGS=['-mmacosx-version-min=10.11'])
env.Append(LINKFLAGS=['-mmacosx-version-min=10.11'])

View file

@ -224,9 +224,8 @@ def configure_msvc(env, manual_msvc_config):
'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt', 'Avrt',
'dwmapi']
env.Prepend(CPPPATH=['#thirdparty/vulkan/include/', "#thirdparty/vulkan/registry/"])
env.AppendUnique(CPPDEFINES = ['VULKAN_ENABLED'])
if not env["builtin_vulkan_loader"]:
env.AppendUnique(CPPDEFINES=['VULKAN_ENABLED'])
if not env['builtin_vulkan']:
LIBS += ['vulkan']
else:
LIBS += ['cfgmgr32']
@ -361,13 +360,12 @@ def configure_mingw(env):
env.Append(CPPDEFINES=[('WINVER', env['target_win_version']), ('_WIN32_WINNT', env['target_win_version'])])
env.Append(LIBS=['mingw32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt', 'avrt', 'uuid', 'dwmapi'])
env.Prepend(CPPPATH=['#thirdparty/vulkan/include/', "#thirdparty/vulkan/registry/"])
env.Append(CPPDEFINES=['VULKAN_ENABLED'])
if not env["builtin_vulkan_loader"]:
if not env['builtin_vulkan']:
env.Append(LIBS=['vulkan'])
else:
env.Append(LIBS=['cfgmgr32'])
## TODO !!! Reenable when OpenGLES Rendering Device is implemented !!!
#env.Append(CPPDEFINES=['OPENGL_ENABLED'])
env.Append(LIBS=['opengl32'])

View file

@ -320,10 +320,9 @@ def configure(env):
env.Prepend(CPPPATH=['#platform/x11'])
env.Append(CPPDEFINES=['X11_ENABLED', 'UNIX_ENABLED'])
env.Prepend(CPPPATH=['#thirdparty/vulkan/include/', "#thirdparty/vulkan/registry/"])
env.Append(CPPDEFINES=['VULKAN_ENABLED'])
if not env["builtin_vulkan_loader"]:
env.Append(LIBS=['vulkan'])
if not env['builtin_vulkan']:
env.ParseConfig('pkg-config vulkan --cflags --libs')
#env.Append(CPPDEFINES=['OPENGL_ENABLED'])
env.Append(LIBS=['GL'])

45
thirdparty/README.md vendored
View file

@ -1,5 +1,10 @@
# Third party libraries
Please keep categories (`##` level) listed alphabetically and matching their
respective folder names. Use two empty lines to separate categories for
readability.
Subcategories (`###` level) where needed are separated by a single empty line.
## assimp
@ -139,6 +144,7 @@ the GLES version Godot targets.
Important: File `glslang/glslang/Include/Common.h` has
Godot-made change marked with `// -- GODOT --` comments.
## jpeg-compressor
- Upstream: https://github.com/richgel999/jpeg-compressor
@ -259,25 +265,6 @@ changes to ensure they build for Javascript/HTML5. Those
changes are marked with `// -- GODOT --` comments.
## Vulkan Ecosystem Components (Vulkan ICD loader and headers)
- Upstream: https://github.com/KhronosGroup/Vulkan-Loader
- Version: 1.1.113
- License: Apache 2.0
## wslay
- Upstream: https://github.com/tatsuhiro-t/wslay
- Version: 1.1.0
- License: MIT
File extracted from upstream release tarball:
- All `*.c` and `*.h` in `lib/` and `lib/includes/`
- `wslay.h` has a small Godot addition to fix MSVC build.
See `thirdparty/wslay/msvcfix.diff`
## mbedtls
- Upstream: https://tls.mbed.org/
@ -528,6 +515,26 @@ They can be reapplied using the patches included in the `vhacd`
folder.
## vulkan
- Upstream: https://github.com/KhronosGroup/Vulkan-Loader
- Version: 1.1.113
- License: Apache 2.0
Unless there is a specific reason to package a more recent version, please stick
to Vulkan SDK releases (prefixed by `sdk-`) for all components.
NOTE: Use `scripts/update_deps.py --ref <version>` in the Loader git repository
to retrieve the `Vulkan-Headers` repository matching the loader version.
Files extracted from upstream source:
- `Vulkan-Headers/include/` as `include/`
- All `.c` and `.h` files in `loader/` and `loader/generated/`, put in a common
`loader/` folder
- `LICENSE.txt`
## wslay
- Upstream: https://github.com/tatsuhiro-t/wslay

0
thirdparty/vulkan/LICENSE.txt vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/asm_offset.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/cJSON.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/cJSON.h vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/debug_utils.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/debug_utils.h vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/dev_ext_trampoline.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/dirent_on_windows.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/dirent_on_windows.h vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/extension_manual.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/extension_manual.h vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/gpa_helper.h vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/loader.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/loader.h vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/murmurhash.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/murmurhash.h vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/phys_dev_ext.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/trampoline.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/unknown_ext_chain.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/unknown_ext_chain_gas.asm vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/unknown_ext_chain_masm.asm vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/vk_loader_layer.h vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/vk_loader_platform.h vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/wsi.c vendored Executable file → Normal file
View file

0
thirdparty/vulkan/loader/wsi.h vendored Executable file → Normal file
View file

View file

@ -1,425 +0,0 @@
#!/usr/bin/python3 -i
#
# Copyright (c) 2013-2019 The Khronos Group Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import re
import sys
from generator import (GeneratorOptions, OutputGenerator, noneStr,
regSortFeatures, write)
# CGeneratorOptions - subclass of GeneratorOptions.
#
# Adds options used by COutputGenerator objects during C language header
# generation.
#
# Additional members
# prefixText - list of strings to prefix generated header with
# (usually a copyright statement + calling convention macros).
# protectFile - True if multiple inclusion protection should be
# generated (based on the filename) around the entire header.
# protectFeature - True if #ifndef..#endif protection should be
# generated around a feature interface in the header file.
# genFuncPointers - True if function pointer typedefs should be
# generated
# protectProto - If conditional protection should be generated
# around prototype declarations, set to either '#ifdef'
# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
# to require opt-out (#ifndef protectProtoStr). Otherwise
# set to None.
# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
# declarations, if protectProto is set
# apicall - string to use for the function declaration prefix,
# such as APICALL on Windows.
# apientry - string to use for the calling convention macro,
# in typedefs, such as APIENTRY.
# apientryp - string to use for the calling convention macro
# in function pointer typedefs, such as APIENTRYP.
# directory - directory into which to generate include files
# indentFuncProto - True if prototype declarations should put each
# parameter on a separate line
# indentFuncPointer - True if typedefed function pointers should put each
# parameter on a separate line
# alignFuncParam - if nonzero and parameters are being put on a
# separate line, align parameter names at the specified column
# genEnumBeginEndRange - True if BEGIN_RANGE / END_RANGE macros should
# be generated for enumerated types
# genAliasMacro - True if the OpenXR alias macro should be generated
# for aliased types (unclear what other circumstances this is useful)
# aliasMacro - alias macro to inject when genAliasMacro is True
class CGeneratorOptions(GeneratorOptions):
"""Represents options during C interface generation for headers"""
def __init__(self,
conventions = None,
filename = None,
directory = '.',
apiname = None,
profile = None,
versions = '.*',
emitversions = '.*',
defaultExtensions = None,
addExtensions = None,
removeExtensions = None,
emitExtensions = None,
sortProcedure = regSortFeatures,
prefixText = "",
genFuncPointers = True,
protectFile = True,
protectFeature = True,
protectProto = None,
protectProtoStr = None,
apicall = '',
apientry = '',
apientryp = '',
indentFuncProto = True,
indentFuncPointer = False,
alignFuncParam = 0,
genEnumBeginEndRange = False,
genAliasMacro = False,
aliasMacro = ''
):
GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile,
versions, emitversions, defaultExtensions,
addExtensions, removeExtensions,
emitExtensions, sortProcedure)
self.prefixText = prefixText
self.genFuncPointers = genFuncPointers
self.protectFile = protectFile
self.protectFeature = protectFeature
self.protectProto = protectProto
self.protectProtoStr = protectProtoStr
self.apicall = apicall
self.apientry = apientry
self.apientryp = apientryp
self.indentFuncProto = indentFuncProto
self.indentFuncPointer = indentFuncPointer
self.alignFuncParam = alignFuncParam
self.genEnumBeginEndRange = genEnumBeginEndRange
self.genAliasMacro = genAliasMacro
self.aliasMacro = aliasMacro
# COutputGenerator - subclass of OutputGenerator.
# Generates C-language API interfaces.
#
# ---- methods ----
# COutputGenerator(errFile, warnFile, diagFile) - args as for
# OutputGenerator. Defines additional internal state.
# ---- methods overriding base class ----
# beginFile(genOpts)
# endFile()
# beginFeature(interface, emit)
# endFeature()
# genType(typeinfo,name)
# genStruct(typeinfo,name)
# genGroup(groupinfo,name)
# genEnum(enuminfo, name)
# genCmd(cmdinfo)
class COutputGenerator(OutputGenerator):
"""Generate specified API interfaces in a specific style, such as a C header"""
# This is an ordered list of sections in the header file.
TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
'group', 'bitmask', 'funcpointer', 'struct']
ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
def __init__(self,
errFile = sys.stderr,
warnFile = sys.stderr,
diagFile = sys.stdout):
OutputGenerator.__init__(self, errFile, warnFile, diagFile)
# Internal state - accumulators for different inner block text
self.sections = {section: [] for section in self.ALL_SECTIONS}
self.feature_not_empty = False
self.need_platform_include = False
self.may_alias = None
def beginFile(self, genOpts):
OutputGenerator.beginFile(self, genOpts)
# C-specific
#
# Multiple inclusion protection & C++ wrappers.
if genOpts.protectFile and self.genOpts.filename:
headerSym = re.sub(r'\.h', '_h_',
os.path.basename(self.genOpts.filename)).upper()
write('#ifndef', headerSym, file=self.outFile)
write('#define', headerSym, '1', file=self.outFile)
self.newline()
write('#ifdef __cplusplus', file=self.outFile)
write('extern "C" {', file=self.outFile)
write('#endif', file=self.outFile)
self.newline()
# User-supplied prefix text, if any (list of strings)
if genOpts.prefixText:
for s in genOpts.prefixText:
write(s, file=self.outFile)
def endFile(self):
# C-specific
# Finish C++ wrapper and multiple inclusion protection
self.newline()
write('#ifdef __cplusplus', file=self.outFile)
write('}', file=self.outFile)
write('#endif', file=self.outFile)
if self.genOpts.protectFile and self.genOpts.filename:
self.newline()
write('#endif', file=self.outFile)
# Finish processing in superclass
OutputGenerator.endFile(self)
def beginFeature(self, interface, emit):
# Start processing in superclass
OutputGenerator.beginFeature(self, interface, emit)
# C-specific
# Accumulate includes, defines, types, enums, function pointer typedefs,
# end function prototypes separately for this feature. They're only
# printed in endFeature().
self.sections = {section: [] for section in self.ALL_SECTIONS}
self.feature_not_empty = False
def endFeature(self):
# C-specific
# Actually write the interface to the output file.
if self.emit:
if self.feature_not_empty:
if self.genOpts.conventions.writeFeature(self.featureExtraProtect, self.genOpts.filename):
self.newline()
if self.genOpts.protectFeature:
write('#ifndef', self.featureName, file=self.outFile)
# If type declarations are needed by other features based on
# this one, it may be necessary to suppress the ExtraProtect,
# or move it below the 'for section...' loop.
if self.featureExtraProtect is not None:
write('#ifdef', self.featureExtraProtect, file=self.outFile)
self.newline()
write('#define', self.featureName, '1', file=self.outFile)
for section in self.TYPE_SECTIONS:
# OpenXR:
# If we need the explicit include of the external platform header,
# put it right before the function pointer definitions
if section == "funcpointer" and self.need_platform_include:
write('// Include for OpenXR Platform-Specific Types', file=self.outFile)
write('#include "openxr_platform.h"', file=self.outFile)
self.newline()
self.need_platform_include = False
contents = self.sections[section]
if contents:
write('\n'.join(contents), file=self.outFile)
if self.genOpts.genFuncPointers and self.sections['commandPointer']:
write('\n'.join(self.sections['commandPointer']), file=self.outFile)
self.newline()
if self.sections['command']:
if self.genOpts.protectProto:
write(self.genOpts.protectProto,
self.genOpts.protectProtoStr, file=self.outFile)
write('\n'.join(self.sections['command']), end='', file=self.outFile)
if self.genOpts.protectProto:
write('#endif', file=self.outFile)
else:
self.newline()
if self.featureExtraProtect is not None:
write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
if self.genOpts.protectFeature:
write('#endif /*', self.featureName, '*/', file=self.outFile)
# Finish processing in superclass
OutputGenerator.endFeature(self)
# Append a definition to the specified section
def appendSection(self, section, text):
# self.sections[section].append('SECTION: ' + section + '\n')
self.sections[section].append(text)
self.feature_not_empty = True
# Type generation
def genType(self, typeinfo, name, alias):
OutputGenerator.genType(self, typeinfo, name, alias)
typeElem = typeinfo.elem
# Vulkan:
# Determine the category of the type, and the type section to add
# its definition to.
# 'funcpointer' is added to the 'struct' section as a workaround for
# internal issue #877, since structures and function pointer types
# can have cross-dependencies.
category = typeElem.get('category')
if category == 'funcpointer':
section = 'struct'
else:
section = category
if category in ('struct', 'union'):
# If the type is a struct type, generate it using the
# special-purpose generator.
self.genStruct(typeinfo, name, alias)
else:
# OpenXR: this section was not under 'else:' previously, just fell through
if alias:
# If the type is an alias, just emit a typedef declaration
body = 'typedef ' + alias + ' ' + name + ';\n'
else:
# Replace <apientry /> tags with an APIENTRY-style string
# (from self.genOpts). Copy other text through unchanged.
# If the resulting text is an empty string, don't emit it.
body = noneStr(typeElem.text)
for elem in typeElem:
if elem.tag == 'apientry':
body += self.genOpts.apientry + noneStr(elem.tail)
else:
body += noneStr(elem.text) + noneStr(elem.tail)
if body:
# Add extra newline after multi-line entries.
if '\n' in body[0:-1]:
body += '\n'
self.appendSection(section, body)
# Protection string generation
# Protection strings are the strings defining the OS/Platform/Graphics
# requirements for a given OpenXR command. When generating the
# language header files, we need to make sure the items specific to a
# graphics API or OS platform are properly wrapped in #ifs.
def genProtectString(self, protect_str):
protect_if_str = ''
protect_end_str = ''
protect_list = []
if protect_str:
if ',' in protect_str:
protect_list.extend(protect_str.split(","))
protect_def_str = ''
count = 0
for protect_define in protect_list:
if count > 0:
protect_def_str += ' &&'
protect_def_str += ' defined(%s)' % protect_define
count = count + 1
count = count + 1
protect_if_str = '#if'
protect_if_str += protect_def_str
protect_if_str += '\n'
protect_end_str = '#endif //'
protect_end_str += protect_def_str
protect_end_str += '\n'
else:
protect_if_str += '#ifdef %s\n' % protect_str
protect_end_str += '#endif // %s\n' % protect_str
return (protect_if_str, protect_end_str)
def typeMayAlias(self, typeName):
if not self.may_alias:
# First time we've asked if a type may alias.
# So, let's populate the set of all names of types that may.
# Everyone with an explicit mayalias="true"
self.may_alias = set(typeName
for typeName, data in self.registry.typedict.items()
if data.elem.get('mayalias') == 'true')
# Every type mentioned in some other type's parentstruct attribute.
self.may_alias.update(set(x for x in
[otherType.elem.get('parentstruct')
for _, otherType in self.registry.typedict.items()]
if x is not None
))
return typeName in self.may_alias
# Struct (e.g. C "struct" type) generation.
# This is a special case of the <type> tag where the contents are
# interpreted as a set of <member> tags instead of freeform C
# C type declarations. The <member> tags are just like <param>
# tags - they are a declaration of a struct or union member.
# Only simple member declarations are supported (no nested
# structs etc.)
# If alias is not None, then this struct aliases another; just
# generate a typedef of that alias.
def genStruct(self, typeinfo, typeName, alias):
OutputGenerator.genStruct(self, typeinfo, typeName, alias)
typeElem = typeinfo.elem
if alias:
body = 'typedef ' + alias + ' ' + typeName + ';\n'
else:
body = ''
(protect_begin, protect_end) = self.genProtectString(typeElem.get('protect'))
if protect_begin:
body += protect_begin
body += 'typedef ' + typeElem.get('category')
# This is an OpenXR-specific alternative where aliasing refers
# to an inheritance hierarchy of types rather than C-level type
# aliases.
if self.genOpts.genAliasMacro and self.typeMayAlias(typeName):
body += ' ' + self.genOpts.aliasMacro
body += ' ' + typeName + ' {\n'
targetLen = 0
for member in typeElem.findall('.//member'):
targetLen = max(targetLen, self.getCParamTypeLength(member))
for member in typeElem.findall('.//member'):
body += self.makeCParamDecl(member, targetLen + 4)
body += ';\n'
body += '} ' + typeName + ';\n'
if protect_end:
body += protect_end
self.appendSection('struct', body)
# Group (e.g. C "enum" type) generation.
# These are concatenated together with other types.
# If alias is not None, it is the name of another group type
# which aliases this type; just generate that alias.
def genGroup(self, groupinfo, groupName, alias = None):
OutputGenerator.genGroup(self, groupinfo, groupName, alias)
groupElem = groupinfo.elem
# After either enumerated type or alias paths, add the declaration
# to the appropriate section for the group being defined.
if groupElem.get('type') == 'bitmask':
section = 'bitmask'
else:
section = 'group'
if alias:
# If the group name is aliased, just emit a typedef declaration
# for the alias.
body = 'typedef ' + alias + ' ' + groupName + ';\n'
self.appendSection(section, body)
else:
(section, body) = self.buildEnumCDecl(self.genOpts.genEnumBeginEndRange, groupinfo, groupName)
self.appendSection(section, "\n" + body)
# Enumerant generation
# <enum> tags may specify their values in several ways, but are usually
# just integers.
def genEnum(self, enuminfo, name, alias):
OutputGenerator.genEnum(self, enuminfo, name, alias)
(_, strVal) = self.enumToValue(enuminfo.elem, False)
body = '#define ' + name.ljust(33) + ' ' + strVal
self.appendSection('enum', body)
# Command generation
def genCmd(self, cmdinfo, name, alias):
OutputGenerator.genCmd(self, cmdinfo, name, alias)
# if alias:
# prefix = '// ' + name + ' is an alias of command ' + alias + '\n'
# else:
# prefix = ''
prefix = ''
decls = self.makeCDecls(cmdinfo.elem)
self.appendSection('command', prefix + decls[0] + '\n')
if self.genOpts.genFuncPointers:
self.appendSection('commandPointer', decls[1])

View file

@ -1,73 +0,0 @@
#!/usr/bin/python3 -i
#
# Copyright (c) 2015-2017, 2019 The Khronos Group Inc.
# Copyright (c) 2015-2017, 2019 Valve Corporation
# Copyright (c) 2015-2017, 2019 LunarG, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Mark Lobodzinski <mark@lunarg.com>
import os,re,sys,string
import xml.etree.ElementTree as etree
from generator import *
from collections import namedtuple
# Copyright text prefixing all headers (list of strings).
prefixStrings = [
'/*',
'** Copyright (c) 2015-2017, 2019 The Khronos Group Inc.',
'** Copyright (c) 2015-2017, 2019 Valve Corporation',
'** Copyright (c) 2015-2017, 2019 LunarG, Inc.',
'** Copyright (c) 2015-2017, 2019 Google Inc.',
'**',
'** Licensed under the Apache License, Version 2.0 (the "License");',
'** you may not use this file except in compliance with the License.',
'** You may obtain a copy of the License at',
'**',
'** http://www.apache.org/licenses/LICENSE-2.0',
'**',
'** Unless required by applicable law or agreed to in writing, software',
'** distributed under the License is distributed on an "AS IS" BASIS,',
'** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
'** See the License for the specific language governing permissions and',
'** limitations under the License.',
'*/',
''
]
platform_dict = {
'android' : 'VK_USE_PLATFORM_ANDROID_KHR',
'fuchsia' : 'VK_USE_PLATFORM_FUCHSIA',
'ggp': 'VK_USE_PLATFORM_GGP',
'ios' : 'VK_USE_PLATFORM_IOS_MVK',
'macos' : 'VK_USE_PLATFORM_MACOS_MVK',
'metal' : 'VK_USE_PLATFORM_METAL_EXT',
'vi' : 'VK_USE_PLATFORM_VI_NN',
'wayland' : 'VK_USE_PLATFORM_WAYLAND_KHR',
'win32' : 'VK_USE_PLATFORM_WIN32_KHR',
'xcb' : 'VK_USE_PLATFORM_XCB_KHR',
'xlib' : 'VK_USE_PLATFORM_XLIB_KHR',
'xlib_xrandr' : 'VK_USE_PLATFORM_XLIB_XRANDR_EXT',
}
#
# Return appropriate feature protect string from 'platform' tag on feature
def GetFeatureProtect(interface):
"""Get platform protection string"""
platform = interface.get('platform')
protect = None
if platform is not None:
protect = platform_dict[platform]
return protect

View file

@ -1,132 +0,0 @@
#!/usr/bin/python3 -i
#
# Copyright (c) 2013-2019 The Khronos Group Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Base class for working-group-specific style conventions,
# used in generation.
from abc import ABCMeta, abstractmethod
ABC = ABCMeta('ABC', (object,), {})
class ConventionsBase(ABC):
"""WG-specific conventions."""
@abstractmethod
def formatExtension(self, name):
"""Mark up a name as an extension for the spec."""
raise NotImplementedError
@property
@abstractmethod
def null(self):
"""Preferred spelling of NULL."""
raise NotImplementedError
def makeProseList(self, elements, connective='and'):
"""Make a (comma-separated) list for use in prose.
Adds a connective (by default, 'and')
before the last element if there are more than 1.
Override with a different method or different call to
_implMakeProseList if you want to add a comma for two elements,
or not use a serial comma.
"""
return self._implMakeProseList(elements, connective)
@property
def struct_macro(self):
"""Get the appropriate format macro for a structure.
May override.
"""
return 'sname:'
def makeStructName(self, name):
"""Prepend the appropriate format macro for a structure to a structure type name.
Uses struct_macro, so just override that if you want to change behavior.
"""
return self.struct_macro + name
@property
def external_macro(self):
"""Get the appropriate format macro for an external type like uint32_t.
May override.
"""
return 'basetype:'
def makeExternalTypeName(self, name):
"""Prepend the appropriate format macro for an external type like uint32_t to a type name.
Uses external_macro, so just override that if you want to change behavior.
"""
return self.external_macro + name
def _implMakeProseList(self, elements, connective, comma_for_two_elts=False, serial_comma=True):
"""Internal-use implementation to make a (comma-separated) list for use in prose.
Adds a connective (by default, 'and')
before the last element if there are more than 1,
and only includes commas if there are more than 2
(if comma_for_two_elts is False).
Don't edit these defaults, override self.makeProseList().
"""
assert(serial_comma) # didn't implement what we didn't need
my_elts = list(elements)
if len(my_elts) > 1:
my_elts[-1] = '{} {}'.format(connective, my_elts[-1])
if not comma_for_two_elts and len(my_elts) <= 2:
return ' '.join(my_elts)
return ', '.join(my_elts)
@property
@abstractmethod
def file_suffix(self):
"""Return suffix of generated Asciidoctor files"""
raise NotImplementedError
@abstractmethod
def api_name(self, spectype = None):
"""Return API name"""
raise NotImplementedError
@property
@abstractmethod
def api_prefix(self):
"""Return API token prefix"""
raise NotImplementedError
@property
@abstractmethod
def api_version_prefix(self):
"""Return API core version token prefix"""
raise NotImplementedError
@property
@abstractmethod
def KHR_prefix(self):
"""Return extension name prefix for KHR extensions"""
raise NotImplementedError
@property
@abstractmethod
def EXT_prefix(self):
"""Return extension name prefix for EXT extensions"""
raise NotImplementedError

View file

@ -1,240 +0,0 @@
#!/usr/bin/python3 -i
#
# Copyright (c) 2015-2017 The Khronos Group Inc.
# Copyright (c) 2015-2017 Valve Corporation
# Copyright (c) 2015-2017 LunarG, Inc.
# Copyright (c) 2015-2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Mark Lobodzinski <mark@lunarg.com>
import os,re,sys
import xml.etree.ElementTree as etree
from generator import *
from collections import namedtuple
from common_codegen import *
#
# DispatchTableHelperOutputGeneratorOptions - subclass of GeneratorOptions.
class DispatchTableHelperOutputGeneratorOptions(GeneratorOptions):
def __init__(self,
conventions = None,
filename = None,
directory = '.',
apiname = None,
profile = None,
versions = '.*',
emitversions = '.*',
defaultExtensions = None,
addExtensions = None,
removeExtensions = None,
emitExtensions = None,
sortProcedure = regSortFeatures,
prefixText = "",
genFuncPointers = True,
apicall = '',
apientry = '',
apientryp = '',
alignFuncParam = 0,
expandEnumerants = True):
GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile,
versions, emitversions, defaultExtensions,
addExtensions, removeExtensions, emitExtensions, sortProcedure)
self.prefixText = prefixText
self.genFuncPointers = genFuncPointers
self.prefixText = None
self.apicall = apicall
self.apientry = apientry
self.apientryp = apientryp
self.alignFuncParam = alignFuncParam
#
# DispatchTableHelperOutputGenerator - subclass of OutputGenerator.
# Generates dispatch table helper header files for LVL
class DispatchTableHelperOutputGenerator(OutputGenerator):
"""Generate dispatch table helper header based on XML element attributes"""
def __init__(self,
errFile = sys.stderr,
warnFile = sys.stderr,
diagFile = sys.stdout):
OutputGenerator.__init__(self, errFile, warnFile, diagFile)
# Internal state - accumulators for different inner block text
self.instance_dispatch_list = [] # List of entries for instance dispatch list
self.device_dispatch_list = [] # List of entries for device dispatch list
self.dev_ext_stub_list = [] # List of stub functions for device extension functions
self.device_extension_list = [] # List of device extension functions
self.extension_type = ''
#
# Called once at the beginning of each run
def beginFile(self, genOpts):
OutputGenerator.beginFile(self, genOpts)
write("#pragma once", file=self.outFile)
# User-supplied prefix text, if any (list of strings)
if (genOpts.prefixText):
for s in genOpts.prefixText:
write(s, file=self.outFile)
# File Comment
file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
file_comment += '// See dispatch_helper_generator.py for modifications\n'
write(file_comment, file=self.outFile)
# Copyright Notice
copyright = '/*\n'
copyright += ' * Copyright (c) 2015-2017 The Khronos Group Inc.\n'
copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
copyright += ' * Copyright (c) 2015-2017 LunarG, Inc.\n'
copyright += ' *\n'
copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
copyright += ' * you may not use this file except in compliance with the License.\n'
copyright += ' * You may obtain a copy of the License at\n'
copyright += ' *\n'
copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
copyright += ' *\n'
copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
copyright += ' * See the License for the specific language governing permissions and\n'
copyright += ' * limitations under the License.\n'
copyright += ' *\n'
copyright += ' * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>\n'
copyright += ' * Author: Jon Ashburn <jon@lunarg.com>\n'
copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n'
copyright += ' */\n'
preamble = ''
preamble += '#include <vulkan/vulkan.h>\n'
preamble += '#include <vulkan/vk_layer.h>\n'
preamble += '#include <string.h>\n'
preamble += '#include "vk_layer_dispatch_table.h"\n'
write(copyright, file=self.outFile)
write(preamble, file=self.outFile)
#
# Write generate and write dispatch tables to output file
def endFile(self):
device_table = ''
instance_table = ''
device_table += self.OutputDispatchTableHelper('device')
instance_table += self.OutputDispatchTableHelper('instance')
for stub in self.dev_ext_stub_list:
write(stub, file=self.outFile)
write("\n\n", file=self.outFile)
write(device_table, file=self.outFile);
write("\n", file=self.outFile)
write(instance_table, file=self.outFile);
# Finish processing in superclass
OutputGenerator.endFile(self)
#
# Processing at beginning of each feature or extension
def beginFeature(self, interface, emit):
OutputGenerator.beginFeature(self, interface, emit)
self.featureExtraProtect = GetFeatureProtect(interface)
self.extension_type = interface.get('type')
#
# Process commands, adding to appropriate dispatch tables
def genCmd(self, cmdinfo, name, alias):
OutputGenerator.genCmd(self, cmdinfo, name, alias)
avoid_entries = ['vkCreateInstance',
'vkCreateDevice']
# Get first param type
params = cmdinfo.elem.findall('param')
info = self.getTypeNameTuple(params[0])
if name not in avoid_entries:
self.AddCommandToDispatchList(name, info[0], self.featureExtraProtect, cmdinfo)
#
# Determine if this API should be ignored or added to the instance or device dispatch table
def AddCommandToDispatchList(self, name, handle_type, protect, cmdinfo):
handle = self.registry.tree.find("types/type/[name='" + handle_type + "'][@category='handle']")
if handle is None:
return
if handle_type != 'VkInstance' and handle_type != 'VkPhysicalDevice' and name != 'vkGetInstanceProcAddr':
self.device_dispatch_list.append((name, self.featureExtraProtect))
if "VK_VERSION" not in self.featureName and self.extension_type == 'device':
self.device_extension_list.append(name)
# Build up stub function
return_type = ''
decl = self.makeCDecls(cmdinfo.elem)[1]
if 'typedef VkResult' in decl:
return_type = 'return VK_SUCCESS;'
decl = decl.split('*PFN_vk')[1]
decl = decl.replace(')(', '(')
if return_type == '':
decl = 'static VKAPI_ATTR void VKAPI_CALL Stub' + decl
else:
decl = 'static VKAPI_ATTR VkResult VKAPI_CALL Stub' + decl
func_body = ' { ' + return_type + ' };'
decl = decl.replace (';', func_body)
if self.featureExtraProtect is not None:
self.dev_ext_stub_list.append('#ifdef %s' % self.featureExtraProtect)
self.dev_ext_stub_list.append(decl)
if self.featureExtraProtect is not None:
self.dev_ext_stub_list.append('#endif // %s' % self.featureExtraProtect)
else:
self.instance_dispatch_list.append((name, self.featureExtraProtect))
return
#
# Retrieve the type and name for a parameter
def getTypeNameTuple(self, param):
type = ''
name = ''
for elem in param:
if elem.tag == 'type':
type = noneStr(elem.text)
elif elem.tag == 'name':
name = noneStr(elem.text)
return (type, name)
#
# Create a dispatch table from the appropriate list and return it as a string
def OutputDispatchTableHelper(self, table_type):
entries = []
table = ''
if table_type == 'device':
entries = self.device_dispatch_list
table += 'static inline void layer_init_device_dispatch_table(VkDevice device, VkLayerDispatchTable *table, PFN_vkGetDeviceProcAddr gpa) {\n'
table += ' memset(table, 0, sizeof(*table));\n'
table += ' // Device function pointers\n'
else:
entries = self.instance_dispatch_list
table += 'static inline void layer_init_instance_dispatch_table(VkInstance instance, VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa) {\n'
table += ' memset(table, 0, sizeof(*table));\n'
table += ' // Instance function pointers\n'
for item in entries:
# Remove 'vk' from proto name
base_name = item[0][2:]
if item[1] is not None:
table += '#ifdef %s\n' % item[1]
# If we're looking for the proc we are passing in, just point the table to it. This fixes the issue where
# a layer overrides the function name for the loader.
if (table_type == 'device' and base_name == 'GetDeviceProcAddr'):
table += ' table->GetDeviceProcAddr = gpa;\n'
elif (table_type != 'device' and base_name == 'GetInstanceProcAddr'):
table += ' table->GetInstanceProcAddr = gpa;\n'
else:
table += ' table->%s = (PFN_%s) gpa(%s, "%s");\n' % (base_name, item[0], table_type, item[0])
if item[0] in self.device_extension_list:
stub_check = ' if (table->%s == nullptr) { table->%s = (PFN_%s)Stub%s; }\n' % (base_name, base_name, item[0], base_name)
table += stub_check
if item[1] is not None:
table += '#endif // %s\n' % item[1]
table += '}'
return table

View file

@ -1,9 +0,0 @@
#!/bin/bash
python loader_genvk.py -registry vk.xml -scripts . vk_layer_dispatch_table.h
python loader_genvk.py -registry vk.xml -scripts . vk_dispatch_table_helper.h
python loader_genvk.py -registry vk.xml -scripts . vk_object_types.h
python loader_genvk.py -registry vk.xml -scripts . vk_loader_extensions.h
python loader_genvk.py -registry vk.xml -scripts . vk_loader_extensions.c
mv ./*.c ../loader/
mv ./*.h ../loader/

View file

@ -1,738 +0,0 @@
#!/usr/bin/python3 -i
#
# Copyright (c) 2013-2019 The Khronos Group Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import unicode_literals
import io
import os
import re
import pdb
import sys
from pathlib import Path
def write( *args, **kwargs ):
file = kwargs.pop('file',sys.stdout)
end = kwargs.pop('end','\n')
file.write(' '.join(str(arg) for arg in args))
file.write(end)
# noneStr - returns string argument, or "" if argument is None.
# Used in converting etree Elements into text.
# s - string to convert
def noneStr(s):
if s:
return s
return ""
# enquote - returns string argument with surrounding quotes,
# for serialization into Python code.
def enquote(s):
if s:
return "'{}'".format(s)
return None
# Primary sort key for regSortFeatures.
# Sorts by category of the feature name string:
# Core API features (those defined with a <feature> tag)
# ARB/KHR/OES (Khronos extensions)
# other (EXT/vendor extensions)
# This will need changing for Vulkan!
def regSortCategoryKey(feature):
if feature.elem.tag == 'feature':
return 0
if (feature.category == 'ARB' or
feature.category == 'KHR' or
feature.category == 'OES'):
return 1
return 2
# Secondary sort key for regSortFeatures.
# Sorts by extension name.
def regSortNameKey(feature):
return feature.name
# Second sort key for regSortFeatures.
# Sorts by feature version. <extension> elements all have version number "0"
def regSortFeatureVersionKey(feature):
return float(feature.versionNumber)
# Tertiary sort key for regSortFeatures.
# Sorts by extension number. <feature> elements all have extension number 0.
def regSortExtensionNumberKey(feature):
return int(feature.number)
# regSortFeatures - default sort procedure for features.
# Sorts by primary key of feature category ('feature' or 'extension')
# then by version number (for features)
# then by extension number (for extensions)
def regSortFeatures(featureList):
featureList.sort(key = regSortExtensionNumberKey)
featureList.sort(key = regSortFeatureVersionKey)
featureList.sort(key = regSortCategoryKey)
# GeneratorOptions - base class for options used during header production
# These options are target language independent, and used by
# Registry.apiGen() and by base OutputGenerator objects.
#
# Members
# conventions - may be mandatory for some generators:
# an object that implements ConventionsBase
# filename - basename of file to generate, or None to write to stdout.
# directory - directory in which to generate filename
# apiname - string matching <api> 'apiname' attribute, e.g. 'gl'.
# profile - string specifying API profile , e.g. 'core', or None.
# versions - regex matching API versions to process interfaces for.
# Normally '.*' or '[0-9]\.[0-9]' to match all defined versions.
# emitversions - regex matching API versions to actually emit
# interfaces for (though all requested versions are considered
# when deciding which interfaces to generate). For GL 4.3 glext.h,
# this might be '1\.[2-5]|[2-4]\.[0-9]'.
# defaultExtensions - If not None, a string which must in its
# entirety match the pattern in the "supported" attribute of
# the <extension>. Defaults to None. Usually the same as apiname.
# addExtensions - regex matching names of additional extensions
# to include. Defaults to None.
# removeExtensions - regex matching names of extensions to
# remove (after defaultExtensions and addExtensions). Defaults
# to None.
# emitExtensions - regex matching names of extensions to actually emit
# interfaces for (though all requested versions are considered when
# deciding which interfaces to generate).
# sortProcedure - takes a list of FeatureInfo objects and sorts
# them in place to a preferred order in the generated output.
# Default is core API versions, ARB/KHR/OES extensions, all
# other extensions, alphabetically within each group.
# The regex patterns can be None or empty, in which case they match
# nothing.
class GeneratorOptions:
"""Represents options during header production from an API registry"""
def __init__(self,
conventions = None,
filename = None,
directory = '.',
apiname = None,
profile = None,
versions = '.*',
emitversions = '.*',
defaultExtensions = None,
addExtensions = None,
removeExtensions = None,
emitExtensions = None,
sortProcedure = regSortFeatures):
self.conventions = conventions
self.filename = filename
self.directory = directory
self.apiname = apiname
self.profile = profile
self.versions = self.emptyRegex(versions)
self.emitversions = self.emptyRegex(emitversions)
self.defaultExtensions = defaultExtensions
self.addExtensions = self.emptyRegex(addExtensions)
self.removeExtensions = self.emptyRegex(removeExtensions)
self.emitExtensions = self.emptyRegex(emitExtensions)
self.sortProcedure = sortProcedure
# Substitute a regular expression which matches no version
# or extension names for None or the empty string.
def emptyRegex(self, pat):
if pat is None or pat == '':
return '_nomatch_^'
return pat
# OutputGenerator - base class for generating API interfaces.
# Manages basic logic, logging, and output file control
# Derived classes actually generate formatted output.
#
# ---- methods ----
# OutputGenerator(errFile, warnFile, diagFile)
# errFile, warnFile, diagFile - file handles to write errors,
# warnings, diagnostics to. May be None to not write.
# logMsg(level, *args) - log messages of different categories
# level - 'error', 'warn', or 'diag'. 'error' will also
# raise a UserWarning exception
# *args - print()-style arguments
# setExtMap(map) - specify a dictionary map from extension names to
# numbers, used in creating values for extension enumerants.
# makeDir(directory) - create a directory, if not already done.
# Generally called from derived generators creating hierarchies.
# beginFile(genOpts) - start a new interface file
# genOpts - GeneratorOptions controlling what's generated and how
# endFile() - finish an interface file, closing it when done
# beginFeature(interface, emit) - write interface for a feature
# and tag generated features as having been done.
# interface - element for the <version> / <extension> to generate
# emit - actually write to the header only when True
# endFeature() - finish an interface.
# genType(typeinfo,name,alias) - generate interface for a type
# typeinfo - TypeInfo for a type
# genStruct(typeinfo,name,alias) - generate interface for a C "struct" type.
# typeinfo - TypeInfo for a type interpreted as a struct
# genGroup(groupinfo,name,alias) - generate interface for a group of enums (C "enum")
# groupinfo - GroupInfo for a group
# genEnum(enuminfo,name,alias) - generate interface for an enum (constant)
# enuminfo - EnumInfo for an enum
# name - enum name
# genCmd(cmdinfo,name,alias) - generate interface for a command
# cmdinfo - CmdInfo for a command
# isEnumRequired(enumElem) - return True if this <enum> element is required
# elem - <enum> element to test
# makeCDecls(cmd) - return C prototype and function pointer typedef for a
# <command> Element, as a list of two strings
# cmd - Element for the <command>
# newline() - print a newline to the output file (utility function)
#
class OutputGenerator:
"""Generate specified API interfaces in a specific style, such as a C header"""
# categoryToPath - map XML 'category' to include file directory name
categoryToPath = {
'bitmask' : 'flags',
'enum' : 'enums',
'funcpointer' : 'funcpointers',
'handle' : 'handles',
'define' : 'defines',
'basetype' : 'basetypes',
}
# Constructor
def __init__(self,
errFile = sys.stderr,
warnFile = sys.stderr,
diagFile = sys.stdout):
self.outFile = None
self.errFile = errFile
self.warnFile = warnFile
self.diagFile = diagFile
# Internal state
self.featureName = None
self.genOpts = None
self.registry = None
# Used for extension enum value generation
self.extBase = 1000000000
self.extBlockSize = 1000
self.madeDirs = {}
# logMsg - write a message of different categories to different
# destinations.
# level -
# 'diag' (diagnostic, voluminous)
# 'warn' (warning)
# 'error' (fatal error - raises exception after logging)
# *args - print()-style arguments to direct to corresponding log
def logMsg(self, level, *args):
"""Log a message at the given level. Can be ignored or log to a file"""
if level == 'error':
strfile = io.StringIO()
write('ERROR:', *args, file=strfile)
if self.errFile is not None:
write(strfile.getvalue(), file=self.errFile)
raise UserWarning(strfile.getvalue())
elif level == 'warn':
if self.warnFile is not None:
write('WARNING:', *args, file=self.warnFile)
elif level == 'diag':
if self.diagFile is not None:
write('DIAG:', *args, file=self.diagFile)
else:
raise UserWarning(
'*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
# enumToValue - parses and converts an <enum> tag into a value.
# Returns a list
# first element - integer representation of the value, or None
# if needsNum is False. The value must be a legal number
# if needsNum is True.
# second element - string representation of the value
# There are several possible representations of values.
# A 'value' attribute simply contains the value.
# A 'bitpos' attribute defines a value by specifying the bit
# position which is set in that value.
# A 'offset','extbase','extends' triplet specifies a value
# as an offset to a base value defined by the specified
# 'extbase' extension name, which is then cast to the
# typename specified by 'extends'. This requires probing
# the registry database, and imbeds knowledge of the
# API extension enum scheme in this function.
# A 'alias' attribute contains the name of another enum
# which this is an alias of. The other enum must be
# declared first when emitting this enum.
def enumToValue(self, elem, needsNum):
name = elem.get('name')
numVal = None
if 'value' in elem.keys():
value = elem.get('value')
# print('About to translate value =', value, 'type =', type(value))
if needsNum:
numVal = int(value, 0)
# If there's a non-integer, numeric 'type' attribute (e.g. 'u' or
# 'ull'), append it to the string value.
# t = enuminfo.elem.get('type')
# if t is not None and t != '' and t != 'i' and t != 's':
# value += enuminfo.type
self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
return [numVal, value]
if 'bitpos' in elem.keys():
value = elem.get('bitpos')
bitpos = int(value, 0)
numVal = 1 << bitpos
value = '0x%08x' % numVal
if( bitpos >= 32 ):
value = value + 'ULL'
self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
return [numVal, value]
if 'offset' in elem.keys():
# Obtain values in the mapping from the attributes
enumNegative = False
offset = int(elem.get('offset'),0)
extnumber = int(elem.get('extnumber'),0)
extends = elem.get('extends')
if 'dir' in elem.keys():
enumNegative = True
self.logMsg('diag', 'Enum', name, 'offset =', offset,
'extnumber =', extnumber, 'extends =', extends,
'enumNegative =', enumNegative)
# Now determine the actual enumerant value, as defined
# in the "Layers and Extensions" appendix of the spec.
numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
if enumNegative:
numVal *= -1
value = '%d' % numVal
# More logic needed!
self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
return [numVal, value]
if 'alias' in elem.keys():
return [None, elem.get('alias')]
return [None, None]
# checkDuplicateEnums - sanity check for enumerated values
# enums - list of <enum> Elements
# returns the list with duplicates stripped
def checkDuplicateEnums(self, enums):
# Dictionaries indexed by name and numeric value.
# Entries are [ Element, numVal, strVal ] matching name or value
nameMap = {}
valueMap = {}
stripped = []
for elem in enums:
name = elem.get('name')
(numVal, strVal) = self.enumToValue(elem, True)
if name in nameMap:
# Duplicate name found; check values
(name2, numVal2, strVal2) = nameMap[name]
# Duplicate enum values for the same name are benign. This
# happens when defining the same enum conditionally in
# several extension blocks.
if (strVal2 == strVal or (numVal is not None and
numVal == numVal2)):
True
# self.logMsg('info', 'checkDuplicateEnums: Duplicate enum (' + name +
# ') found with the same value:' + strVal)
else:
self.logMsg('warn', 'checkDuplicateEnums: Duplicate enum (' + name +
') found with different values:' + strVal +
' and ' + strVal2)
# Don't add the duplicate to the returned list
continue
elif numVal in valueMap:
# Duplicate value found (such as an alias); report it, but
# still add this enum to the list.
(name2, numVal2, strVal2) = valueMap[numVal]
try:
self.logMsg('warn', 'Two enums found with the same value: '
+ name + ' = ' + name2.get('name') + ' = ' + strVal)
except:
pdb.set_trace()
# Track this enum to detect followon duplicates
nameMap[name] = [ elem, numVal, strVal ]
if numVal is not None:
valueMap[numVal] = [ elem, numVal, strVal ]
# Add this enum to the list
stripped.append(elem)
# Return the list
return stripped
# buildEnumCDecl
# Generates the C declaration for an enum
def buildEnumCDecl(self, expand, groupinfo, groupName):
groupElem = groupinfo.elem
if self.genOpts.conventions.constFlagBits and groupElem.get('type') == 'bitmask':
return self.buildEnumCDecl_Bitmask( groupinfo, groupName)
else:
return self.buildEnumCDecl_Enum(expand, groupinfo, groupName)
# buildEnumCDecl_Bitmask
# Generates the C declaration for an "enum" that is actually a
# set of flag bits
def buildEnumCDecl_Bitmask(self, groupinfo, groupName):
groupElem = groupinfo.elem
flagTypeName = groupinfo.flagType.elem.get('name')
# Prefix
body = "// Flag bits for " + flagTypeName + "\n"
# Loop over the nested 'enum' tags.
for elem in groupElem.findall('enum'):
# Convert the value to an integer and use that to track min/max.
# Values of form -(number) are accepted but nothing more complex.
# Should catch exceptions here for more complex constructs. Not yet.
(_, strVal) = self.enumToValue(elem, True)
name = elem.get('name')
body += "static const " + flagTypeName + " " + name + " = " + strVal + ";\n"
# Postfix
return ("bitmask", body)
# Generates the C declaration for an enumerated type
def buildEnumCDecl_Enum(self, expand, groupinfo, groupName):
groupElem = groupinfo.elem
# Break the group name into prefix and suffix portions for range
# enum generation
expandName = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2',groupName).upper()
expandPrefix = expandName
expandSuffix = ''
expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
if expandSuffixMatch:
expandSuffix = '_' + expandSuffixMatch.group()
# Strip off the suffix from the prefix
expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
# Prefix
body = "typedef enum " + groupName + " {\n"
# @@ Should use the type="bitmask" attribute instead
isEnum = ('FLAG_BITS' not in expandPrefix)
# Get a list of nested 'enum' tags.
enums = groupElem.findall('enum')
# Check for and report duplicates, and return a list with them
# removed.
enums = self.checkDuplicateEnums(enums)
# Loop over the nested 'enum' tags. Keep track of the minimum and
# maximum numeric values, if they can be determined; but only for
# core API enumerants, not extension enumerants. This is inferred
# by looking for 'extends' attributes.
minName = None
# Accumulate non-numeric enumerant values separately and append
# them following the numeric values, to allow for aliases.
# NOTE: this doesn't do a topological sort yet, so aliases of
# aliases can still get in the wrong order.
aliasText = ""
for elem in enums:
# Convert the value to an integer and use that to track min/max.
# Values of form -(number) are accepted but nothing more complex.
# Should catch exceptions here for more complex constructs. Not yet.
(numVal,strVal) = self.enumToValue(elem, True)
name = elem.get('name')
# Extension enumerants are only included if they are required
if self.isEnumRequired(elem):
decl = " " + name + " = " + strVal + ",\n"
if numVal is not None:
body += decl
else:
aliasText += decl
# Don't track min/max for non-numbers (numVal is None)
if isEnum and numVal is not None and elem.get('extends') is None:
if minName is None:
minName = maxName = name
minValue = maxValue = numVal
elif numVal < minValue:
minName = name
minValue = numVal
elif numVal > maxValue:
maxName = name
maxValue = numVal
# Now append the non-numeric enumerant values
body += aliasText
# Generate min/max value tokens and a range-padding enum. Need some
# additional padding to generate correct names...
if isEnum and expand:
body += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
body += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
body += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
# Always generate this to make sure the enumerated type is 32 bits
body += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
# Postfix
body += "} " + groupName + ";"
# Determine appropriate section for this declaration
if groupElem.get('type') == 'bitmask':
section = 'bitmask'
else:
section = 'group'
return (section, body)
def makeDir(self, path):
self.logMsg('diag', 'OutputGenerator::makeDir(' + path + ')')
if path not in self.madeDirs:
# This can get race conditions with multiple writers, see
# https://stackoverflow.com/questions/273192/
if not os.path.exists(path):
os.makedirs(path)
self.madeDirs[path] = None
def beginFile(self, genOpts):
self.genOpts = genOpts
# Open specified output file. Not done in constructor since a
# Generator can be used without writing to a file.
if self.genOpts.filename is not None:
if sys.platform == 'win32':
directory = Path(self.genOpts.directory)
if not Path.exists(directory):
os.makedirs(directory)
self.outFile = (directory / self.genOpts.filename).open('w', encoding='utf-8')
else:
filename = self.genOpts.directory + '/' + self.genOpts.filename
self.outFile = io.open(filename, 'w', encoding='utf-8')
else:
self.outFile = sys.stdout
def endFile(self):
if self.errFile:
self.errFile.flush()
if self.warnFile:
self.warnFile.flush()
if self.diagFile:
self.diagFile.flush()
self.outFile.flush()
if self.outFile != sys.stdout and self.outFile != sys.stderr:
self.outFile.close()
self.genOpts = None
def beginFeature(self, interface, emit):
self.emit = emit
self.featureName = interface.get('name')
# If there's an additional 'protect' attribute in the feature, save it
self.featureExtraProtect = interface.get('protect')
def endFeature(self):
# Derived classes responsible for emitting feature
self.featureName = None
self.featureExtraProtect = None
# Utility method to validate we're generating something only inside a
# <feature> tag
def validateFeature(self, featureType, featureName):
if self.featureName is None:
raise UserWarning('Attempt to generate', featureType,
featureName, 'when not in feature')
# Type generation
def genType(self, typeinfo, name, alias):
self.validateFeature('type', name)
# Struct (e.g. C "struct" type) generation
def genStruct(self, typeinfo, typeName, alias):
self.validateFeature('struct', typeName)
# The mixed-mode <member> tags may contain no-op <comment> tags.
# It is convenient to remove them here where all output generators
# will benefit.
for member in typeinfo.elem.findall('.//member'):
for comment in member.findall('comment'):
member.remove(comment)
# Group (e.g. C "enum" type) generation
def genGroup(self, groupinfo, groupName, alias):
self.validateFeature('group', groupName)
# Enumerant (really, constant) generation
def genEnum(self, enuminfo, typeName, alias):
self.validateFeature('enum', typeName)
# Command generation
def genCmd(self, cmd, cmdinfo, alias):
self.validateFeature('command', cmdinfo)
# Utility functions - turn a <proto> <name> into C-language prototype
# and typedef declarations for that name.
# name - contents of <name> tag
# tail - whatever text follows that tag in the Element
def makeProtoName(self, name, tail):
return self.genOpts.apientry + name + tail
def makeTypedefName(self, name, tail):
return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')'
# makeCParamDecl - return a string which is an indented, formatted
# declaration for a <param> or <member> block (e.g. function parameter
# or structure/union member).
# param - Element (<param> or <member>) to format
# aligncol - if non-zero, attempt to align the nested <name> element
# at this column
def makeCParamDecl(self, param, aligncol):
paramdecl = ' ' + noneStr(param.text)
for elem in param:
text = noneStr(elem.text)
tail = noneStr(elem.tail)
if self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail):
# OpenXR-specific macro insertion
tail = self.genOpts.conventions.make_voidpointer_alias(tail)
if elem.tag == 'name' and aligncol > 0:
self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam)
# Align at specified column, if possible
paramdecl = paramdecl.rstrip()
oldLen = len(paramdecl)
# This works around a problem where very long type names -
# longer than the alignment column - would run into the tail
# text.
paramdecl = paramdecl.ljust(aligncol-1) + ' '
newLen = len(paramdecl)
self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl)
paramdecl += text + tail
return paramdecl
# getCParamTypeLength - return the length of the type field is an indented, formatted
# declaration for a <param> or <member> block (e.g. function parameter
# or structure/union member).
# param - Element (<param> or <member>) to identify
def getCParamTypeLength(self, param):
paramdecl = ' ' + noneStr(param.text)
for elem in param:
text = noneStr(elem.text)
tail = noneStr(elem.tail)
if self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail):
# OpenXR-specific macro insertion
tail = self.genOpts.conventions.make_voidpointer_alias(tail)
if elem.tag == 'name':
# Align at specified column, if possible
newLen = len(paramdecl.rstrip())
self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen)
paramdecl += text + tail
return newLen
# isEnumRequired(elem) - return True if this <enum> element is
# required, False otherwise
# elem - <enum> element to test
def isEnumRequired(self, elem):
required = elem.get('required') is not None
self.logMsg('diag', 'isEnumRequired:', elem.get('name'),
'->', required)
return required
#@@@ This code is overridden by equivalent code now run in
#@@@ Registry.generateFeature
required = False
extname = elem.get('extname')
if extname is not None:
# 'supported' attribute was injected when the <enum> element was
# moved into the <enums> group in Registry.parseTree()
if self.genOpts.defaultExtensions == elem.get('supported'):
required = True
elif re.match(self.genOpts.addExtensions, extname) is not None:
required = True
elif elem.get('version') is not None:
required = re.match(self.genOpts.emitversions, elem.get('version')) is not None
else:
required = True
return required
# makeCDecls - return C prototype and function pointer typedef for a
# command, as a two-element list of strings.
# cmd - Element containing a <command> tag
def makeCDecls(self, cmd):
"""Generate C function pointer typedef for <command> Element"""
proto = cmd.find('proto')
params = cmd.findall('param')
# Begin accumulating prototype and typedef strings
pdecl = self.genOpts.apicall
tdecl = 'typedef '
# Insert the function return type/name.
# For prototypes, add APIENTRY macro before the name
# For typedefs, add (APIENTRY *<name>) around the name and
# use the PFN_cmdnameproc naming convention.
# Done by walking the tree for <proto> element by element.
# etree has elem.text followed by (elem[i], elem[i].tail)
# for each child element and any following text
# Leading text
pdecl += noneStr(proto.text)
tdecl += noneStr(proto.text)
# For each child element, if it's a <name> wrap in appropriate
# declaration. Otherwise append its contents and tail contents.
for elem in proto:
text = noneStr(elem.text)
tail = noneStr(elem.tail)
if elem.tag == 'name':
pdecl += self.makeProtoName(text, tail)
tdecl += self.makeTypedefName(text, tail)
else:
pdecl += text + tail
tdecl += text + tail
# Now add the parameter declaration list, which is identical
# for prototypes and typedefs. Concatenate all the text from
# a <param> node without the tags. No tree walking required
# since all tags are ignored.
# Uses: self.indentFuncProto
# self.indentFuncPointer
# self.alignFuncParam
n = len(params)
# Indented parameters
if n > 0:
indentdecl = '(\n'
indentdecl += ',\n'.join(self.makeCParamDecl(p, self.genOpts.alignFuncParam)
for p in params)
indentdecl += ');'
else:
indentdecl = '(void);'
# Non-indented parameters
paramdecl = '('
if n > 0:
paramnames = (''.join(t for t in p.itertext())
for p in params)
paramdecl += ', '.join(paramnames)
else:
paramdecl += 'void'
paramdecl += ");"
return [ pdecl + indentdecl, tdecl + paramdecl ]
def newline(self):
write('', file=self.outFile)
def setRegistry(self, registry):
self.registry = registry

View file

@ -1,541 +0,0 @@
#!/usr/bin/python3
#
# Copyright (c) 2013-2019 The Khronos Group Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import pdb
import re
import sys
import time
import xml.etree.ElementTree as etree
from cgenerator import CGeneratorOptions, COutputGenerator
from docgenerator import DocGeneratorOptions, DocOutputGenerator
from extensionmetadocgenerator import (ExtensionMetaDocGeneratorOptions,
ExtensionMetaDocOutputGenerator)
from generator import write
from hostsyncgenerator import HostSynchronizationOutputGenerator
from pygenerator import PyOutputGenerator
from reg import Registry
from validitygenerator import ValidityOutputGenerator
from vkconventions import VulkanConventions
# Simple timer functions
startTime = None
def startTimer(timeit):
global startTime
if timeit:
startTime = time.process_time()
def endTimer(timeit, msg):
global startTime
if timeit:
endTime = time.process_time()
write(msg, endTime - startTime, file=sys.stderr)
startTime = None
# Turn a list of strings into a regexp string matching exactly those strings
def makeREstring(list, default = None):
if len(list) > 0 or default is None:
return '^(' + '|'.join(list) + ')$'
else:
return default
# Returns a directory of [ generator function, generator options ] indexed
# by specified short names. The generator options incorporate the following
# parameters:
#
# args is an parsed argument object; see below for the fields that are used.
def makeGenOpts(args):
global genOpts
genOpts = {}
# Default class of extensions to include, or None
defaultExtensions = args.defaultExtensions
# Additional extensions to include (list of extensions)
extensions = args.extension
# Extensions to remove (list of extensions)
removeExtensions = args.removeExtensions
# Extensions to emit (list of extensions)
emitExtensions = args.emitExtensions
# Features to include (list of features)
features = args.feature
# Whether to disable inclusion protect in headers
protect = args.protect
# Output target directory
directory = args.directory
# Descriptive names for various regexp patterns used to select
# versions and extensions
allFeatures = allExtensions = r'.*'
# Turn lists of names/patterns into matching regular expressions
addExtensionsPat = makeREstring(extensions, None)
removeExtensionsPat = makeREstring(removeExtensions, None)
emitExtensionsPat = makeREstring(emitExtensions, allExtensions)
featuresPat = makeREstring(features, allFeatures)
# Copyright text prefixing all headers (list of strings).
prefixStrings = [
'/*',
'** Copyright (c) 2015-2019 The Khronos Group Inc.',
'**',
'** Licensed under the Apache License, Version 2.0 (the "License");',
'** you may not use this file except in compliance with the License.',
'** You may obtain a copy of the License at',
'**',
'** http://www.apache.org/licenses/LICENSE-2.0',
'**',
'** Unless required by applicable law or agreed to in writing, software',
'** distributed under the License is distributed on an "AS IS" BASIS,',
'** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
'** See the License for the specific language governing permissions and',
'** limitations under the License.',
'*/',
''
]
# Text specific to Vulkan headers
vkPrefixStrings = [
'/*',
'** This header is generated from the Khronos Vulkan XML API Registry.',
'**',
'*/',
''
]
# Defaults for generating re-inclusion protection wrappers (or not)
protectFile = protect
# An API style conventions object
conventions = VulkanConventions()
# API include files for spec and ref pages
# Overwrites include subdirectories in spec source tree
# The generated include files do not include the calling convention
# macros (apientry etc.), unlike the header files.
# Because the 1.0 core branch includes ref pages for extensions,
# all the extension interfaces need to be generated, even though
# none are used by the core spec itself.
genOpts['apiinc'] = [
DocOutputGenerator,
DocGeneratorOptions(
conventions = conventions,
filename = 'timeMarker',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = None,
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = '',
apientry = '',
apientryp = '*',
alignFuncParam = 48,
expandEnumerants = False)
]
# API names to validate man/api spec includes & links
genOpts['vkapi.py'] = [
PyOutputGenerator,
DocGeneratorOptions(
conventions = conventions,
filename = 'vkapi.py',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = None,
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat)
]
# API validity files for spec
genOpts['validinc'] = [
ValidityOutputGenerator,
DocGeneratorOptions(
conventions = conventions,
filename = 'timeMarker',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = None,
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat)
]
# API host sync table files for spec
genOpts['hostsyncinc'] = [
HostSynchronizationOutputGenerator,
DocGeneratorOptions(
conventions = conventions,
filename = 'timeMarker',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = None,
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat)
]
# Extension metainformation for spec extension appendices
genOpts['extinc'] = [
ExtensionMetaDocOutputGenerator,
ExtensionMetaDocGeneratorOptions(
conventions = conventions,
filename = 'timeMarker',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = None,
defaultExtensions = defaultExtensions,
addExtensions = None,
removeExtensions = None,
emitExtensions = emitExtensionsPat)
]
# Platform extensions, in their own header files
# Each element of the platforms[] array defines information for
# generating a single platform:
# [0] is the generated header file name
# [1] is the set of platform extensions to generate
# [2] is additional extensions whose interfaces should be considered,
# but suppressed in the output, to avoid duplicate definitions of
# dependent types like VkDisplayKHR and VkSurfaceKHR which come from
# non-platform extensions.
# Track all platform extensions, for exclusion from vulkan_core.h
allPlatformExtensions = []
# Extensions suppressed for all platforms.
# Covers common WSI extension types.
commonSuppressExtensions = [ 'VK_KHR_display', 'VK_KHR_swapchain' ]
platforms = [
[ 'vulkan_android.h', [ 'VK_KHR_android_surface',
'VK_ANDROID_external_memory_android_hardware_buffer'
], commonSuppressExtensions ],
[ 'vulkan_fuchsia.h', [ 'VK_FUCHSIA_imagepipe_surface'], commonSuppressExtensions ],
[ 'vulkan_ggp.h', [ 'VK_GGP_stream_descriptor_surface',
'VK_GGP_frame_token' ], commonSuppressExtensions ],
[ 'vulkan_ios.h', [ 'VK_MVK_ios_surface' ], commonSuppressExtensions ],
[ 'vulkan_macos.h', [ 'VK_MVK_macos_surface' ], commonSuppressExtensions ],
[ 'vulkan_vi.h', [ 'VK_NN_vi_surface' ], commonSuppressExtensions ],
[ 'vulkan_wayland.h', [ 'VK_KHR_wayland_surface' ], commonSuppressExtensions ],
[ 'vulkan_win32.h', [ 'VK_.*_win32(|_.*)', 'VK_EXT_full_screen_exclusive' ],
commonSuppressExtensions +
[ 'VK_KHR_external_semaphore',
'VK_KHR_external_memory_capabilities',
'VK_KHR_external_fence',
'VK_KHR_external_fence_capabilities',
'VK_KHR_get_surface_capabilities2',
'VK_NV_external_memory_capabilities',
] ],
[ 'vulkan_xcb.h', [ 'VK_KHR_xcb_surface' ], commonSuppressExtensions ],
[ 'vulkan_xlib.h', [ 'VK_KHR_xlib_surface' ], commonSuppressExtensions ],
[ 'vulkan_xlib_xrandr.h', [ 'VK_EXT_acquire_xlib_display' ], commonSuppressExtensions ],
[ 'vulkan_metal.h', [ 'VK_EXT_metal_surface' ], commonSuppressExtensions ],
]
for platform in platforms:
headername = platform[0]
allPlatformExtensions += platform[1]
addPlatformExtensionsRE = makeREstring(platform[1] + platform[2])
emitPlatformExtensionsRE = makeREstring(platform[1])
opts = CGeneratorOptions(
conventions = conventions,
filename = headername,
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = None,
defaultExtensions = None,
addExtensions = addPlatformExtensionsRE,
removeExtensions = None,
emitExtensions = emitPlatformExtensionsRE,
prefixText = prefixStrings + vkPrefixStrings,
genFuncPointers = True,
protectFile = protectFile,
protectFeature = False,
protectProto = '#ifndef',
protectProtoStr = 'VK_NO_PROTOTYPES',
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
genEnumBeginEndRange = True)
genOpts[headername] = [ COutputGenerator, opts ]
# Header for core API + extensions.
# To generate just the core API,
# change to 'defaultExtensions = None' below.
#
# By default this adds all enabled, non-platform extensions.
# It removes all platform extensions (from the platform headers options
# constructed above) as well as any explicitly specified removals.
removeExtensionsPat = makeREstring(allPlatformExtensions + removeExtensions, None)
genOpts['vulkan_core.h'] = [
COutputGenerator,
CGeneratorOptions(
conventions = conventions,
filename = 'vulkan_core.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = defaultExtensions,
addExtensions = None,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
genFuncPointers = True,
protectFile = protectFile,
protectFeature = False,
protectProto = '#ifndef',
protectProtoStr = 'VK_NO_PROTOTYPES',
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
genEnumBeginEndRange = True)
]
# Unused - vulkan10.h target.
# It is possible to generate a header with just the Vulkan 1.0 +
# extension interfaces defined, but since the promoted KHR extensions
# are now defined in terms of the 1.1 interfaces, such a header is very
# similar to vulkan_core.h.
genOpts['vulkan10.h'] = [
COutputGenerator,
CGeneratorOptions(
conventions = conventions,
filename = 'vulkan10.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = 'VK_VERSION_1_0',
emitversions = 'VK_VERSION_1_0',
defaultExtensions = defaultExtensions,
addExtensions = None,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
genFuncPointers = True,
protectFile = protectFile,
protectFeature = False,
protectProto = '#ifndef',
protectProtoStr = 'VK_NO_PROTOTYPES',
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48)
]
genOpts['alias.h'] = [
COutputGenerator,
CGeneratorOptions(
conventions = conventions,
filename = 'alias.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = defaultExtensions,
addExtensions = None,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = None,
genFuncPointers = False,
protectFile = False,
protectFeature = False,
protectProto = '',
protectProtoStr = '',
apicall = '',
apientry = '',
apientryp = '',
alignFuncParam = 36)
]
# Generate a target based on the options in the matching genOpts{} object.
# This is encapsulated in a function so it can be profiled and/or timed.
# The args parameter is an parsed argument object containing the following
# fields that are used:
# target - target to generate
# directory - directory to generate it in
# protect - True if re-inclusion wrappers should be created
# extensions - list of additional extensions to include in generated
# interfaces
def genTarget(args):
# Create generator options with specified parameters
makeGenOpts(args)
if args.target in genOpts:
createGenerator = genOpts[args.target][0]
options = genOpts[args.target][1]
if not args.quiet:
write('* Building', options.filename, file=sys.stderr)
write('* options.versions =', options.versions, file=sys.stderr)
write('* options.emitversions =', options.emitversions, file=sys.stderr)
write('* options.defaultExtensions =', options.defaultExtensions, file=sys.stderr)
write('* options.addExtensions =', options.addExtensions, file=sys.stderr)
write('* options.removeExtensions =', options.removeExtensions, file=sys.stderr)
write('* options.emitExtensions =', options.emitExtensions, file=sys.stderr)
startTimer(args.time)
gen = createGenerator(errFile=errWarn,
warnFile=errWarn,
diagFile=diag)
reg.setGenerator(gen)
reg.apiGen(options)
if not args.quiet:
write('* Generated', options.filename, file=sys.stderr)
endTimer(args.time, '* Time to generate ' + options.filename + ' =')
else:
write('No generator options for unknown target:',
args.target, file=sys.stderr)
# -feature name
# -extension name
# For both, "name" may be a single name, or a space-separated list
# of names, or a regular expression.
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-defaultExtensions', action='store',
default='vulkan',
help='Specify a single class of extensions to add to targets')
parser.add_argument('-extension', action='append',
default=[],
help='Specify an extension or extensions to add to targets')
parser.add_argument('-removeExtensions', action='append',
default=[],
help='Specify an extension or extensions to remove from targets')
parser.add_argument('-emitExtensions', action='append',
default=[],
help='Specify an extension or extensions to emit in targets')
parser.add_argument('-feature', action='append',
default=[],
help='Specify a core API feature name or names to add to targets')
parser.add_argument('-debug', action='store_true',
help='Enable debugging')
parser.add_argument('-dump', action='store_true',
help='Enable dump to stderr')
parser.add_argument('-diagfile', action='store',
default=None,
help='Write diagnostics to specified file')
parser.add_argument('-errfile', action='store',
default=None,
help='Write errors and warnings to specified file instead of stderr')
parser.add_argument('-noprotect', dest='protect', action='store_false',
help='Disable inclusion protection in output headers')
parser.add_argument('-profile', action='store_true',
help='Enable profiling')
parser.add_argument('-registry', action='store',
default='vk.xml',
help='Use specified registry file instead of vk.xml')
parser.add_argument('-time', action='store_true',
help='Enable timing')
parser.add_argument('-validate', action='store_true',
help='Enable group validation')
parser.add_argument('-o', action='store', dest='directory',
default='.',
help='Create target and related files in specified directory')
parser.add_argument('target', metavar='target', nargs='?',
help='Specify target')
parser.add_argument('-quiet', action='store_true', default=True,
help='Suppress script output during normal execution.')
parser.add_argument('-verbose', action='store_false', dest='quiet', default=True,
help='Enable script output during normal execution.')
args = parser.parse_args()
# This splits arguments which are space-separated lists
args.feature = [name for arg in args.feature for name in arg.split()]
args.extension = [name for arg in args.extension for name in arg.split()]
# Load & parse registry
reg = Registry()
startTimer(args.time)
tree = etree.parse(args.registry)
endTimer(args.time, '* Time to make ElementTree =')
if args.debug:
pdb.run('reg.loadElementTree(tree)')
else:
startTimer(args.time)
reg.loadElementTree(tree)
endTimer(args.time, '* Time to parse ElementTree =')
if args.validate:
reg.validateGroups()
if args.dump:
write('* Dumping registry to regdump.txt', file=sys.stderr)
reg.dumpReg(filehandle = open('regdump.txt', 'w', encoding='utf-8'))
# create error/warning & diagnostic files
if args.errfile:
errWarn = open(args.errfile, 'w', encoding='utf-8')
else:
errWarn = sys.stderr
if args.diagfile:
diag = open(args.diagfile, 'w', encoding='utf-8')
else:
diag = None
if args.debug:
pdb.run('genTarget(args)')
elif args.profile:
import cProfile, pstats
cProfile.run('genTarget(args)', 'profile.txt')
p = pstats.Stats('profile.txt')
p.strip_dirs().sort_stats('time').print_stats(50)
else:
genTarget(args)

View file

@ -1,513 +0,0 @@
#!/usr/bin/python3 -i
#
# Copyright (c) 2015-2017 The Khronos Group Inc.
# Copyright (c) 2015-2017 Valve Corporation
# Copyright (c) 2015-2017 LunarG, Inc.
# Copyright (c) 2015-2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Mark Lobodzinski <mark@lunarg.com>
# Author: Tobin Ehlis <tobine@google.com>
# Author: John Zulauf <jzulauf@lunarg.com>
import os,re,sys
import xml.etree.ElementTree as etree
from generator import *
from collections import namedtuple
from common_codegen import *
#
# HelperFileOutputGeneratorOptions - subclass of GeneratorOptions.
class HelperFileOutputGeneratorOptions(GeneratorOptions):
def __init__(self,
conventions = None,
filename = None,
directory = '.',
apiname = None,
profile = None,
versions = '.*',
emitversions = '.*',
defaultExtensions = None,
addExtensions = None,
removeExtensions = None,
emitExtensions = None,
sortProcedure = regSortFeatures,
prefixText = "",
genFuncPointers = True,
protectFile = True,
protectFeature = True,
apicall = '',
apientry = '',
apientryp = '',
alignFuncParam = 0,
library_name = '',
expandEnumerants = True,
helper_file_type = ''):
GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile,
versions, emitversions, defaultExtensions,
addExtensions, removeExtensions, emitExtensions, sortProcedure)
self.prefixText = prefixText
self.genFuncPointers = genFuncPointers
self.protectFile = protectFile
self.protectFeature = protectFeature
self.apicall = apicall
self.apientry = apientry
self.apientryp = apientryp
self.alignFuncParam = alignFuncParam
self.library_name = library_name
self.helper_file_type = helper_file_type
#
# HelperFileOutputGenerator - subclass of OutputGenerator. Outputs Vulkan helper files
class HelperFileOutputGenerator(OutputGenerator):
"""Generate helper file based on XML element attributes"""
def __init__(self,
errFile = sys.stderr,
warnFile = sys.stderr,
diagFile = sys.stdout):
OutputGenerator.__init__(self, errFile, warnFile, diagFile)
# Internal state - accumulators for different inner block text
self.enum_output = '' # string built up of enum string routines
# Internal state - accumulators for different inner block text
self.structNames = [] # List of Vulkan struct typenames
self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
self.structMembers = [] # List of StructMemberData records for all Vulkan structs
self.object_types = [] # List of all handle types
self.object_type_aliases = [] # Aliases to handles types (for handles that were extensions)
self.debug_report_object_types = [] # Handy copy of debug_report_object_type enum data
self.core_object_types = [] # Handy copy of core_object_type enum data
self.device_extension_info = dict() # Dict of device extension name defines and ifdef values
self.instance_extension_info = dict() # Dict of instance extension name defines and ifdef values
# Named tuples to store struct and command data
self.StructType = namedtuple('StructType', ['name', 'value'])
self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isconst', 'iscount', 'len', 'extstructs', 'cdecl'])
self.StructMemberData = namedtuple('StructMemberData', ['name', 'members', 'ifdef_protect'])
self.custom_construct_params = {
# safe_VkGraphicsPipelineCreateInfo needs to know if subpass has color and\or depth\stencil attachments to use its pointers
'VkGraphicsPipelineCreateInfo' :
', const bool uses_color_attachment, const bool uses_depthstencil_attachment',
# safe_VkPipelineViewportStateCreateInfo needs to know if viewport and scissor is dynamic to use its pointers
'VkPipelineViewportStateCreateInfo' :
', const bool is_dynamic_viewports, const bool is_dynamic_scissors',
}
#
# Called once at the beginning of each run
def beginFile(self, genOpts):
OutputGenerator.beginFile(self, genOpts)
# User-supplied prefix text, if any (list of strings)
self.helper_file_type = genOpts.helper_file_type
self.library_name = genOpts.library_name
# File Comment
file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
file_comment += '// See helper_file_generator.py for modifications\n'
write(file_comment, file=self.outFile)
# Copyright Notice
copyright = ''
copyright += '\n'
copyright += '/***************************************************************************\n'
copyright += ' *\n'
copyright += ' * Copyright (c) 2015-2017 The Khronos Group Inc.\n'
copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
copyright += ' * Copyright (c) 2015-2017 LunarG, Inc.\n'
copyright += ' * Copyright (c) 2015-2017 Google Inc.\n'
copyright += ' *\n'
copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
copyright += ' * you may not use this file except in compliance with the License.\n'
copyright += ' * You may obtain a copy of the License at\n'
copyright += ' *\n'
copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
copyright += ' *\n'
copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
copyright += ' * See the License for the specific language governing permissions and\n'
copyright += ' * limitations under the License.\n'
copyright += ' *\n'
copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n'
copyright += ' * Author: Courtney Goeltzenleuchter <courtneygo@google.com>\n'
copyright += ' * Author: Tobin Ehlis <tobine@google.com>\n'
copyright += ' * Author: Chris Forbes <chrisforbes@google.com>\n'
copyright += ' * Author: John Zulauf<jzulauf@lunarg.com>\n'
copyright += ' *\n'
copyright += ' ****************************************************************************/\n'
write(copyright, file=self.outFile)
#
# Write generated file content to output file
def endFile(self):
dest_file = ''
dest_file += self.OutputDestFile()
# Remove blank lines at EOF
if dest_file.endswith('\n'):
dest_file = dest_file[:-1]
write(dest_file, file=self.outFile);
# Finish processing in superclass
OutputGenerator.endFile(self)
#
# Override parent class to be notified of the beginning of an extension
def beginFeature(self, interface, emit):
# Start processing in superclass
OutputGenerator.beginFeature(self, interface, emit)
self.featureExtraProtect = GetFeatureProtect(interface)
if self.featureName == 'VK_VERSION_1_0' or self.featureName == 'VK_VERSION_1_1':
return
name = self.featureName
nameElem = interface[0][1]
name_define = nameElem.get('name')
if 'EXTENSION_NAME' not in name_define:
print("Error in vk.xml file -- extension name is not available")
requires = interface.get('requires')
if requires is not None:
required_extensions = requires.split(',')
else:
required_extensions = list()
info = { 'define': name_define, 'ifdef':self.featureExtraProtect, 'reqs':required_extensions }
if interface.get('type') == 'instance':
self.instance_extension_info[name] = info
else:
self.device_extension_info[name] = info
#
# Override parent class to be notified of the end of an extension
def endFeature(self):
# Finish processing in superclass
OutputGenerator.endFeature(self)
#
# Grab group (e.g. C "enum" type) info to output for enum-string conversion helper
def genGroup(self, groupinfo, groupName, alias):
OutputGenerator.genGroup(self, groupinfo, groupName, alias)
groupElem = groupinfo.elem
# For enum_string_header
if self.helper_file_type == 'enum_string_header':
value_set = set()
for elem in groupElem.findall('enum'):
if elem.get('supported') != 'disabled' and elem.get('alias') is None:
value_set.add(elem.get('name'))
self.enum_output += self.GenerateEnumStringConversion(groupName, value_set)
elif self.helper_file_type == 'object_types_header':
if groupName == 'VkDebugReportObjectTypeEXT':
for elem in groupElem.findall('enum'):
if elem.get('supported') != 'disabled':
item_name = elem.get('name')
self.debug_report_object_types.append(item_name)
elif groupName == 'VkObjectType':
for elem in groupElem.findall('enum'):
if elem.get('supported') != 'disabled':
item_name = elem.get('name')
self.core_object_types.append(item_name)
#
# Called for each type -- if the type is a struct/union, grab the metadata
def genType(self, typeinfo, name, alias):
OutputGenerator.genType(self, typeinfo, name, alias)
typeElem = typeinfo.elem
# If the type is a struct type, traverse the imbedded <member> tags generating a structure.
# Otherwise, emit the tag text.
category = typeElem.get('category')
if category == 'handle':
if alias:
self.object_type_aliases.append((name,alias))
else:
self.object_types.append(name)
elif (category == 'struct' or category == 'union'):
self.structNames.append(name)
self.genStruct(typeinfo, name, alias)
#
# Generate a VkStructureType based on a structure typename
def genVkStructureType(self, typename):
# Add underscore between lowercase then uppercase
value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
# Change to uppercase
value = value.upper()
# Add STRUCTURE_TYPE_
return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
#
# Check if the parameter passed in is a pointer
def paramIsPointer(self, param):
ispointer = False
for elem in param:
if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
ispointer = True
return ispointer
#
# Check if the parameter passed in is a static array
def paramIsStaticArray(self, param):
isstaticarray = 0
paramname = param.find('name')
if (paramname.tail is not None) and ('[' in paramname.tail):
isstaticarray = paramname.tail.count('[')
return isstaticarray
#
# Retrieve the type and name for a parameter
def getTypeNameTuple(self, param):
type = ''
name = ''
for elem in param:
if elem.tag == 'type':
type = noneStr(elem.text)
elif elem.tag == 'name':
name = noneStr(elem.text)
return (type, name)
# Extract length values from latexmath. Currently an inflexible solution that looks for specific
# patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
def parseLateXMath(self, source):
name = 'ERROR'
decoratedName = 'ERROR'
if 'mathit' in source:
# Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
match = re.match(r'latexmath\s*\:\s*\[\s*\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\s*\]', source)
if not match or match.group(1) != match.group(4):
raise 'Unrecognized latexmath expression'
name = match.group(2)
# Need to add 1 for ceiling function; otherwise, the allocated packet
# size will be less than needed during capture for some title which use
# this in VkPipelineMultisampleStateCreateInfo. based on ceiling function
# definition,it is '{0}%{1}?{0}/{1} + 1:{0}/{1}'.format(*match.group(2, 3)),
# its value <= '{}/{} + 1'.
if match.group(1) == 'ceil':
decoratedName = '{}/{} + 1'.format(*match.group(2, 3))
else:
decoratedName = '{}/{}'.format(*match.group(2, 3))
else:
# Matches expressions similar to 'latexmath : [dataSize \over 4]'
match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
name = match.group(2)
decoratedName = '{}/{}'.format(*match.group(2, 3))
return name, decoratedName
#
# Retrieve the value of the len tag
def getLen(self, param):
result = None
len = param.attrib.get('len')
if len and len != 'null-terminated':
# For string arrays, 'len' can look like 'count,null-terminated', indicating that we
# have a null terminated array of strings. We strip the null-terminated from the
# 'len' field and only return the parameter specifying the string count
if 'null-terminated' in len:
result = len.split(',')[0]
else:
result = len
if 'latexmath' in len:
param_type, param_name = self.getTypeNameTuple(param)
len_name, result = self.parseLateXMath(len)
# Spec has now notation for len attributes, using :: instead of platform specific pointer symbol
result = str(result).replace('::', '->')
return result
#
# Check if a structure is or contains a dispatchable (dispatchable = True) or
# non-dispatchable (dispatchable = False) handle
def TypeContainsObjectHandle(self, handle_type, dispatchable):
if dispatchable:
type_key = 'VK_DEFINE_HANDLE'
else:
type_key = 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
handle = self.registry.tree.find("types/type/[name='" + handle_type + "'][@category='handle']")
if handle is not None and handle.find('type').text == type_key:
return True
# if handle_type is a struct, search its members
if handle_type in self.structNames:
member_index = next((i for i, v in enumerate(self.structMembers) if v[0] == handle_type), None)
if member_index is not None:
for item in self.structMembers[member_index].members:
handle = self.registry.tree.find("types/type/[name='" + item.type + "'][@category='handle']")
if handle is not None and handle.find('type').text == type_key:
return True
return False
#
# Generate local ready-access data describing Vulkan structures and unions from the XML metadata
def genStruct(self, typeinfo, typeName, alias):
OutputGenerator.genStruct(self, typeinfo, typeName, alias)
members = typeinfo.elem.findall('.//member')
# Iterate over members once to get length parameters for arrays
lens = set()
for member in members:
len = self.getLen(member)
if len:
lens.add(len)
# Generate member info
membersInfo = []
for member in members:
# Get the member's type and name
info = self.getTypeNameTuple(member)
type = info[0]
name = info[1]
cdecl = self.makeCParamDecl(member, 1)
# Process VkStructureType
if type == 'VkStructureType':
# Extract the required struct type value from the comments
# embedded in the original text defining the 'typeinfo' element
rawXml = etree.tostring(typeinfo.elem).decode('ascii')
result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
if result:
value = result.group(0)
else:
value = self.genVkStructureType(typeName)
# Store the required type value
self.structTypes[typeName] = self.StructType(name=name, value=value)
# Store pointer/array/string info
isstaticarray = self.paramIsStaticArray(member)
membersInfo.append(self.CommandParam(type=type,
name=name,
ispointer=self.paramIsPointer(member),
isstaticarray=isstaticarray,
isconst=True if 'const' in cdecl else False,
iscount=True if name in lens else False,
len=self.getLen(member),
extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
cdecl=cdecl))
self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo, ifdef_protect=self.featureExtraProtect))
#
# Enum_string_header: Create a routine to convert an enumerated value into a string
def GenerateEnumStringConversion(self, groupName, value_list):
outstring = '\n'
outstring += 'static inline const char* string_%s(%s input_value)\n' % (groupName, groupName)
outstring += '{\n'
outstring += ' switch ((%s)input_value)\n' % groupName
outstring += ' {\n'
for item in value_list:
outstring += ' case %s:\n' % item
outstring += ' return "%s";\n' % item
outstring += ' default:\n'
outstring += ' return "Unhandled %s";\n' % groupName
outstring += ' }\n'
outstring += '}\n'
return outstring
#
# Combine object types helper header file preamble with body text and return
def GenerateObjectTypesHelperHeader(self):
object_types_helper_header = '\n'
object_types_helper_header += '#pragma once\n'
object_types_helper_header += '\n'
object_types_helper_header += '#include <vulkan/vulkan.h>\n\n'
object_types_helper_header += self.GenerateObjectTypesHeader()
return object_types_helper_header
#
# Object types header: create object enum type header file
def GenerateObjectTypesHeader(self):
object_types_header = ''
object_types_header += '// Object Type enum for validation layer internal object handling\n'
object_types_header += 'typedef enum VulkanObjectType {\n'
object_types_header += ' kVulkanObjectTypeUnknown = 0,\n'
enum_num = 1
type_list = [];
enum_entry_map = {}
# Output enum definition as each handle is processed, saving the names to use for the conversion routine
for item in self.object_types:
fixup_name = item[2:]
enum_entry = 'kVulkanObjectType%s' % fixup_name
enum_entry_map[item] = enum_entry
object_types_header += ' ' + enum_entry
object_types_header += ' = %d,\n' % enum_num
enum_num += 1
type_list.append(enum_entry)
object_types_header += ' kVulkanObjectTypeMax = %d,\n' % enum_num
object_types_header += ' // Aliases for backwards compatibilty of "promoted" types\n'
for (name, alias) in self.object_type_aliases:
fixup_name = name[2:]
object_types_header += ' kVulkanObjectType{} = {},\n'.format(fixup_name, enum_entry_map[alias])
object_types_header += '} VulkanObjectType;\n\n'
# Output name string helper
object_types_header += '// Array of object name strings for OBJECT_TYPE enum conversion\n'
object_types_header += 'static const char * const object_string[kVulkanObjectTypeMax] = {\n'
object_types_header += ' "Unknown",\n'
for item in self.object_types:
fixup_name = item[2:]
object_types_header += ' "%s",\n' % fixup_name
object_types_header += '};\n'
# Key creation helper for map comprehensions that convert between k<Name> and VK<Name> symbols
def to_key(regex, raw_key): return re.search(regex, raw_key).group(1).lower().replace("_","")
# Output a conversion routine from the layer object definitions to the debug report definitions
# As the VK_DEBUG_REPORT types are not being updated, specify UNKNOWN for unmatched types
object_types_header += '\n'
object_types_header += '// Helper array to get Vulkan VK_EXT_debug_report object type enum from the internal layers version\n'
object_types_header += 'const VkDebugReportObjectTypeEXT get_debug_report_enum[] = {\n'
object_types_header += ' VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeUnknown\n'
dbg_re = '^VK_DEBUG_REPORT_OBJECT_TYPE_(.*)_EXT$'
dbg_map = {to_key(dbg_re, dbg) : dbg for dbg in self.debug_report_object_types}
dbg_default = 'VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT'
for object_type in type_list:
vk_object_type = dbg_map.get(object_type.replace("kVulkanObjectType", "").lower(), dbg_default)
object_types_header += ' %s, // %s\n' % (vk_object_type, object_type)
object_types_header += '};\n'
# Output a conversion routine from the layer object definitions to the core object type definitions
# This will intentionally *fail* for unmatched types as the VK_OBJECT_TYPE list should match the kVulkanObjectType list
object_types_header += '\n'
object_types_header += '// Helper array to get Official Vulkan VkObjectType enum from the internal layers version\n'
object_types_header += 'const VkObjectType get_object_type_enum[] = {\n'
object_types_header += ' VK_OBJECT_TYPE_UNKNOWN, // kVulkanObjectTypeUnknown\n'
vko_re = '^VK_OBJECT_TYPE_(.*)'
vko_map = {to_key(vko_re, vko) : vko for vko in self.core_object_types}
for object_type in type_list:
vk_object_type = vko_map[object_type.replace("kVulkanObjectType", "").lower()]
object_types_header += ' %s, // %s\n' % (vk_object_type, object_type)
object_types_header += '};\n'
# Create a function to convert from VkDebugReportObjectTypeEXT to VkObjectType
object_types_header += '\n'
object_types_header += '// Helper function to convert from VkDebugReportObjectTypeEXT to VkObjectType\n'
object_types_header += 'static inline VkObjectType convertDebugReportObjectToCoreObject(VkDebugReportObjectTypeEXT debug_report_obj){\n'
object_types_header += ' if (debug_report_obj == VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT) {\n'
object_types_header += ' return VK_OBJECT_TYPE_UNKNOWN;\n'
for core_object_type in self.core_object_types:
core_target_type = core_object_type.replace("VK_OBJECT_TYPE_", "").lower()
core_target_type = core_target_type.replace("_", "")
for dr_object_type in self.debug_report_object_types:
dr_target_type = dr_object_type.replace("VK_DEBUG_REPORT_OBJECT_TYPE_", "").lower()
dr_target_type = dr_target_type[:-4]
dr_target_type = dr_target_type.replace("_", "")
if core_target_type == dr_target_type:
object_types_header += ' } else if (debug_report_obj == %s) {\n' % dr_object_type
object_types_header += ' return %s;\n' % core_object_type
break
object_types_header += ' }\n'
object_types_header += ' return VK_OBJECT_TYPE_UNKNOWN;\n'
object_types_header += '}\n'
# Create a function to convert from VkObjectType to VkDebugReportObjectTypeEXT
object_types_header += '\n'
object_types_header += '// Helper function to convert from VkDebugReportObjectTypeEXT to VkObjectType\n'
object_types_header += 'static inline VkDebugReportObjectTypeEXT convertCoreObjectToDebugReportObject(VkObjectType core_report_obj){\n'
object_types_header += ' if (core_report_obj == VK_OBJECT_TYPE_UNKNOWN) {\n'
object_types_header += ' return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT;\n'
for core_object_type in self.core_object_types:
core_target_type = core_object_type.replace("VK_OBJECT_TYPE_", "").lower()
core_target_type = core_target_type.replace("_", "")
for dr_object_type in self.debug_report_object_types:
dr_target_type = dr_object_type.replace("VK_DEBUG_REPORT_OBJECT_TYPE_", "").lower()
dr_target_type = dr_target_type[:-4]
dr_target_type = dr_target_type.replace("_", "")
if core_target_type == dr_target_type:
object_types_header += ' } else if (core_report_obj == %s) {\n' % core_object_type
object_types_header += ' return %s;\n' % dr_object_type
break
object_types_header += ' }\n'
object_types_header += ' return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT;\n'
object_types_header += '}\n'
return object_types_header
#
# Create a helper file and return it as a string
def OutputDestFile(self):
if self.helper_file_type == 'object_types_header':
return self.GenerateObjectTypesHelperHeader()
else:
return 'Bad Helper File Generator Option %s' % self.helper_file_type

View file

@ -1,15 +0,0 @@
{
"repos" : [
{
"name" : "Vulkan-Headers",
"url" : "https://github.com/KhronosGroup/Vulkan-Headers.git",
"sub_dir" : "Vulkan-Headers",
"build_dir" : "Vulkan-Headers/build",
"install_dir" : "Vulkan-Headers/build/install",
"commit" : "v1.1.113"
}
],
"install_names" : {
"Vulkan-Headers" : "VULKAN_HEADERS_INSTALL_DIR"
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,516 +0,0 @@
#!/usr/bin/python3
#
# Copyright (c) 2013-2019 The Khronos Group Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse, cProfile, pdb, string, sys, time, os
# Simple timer functions
startTime = None
def startTimer(timeit):
global startTime
if timeit:
startTime = time.process_time()
def endTimer(timeit, msg):
global startTime
if timeit:
endTime = time.process_time()
write(msg, endTime - startTime, file=sys.stderr)
startTime = None
# Turn a list of strings into a regexp string matching exactly those strings
def makeREstring(list, default = None):
if len(list) > 0 or default is None:
return '^(' + '|'.join(list) + ')$'
else:
return default
# Returns a directory of [ generator function, generator options ] indexed
# by specified short names. The generator options incorporate the following
# parameters:
#
# args is an parsed argument object; see below for the fields that are used.
def makeGenOpts(args):
global genOpts
genOpts = {}
# Default class of extensions to include, or None
defaultExtensions = args.defaultExtensions
# Additional extensions to include (list of extensions)
extensions = args.extension
# Extensions to remove (list of extensions)
removeExtensions = args.removeExtensions
# Extensions to emit (list of extensions)
emitExtensions = args.emitExtensions
# Features to include (list of features)
features = args.feature
# Whether to disable inclusion protect in headers
protect = args.protect
# Output target directory
directory = args.directory
# Descriptive names for various regexp patterns used to select
# versions and extensions
allFeatures = allExtensions = '.*'
noFeatures = noExtensions = None
# Turn lists of names/patterns into matching regular expressions
addExtensionsPat = makeREstring(extensions, None)
removeExtensionsPat = makeREstring(removeExtensions, None)
emitExtensionsPat = makeREstring(emitExtensions, allExtensions)
featuresPat = makeREstring(features, allFeatures)
# Copyright text prefixing all headers (list of strings).
prefixStrings = [
'/*',
'** Copyright (c) 2015-2019 The Khronos Group Inc.',
'**',
'** Licensed under the Apache License, Version 2.0 (the "License");',
'** you may not use this file except in compliance with the License.',
'** You may obtain a copy of the License at',
'**',
'** http://www.apache.org/licenses/LICENSE-2.0',
'**',
'** Unless required by applicable law or agreed to in writing, software',
'** distributed under the License is distributed on an "AS IS" BASIS,',
'** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
'** See the License for the specific language governing permissions and',
'** limitations under the License.',
'*/',
''
]
# Text specific to Vulkan headers
vkPrefixStrings = [
'/*',
'** This header is generated from the Khronos Vulkan XML API Registry.',
'**',
'*/',
''
]
# Defaults for generating re-inclusion protection wrappers (or not)
protectFeature = protect
# An API style conventions object
conventions = VulkanConventions()
# Loader Generators
# Options for dispatch table helper generator
genOpts['vk_dispatch_table_helper.h'] = [
DispatchTableHelperOutputGenerator,
DispatchTableHelperOutputGeneratorOptions(
conventions = conventions,
filename = 'vk_dispatch_table_helper.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False)
]
# Options for Layer dispatch table generator
genOpts['vk_layer_dispatch_table.h'] = [
LoaderExtensionOutputGenerator,
LoaderExtensionGeneratorOptions(
conventions = conventions,
filename = 'vk_layer_dispatch_table.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False)
]
# Options for loader extension source generator
genOpts['vk_loader_extensions.h'] = [
LoaderExtensionOutputGenerator,
LoaderExtensionGeneratorOptions(
conventions = conventions,
filename = 'vk_loader_extensions.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False)
]
# Options for loader extension source generator
genOpts['vk_loader_extensions.c'] = [
LoaderExtensionOutputGenerator,
LoaderExtensionGeneratorOptions(
conventions = conventions,
filename = 'vk_loader_extensions.c',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False)
]
# Helper file generator options for vk_enum_string_helper.h
genOpts['vk_enum_string_helper.h'] = [
HelperFileOutputGenerator,
HelperFileOutputGeneratorOptions(
conventions = conventions,
filename = 'vk_enum_string_helper.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False,
helper_file_type = 'enum_string_header')
]
# Helper file generator options for vk_safe_struct.h
genOpts['vk_safe_struct.h'] = [
HelperFileOutputGenerator,
HelperFileOutputGeneratorOptions(
conventions = conventions,
filename = 'vk_safe_struct.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False,
helper_file_type = 'safe_struct_header')
]
# Helper file generator options for vk_safe_struct.cpp
genOpts['vk_safe_struct.cpp'] = [
HelperFileOutputGenerator,
HelperFileOutputGeneratorOptions(
conventions = conventions,
filename = 'vk_safe_struct.cpp',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False,
helper_file_type = 'safe_struct_source')
]
# Helper file generator options for vk_object_types.h
genOpts['vk_object_types.h'] = [
HelperFileOutputGenerator,
HelperFileOutputGeneratorOptions(
conventions = conventions,
filename = 'vk_object_types.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False,
helper_file_type = 'object_types_header')
]
# Helper file generator options for extension_helper.h
genOpts['vk_extension_helper.h'] = [
HelperFileOutputGenerator,
HelperFileOutputGeneratorOptions(
conventions = conventions,
filename = 'vk_extension_helper.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False,
helper_file_type = 'extension_helper_header')
]
# Helper file generator options for typemap_helper.h
genOpts['vk_typemap_helper.h'] = [
HelperFileOutputGenerator,
HelperFileOutputGeneratorOptions(
conventions = conventions,
filename = 'vk_typemap_helper.h',
directory = directory,
apiname = 'vulkan',
profile = None,
versions = featuresPat,
emitversions = featuresPat,
defaultExtensions = 'vulkan',
addExtensions = addExtensionsPat,
removeExtensions = removeExtensionsPat,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
protectFeature = False,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
expandEnumerants = False,
helper_file_type = 'typemap_helper_header')
]
# Generate a target based on the options in the matching genOpts{} object.
# This is encapsulated in a function so it can be profiled and/or timed.
# The args parameter is an parsed argument object containing the following
# fields that are used:
# target - target to generate
# directory - directory to generate it in
# protect - True if re-inclusion wrappers should be created
# extensions - list of additional extensions to include in generated
# interfaces
def genTarget(args):
global genOpts
# Create generator options with specified parameters
makeGenOpts(args)
if (args.target in genOpts.keys()):
createGenerator = genOpts[args.target][0]
options = genOpts[args.target][1]
if not args.quiet:
write('* Building', options.filename, file=sys.stderr)
write('* options.versions =', options.versions, file=sys.stderr)
write('* options.emitversions =', options.emitversions, file=sys.stderr)
write('* options.defaultExtensions =', options.defaultExtensions, file=sys.stderr)
write('* options.addExtensions =', options.addExtensions, file=sys.stderr)
write('* options.removeExtensions =', options.removeExtensions, file=sys.stderr)
write('* options.emitExtensions =', options.emitExtensions, file=sys.stderr)
startTimer(args.time)
gen = createGenerator(errFile=errWarn,
warnFile=errWarn,
diagFile=diag)
reg.setGenerator(gen)
reg.apiGen(options)
if not args.quiet:
write('* Generated', options.filename, file=sys.stderr)
endTimer(args.time, '* Time to generate ' + options.filename + ' =')
else:
write('No generator options for unknown target:',
args.target, file=sys.stderr)
# -feature name
# -extension name
# For both, "name" may be a single name, or a space-separated list
# of names, or a regular expression.
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-defaultExtensions', action='store',
default='vulkan',
help='Specify a single class of extensions to add to targets')
parser.add_argument('-extension', action='append',
default=[],
help='Specify an extension or extensions to add to targets')
parser.add_argument('-removeExtensions', action='append',
default=[],
help='Specify an extension or extensions to remove from targets')
parser.add_argument('-emitExtensions', action='append',
default=[],
help='Specify an extension or extensions to emit in targets')
parser.add_argument('-feature', action='append',
default=[],
help='Specify a core API feature name or names to add to targets')
parser.add_argument('-debug', action='store_true',
help='Enable debugging')
parser.add_argument('-dump', action='store_true',
help='Enable dump to stderr')
parser.add_argument('-diagfile', action='store',
default=None,
help='Write diagnostics to specified file')
parser.add_argument('-errfile', action='store',
default=None,
help='Write errors and warnings to specified file instead of stderr')
parser.add_argument('-noprotect', dest='protect', action='store_false',
help='Disable inclusion protection in output headers')
parser.add_argument('-profile', action='store_true',
help='Enable profiling')
parser.add_argument('-registry', action='store',
default='vk.xml',
help='Use specified registry file instead of vk.xml')
parser.add_argument('-time', action='store_true',
help='Enable timing')
parser.add_argument('-validate', action='store_true',
help='Enable group validation')
parser.add_argument('-o', action='store', dest='directory',
default='.',
help='Create target and related files in specified directory')
parser.add_argument('target', metavar='target', nargs='?',
help='Specify target')
parser.add_argument('-quiet', action='store_true', default=True,
help='Suppress script output during normal execution.')
parser.add_argument('-verbose', action='store_false', dest='quiet', default=True,
help='Enable script output during normal execution.')
# This argument tells us where to load the script from the Vulkan-Headers registry
parser.add_argument('-scripts', action='store',
help='Find additional scripts in this directory')
args = parser.parse_args()
scripts_dir = os.path.dirname(os.path.abspath(__file__))
registry_dir = os.path.join(scripts_dir, args.scripts)
sys.path.insert(0, registry_dir)
# The imports need to be done here so that they can be picked up from Vulkan-Headers
from reg import *
from generator import write
from cgenerator import CGeneratorOptions, COutputGenerator
from dispatch_table_helper_generator import DispatchTableHelperOutputGenerator, DispatchTableHelperOutputGeneratorOptions
from helper_file_generator import HelperFileOutputGenerator, HelperFileOutputGeneratorOptions
from loader_extension_generator import LoaderExtensionOutputGenerator, LoaderExtensionGeneratorOptions
# Temporary workaround for vkconventions python2 compatibility
import abc; abc.ABC = abc.ABCMeta('ABC', (object,), {})
from vkconventions import VulkanConventions
# This splits arguments which are space-separated lists
args.feature = [name for arg in args.feature for name in arg.split()]
args.extension = [name for arg in args.extension for name in arg.split()]
# Load & parse registry
reg = Registry()
startTimer(args.time)
tree = etree.parse(args.registry)
endTimer(args.time, '* Time to make ElementTree =')
if args.debug:
pdb.run('reg.loadElementTree(tree)')
else:
startTimer(args.time)
reg.loadElementTree(tree)
endTimer(args.time, '* Time to parse ElementTree =')
if (args.validate):
reg.validateGroups()
if (args.dump):
write('* Dumping registry to regdump.txt', file=sys.stderr)
reg.dumpReg(filehandle = open('regdump.txt', 'w', encoding='utf-8'))
# create error/warning & diagnostic files
if (args.errfile):
errWarn = open(args.errfile, 'w', encoding='utf-8')
else:
errWarn = sys.stderr
if (args.diagfile):
diag = open(args.diagfile, 'w', encoding='utf-8')
else:
diag = None
if (args.debug):
pdb.run('genTarget(args)')
elif (args.profile):
import cProfile, pstats
cProfile.run('genTarget(args)', 'profile.txt')
p = pstats.Stats('profile.txt')
p.strip_dirs().sort_stats('time').print_stats(50)
else:
genTarget(args)

File diff suppressed because it is too large Load diff

View file

@ -1,679 +0,0 @@
#!/usr/bin/env python
# Copyright 2017 The Glslang Authors. All rights reserved.
# Copyright (c) 2018 Valve Corporation
# Copyright (c) 2018 LunarG, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script was heavily leveraged from KhronosGroup/glslang
# update_glslang_sources.py.
"""update_deps.py
Get and build dependent repositories using known-good commits.
Purpose
-------
This program is intended to assist a developer of this repository
(the "home" repository) by gathering and building the repositories that
this home repository depend on. It also checks out each dependent
repository at a "known-good" commit in order to provide stability in
the dependent repositories.
Python Compatibility
--------------------
This program can be used with Python 2.7 and Python 3.
Known-Good JSON Database
------------------------
This program expects to find a file named "known-good.json" in the
same directory as the program file. This JSON file is tailored for
the needs of the home repository by including its dependent repositories.
Program Options
---------------
See the help text (update_deps.py --help) for a complete list of options.
Program Operation
-----------------
The program uses the user's current directory at the time of program
invocation as the location for fetching and building the dependent
repositories. The user can override this by using the "--dir" option.
For example, a directory named "build" in the repository's root directory
is a good place to put the dependent repositories because that directory
is not tracked by Git. (See the .gitignore file.) The "external" directory
may also be a suitable location.
A user can issue:
$ cd My-Repo
$ mkdir build
$ cd build
$ ../scripts/update_deps.py
or, to do the same thing, but using the --dir option:
$ cd My-Repo
$ mkdir build
$ scripts/update_deps.py --dir=build
With these commands, the "build" directory is considered the "top"
directory where the program clones the dependent repositories. The
JSON file configures the build and install working directories to be
within this "top" directory.
Note that the "dir" option can also specify an absolute path:
$ cd My-Repo
$ scripts/update_deps.py --dir=/tmp/deps
The "top" dir is then /tmp/deps (Linux filesystem example) and is
where this program will clone and build the dependent repositories.
Helper CMake Config File
------------------------
When the program finishes building the dependencies, it writes a file
named "helper.cmake" to the "top" directory that contains CMake commands
for setting CMake variables for locating the dependent repositories.
This helper file can be used to set up the CMake build files for this
"home" repository.
A complete sequence might look like:
$ git clone git@github.com:My-Group/My-Repo.git
$ cd My-Repo
$ mkdir build
$ cd build
$ ../scripts/update_deps.py
$ cmake -C helper.cmake ..
$ cmake --build .
JSON File Schema
----------------
There's no formal schema for the "known-good" JSON file, but here is
a description of its elements. All elements are required except those
marked as optional. Please see the "known_good.json" file for
examples of all of these elements.
- name
The name of the dependent repository. This field can be referenced
by the "deps.repo_name" structure to record a dependency.
- url
Specifies the URL of the repository.
Example: https://github.com/KhronosGroup/Vulkan-Loader.git
- sub_dir
The directory where the program clones the repository, relative to
the "top" directory.
- build_dir
The directory used to build the repository, relative to the "top"
directory.
- install_dir
The directory used to store the installed build artifacts, relative
to the "top" directory.
- commit
The commit used to checkout the repository. This can be a SHA-1
object name or a refname used with the remote name "origin".
For example, this field can be set to "origin/sdk-1.1.77" to
select the end of the sdk-1.1.77 branch.
- deps (optional)
An array of pairs consisting of a CMake variable name and a
repository name to specify a dependent repo and a "link" to
that repo's install artifacts. For example:
"deps" : [
{
"var_name" : "VULKAN_HEADERS_INSTALL_DIR",
"repo_name" : "Vulkan-Headers"
}
]
which represents that this repository depends on the Vulkan-Headers
repository and uses the VULKAN_HEADERS_INSTALL_DIR CMake variable to
specify the location where it expects to find the Vulkan-Headers install
directory.
Note that the "repo_name" element must match the "name" element of some
other repository in the JSON file.
- prebuild (optional)
- prebuild_linux (optional) (For Linux and MacOS)
- prebuild_windows (optional)
A list of commands to execute before building a dependent repository.
This is useful for repositories that require the execution of some
sort of "update" script or need to clone an auxillary repository like
googletest.
The commands listed in "prebuild" are executed first, and then the
commands for the specific platform are executed.
- custom_build (optional)
A list of commands to execute as a custom build instead of using
the built in CMake way of building. Requires "build_step" to be
set to "custom"
You can insert the following keywords into the commands listed in
"custom_build" if they require runtime information (like whether the
build config is "Debug" or "Release").
Keywords:
{0} reference to a dictionary of repos and their attributes
{1} reference to the command line arguments set before start
{2} reference to the CONFIG_MAP value of config.
Example:
{2} returns the CONFIG_MAP value of config e.g. debug -> Debug
{1}.config returns the config variable set when you ran update_dep.py
{0}[Vulkan-Headers][repo_root] returns the repo_root variable from
the Vulkan-Headers GoodRepo object.
- cmake_options (optional)
A list of options to pass to CMake during the generation phase.
- ci_only (optional)
A list of environment variables where one must be set to "true"
(case-insensitive) in order for this repo to be fetched and built.
This list can be used to specify repos that should be built only in CI.
Typically, this list might contain "TRAVIS" and/or "APPVEYOR" because
each of these CI systems sets an environment variable with its own
name to "true". Note that this could also be (ab)used to control
the processing of the repo with any environment variable. The default
is an empty list, which means that the repo is always processed.
- build_step (optional)
Specifies if the dependent repository should be built or not. This can
have a value of 'build', 'custom', or 'skip'. The dependent repositories are
built by default.
- build_platforms (optional)
A list of platforms the repository will be built on.
Legal options include:
"windows"
"linux"
"darwin"
Builds on all platforms by default.
Note
----
The "sub_dir", "build_dir", and "install_dir" elements are all relative
to the effective "top" directory. Specifying absolute paths is not
supported. However, the "top" directory specified with the "--dir"
option can be a relative or absolute path.
"""
from __future__ import print_function
import argparse
import json
import distutils.dir_util
import os.path
import subprocess
import sys
import platform
import multiprocessing
import shlex
import shutil
KNOWN_GOOD_FILE_NAME = 'known_good.json'
CONFIG_MAP = {
'debug': 'Debug',
'release': 'Release',
'relwithdebinfo': 'RelWithDebInfo',
'minsizerel': 'MinSizeRel'
}
VERBOSE = False
DEVNULL = open(os.devnull, 'wb')
def command_output(cmd, directory, fail_ok=False):
"""Runs a command in a directory and returns its standard output stream.
Captures the standard error stream and prints it if error.
Raises a RuntimeError if the command fails to launch or otherwise fails.
"""
if VERBOSE:
print('In {d}: {cmd}'.format(d=directory, cmd=cmd))
p = subprocess.Popen(
cmd, cwd=directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdout, stderr) = p.communicate()
if p.returncode != 0:
print('*** Error ***\nstderr contents:\n{}'.format(stderr))
if not fail_ok:
raise RuntimeError('Failed to run {} in {}'.format(cmd, directory))
if VERBOSE:
print(stdout)
return stdout
class GoodRepo(object):
"""Represents a repository at a known-good commit."""
def __init__(self, json, args):
"""Initializes this good repo object.
Args:
'json': A fully populated JSON object describing the repo.
'args': Results from ArgumentParser
"""
self._json = json
self._args = args
# Required JSON elements
self.name = json['name']
self.url = json['url']
self.sub_dir = json['sub_dir']
self.commit = json['commit']
# Optional JSON elements
self.build_dir = None
self.install_dir = None
if json.get('build_dir'):
self.build_dir = os.path.normpath(json['build_dir'])
if json.get('install_dir'):
self.install_dir = os.path.normpath(json['install_dir'])
self.deps = json['deps'] if ('deps' in json) else []
self.prebuild = json['prebuild'] if ('prebuild' in json) else []
self.prebuild_linux = json['prebuild_linux'] if (
'prebuild_linux' in json) else []
self.prebuild_windows = json['prebuild_windows'] if (
'prebuild_windows' in json) else []
self.custom_build = json['custom_build'] if ('custom_build' in json) else []
self.cmake_options = json['cmake_options'] if (
'cmake_options' in json) else []
self.ci_only = json['ci_only'] if ('ci_only' in json) else []
self.build_step = json['build_step'] if ('build_step' in json) else 'build'
self.build_platforms = json['build_platforms'] if ('build_platforms' in json) else []
# Absolute paths for a repo's directories
dir_top = os.path.abspath(args.dir)
self.repo_dir = os.path.join(dir_top, self.sub_dir)
if self.build_dir:
self.build_dir = os.path.join(dir_top, self.build_dir)
if self.install_dir:
self.install_dir = os.path.join(dir_top, self.install_dir)
# Check if platform is one to build on
self.on_build_platform = False
if self.build_platforms == [] or platform.system().lower() in self.build_platforms:
self.on_build_platform = True
def Clone(self):
distutils.dir_util.mkpath(self.repo_dir)
command_output(['git', 'clone', self.url, '.'], self.repo_dir)
def Fetch(self):
command_output(['git', 'fetch', 'origin'], self.repo_dir)
def Checkout(self):
print('Checking out {n} in {d}'.format(n=self.name, d=self.repo_dir))
if self._args.do_clean_repo:
shutil.rmtree(self.repo_dir, ignore_errors=True)
if not os.path.exists(os.path.join(self.repo_dir, '.git')):
self.Clone()
self.Fetch()
if len(self._args.ref):
command_output(['git', 'checkout', self._args.ref], self.repo_dir)
else:
command_output(['git', 'checkout', self.commit], self.repo_dir)
print(command_output(['git', 'status'], self.repo_dir))
def CustomPreProcess(self, cmd_str, repo_dict):
return cmd_str.format(repo_dict, self._args, CONFIG_MAP[self._args.config])
def PreBuild(self):
"""Execute any prebuild steps from the repo root"""
for p in self.prebuild:
command_output(shlex.split(p), self.repo_dir)
if platform.system() == 'Linux' or platform.system() == 'Darwin':
for p in self.prebuild_linux:
command_output(shlex.split(p), self.repo_dir)
if platform.system() == 'Windows':
for p in self.prebuild_windows:
command_output(shlex.split(p), self.repo_dir)
def CustomBuild(self, repo_dict):
"""Execute any custom_build steps from the repo root"""
for p in self.custom_build:
cmd = self.CustomPreProcess(p, repo_dict)
command_output(shlex.split(cmd), self.repo_dir)
def CMakeConfig(self, repos):
"""Build CMake command for the configuration phase and execute it"""
if self._args.do_clean_build:
shutil.rmtree(self.build_dir)
if self._args.do_clean_install:
shutil.rmtree(self.install_dir)
# Create and change to build directory
distutils.dir_util.mkpath(self.build_dir)
os.chdir(self.build_dir)
cmake_cmd = [
'cmake', self.repo_dir,
'-DCMAKE_INSTALL_PREFIX=' + self.install_dir
]
# For each repo this repo depends on, generate a CMake variable
# definitions for "...INSTALL_DIR" that points to that dependent
# repo's install dir.
for d in self.deps:
dep_commit = [r for r in repos if r.name == d['repo_name']]
if len(dep_commit):
cmake_cmd.append('-D{var_name}={install_dir}'.format(
var_name=d['var_name'],
install_dir=dep_commit[0].install_dir))
# Add any CMake options
for option in self.cmake_options:
cmake_cmd.append(option)
# Set build config for single-configuration generators
if platform.system() == 'Linux' or platform.system() == 'Darwin':
cmake_cmd.append('-DCMAKE_BUILD_TYPE={config}'.format(
config=CONFIG_MAP[self._args.config]))
# Use the CMake -A option to select the platform architecture
# without needing a Visual Studio generator.
if platform.system() == 'Windows':
if self._args.arch == '64' or self._args.arch == 'x64' or self._args.arch == 'win64':
cmake_cmd.append('-A')
cmake_cmd.append('x64')
# Apply a generator, if one is specified. This can be used to supply
# a specific generator for the dependent repositories to match
# that of the main repository.
if self._args.generator is not None:
cmake_cmd.extend(['-G', self._args.generator])
if VERBOSE:
print("CMake command: " + " ".join(cmake_cmd))
ret_code = subprocess.call(cmake_cmd)
if ret_code != 0:
sys.exit(ret_code)
def CMakeBuild(self):
"""Build CMake command for the build phase and execute it"""
cmake_cmd = ['cmake', '--build', self.build_dir, '--target', 'install']
if self._args.do_clean:
cmake_cmd.append('--clean-first')
if platform.system() == 'Windows':
cmake_cmd.append('--config')
cmake_cmd.append(CONFIG_MAP[self._args.config])
# Speed up the build.
if platform.system() == 'Linux' or platform.system() == 'Darwin':
cmake_cmd.append('--')
num_make_jobs = multiprocessing.cpu_count()
env_make_jobs = os.environ.get('MAKE_JOBS', None)
if env_make_jobs is not None:
try:
num_make_jobs = min(num_make_jobs, int(env_make_jobs))
except ValueError:
print('warning: environment variable MAKE_JOBS has non-numeric value "{}". '
'Using {} (CPU count) instead.'.format(env_make_jobs, num_make_jobs))
cmake_cmd.append('-j{}'.format(num_make_jobs))
if platform.system() == 'Windows':
cmake_cmd.append('--')
cmake_cmd.append('/maxcpucount')
if VERBOSE:
print("CMake command: " + " ".join(cmake_cmd))
ret_code = subprocess.call(cmake_cmd)
if ret_code != 0:
sys.exit(ret_code)
def Build(self, repos, repo_dict):
"""Build the dependent repo"""
print('Building {n} in {d}'.format(n=self.name, d=self.repo_dir))
print('Build dir = {b}'.format(b=self.build_dir))
print('Install dir = {i}\n'.format(i=self.install_dir))
# Run any prebuild commands
self.PreBuild()
if self.build_step == 'custom':
self.CustomBuild(repo_dict)
return
# Build and execute CMake command for creating build files
self.CMakeConfig(repos)
# Build and execute CMake command for the build
self.CMakeBuild()
def GetGoodRepos(args):
"""Returns the latest list of GoodRepo objects.
The known-good file is expected to be in the same
directory as this script unless overridden by the 'known_good_dir'
parameter.
"""
if args.known_good_dir:
known_good_file = os.path.join( os.path.abspath(args.known_good_dir),
KNOWN_GOOD_FILE_NAME)
else:
known_good_file = os.path.join(
os.path.dirname(os.path.abspath(__file__)), KNOWN_GOOD_FILE_NAME)
with open(known_good_file) as known_good:
return [
GoodRepo(repo, args)
for repo in json.loads(known_good.read())['repos']
]
def GetInstallNames(args):
"""Returns the install names list.
The known-good file is expected to be in the same
directory as this script unless overridden by the 'known_good_dir'
parameter.
"""
if args.known_good_dir:
known_good_file = os.path.join(os.path.abspath(args.known_good_dir),
KNOWN_GOOD_FILE_NAME)
else:
known_good_file = os.path.join(
os.path.dirname(os.path.abspath(__file__)), KNOWN_GOOD_FILE_NAME)
with open(known_good_file) as known_good:
install_info = json.loads(known_good.read())
if install_info.get('install_names'):
return install_info['install_names']
else:
return None
def CreateHelper(args, repos, filename):
"""Create a CMake config helper file.
The helper file is intended to be used with 'cmake -C <file>'
to build this home repo using the dependencies built by this script.
The install_names dictionary represents the CMake variables used by the
home repo to locate the install dirs of the dependent repos.
This information is baked into the CMake files of the home repo and so
this dictionary is kept with the repo via the json file.
"""
def escape(path):
return path.replace('\\', '\\\\')
install_names = GetInstallNames(args)
with open(filename, 'w') as helper_file:
for repo in repos:
if install_names and repo.name in install_names and repo.on_build_platform:
helper_file.write('set({var} "{dir}" CACHE STRING "" FORCE)\n'
.format(
var=install_names[repo.name],
dir=escape(repo.install_dir)))
def main():
parser = argparse.ArgumentParser(
description='Get and build dependent repos at known-good commits')
parser.add_argument(
'--known_good_dir',
dest='known_good_dir',
help="Specify directory for known_good.json file.")
parser.add_argument(
'--dir',
dest='dir',
default='.',
help="Set target directory for repository roots. Default is \'.\'.")
parser.add_argument(
'--ref',
dest='ref',
default='',
help="Override 'commit' with git reference. E.g., 'origin/master'")
parser.add_argument(
'--no-build',
dest='do_build',
action='store_false',
help=
"Clone/update repositories and generate build files without performing compilation",
default=True)
parser.add_argument(
'--clean',
dest='do_clean',
action='store_true',
help="Clean files generated by compiler and linker before building",
default=False)
parser.add_argument(
'--clean-repo',
dest='do_clean_repo',
action='store_true',
help="Delete repository directory before building",
default=False)
parser.add_argument(
'--clean-build',
dest='do_clean_build',
action='store_true',
help="Delete build directory before building",
default=False)
parser.add_argument(
'--clean-install',
dest='do_clean_install',
action='store_true',
help="Delete install directory before building",
default=False)
parser.add_argument(
'--arch',
dest='arch',
choices=['32', '64', 'x86', 'x64', 'win32', 'win64'],
type=str.lower,
help="Set build files architecture (Windows)",
default='64')
parser.add_argument(
'--config',
dest='config',
choices=['debug', 'release', 'relwithdebinfo', 'minsizerel'],
type=str.lower,
help="Set build files configuration",
default='debug')
parser.add_argument(
'--generator',
dest='generator',
help="Set the CMake generator",
default=None)
args = parser.parse_args()
save_cwd = os.getcwd()
# Create working "top" directory if needed
distutils.dir_util.mkpath(args.dir)
abs_top_dir = os.path.abspath(args.dir)
repos = GetGoodRepos(args)
repo_dict = {}
print('Starting builds in {d}'.format(d=abs_top_dir))
for repo in repos:
# If the repo has a platform whitelist, skip the repo
# unless we are building on a whitelisted platform.
if not repo.on_build_platform:
continue
field_list = ('url',
'sub_dir',
'commit',
'build_dir',
'install_dir',
'deps',
'prebuild',
'prebuild_linux',
'prebuild_windows',
'custom_build',
'cmake_options',
'ci_only',
'build_step',
'build_platforms',
'repo_dir',
'on_build_platform')
repo_dict[repo.name] = {field: getattr(repo, field) for field in field_list}
# If the repo has a CI whitelist, skip the repo unless
# one of the CI's environment variable is set to true.
if len(repo.ci_only):
do_build = False
for env in repo.ci_only:
if not env in os.environ:
continue
if os.environ[env].lower() == 'true':
do_build = True
break
if not do_build:
continue
# Clone/update the repository
repo.Checkout()
# Build the repository
if args.do_build and repo.build_step != 'skip':
repo.Build(repos, repo_dict)
# Need to restore original cwd in order for CreateHelper to find json file
os.chdir(save_cwd)
CreateHelper(args, repos, os.path.join(abs_top_dir, 'helper.cmake'))
sys.exit(0)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,234 +0,0 @@
#!/usr/bin/python3 -i
#
# Copyright (c) 2013-2019 The Khronos Group Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Working-group-specific style conventions,
# used in generation.
import re
from conventions import ConventionsBase
class VulkanConventions(ConventionsBase):
def formatExtension(self, name):
"""Mark up a name as an extension for the spec."""
return '`<<{}>>`'.format(name)
@property
def null(self):
"""Preferred spelling of NULL."""
return '`NULL`'
@property
def constFlagBits(self):
"""Returns True if static const flag bits should be generated, False if an enumerated type should be generated."""
return False
@property
def struct_macro(self):
return 'sname:'
@property
def external_macro(self):
return 'code:'
@property
def structtype_member_name(self):
"""Return name of the structure type member"""
return 'sType'
@property
def nextpointer_member_name(self):
"""Return name of the structure pointer chain member"""
return 'pNext'
@property
def valid_pointer_prefix(self):
"""Return prefix to pointers which must themselves be valid"""
return 'valid'
def is_structure_type_member(self, paramtype, paramname):
"""Determine if member type and name match the structure type member."""
return paramtype == 'VkStructureType' and paramname == self.structtype_member_name
def is_nextpointer_member(self, paramtype, paramname):
"""Determine if member type and name match the next pointer chain member."""
return paramtype == 'void' and paramname == self.nextpointer_member_name
def generate_structure_type_from_name(self, structname):
"""Generate a structure type name, like VK_STRUCTURE_TYPE_CREATE_INSTANCE_INFO"""
structure_type_parts = []
# Tokenize into "words"
for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', structname):
if elem[0] == 'Vk':
structure_type_parts.append('VK_STRUCTURE_TYPE')
else:
structure_type_parts.append(elem[0].upper())
return '_'.join(structure_type_parts)
@property
def warning_comment(self):
"""Return warning comment to be placed in header of generated Asciidoctor files"""
return '// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry'
@property
def file_suffix(self):
"""Return suffix of generated Asciidoctor files"""
return '.txt'
def api_name(self, spectype = 'api'):
"""Return API or specification name for citations in ref pages.ref
pages should link to for
spectype is the spec this refpage is for: 'api' is the Vulkan API
Specification. Defaults to 'api'. If an unrecognized spectype is
given, returns None.
"""
if spectype == 'api' or spectype is None:
return 'Vulkan'
else:
return None
@property
def xml_supported_name_of_api(self):
"""Return the supported= attribute used in API XML"""
return 'vulkan'
@property
def api_prefix(self):
"""Return API token prefix"""
return 'VK_'
@property
def api_version_prefix(self):
"""Return API core version token prefix"""
return 'VK_VERSION_'
@property
def KHR_prefix(self):
"""Return extension name prefix for KHR extensions"""
return 'VK_KHR_'
@property
def EXT_prefix(self):
"""Return extension name prefix for EXT extensions"""
return 'VK_EXT_'
@property
def write_contacts(self):
"""Return whether contact list should be written to extension appendices"""
return True
@property
def write_refpage_include(self):
"""Return whether refpage include should be written to extension appendices"""
return True
def writeFeature(self, featureExtraProtect, filename):
"""Returns True if OutputGenerator.endFeature should write this feature.
Used in COutputGenerator
"""
return True
def requires_error_validation(self, return_type):
"""Returns True if the return_type element is an API result code
requiring error validation.
"""
return False
@property
def required_errors(self):
"""Return a list of required error codes for validation."""
return []
def is_externsync_command(self, protoname):
"""Returns True if the protoname element is an API command requiring
external synchronization
"""
return protoname is not None and 'vkCmd' in protoname
def is_api_name(self, name):
"""Returns True if name is in the reserved API namespace.
For Vulkan, these are names with a case-insensitive 'vk' prefix, or
a 'PFN_vk' function pointer type prefix.
"""
return name[0:2].lower() == 'vk' or name[0:6] == 'PFN_vk'
def is_voidpointer_alias(self, tag, text, tail):
"""Return True if the declaration components (tag,text,tail) of an
element represents a void * type
"""
return tag == 'type' and text == 'void' and tail.startswith('*')
def make_voidpointer_alias(self, tail):
"""Reformat a void * declaration to include the API alias macro.
Vulkan doesn't have an API alias macro, so do nothing.
"""
return tail
def specURL(self, spectype = 'api'):
"""Return public registry URL which ref pages should link to for the
current all-extensions HTML specification, so xrefs in the
asciidoc source that aren't to ref pages can link into it
instead. N.b. this may need to change on a per-refpage basis if
there are multiple documents involved.
"""
return 'https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html'
@property
def xml_api_name(self):
"""Return the name used in the default API XML registry for the default API"""
return 'vulkan'
@property
def registry_path(self):
"""Return relpath to the default API XML registry in this project."""
return 'xml/vk.xml'
@property
def specification_path(self):
"""Return relpath to the Asciidoctor specification sources in this project."""
return '../appendices/meta'
@property
def extra_refpage_headers(self):
"""Return any extra text to add to refpage headers."""
return 'include::../config/attribs.txt[]'
@property
def extension_index_prefixes(self):
"""Return a list of extension prefixes used to group extension refpages."""
return ['VK_KHR', 'VK_EXT', 'VK']
@property
def unified_flag_refpages(self):
"""Returns True if Flags/FlagBits refpages are unified, False if
they're separate.
"""
return False
@property
def spec_reflow_path(self):
"""Return the relative path to the spec source folder to reflow"""
return '.'
@property
def spec_no_reflow_dirs(self):
"""Return a set of directories not to automatically descend into
when reflowing spec text
"""
return ('scripts', 'style')