1#!/usr/bin/env python3 2# Copyright © 2020 Arm Ltd. All rights reserved. 3# Copyright 2020 NXP 4# SPDX-License-Identifier: MIT 5"""This script executes SWIG commands to generate armnn and armnn version wrappers. 6This script cannot be moved to ./script dir because it uses find_armnn function from setup.py script. 7Both scripts must be in the same folder. 8""" 9import os 10import re 11import subprocess 12import argparse 13 14from setup import find_includes 15 16__current_dir = os.path.dirname(os.path.realpath(__file__)) 17__swig_exec = None 18__verbose = False 19 20SWIG_EXEC_ENV = "SWIG_EXECUTABLE" 21 22 23def get_swig_exec(swig_exec_env: str = SWIG_EXEC_ENV): 24 """Returns the swig command. Uses either an env variable or the `swig` command 25 and verifies it works. 26 27 Args: 28 swig_exec_env(str): Env variable pointing to the swig executable. 29 30 Returns: 31 str: Path to swig executable. 32 33 Raises: 34 RuntimeError: If unable to execute any version of swig. 35 """ 36 swig_exec = os.getenv(swig_exec_env) 37 if swig_exec is None: 38 swig_exec = "swig" 39 if subprocess.Popen([swig_exec, "-version"], stdout=subprocess.DEVNULL): 40 return swig_exec 41 else: 42 raise RuntimeError("Unable to execute swig.") 43 44 45def check_swig_version(expected_version: str): 46 """Checks version of swig. 47 48 Args: 49 expected_version(str): String containing expected version. 50 51 Returns: 52 bool: True if version is correct, False otherwise 53 """ 54 cmd = subprocess.Popen([__swig_exec, "-version"], stdout=subprocess.PIPE) 55 out, _ = cmd.communicate() 56 57 pattern = re.compile(r"(?<=Version ).+(?=$)", re.MULTILINE) 58 match = pattern.search(out.decode('utf-8')) 59 60 if match: 61 version_string = match.group(0).strip() 62 if __verbose: 63 print(f"SWIG version: {version_string}") 64 return version_string.startswith(expected_version) 65 else: 66 return False 67 68 69def generate_wrap(name: str, extr_includes): 70 """Generates the python wrapper using swig. 71 72 Args: 73 name(str): Name of the wrapper template. 74 extr_includes(str): Include paths. 75 76 Raises: 77 RuntimeError: If wrapper fails to be generated. 78 """ 79 in_dir = os.path.join(__current_dir, "src", "pyarmnn", "swig") 80 out_dir = os.path.join(__current_dir, "src", "pyarmnn", "_generated") 81 if __verbose: 82 print(f"Generating wrap for {name} ...") 83 code = os.system(f"{__swig_exec} -c++ -python -Wall " 84 + "-o {} ".format(os.path.join(out_dir, f"{name}_wrap.cpp")) 85 + f"-outdir {out_dir} " 86 + f"{extr_includes} " 87 + f"-I{in_dir} " 88 + os.path.join(in_dir, f"{name}.i")) 89 if code != 0: 90 raise RuntimeError(f"Failed to generate {name} ext.") 91 92 93if __name__ == "__main__": 94 __swig_exec = get_swig_exec() 95 96 # This check is redundant in case CMake is used, it's here for standalone use 97 if not check_swig_version('4.'): 98 raise RuntimeError("Wrong swig version was found. Expected SWIG version is 4.x.x") 99 100 armnn_includes = find_includes() 101 102 parser = argparse.ArgumentParser("Script to generate SWIG wrappers.") 103 parser.add_argument("-v", "--verbose", help="Verbose output.", action="store_true") 104 args = parser.parse_args() 105 106 __verbose = args.verbose 107 108 wrap_names = ['armnn_version', 109 'armnn', 110 'armnn_onnxparser', 111 'armnn_tfliteparser', 112 'armnn_deserializer'] 113 114 for n in wrap_names: 115 generate_wrap(n, f"-I{' -I'.join(armnn_includes)} ") 116