1#!/usr/bin/env python3 2# 3# Copyright 2020-2023 The Khronos Group Inc. 4# 5# SPDX-License-Identifier: Apache-2.0 6 7# Build a spec with requested extension sets and options. 8# 9# Usage: makeSpec script-options make-options 10# Script options are parsed by this script before invoking 'make': 11# -genpath path - directory for generated files and outputs 12# -spec core - make a spec with no extensions (default) 13# -spec khr - make a spec with all KHR extensions 14# -spec ratified - make a spec with all ratified (KHR + some EXT) extensions 15# -spec all - make a spec with all registered extensions 16# -version {1.0 | 1.1 | 1.2 | 1.3 | sc1.0} - make a spec with this core version 17# -ext name - add specified extension and its dependencies 18# -clean - clean generated files before building 19# -registry path - API XML to use instead of default 20# -apiname name - API name to use instead of default 21# -test - Build the test spec instead 22# -v - verbose, print actions before executing them 23# -n - dry-run, print actions instead of executing them 24# make-options - all other options are passed to 'make', including 25# requested build targets 26 27import argparse, copy, io, os, re, string, subprocess, sys 28 29def execute(args, results): 30 if results.verbose or results.dryrun: 31 print("'" + "' '".join(args) + "'") 32 if not results.dryrun: 33 subprocess.check_call(args) 34 35if __name__ == '__main__': 36 parser = argparse.ArgumentParser() 37 38 parser.add_argument('-clean', action='store_true', 39 help='Clean generated files before building') 40 parser.add_argument('-extension', action='append', 41 default=[], 42 help='Specify a required extension or extensions to add to targets') 43 parser.add_argument('-genpath', action='store', 44 default='gen', 45 help='Path to directory containing generated files') 46 parser.add_argument('-spec', action='store', 47 choices=[ 'core', 'khr', 'ratified', 'all' ], 48 default='core', 49 help='Type of spec to generate') 50 parser.add_argument('-version', action='store', 51 choices=[ '1.0', '1.1', '1.2', '1.3', 'sc1.0' ], 52 default='1.3', 53 help='Type of spec to generate') 54 parser.add_argument('-registry', action='store', 55 default=None, 56 help='Path to API XML registry file specifying version and extension dependencies') 57 parser.add_argument('-apiname', action='store', 58 default=None, 59 help='API name to generate') 60 parser.add_argument('-test', action='store_true', 61 help='Build the test spec instead of the Vulkan spec') 62 parser.add_argument('-n', action='store_true', dest='dryrun', 63 help='Only prints actions, do not execute them') 64 parser.add_argument('-v', action='store_true', dest='verbose', 65 help='Print actions before executing them') 66 67 (results, options) = parser.parse_known_args() 68 69 # Ensure genpath is an absolute path, not relative 70 if results.genpath[0] != '/': 71 results.genpath = os.getcwd() + '/' + results.genpath 72 73 # Look for scripts/extdependency.py 74 # This requires makeSpec to be invoked from the repository root, but we 75 # could derive that path. 76 sys.path.insert(0, 'scripts') 77 from extdependency import ApiDependencies 78 deps = ApiDependencies(results.registry, results.apiname) 79 80 # List of versions to build with from the requested -version 81 # This should come from the extdependency module as well, eventually 82 #@ Note that at present, building sc1.0 does *not* include Vulkan 1.3 automatically. 83 versionDict = { 84 '1.0' : [ 'VK_VERSION_1_0' ], 85 '1.1' : [ 'VK_VERSION_1_0', 'VK_VERSION_1_1' ], 86 '1.2' : [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2' ], 87 '1.3' : [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2', 'VK_VERSION_1_3' ], 88 'sc1.0' : [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2', 'VKSC_VERSION_1_0' ], 89 } 90 versions = 'VERSIONS={}'.format(' '.join(versionDict[results.version])) 91 92 # List of extensions to build with from the requested -spec 93 # Also construct a spec title 94 # This should respect version dependencies as well 95 if results.spec == 'core': 96 title = '' 97 exts = set() 98 elif results.spec == 'khr': 99 title = 'with all KHR extensions' 100 exts = set(deps.khrExtensions()) 101 elif results.spec == 'ratified': 102 title = 'with all ratified extensions' 103 exts = set(deps.ratifiedExtensions()) 104 elif results.spec == 'all': 105 title = 'with all registered extensions' 106 exts = set(deps.allExtensions()) 107 108 # List of explicitly requested extension and all its supported dependencies 109 extraexts = set() 110 for name in results.extension: 111 if name in deps.allExtensions(): 112 extraexts.add(name) 113 for dep in deps.children(name): 114 if dep in deps.allExtensions(): 115 extraexts.update({dep}) 116 else: 117 raise Exception(f'ERROR: unknown extension {name}') 118 119 # See if any explicitly requested extensions are not implicitly requested 120 # Add any such extensions to the spec title 121 extraexts -= exts 122 if len(extraexts) > 0: 123 exts.update(extraexts) 124 if title != '': 125 title += ' and ' + ', '.join(sorted(extraexts)) 126 else: 127 title += 'with ' + ', '.join(sorted(extraexts)) 128 129 if title != '': 130 title = '(' + title + ')' 131 132 # Finally, actually invoke make as needed for the targets 133 args = [ 'make', 'GENERATED=' + results.genpath ] 134 135 if results.clean: 136 # If OUTDIR is set on the command line, pass it to the 'clean' 137 # target so it is cleaned as well. 138 cleanopts = ['clean'] 139 for opt in options: 140 if opt[:7] == 'OUTDIR=': 141 cleanopts.append(opt) 142 try: 143 execute(args + cleanopts, results) 144 except: 145 sys.exit(1) 146 147 # Use the test spec if specified. This is used solely by self tests. 148 rootdir = os.path.dirname(os.path.abspath(__file__)) 149 if results.test: 150 # Set the spec source to the test spec 151 args.append(f'SPECSRC={rootdir}/build_tests/testspec.adoc') 152 args.append(f'SPECDIR={rootdir}/build_tests/') 153 # Make sure the build is invariant 154 args.append('SPECREVISION=1.2.3') 155 args.append('SPECDATE=\\"2100-11-22 00:33:44Z\\"') 156 args.append('SPECREMARK=\\"test build\\"') 157 158 args.append(versions) 159 160 # The actual target 161 if len(exts) > 0: 162 args.append(f'EXTENSIONS={" ".join(sorted(exts))}') 163 args.append(f'APITITLE={title}') 164 args += options 165 166 try: 167 execute(args, results) 168 except: 169 sys.exit(1) 170