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 gen_cmds = [[common_codegen.repo_relative('scripts/loader_genvk.py'), 48 '-registry', os.path.abspath(os.path.join(args.registry, 'vk.xml')), 49 '-quiet', 50 filename] for filename in ['vk_layer_dispatch_table.h', 51 'vk_loader_extensions.h', 52 'vk_loader_extensions.c', 53 'vk_object_types.h']] 54 55 repo_dir = common_codegen.repo_relative('loader/generated') 56 57 # get directory where generators will run 58 if args.verify or args.incremental: 59 # generate in temp directory so we can compare or copy later 60 temp_obj = tempfile.TemporaryDirectory(prefix='loader_codegen_') 61 temp_dir = temp_obj.name 62 gen_dir = temp_dir 63 else: 64 # generate directly in the repo 65 gen_dir = repo_dir 66 67 # run each code generator 68 for cmd in gen_cmds: 69 print(' '.join(cmd)) 70 try: 71 subprocess.check_call([sys.executable] + cmd, 72 # ignore generator output, vk_validation_stats.py is especially noisy 73 stdout=subprocess.DEVNULL, 74 cwd=gen_dir) 75 except Exception as e: 76 print('ERROR:', str(e)) 77 return 1 78 79 # optional post-generation steps 80 if args.verify: 81 # compare contents of temp dir and repo 82 temp_files = set(os.listdir(temp_dir)) 83 repo_files = set(os.listdir(repo_dir)) 84 files_match = True 85 for filename in sorted((temp_files | repo_files)): 86 if filename not in repo_files: 87 print('ERROR: Missing repo file', filename) 88 files_match = False 89 elif filename not in temp_files: 90 print('ERROR: Missing generator for', filename) 91 files_match = False 92 elif not filecmp.cmp(os.path.join(temp_dir, filename), 93 os.path.join(repo_dir, filename), 94 shallow=False): 95 print('ERROR: Repo files do not match generator output for', filename) 96 files_match = False 97 98 # return code for test scripts 99 if files_match: 100 print('SUCCESS: Repo files match generator output') 101 return 0 102 return 1 103 104 elif args.incremental: 105 # copy missing or differing files from temp directory to repo 106 for filename in os.listdir(temp_dir): 107 temp_filename = os.path.join(temp_dir, filename) 108 repo_filename = os.path.join(repo_dir, filename) 109 if not os.path.exists(repo_filename) or \ 110 not filecmp.cmp(temp_filename, repo_filename, shallow=False): 111 print('update', repo_filename) 112 shutil.copyfile(temp_filename, repo_filename) 113 114 # write out the header version used to generate the code to a checked in CMake file 115 if args.generated_version: 116 # Update the CMake project version 117 with open(common_codegen.repo_relative('CMakeLists.txt'), "r+") as f: 118 data = f.read() 119 f.seek(0) 120 f.write(re.sub("project.*VERSION.*", f"project(VULKAN_LOADER VERSION {args.generated_version} LANGUAGES C)", data)) 121 f.truncate() 122 123 with open(common_codegen.repo_relative('loader/loader.rc.in'), "r") as rc_file: 124 rc_file_contents = rc_file.read() 125 rc_ver = ', '.join(args.generated_version.split('.') + ['0']) 126 rc_file_contents = rc_file_contents.replace('${LOADER_VER_FILE_VERSION}', f'{rc_ver}') 127 rc_file_contents = rc_file_contents.replace('${LOADER_VER_FILE_DESCRIPTION_STR}', f'"{args.generated_version}.Dev Build"') 128 rc_file_contents = rc_file_contents.replace('${LOADER_VER_FILE_VERSION_STR}', f'"Vulkan Loader - Dev Build"') 129 rc_file_contents = rc_file_contents.replace('${LOADER_CUR_COPYRIGHT_YEAR}', f'{datetime.date.today().year}') 130 with open(common_codegen.repo_relative('loader/loader.rc'), "w") as rc_file_out: 131 rc_file_out.write(rc_file_contents) 132 rc_file_out.close() 133 134 return 0 135 136if __name__ == '__main__': 137 sys.exit(main(sys.argv[1:])) 138 139