xref: /aosp_15_r20/external/armnn/python/pyarmnn/swig_generate.py (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
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