2021-05-03 23:29:53 +00:00
2021-05-06 21:21:34 +00:00
#
# generate_dispatch.py
2022-02-06 01:26:21 +00:00
#
2021-05-06 21:21:34 +00:00
# Copyright © 2021 Cody Goodson (contact@vibimanx.com)
2022-02-06 01:26:21 +00:00
# Copyright © 2022 Charles Giessen (charles@lunarg.com)
2021-05-06 21:21:34 +00:00
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the “Software”), to deal in the Software without restriction, including without
# limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2021-05-03 23:29:53 +00:00
2021-05-06 21:21:34 +00:00
# This file is a part of VkBootstrap
# https://github.com/charles-lunarg/vk-bootstrap
2021-06-06 19:41:03 +00:00
# On run, vk.xml is pulled from the master of Khronos's Vulkan-Headers repo and a VkBoostrapDispatch header
2021-05-06 21:21:34 +00:00
# is generated and placed in VkBoostrap's source directory
# https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/registry/vk.xml
# This script makes use of xmltodict
# https://github.com/martinblech/xmltodict
2021-06-06 19:41:03 +00:00
# User will be prompted to install if not detected
2021-05-06 21:21:34 +00:00
2022-02-16 04:29:05 +00:00
# Command Line Arguments
2022-06-20 15:52:16 +00:00
# [--auto] Don't ask for input from the command line
2022-02-16 04:29:05 +00:00
2023-09-19 20:32:22 +00:00
import sys
import os
import subprocess
import copy
import codecs
import re
from string import Template
import urllib . request
import pkg_resources
2021-05-06 21:21:34 +00:00
# Exclusions
2021-05-06 06:24:19 +00:00
exclusions = [
2023-09-19 20:32:22 +00:00
' vkGetDeviceProcAddr ' ,
' vkCreateDevice ' ,
' vkDestroyDevice '
2021-05-06 06:24:19 +00:00
]
2021-05-05 22:55:02 +00:00
2022-02-06 03:36:34 +00:00
# Excluded extension authors - don't generate anything for these types of extensions
excluded_extension_authors = [
2023-09-19 20:32:22 +00:00
' NVX '
2022-02-06 03:36:34 +00:00
]
2022-06-20 15:52:16 +00:00
excluded_alias_types = [
' VkPipelineInfoKHR '
]
2021-05-06 21:21:34 +00:00
# Check for/install xmltodict
installed = { pkg . key for pkg in pkg_resources . working_set }
xmltodict_missing = { ' xmltodict ' } - installed
2021-05-06 06:24:19 +00:00
2021-06-06 19:41:03 +00:00
# Install xmltodict
2021-05-06 21:21:34 +00:00
if xmltodict_missing :
2023-09-19 20:32:22 +00:00
if ' --auto ' not in sys . argv :
val = input ( ' xmltodict is required to run this script. Would you like to install? (y/n): ' )
else :
val = ' y '
if val . lower ( ) == ' y ' :
try :
subprocess . check_call ( [ sys . executable , ' -m ' , ' pip ' , ' install ' , ' xmltodict ' ] )
except subprocess . CalledProcessError as error :
print ( ' Failed to install xmltodict due to error: ' )
print ( error )
if ' --auto ' not in sys . argv :
input ( ' Press Enter to continue... ' )
sys . exit ( )
else :
sys . exit ( )
2021-05-06 06:24:19 +00:00
2021-05-06 21:21:34 +00:00
# Fetch fresh vk.xml from Khronos repo
2021-05-06 06:24:19 +00:00
import xmltodict
2022-02-06 01:26:21 +00:00
try :
2023-09-19 20:32:22 +00:00
response = urllib . request . urlopen ( ' https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/registry/vk.xml ' )
2021-06-03 23:15:10 +00:00
except urllib . error . URLError as error :
2023-09-19 20:32:22 +00:00
print ( ' Failed to download vk.xml due to error: ' )
print ( error . reason )
if ' -q ' not in sys . argv :
input ( ' Press Enter to continue... ' )
sys . exit ( )
2021-06-03 23:15:10 +00:00
vk_xml_raw = response . read ( )
2021-05-06 06:24:19 +00:00
vk_xml = xmltodict . parse ( vk_xml_raw , process_namespaces = True )
2023-09-25 00:28:13 +00:00
command_params = { ' return_type ' : ' ' , ' args ' : [ ] , ' dispatch_type ' : ' ' , ' requirements ' : [ ] , ' macro_template ' : Template ( ' ' ) }
2021-05-19 23:03:49 +00:00
2023-09-25 00:28:13 +00:00
commands = { }
INSTANCE = ' instance '
DEVICE = ' device '
2021-05-06 18:47:16 +00:00
2022-02-06 03:36:34 +00:00
aliased_types = { }
types_node = vk_xml [ ' registry ' ] [ ' types ' ] [ ' type ' ]
for type_node in types_node :
2023-09-19 20:32:22 +00:00
if ' @alias ' in type_node :
aliased_types [ type_node [ ' @alias ' ] ] = type_node [ ' @name ' ]
2022-02-06 03:36:34 +00:00
2021-05-06 21:21:34 +00:00
# Gather all device functions/aliases for filtering core/extension function fetching
2021-05-19 23:03:49 +00:00
commands_node = vk_xml [ ' registry ' ] [ ' commands ' ] [ ' command ' ]
2021-05-06 18:47:16 +00:00
aliases = { }
2021-05-19 23:03:49 +00:00
for command_node in commands_node :
2023-09-19 20:32:22 +00:00
if ' proto ' in command_node :
command_name = command_node [ ' proto ' ] [ ' name ' ]
new_command_params = copy . deepcopy ( command_params )
new_command_params [ ' return_type ' ] = command_node [ ' proto ' ] [ ' type ' ]
if isinstance ( command_node [ ' param ' ] , list ) :
params = command_node [ ' param ' ]
else :
params = [ command_node [ ' param ' ] ]
new_command_params [ ' args ' ] = [ ]
for param in params :
# if the api attribute does exist, make sure it is for vulkan
if not ' @api ' in param or param [ ' @api ' ] == ' vulkan ' :
new_command_params [ ' args ' ] . append ( param )
if not command_name in exclusions :
2023-09-25 00:28:13 +00:00
commands [ command_name ] = new_command_params
commands [ command_name ] [ ' is_alias ' ] = False
2023-09-19 20:32:22 +00:00
if new_command_params [ ' args ' ] [ 0 ] [ ' type ' ] in [ ' VkDevice ' , ' VkCommandBuffer ' , ' VkQueue ' ] :
2023-09-25 00:28:13 +00:00
commands [ command_name ] [ ' dispatch_type ' ] = DEVICE
elif new_command_params [ ' args ' ] [ 0 ] [ ' type ' ] in [ ' VkInstance ' , ' VkPhysicalDevice ' ] :
commands [ command_name ] [ ' dispatch_type ' ] = INSTANCE
2023-09-19 20:32:22 +00:00
elif ' @alias ' in command_node :
aliases [ command_node [ ' @alias ' ] ] = command_node [ ' @name ' ]
2021-05-06 18:47:16 +00:00
2021-05-06 21:21:34 +00:00
# Push the alias name as a device function if the alias exists in device commands
2023-09-19 20:32:22 +00:00
for alias_name , alias in aliases . items ( ) :
2023-09-25 00:28:13 +00:00
if alias in commands :
commands [ alias ] = copy . deepcopy ( commands [ alias_name ] )
commands [ alias ] [ ' is_alias ' ] = True
2021-05-06 18:47:16 +00:00
2021-05-20 01:15:29 +00:00
# Add requirements for core PFN's
2021-05-19 23:03:49 +00:00
features_node = vk_xml [ ' registry ' ] [ ' feature ' ]
for feature_node in features_node :
2023-09-19 20:32:22 +00:00
if feature_node [ ' @name ' ] != ' VK_VERSION_1_0 ' :
for require_node in feature_node [ ' require ' ] :
for param_node in require_node :
if param_node == ' command ' :
if not isinstance ( require_node [ param_node ] , list ) :
require_node [ param_node ] = [ require_node [ param_node ] ]
for param in require_node [ param_node ] :
2023-09-25 00:28:13 +00:00
if param [ ' @name ' ] in commands :
commands [ param [ ' @name ' ] ] [ ' requirements ' ] + = [ [ feature_node [ ' @name ' ] ] ]
2021-05-06 18:47:16 +00:00
2021-05-20 01:15:29 +00:00
# Add requirements for extension PFN's
2021-05-19 23:03:49 +00:00
extensions_node = vk_xml [ ' registry ' ] [ ' extensions ' ] [ ' extension ' ]
for extension_node in extensions_node :
2023-09-19 20:32:22 +00:00
extension_name = extension_node [ ' @name ' ]
if ' require ' in extension_node . keys ( ) :
require_nodes = extension_node [ ' require ' ]
for require_node in require_nodes :
requirements = [ extension_name ]
if not isinstance ( require_node , str ) :
if ' command ' in require_node . keys ( ) :
if ' @feature ' in require_node . keys ( ) :
requirements . append ( require_node [ ' @feature ' ] )
if ' @extension ' in require_node . keys ( ) :
requirements . extend ( require_node [ ' @extension ' ] . split ( ' , ' ) )
if not isinstance ( require_node [ ' command ' ] , list ) :
require_node [ ' command ' ] = [ require_node [ ' command ' ] ]
for command_node in require_node [ ' command ' ] :
2023-09-25 00:28:13 +00:00
if command_node [ ' @name ' ] in commands :
2023-09-19 20:32:22 +00:00
if ' @author ' in extension_node and extension_node [ ' @author ' ] in excluded_extension_authors :
2023-09-25 00:28:13 +00:00
commands . pop ( command_node [ ' @name ' ] )
2023-09-19 20:32:22 +00:00
else :
2023-09-25 00:28:13 +00:00
commands [ command_node [ ' @name ' ] ] [ ' requirements ' ] + = [ requirements ]
2023-09-19 20:32:22 +00:00
elif require_node == ' command ' :
if not isinstance ( require_nodes [ ' command ' ] , list ) :
require_nodes [ ' command ' ] = [ require_nodes [ ' command ' ] ]
for command_node in require_nodes [ ' command ' ] :
2023-09-25 00:28:13 +00:00
if command_node [ ' @name ' ] in commands :
2023-09-19 20:32:22 +00:00
if ' @author ' in extension_node and extension_node [ ' @author ' ] in excluded_extension_authors :
2023-09-25 00:28:13 +00:00
commands . pop ( command_node [ ' @name ' ] )
2023-09-19 20:32:22 +00:00
else :
2023-09-25 00:28:13 +00:00
commands [ command_node [ ' @name ' ] ] [ ' requirements ' ] + = [ requirements ]
2021-05-20 01:15:29 +00:00
2021-05-20 08:19:51 +00:00
# Generate macro templates
2023-09-25 00:28:13 +00:00
for command_name , command in commands . items ( ) :
2023-09-19 20:32:22 +00:00
macro = ' '
2023-09-25 00:28:13 +00:00
requirements_collection = commands [ command_name ] [ ' requirements ' ]
2023-09-19 20:32:22 +00:00
collection_count = len ( requirements_collection )
if collection_count > 0 :
macro = ' #if '
while collection_count > 0 :
for requirements in requirements_collection :
requirements_count = len ( requirements )
macro + = ' ( '
for requirement in requirements :
macro + = f ' defined( { requirement } ) '
requirements_count - = 1
if requirements_count > 0 :
macro + = ' && '
macro + = ' ) '
if collection_count > 0 :
collection_count - = 1
if collection_count > 0 :
macro + = ' || '
macro + = ' \n $body#endif \n '
else :
macro = ' $body '
2023-09-25 00:28:13 +00:00
commands [ command_name ] [ ' macro_template ' ] = Template ( macro )
2021-05-06 21:21:34 +00:00
2023-10-13 17:12:13 +00:00
for command_name , command in commands . items ( ) :
pfn_decl_macro = ' '
requirements_collection = commands [ command_name ] [ ' requirements ' ]
collection_count = len ( requirements_collection )
if collection_count > 0 :
pfn_decl_macro = ' #if '
while collection_count > 0 :
for requirements in requirements_collection :
requirements_count = len ( requirements )
pfn_decl_macro + = ' ( '
for requirement in requirements :
pfn_decl_macro + = f ' defined( { requirement } ) '
requirements_count - = 1
if requirements_count > 0 :
pfn_decl_macro + = ' && '
pfn_decl_macro + = ' ) '
if collection_count > 0 :
collection_count - = 1
if collection_count > 0 :
pfn_decl_macro + = ' || '
pfn_decl_macro + = f ' \n $body#else \n void * fp_ { command_name } {{ }} ; \n #endif \n '
else :
pfn_decl_macro = ' $body '
commands [ command_name ] [ ' pfn_decl_macro_template ' ] = Template ( pfn_decl_macro )
2021-05-06 21:21:34 +00:00
# License
2023-09-19 20:32:22 +00:00
dispatch_license = ''' /*
* Copyright © 2021 Cody Goodson ( contact @vibimanx.com )
* Copyright © 2022 Charles Giessen ( charles @lunarg.com )
*
* Permission is hereby granted , free of charge , to any person obtaining a copy of this software and associated
* documentation files ( the “ Software ” ) , to deal in the Software without restriction , including without
* limitation the rights to use , copy , modify , merge , publish , distribute , sublicense , and / or sell copies
* of the Software , and to permit persons to whom the Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED “ AS IS ” , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR IMPLIED , INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY ,
* WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*
* /
'''
2021-05-06 21:21:34 +00:00
2021-05-20 08:19:51 +00:00
# Info
info = ' // This file is a part of VkBootstrap \n '
info + = ' // https://github.com/charles-lunarg/vk-bootstrap \n \n '
2021-05-06 21:21:34 +00:00
2021-05-19 23:03:49 +00:00
# # Content
2023-09-25 00:28:13 +00:00
head = ' \n #pragma once \n \n #include <vulkan/vulkan.h> \n \n '
head + = ' namespace vkb { \n \n '
def create_dispatch_table ( dispatch_type ) :
out = ' '
if dispatch_type == INSTANCE :
out + = ' struct InstanceDispatchTable { \n '
2023-10-13 16:58:15 +00:00
out + = ' InstanceDispatchTable() = default; \n '
out + = ' InstanceDispatchTable(VkInstance instance, PFN_vkGetInstanceProcAddr procAddr) : instance(instance), populated(true) { \n '
2023-09-19 20:32:22 +00:00
else :
2023-09-25 00:28:13 +00:00
out + = ' struct DispatchTable { \n '
2023-10-13 16:58:15 +00:00
out + = ' DispatchTable() = default; \n '
out + = ' DispatchTable(VkDevice device, PFN_vkGetDeviceProcAddr procAddr) : device(device), populated(true) { \n '
2023-09-25 00:28:13 +00:00
proxy_section = ' '
fp_decl_section = ' '
pfn_load_section = ' '
2023-10-13 16:58:15 +00:00
proxy_template = Template ( ' $return_type $proxy_name($args_full) const noexcept { \n $opt_return$fp_name($args_names); \n } \n ' )
fp_decl_template = Template ( ' $pfn_name $fp_name = nullptr; \n ' )
2023-09-25 00:28:13 +00:00
if dispatch_type == INSTANCE :
2023-10-13 16:58:15 +00:00
pfn_load_template = Template ( ' $fp_name = reinterpret_cast<$pfn_name>(procAddr(instance, " $command_name " )); \n ' )
2023-09-25 00:28:13 +00:00
else :
2023-10-13 16:58:15 +00:00
pfn_load_template = Template ( ' $fp_name = reinterpret_cast<$pfn_name>(procAddr(device, " $command_name " )); \n ' )
2023-09-25 00:28:13 +00:00
for command_name , command in commands . items ( ) :
if command [ ' dispatch_type ' ] != dispatch_type :
continue
params = commands [ command_name ]
# easy stuff out of the way
return_type = params [ ' return_type ' ]
if return_type != ' void ' :
opt_return = ' return '
2023-09-19 20:32:22 +00:00
else :
2023-09-25 00:28:13 +00:00
opt_return = ' '
proxy_name = command_name [ 2 ] . lower ( ) + command_name [ 3 : ]
fp_name = ' fp_ ' + command_name
pfn_name = ' PFN_ ' + command_name
# Now for args
arg_template = Template ( ' $front_mods$arg_type$back_mods$arg_name$array ' )
args_full = ' '
args_names = ' '
args_count = len ( params [ ' args ' ] )
i = args_count
for arg in params [ ' args ' ] :
front_mods = ' '
back_mods = ' '
array = ' '
arg_type = arg [ ' type ' ]
arg_name = arg [ ' name ' ]
if ' #text ' in arg :
text = arg [ ' #text ' ]
text = text . replace ( ' ' , ' ' )
array_index = text . find ( ' [ ' )
if array_index != - 1 :
array = text [ array_index : ]
text = text [ 0 : array_index ]
if text == ' * ' :
front_mods = ' '
back_mods = ' * '
elif text == ' ** ' :
front_mods = ' '
back_mods = ' ** '
elif text == ' struct** ' :
front_mods = ' struct '
back_mods = ' ** '
elif text == ' const* ' :
front_mods = ' const '
back_mods = ' * '
elif text == ' const** ' :
front_mods = ' const '
back_mods = ' ** '
elif text == ' const*const* ' :
front_mods = ' const '
back_mods = ' * const* '
elif text == ' conststruct* ' :
front_mods = ' const struct '
back_mods = ' * '
if i == args_count and ( dispatch_type == INSTANCE and arg_type == ' VkInstance ' ) or ( dispatch_type == DEVICE and arg_type == ' VkDevice ' ) :
args_names + = arg_name
if i > 0 :
i - = 1
if i > 0 :
args_names + = ' , '
else :
if arg_type in aliased_types and arg_type not in excluded_alias_types :
arg_type = aliased_types [ arg_type ]
args_full + = arg_template . substitute ( front_mods = front_mods , arg_type = arg_type , back_mods = back_mods , arg_name = arg_name , array = array )
args_names + = arg_name
2023-09-19 20:32:22 +00:00
if i > 0 :
2023-09-25 00:28:13 +00:00
i - = 1
if i > 0 :
args_full + = ' , '
args_names + = ' , '
proxy_body = proxy_template . substitute ( return_type = return_type , proxy_name = proxy_name , args_full = args_full , opt_return = opt_return , fp_name = fp_name , args_names = args_names )
fp_decl_body = fp_decl_template . substitute ( pfn_name = pfn_name , fp_name = fp_name )
pfn_load_body = pfn_load_template . substitute ( fp_name = fp_name , pfn_name = pfn_name , command_name = command_name )
macro_template = params [ ' macro_template ' ]
2023-10-13 17:12:13 +00:00
pfn_decl_macro_template = params [ ' pfn_decl_macro_template ' ]
2023-09-25 00:28:13 +00:00
proxy_section + = macro_template . substitute ( body = proxy_body )
2023-10-13 17:12:13 +00:00
fp_decl_section + = pfn_decl_macro_template . substitute ( body = fp_decl_body )
2023-09-25 00:28:13 +00:00
pfn_load_section + = macro_template . substitute ( body = pfn_load_body )
out + = pfn_load_section
2023-10-13 16:58:15 +00:00
out + = ' } \n '
2023-09-25 00:28:13 +00:00
out + = proxy_section
out + = fp_decl_section
2023-10-13 16:58:15 +00:00
out + = ' bool is_populated() const { return populated; } \n '
2023-09-25 00:28:13 +00:00
if dispatch_type == INSTANCE :
2023-10-13 16:58:15 +00:00
out + = ' VkInstance instance = VK_NULL_HANDLE; \n '
2023-09-25 00:28:13 +00:00
else :
2023-10-13 16:58:15 +00:00
out + = ' VkDevice device = VK_NULL_HANDLE; \n '
2023-09-25 00:28:13 +00:00
out + = ' private: \n '
2023-10-13 16:58:15 +00:00
out + = ' bool populated = false; \n '
2023-09-25 00:28:13 +00:00
out + = ' }; \n \n '
return out
tail = ' } // namespace vkb '
2021-05-06 20:46:33 +00:00
2022-02-16 04:29:05 +00:00
# find the version used to generate the code
for type_node in types_node :
2023-09-19 20:32:22 +00:00
if ' @api ' in type_node and type_node [ ' @api ' ] == ' vulkan ' and ' name ' in type_node and type_node [ ' name ' ] == ' VK_HEADER_VERSION_COMPLETE ' :
complete_header_version = type_node [ ' #text ' ]
if ' @api ' in type_node and type_node [ ' @api ' ] == ' vulkan ' and ' name ' in type_node and type_node [ ' name ' ] == ' VK_HEADER_VERSION ' :
vk_header_version = type_node [ ' #text ' ]
2022-02-16 04:29:05 +00:00
find_number_fields = re . compile ( ' [0-9]+ ' )
version_fields = find_number_fields . findall ( complete_header_version )
header_version_field = find_number_fields . findall ( vk_header_version ) [ 0 ]
version_tag = f ' { version_fields [ 1 ] } . { version_fields [ 2 ] } . { header_version_field } '
path_to_src = os . path . join ( ' src ' )
if not os . path . exists ( path_to_src ) :
2023-09-19 20:32:22 +00:00
path_to_src = os . path . join ( ' .. ' , ' src ' )
2022-02-16 04:29:05 +00:00
if not os . path . exists ( path_to_src ) :
2023-09-19 20:32:22 +00:00
print ( ' Could not find source folder. Is the current directory wrong? ' )
sys . exit ( )
2022-02-16 04:29:05 +00:00
2023-09-19 20:32:22 +00:00
header_file = codecs . open ( os . path . join ( path_to_src , ' VkBootstrapDispatch.h ' ) , ' w ' , ' utf-8 ' )
2023-09-25 00:28:13 +00:00
header_file . write ( dispatch_license + info + head + create_dispatch_table ( ' instance ' ) + create_dispatch_table ( ' device ' ) + tail )
2022-02-06 01:26:21 +00:00
header_file . close ( )
2021-06-03 19:27:34 +00:00
2022-02-16 04:29:05 +00:00
path_to_gen = os . path . join ( ' gen ' )
if not os . path . exists ( path_to_gen ) :
2023-09-19 20:32:22 +00:00
path_to_gen = os . path . join ( ' .. ' , ' gen ' )
2022-02-16 04:29:05 +00:00
if not os . path . exists ( path_to_gen ) :
2023-09-19 20:32:22 +00:00
print ( ' Could not find gen folder. Is the current directory wrong? ' )
sys . exit ( )
2022-02-16 04:29:05 +00:00
# Generate a CMake file that contains the header version used.
2023-09-19 20:32:22 +00:00
cmake_version_file = codecs . open ( os . path . join ( path_to_gen , ' CurrentBuildVulkanVersion.cmake ' ) , ' w ' , ' utf-8 ' )
2022-02-16 04:29:05 +00:00
cmake_version_file . write ( f ' set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION { version_tag } ) \n ' )
cmake_version_file . write ( f ' set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION_GIT_TAG v { version_tag } ) \n ' )
cmake_version_file . close ( )
2023-09-19 20:32:22 +00:00
print ( ' Generation finished. ' )