• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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