1#!/usr/bin/env python3 2 3##################################################################### 4# Copyright (c) 2020 The Khronos Group Inc. All Rights Reserved. 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17##################################################################### 18 19"""Assembles the SPIR-V assembly files used by spirv_new into binaries, 20 and validates them using spirv-val. Either run this from the parent 21 of the spirv_asm directory, or pass the --source-dir and --output-dir 22 options to specify the locations of the assembly files and the 23 binaries to be generated. 24""" 25 26import argparse 27import glob 28import os 29import subprocess 30import sys 31from textwrap import wrap 32 33 34def fatal(message): 35 """Print an error message and exit with a non-zero status, to 36 indicate failure. 37 """ 38 print(message) 39 sys.exit(1) 40 41 42def assemble_spirv(asm_dir, bin_dir, spirv_as, verbose): 43 """Assemble SPIR-V source into binaries.""" 44 45 if not os.path.exists(bin_dir): 46 os.makedirs(bin_dir) 47 48 assembly_failures = False 49 50 for asm_file_path in glob.glob(os.path.join(asm_dir, '*.spvasm*')): 51 asm_file = os.path.basename(asm_file_path) 52 if os.path.isfile(asm_file_path): 53 if verbose: 54 print(' Assembling {}'.format(asm_file)) 55 56 asm_file_root, asm_file_ext = os.path.splitext(asm_file) 57 bin_file = asm_file_root + asm_file_ext.replace('asm', '') 58 bin_file_path = os.path.join(bin_dir, bin_file) 59 60 command = '"{}" --target-env spv1.0 "{}" -o "{}"'.format( 61 spirv_as, asm_file_path, bin_file_path) 62 if subprocess.call(command, shell=True) != 0: 63 assembly_failures = True 64 print('ERROR: Failure assembling {}: ' 65 'see above output.'.format( 66 asm_file)) 67 print() 68 69 if assembly_failures: 70 fatal('\n'.join(wrap( 71 'ERROR: Assembly failure(s) occurred. See above for error ' 72 'messages from the assembler, if any.'))) 73 74 75def validate_spirv(bin_dir, spirv_val, verbose): 76 """Validates SPIR-V binaries. Ignores known failures.""" 77 78 validation_failures = False 79 80 for bin_file_path in glob.glob(os.path.join(bin_dir, '*.spv*')): 81 bin_file = os.path.basename(bin_file_path) 82 if os.path.isfile(bin_file_path): 83 if verbose: 84 print(' Validating {}'.format(bin_file)) 85 86 command = '"{}" "{}"'.format( 87 spirv_val, bin_file_path) 88 if subprocess.call(command, shell=True) != 0: 89 print('ERROR: Failure validating {}: ' 90 'see above output.'.format( 91 bin_file)) 92 validation_failures = True 93 print() 94 95 if validation_failures: 96 fatal('ERROR: Validation failure(s) found. ' 97 'See above for validation output.') 98 else: 99 print('All SPIR-V binaries validated successfully.') 100 101 102def parse_args(): 103 """Parse the command-line arguments.""" 104 105 argparse_kwargs = ( 106 {'allow_abbrev': False} if sys.version_info >= (3, 5) else {}) 107 argparse_kwargs['description'] = ( 108 '''Assembles the SPIR-V assembly files used by spirv_new into 109 binaries, and validates them using spirv-val. Either run this 110 from the parent of the spirv_asm directory, or pass the 111 --source-dir and --output-dir options to specify the locations 112 the assembly files and the binaries to be generated. 113 ''') 114 parser = argparse.ArgumentParser(**argparse_kwargs) 115 parser.add_argument('-s', '--source-dir', metavar='DIR', 116 default='spirv_asm', 117 help='''specifies the directory containing SPIR-V 118 assembly files''') 119 parser.add_argument('-o', '--output-dir', metavar='DIR', 120 default='spirv_bin', 121 help='''specifies the directory in which to 122 output SPIR-V binary files''') 123 parser.add_argument('-a', '--assembler', metavar='PROGRAM', 124 default='spirv-as', 125 help='''specifies the program to use for assembly 126 of SPIR-V, defaults to spirv-as''') 127 parser.add_argument('-l', '--validator', metavar='PROGRAM', 128 default='spirv-val', 129 help='''specifies the program to use for validation 130 of SPIR-V, defaults to spirv-val''') 131 parser.add_argument('-k', '--skip-validation', action='store_true', 132 default=False, 133 help='skips validation of the genareted SPIR-V') 134 parser.add_argument('-v', '--verbose', action='store_true', default=False, 135 help='''enable verbose output (i.e. prints the 136 name of each SPIR-V assembly file or 137 binary as it is assembled or validated) 138 ''') 139 return parser.parse_args() 140 141 142def main(): 143 """Main function. Assembles and validates SPIR-V.""" 144 145 args = parse_args() 146 147 print('Assembling SPIR-V source into binaries...') 148 assemble_spirv(args.source_dir, args.output_dir, args.assembler, 149 args.verbose) 150 print('Finished assembling SPIR-V binaries.') 151 print() 152 153 if args.skip_validation: 154 print('Skipping validation of SPIR-V binaries as requested.') 155 else: 156 print('Validating SPIR-V binaries...') 157 validate_spirv(args.output_dir, args.validator, args.verbose) 158 print() 159 160 print('Done.') 161 162 163if __name__ == '__main__': 164 main() 165