| #!/usr/bin/env python3 |
| |
| ##################################################################### |
| # Copyright (c) 2020 The Khronos Group Inc. All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| ##################################################################### |
| |
| """Assembles the SPIR-V assembly files used by spirv_new into binaries, |
| and validates them using spirv-val. Either run this from the parent |
| of the spirv_asm directory, or pass the --source-dir and --output-dir |
| options to specify the locations of the assembly files and the |
| binaries to be generated. |
| """ |
| |
| import argparse |
| import glob |
| import os |
| import subprocess |
| import sys |
| from textwrap import wrap |
| |
| |
| def fatal(message): |
| """Print an error message and exit with a non-zero status, to |
| indicate failure. |
| """ |
| print(message) |
| sys.exit(1) |
| |
| |
| def assemble_spirv(asm_dir, bin_dir, spirv_as, verbose): |
| """Assemble SPIR-V source into binaries.""" |
| |
| if not os.path.exists(bin_dir): |
| os.makedirs(bin_dir) |
| |
| assembly_failures = False |
| |
| for asm_file_path in glob.glob(os.path.join(asm_dir, '*.spvasm*')): |
| asm_file = os.path.basename(asm_file_path) |
| if os.path.isfile(asm_file_path): |
| if verbose: |
| print(' Assembling {}'.format(asm_file)) |
| |
| asm_file_root, asm_file_ext = os.path.splitext(asm_file) |
| bin_file = asm_file_root + asm_file_ext.replace('asm', '') |
| bin_file_path = os.path.join(bin_dir, bin_file) |
| |
| command = '"{}" --target-env spv1.0 "{}" -o "{}"'.format( |
| spirv_as, asm_file_path, bin_file_path) |
| if subprocess.call(command, shell=True) != 0: |
| assembly_failures = True |
| print('ERROR: Failure assembling {}: ' |
| 'see above output.'.format( |
| asm_file)) |
| print() |
| |
| if assembly_failures: |
| fatal('\n'.join(wrap( |
| 'ERROR: Assembly failure(s) occurred. See above for error ' |
| 'messages from the assembler, if any.'))) |
| |
| |
| def validate_spirv(bin_dir, spirv_val, verbose): |
| """Validates SPIR-V binaries. Ignores known failures.""" |
| |
| validation_failures = False |
| |
| for bin_file_path in glob.glob(os.path.join(bin_dir, '*.spv*')): |
| bin_file = os.path.basename(bin_file_path) |
| if os.path.isfile(bin_file_path): |
| if verbose: |
| print(' Validating {}'.format(bin_file)) |
| |
| command = '"{}" "{}"'.format( |
| spirv_val, bin_file_path) |
| if subprocess.call(command, shell=True) != 0: |
| print('ERROR: Failure validating {}: ' |
| 'see above output.'.format( |
| bin_file)) |
| validation_failures = True |
| print() |
| |
| if validation_failures: |
| fatal('ERROR: Validation failure(s) found. ' |
| 'See above for validation output.') |
| else: |
| print('All SPIR-V binaries validated successfully.') |
| |
| |
| def parse_args(): |
| """Parse the command-line arguments.""" |
| |
| argparse_kwargs = ( |
| {'allow_abbrev': False} if sys.version_info >= (3, 5) else {}) |
| argparse_kwargs['description'] = ( |
| '''Assembles the SPIR-V assembly files used by spirv_new into |
| binaries, and validates them using spirv-val. Either run this |
| from the parent of the spirv_asm directory, or pass the |
| --source-dir and --output-dir options to specify the locations |
| the assembly files and the binaries to be generated. |
| ''') |
| parser = argparse.ArgumentParser(**argparse_kwargs) |
| parser.add_argument('-s', '--source-dir', metavar='DIR', |
| default='spirv_asm', |
| help='''specifies the directory containing SPIR-V |
| assembly files''') |
| parser.add_argument('-o', '--output-dir', metavar='DIR', |
| default='spirv_bin', |
| help='''specifies the directory in which to |
| output SPIR-V binary files''') |
| parser.add_argument('-a', '--assembler', metavar='PROGRAM', |
| default='spirv-as', |
| help='''specifies the program to use for assembly |
| of SPIR-V, defaults to spirv-as''') |
| parser.add_argument('-l', '--validator', metavar='PROGRAM', |
| default='spirv-val', |
| help='''specifies the program to use for validation |
| of SPIR-V, defaults to spirv-val''') |
| parser.add_argument('-k', '--skip-validation', action='store_true', |
| default=False, |
| help='skips validation of the genareted SPIR-V') |
| parser.add_argument('-v', '--verbose', action='store_true', default=False, |
| help='''enable verbose output (i.e. prints the |
| name of each SPIR-V assembly file or |
| binary as it is assembled or validated) |
| ''') |
| return parser.parse_args() |
| |
| |
| def main(): |
| """Main function. Assembles and validates SPIR-V.""" |
| |
| args = parse_args() |
| |
| print('Assembling SPIR-V source into binaries...') |
| assemble_spirv(args.source_dir, args.output_dir, args.assembler, |
| args.verbose) |
| print('Finished assembling SPIR-V binaries.') |
| print() |
| |
| if args.skip_validation: |
| print('Skipping validation of SPIR-V binaries as requested.') |
| else: |
| print('Validating SPIR-V binaries...') |
| validate_spirv(args.output_dir, args.validator, args.verbose) |
| print() |
| |
| print('Done.') |
| |
| |
| if __name__ == '__main__': |
| main() |