1#!/usr/bin/env python3 2# Copyright (c) 2019 The Khronos Group Inc. 3# Copyright (c) 2019 Valve Corporation 4# Copyright (c) 2019 LunarG, Inc. 5# Copyright (c) 2019 Google Inc. 6# Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 7# Copyright (c) 2023-2023 RasterGrid Kft. 8# 9# Licensed under the Apache License, Version 2.0 (the "License"); 10# you may not use this file except in compliance with the License. 11# You may obtain a copy of the License at 12# 13# http://www.apache.org/licenses/LICENSE-2.0 14# 15# Unless required by applicable law or agreed to in writing, software 16# distributed under the License is distributed on an "AS IS" BASIS, 17# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18# See the License for the specific language governing permissions and 19# limitations under the License. 20# 21# Author: Mike Schuchardt <mikes@lunarg.com> 22 23import argparse 24import common_codegen 25import filecmp 26import os 27import shutil 28import subprocess 29import sys 30import tempfile 31import datetime 32import re 33 34def main(argv): 35 parser = argparse.ArgumentParser(description='Generate source code for this repository') 36 parser.add_argument('registry', metavar='REGISTRY_PATH', help='path to the Vulkan-Headers registry directory') 37 parser.add_argument('--api', 38 default='vulkan', 39 choices=['vulkan'], 40 help='Specify API name to generate') 41 parser.add_argument('--generated-version', help='sets the header version used to generate the repo') 42 group = parser.add_mutually_exclusive_group() 43 group.add_argument('-i', '--incremental', action='store_true', help='only update repo files that change') 44 group.add_argument('-v', '--verify', action='store_true', help='verify repo files match generator output') 45 args = parser.parse_args(argv) 46 47 registry = os.path.abspath(os.path.join(args.registry, 'vk.xml')) 48 if not os.path.isfile(registry): 49 registry = os.path.abspath(os.path.join(args.registry, 'Vulkan-Headers/registry/vk.xml')) 50 if not os.path.isfile(registry): 51 print(f'cannot find vk.xml in {args.registry}') 52 return -1 53 54 gen_cmds = [[common_codegen.repo_relative('scripts/loader_genvk.py'), 55 '-registry', registry, 56 '-quiet', 57 filename] for filename in ['vk_layer_dispatch_table.h', 58 'vk_loader_extensions.h', 59 'vk_loader_extensions.c', 60 'vk_object_types.h']] 61 62 repo_dir = common_codegen.repo_relative('loader/generated') 63 64 # get directory where generators will run 65 if args.verify or args.incremental: 66 # generate in temp directory so we can compare or copy later 67 temp_obj = tempfile.TemporaryDirectory(prefix='loader_codegen_') 68 temp_dir = temp_obj.name 69 gen_dir = temp_dir 70 else: 71 # generate directly in the repo 72 gen_dir = repo_dir 73 74 # run each code generator 75 for cmd in gen_cmds: 76 print(' '.join(cmd)) 77 try: 78 subprocess.check_call([sys.executable] + cmd, 79 # ignore generator output, vk_validation_stats.py is especially noisy 80 stdout=subprocess.DEVNULL, 81 cwd=gen_dir) 82 except Exception as e: 83 print('ERROR:', str(e)) 84 return 1 85 86 # optional post-generation steps 87 if args.verify: 88 # compare contents of temp dir and repo 89 temp_files = set(os.listdir(temp_dir)) 90 repo_files = set(os.listdir(repo_dir)) 91 files_match = True 92 for filename in sorted((temp_files | repo_files)): 93 if filename not in repo_files: 94 print('ERROR: Missing repo file', filename) 95 files_match = False 96 elif filename not in temp_files: 97 print('ERROR: Missing generator for', filename) 98 files_match = False 99 elif not filecmp.cmp(os.path.join(temp_dir, filename), 100 os.path.join(repo_dir, filename), 101 shallow=False): 102 print('ERROR: Repo files do not match generator output for', filename) 103 files_match = False 104 105 # return code for test scripts 106 if files_match: 107 print('SUCCESS: Repo files match generator output') 108 return 0 109 return 1 110 111 elif args.incremental: 112 # copy missing or differing files from temp directory to repo 113 for filename in os.listdir(temp_dir): 114 temp_filename = os.path.join(temp_dir, filename) 115 repo_filename = os.path.join(repo_dir, filename) 116 if not os.path.exists(repo_filename) or \ 117 not filecmp.cmp(temp_filename, repo_filename, shallow=False): 118 print('update', repo_filename) 119 shutil.copyfile(temp_filename, repo_filename) 120 121 # write out the header version used to generate the code to a checked in CMake file 122 if args.generated_version: 123 # Update the CMake project version 124 with open(common_codegen.repo_relative('CMakeLists.txt'), "r+") as f: 125 data = f.read() 126 f.seek(0) 127 f.write(re.sub("project.*VERSION.*", f"project(VULKAN_LOADER VERSION {args.generated_version} LANGUAGES C)", data)) 128 f.truncate() 129 130 with open(common_codegen.repo_relative('loader/loader.rc.in'), "r") as rc_file: 131 rc_file_contents = rc_file.read() 132 rc_ver = ', '.join(args.generated_version.split('.') + ['0']) 133 rc_file_contents = rc_file_contents.replace('${LOADER_VER_FILE_VERSION}', f'{rc_ver}') 134 rc_file_contents = rc_file_contents.replace('${LOADER_VER_FILE_DESCRIPTION_STR}', f'"{args.generated_version}.Dev Build"') 135 rc_file_contents = rc_file_contents.replace('${LOADER_VER_FILE_VERSION_STR}', f'"Vulkan Loader - Dev Build"') 136 rc_file_contents = rc_file_contents.replace('${LOADER_CUR_COPYRIGHT_YEAR}', f'{datetime.date.today().year}') 137 with open(common_codegen.repo_relative('loader/loader.rc'), "w") as rc_file_out: 138 rc_file_out.write(rc_file_contents) 139 rc_file_out.close() 140 141 return 0 142 143if __name__ == '__main__': 144 sys.exit(main(sys.argv[1:])) 145 146