blob: e1eba4b082d540fb74c1f0e6c2c526502b0d24c0 [file] [log] [blame]
Pavel Macenauer59e057f2020-04-15 14:17:26 +00001#!/usr/bin/env python3
Richard Burtondc0c6ed2020-04-08 16:39:05 +01002# Copyright © 2020 Arm Ltd. All rights reserved.
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +01003# Copyright © 2020 NXP and Contributors. All rights reserved.
Richard Burtondc0c6ed2020-04-08 16:39:05 +01004# SPDX-License-Identifier: MIT
Pavel Macenauer59e057f2020-04-15 14:17:26 +00005"""Python bindings for Arm NN
6
7PyArmNN is a python extension for Arm NN SDK providing an interface similar to Arm NN C++ API.
8"""
9__version__ = None
10__arm_ml_version__ = None
11
Richard Burtondc0c6ed2020-04-08 16:39:05 +010012import logging
13import os
14import sys
Pavel Macenauer59e057f2020-04-15 14:17:26 +000015import subprocess
Richard Burtondc0c6ed2020-04-08 16:39:05 +010016from functools import lru_cache
17from pathlib import Path
18from itertools import chain
19
20from setuptools import setup
21from distutils.core import Extension
22from setuptools.command.build_py import build_py
23from setuptools.command.build_ext import build_ext
24
25logger = logging.Logger(__name__)
26
Pavel Macenauer59e057f2020-04-15 14:17:26 +000027DOCLINES = __doc__.split("\n")
28LIB_ENV_NAME = "ARMNN_LIB"
29INCLUDE_ENV_NAME = "ARMNN_INCLUDE"
Richard Burtondc0c6ed2020-04-08 16:39:05 +010030
31
32def check_armnn_version(*args):
33 pass
34
Pavel Macenauer59e057f2020-04-15 14:17:26 +000035__current_dir = os.path.dirname(os.path.realpath(__file__))
Richard Burtondc0c6ed2020-04-08 16:39:05 +010036
Kevin May8acafd12020-06-02 11:06:46 +010037exec(open(os.path.join(__current_dir, 'src', 'pyarmnn', '_version.py'), encoding="utf-8").read())
Richard Burtondc0c6ed2020-04-08 16:39:05 +010038
39
40class ExtensionPriorityBuilder(build_py):
Pavel Macenauer59e057f2020-04-15 14:17:26 +000041 """Runs extension builder before other stages. Otherwise generated files are not included to the distribution.
Richard Burtondc0c6ed2020-04-08 16:39:05 +010042 """
43
44 def run(self):
45 self.run_command('build_ext')
46 return super().run()
47
48
49class ArmnnVersionCheckerExtBuilder(build_ext):
Pavel Macenauer59e057f2020-04-15 14:17:26 +000050 """Builds an extension (i.e. wrapper). Additionally checks for version.
51 """
Richard Burtondc0c6ed2020-04-08 16:39:05 +010052
53 def __init__(self, dist):
54 super().__init__(dist)
55 self.failed_ext = []
56
57 def build_extension(self, ext):
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +010058 if ext.optional:
59 try:
60 super().build_extension(ext)
61 except Exception as err:
62 self.failed_ext.append(ext)
63 logger.warning('Failed to build extension %s. \n %s', ext.name, str(err))
64 else:
Richard Burtondc0c6ed2020-04-08 16:39:05 +010065 super().build_extension(ext)
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +010066 if ext.name == 'pyarmnn._generated._pyarmnn_version':
67 sys.path.append(os.path.abspath(os.path.join(self.build_lib, str(Path(ext._file_name).parent))))
68 from _pyarmnn_version import GetVersion
69 check_armnn_version(GetVersion(), __arm_ml_version__)
Richard Burtondc0c6ed2020-04-08 16:39:05 +010070
71 def copy_extensions_to_source(self):
72
73 for ext in self.failed_ext:
74 self.extensions.remove(ext)
75 super().copy_extensions_to_source()
76
77
Pavel Macenauer59e057f2020-04-15 14:17:26 +000078def linux_gcc_name():
79 """Returns the name of the `gcc` compiler. Might happen that we are cross-compiling and the
80 compiler has a longer name.
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +010081
Pavel Macenauer59e057f2020-04-15 14:17:26 +000082 Args:
83 None
84
Richard Burtondc0c6ed2020-04-08 16:39:05 +010085 Returns:
Pavel Macenauer59e057f2020-04-15 14:17:26 +000086 str: Name of the `gcc` compiler or None
Richard Burtondc0c6ed2020-04-08 16:39:05 +010087 """
Pavel Macenauer59e057f2020-04-15 14:17:26 +000088 cc_env = os.getenv('CC')
89 if cc_env is not None:
90 if subprocess.Popen([cc_env, "--version"], stdout=subprocess.DEVNULL):
91 return cc_env
92 return "gcc" if subprocess.Popen(["gcc", "--version"], stdout=subprocess.DEVNULL) else None
Richard Burtondc0c6ed2020-04-08 16:39:05 +010093
94
Pavel Macenauer59e057f2020-04-15 14:17:26 +000095def linux_gcc_lib_search(gcc_compiler_name: str = linux_gcc_name()):
96 """Calls the `gcc` to get linker default system paths.
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +010097
Pavel Macenauer59e057f2020-04-15 14:17:26 +000098 Args:
99 gcc_compiler_name(str): Name of the GCC compiler
100
101 Returns:
102 list: A list of paths.
103
104 Raises:
105 RuntimeError: If unable to find GCC.
106 """
107 if gcc_compiler_name is None:
108 raise RuntimeError("Unable to find gcc compiler")
109 cmd1 = subprocess.Popen([gcc_compiler_name, "--print-search-dirs"], stdout=subprocess.PIPE)
110 cmd2 = subprocess.Popen(["grep", "libraries"], stdin=cmd1.stdout,
111 stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
112 cmd1.stdout.close()
113 out, _ = cmd2.communicate()
114 out = out.decode("utf-8").split('=')
115 return tuple(out[1].split(':')) if len(out) > 0 else None
116
117
118def find_includes(armnn_include_env: str = INCLUDE_ENV_NAME):
119 """Searches for ArmNN includes.
120
121 Args:
122 armnn_include_env(str): Environmental variable to use as path.
123
124 Returns:
125 list: A list of paths to include.
126 """
127 armnn_include_path = os.getenv(armnn_include_env)
128 if armnn_include_path is not None and os.path.exists(armnn_include_path):
129 armnn_include_path = [armnn_include_path]
130 else:
131 armnn_include_path = ['/usr/local/include', '/usr/include']
132 return armnn_include_path
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100133
134
135@lru_cache(maxsize=1)
136def find_armnn(lib_name: str,
137 optional: bool = False,
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000138 armnn_libs_env: str = LIB_ENV_NAME,
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100139 default_lib_search: tuple = linux_gcc_lib_search()):
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000140 """Searches for ArmNN installation on the local machine.
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100141
142 Args:
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000143 lib_name(str): Lib name to find.
144 optional(bool): Do not fail if optional. Default is False - fail if library was not found.
145 armnn_libs_env(str): Custom environment variable pointing to ArmNN libraries location, default is 'ARMNN_LIBS'
146 default_lib_search(tuple): list of paths to search for ArmNN if not found within path provided by 'ARMNN_LIBS'
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100147 env variable
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100148 Returns:
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000149 tuple: Contains name of the armnn libs, paths to the libs.
150
151 Raises:
152 RuntimeError: If armnn libs are not found.
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100153 """
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000154 armnn_lib_path = os.getenv(armnn_libs_env)
155 lib_search = [armnn_lib_path] if armnn_lib_path is not None else default_lib_search
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100156 armnn_libs = dict(map(lambda path: (':{}'.format(path.name), path),
157 chain.from_iterable(map(lambda lib_path: Path(lib_path).glob(lib_name),
158 lib_search))))
159 if not optional and len(armnn_libs) == 0:
160 raise RuntimeError("""ArmNN library {} was not found in {}. Please install ArmNN to one of the standard
161 locations or set correct ARMNN_INCLUDE and ARMNN_LIB env variables.""".format(lib_name,
162 lib_search))
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +0100163 if optional and len(armnn_libs) == 0:
164 logger.warning("""Optional parser library %s was not found in %s and will not be installed.""", lib_name,
165 lib_search)
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100166
167 # gives back tuple of names of the libs, set of unique libs locations and includes.
168 return list(armnn_libs.keys()), list(set(
169 map(lambda path: str(path.absolute().parent), armnn_libs.values())))
170
171
172class LazyArmnnFinderExtension(Extension):
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000173 """Derived from `Extension` this class adds ArmNN libraries search on the user's machine.
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100174 SWIG options and compilation flags are updated with relevant ArmNN libraries files locations (-L) and headers (-I).
175
176 Search for ArmNN is executed only when attributes include_dirs, library_dirs, runtime_library_dirs, libraries or
177 swig_opts are queried.
178
179 """
180
181 def __init__(self, name, sources, armnn_libs, include_dirs=None, define_macros=None, undef_macros=None,
182 library_dirs=None,
183 libraries=None, runtime_library_dirs=None, extra_objects=None, extra_compile_args=None,
184 extra_link_args=None, export_symbols=None, language=None, optional=None, **kw):
185 self._include_dirs = None
186 self._library_dirs = None
187 self._runtime_library_dirs = None
188 self._armnn_libs = armnn_libs
Nina Drozd6249d7e2020-12-01 11:56:12 +0000189 self._optional = False if optional is None else optional
190
191 super().__init__(name=name, sources=sources, include_dirs=include_dirs, define_macros=define_macros,
192 undef_macros=undef_macros, library_dirs=library_dirs, libraries=libraries,
193 runtime_library_dirs=runtime_library_dirs, extra_objects=extra_objects,
194 extra_compile_args=extra_compile_args, extra_link_args=extra_link_args,
195 export_symbols=export_symbols, language=language, optional=optional, **kw)
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100196
197 @property
198 def include_dirs(self):
199 return self._include_dirs + find_includes()
200
201 @include_dirs.setter
202 def include_dirs(self, include_dirs):
203 self._include_dirs = include_dirs
204
205 @property
206 def library_dirs(self):
207 library_dirs = self._library_dirs
208 for lib in self._armnn_libs:
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +0100209 _, lib_path = find_armnn(lib, self._optional)
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100210 library_dirs = library_dirs + lib_path
211
212 return library_dirs
213
214 @library_dirs.setter
215 def library_dirs(self, library_dirs):
216 self._library_dirs = library_dirs
217
218 @property
219 def runtime_library_dirs(self):
220 library_dirs = self._runtime_library_dirs
221 for lib in self._armnn_libs:
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +0100222 _, lib_path = find_armnn(lib, self._optional)
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100223 library_dirs = library_dirs + lib_path
224
225 return library_dirs
226
227 @runtime_library_dirs.setter
228 def runtime_library_dirs(self, runtime_library_dirs):
229 self._runtime_library_dirs = runtime_library_dirs
230
231 @property
232 def libraries(self):
233 libraries = self._libraries
234 for lib in self._armnn_libs:
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +0100235 lib_names, _ = find_armnn(lib, self._optional)
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100236 libraries = libraries + lib_names
237
238 return libraries
239
240 @libraries.setter
241 def libraries(self, libraries):
242 self._libraries = libraries
243
244 def __eq__(self, other):
245 return self.__class__ == other.__class__ and self.name == other.name
246
247 def __ne__(self, other):
248 return not self.__eq__(other)
249
250 def __hash__(self):
251 return self.name.__hash__()
252
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000253
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100254if __name__ == '__main__':
255 # mandatory extensions
256 pyarmnn_module = LazyArmnnFinderExtension('pyarmnn._generated._pyarmnn',
257 sources=['src/pyarmnn/_generated/armnn_wrap.cpp'],
258 extra_compile_args=['-std=c++14'],
259 language='c++',
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +0100260 armnn_libs=['libarmnn.so'],
Nina Drozd6249d7e2020-12-01 11:56:12 +0000261 optional=False
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100262 )
263 pyarmnn_v_module = LazyArmnnFinderExtension('pyarmnn._generated._pyarmnn_version',
264 sources=['src/pyarmnn/_generated/armnn_version_wrap.cpp'],
265 extra_compile_args=['-std=c++14'],
266 language='c++',
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +0100267 armnn_libs=['libarmnn.so'],
Nina Drozd6249d7e2020-12-01 11:56:12 +0000268 optional=False
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100269 )
270 extensions_to_build = [pyarmnn_v_module, pyarmnn_module]
271
272
273 # optional extensions
274 def add_parsers_ext(name: str, ext_list: list):
275 pyarmnn_optional_module = LazyArmnnFinderExtension('pyarmnn._generated._pyarmnn_{}'.format(name.lower()),
276 sources=['src/pyarmnn/_generated/armnn_{}_wrap.cpp'.format(
277 name.lower())],
278 extra_compile_args=['-std=c++14'],
279 language='c++',
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +0100280 armnn_libs=['libarmnn.so', 'libarmnn{}.so'.format(name)],
Nina Drozd6249d7e2020-12-01 11:56:12 +0000281 optional=True
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100282 )
283 ext_list.append(pyarmnn_optional_module)
284
285
286 add_parsers_ext('CaffeParser', extensions_to_build)
287 add_parsers_ext('OnnxParser', extensions_to_build)
288 add_parsers_ext('TfParser', extensions_to_build)
289 add_parsers_ext('TfLiteParser', extensions_to_build)
wangg5f960d92020-08-26 01:44:32 +0000290 add_parsers_ext('Deserializer', extensions_to_build)
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100291
292 setup(
293 name='pyarmnn',
294 version=__version__,
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000295 author='Arm Ltd, NXP Semiconductors',
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100296 author_email='support@linaro.org',
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000297 description=DOCLINES[0],
298 long_description="\n".join(DOCLINES[2:]),
299 url='https://mlplatform.org/',
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100300 license='MIT',
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000301 keywords='armnn neural network machine learning',
302 classifiers=[
303 'Development Status :: 3 - Alpha',
304 'Intended Audience :: Developers',
305 'Intended Audience :: Education',
306 'Intended Audience :: Science/Research',
307 'License :: OSI Approved :: MIT License',
308 'Programming Language :: Python :: 3',
309 'Programming Language :: Python :: 3 :: Only',
310 'Programming Language :: Python :: 3.6',
311 'Programming Language :: Python :: 3.7',
312 'Programming Language :: Python :: 3.8',
313 'Topic :: Scientific/Engineering',
314 'Topic :: Scientific/Engineering :: Artificial Intelligence',
315 'Topic :: Software Development',
316 'Topic :: Software Development :: Libraries',
317 'Topic :: Software Development :: Libraries :: Python Modules',
318 ],
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100319 package_dir={'': 'src'},
320 packages=[
321 'pyarmnn',
322 'pyarmnn._generated',
323 'pyarmnn._quantization',
324 'pyarmnn._tensor',
325 'pyarmnn._utilities'
326 ],
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000327 data_files=[('', ['LICENSE'])],
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100328 python_requires='>=3.5',
329 install_requires=['numpy'],
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000330 cmdclass={
Éanna Ó Catháin6c3dee42020-09-10 13:02:37 +0100331 'build_py': ExtensionPriorityBuilder,
Pavel Macenauer59e057f2020-04-15 14:17:26 +0000332 'build_ext': ArmnnVersionCheckerExtBuilder
333 },
Richard Burtondc0c6ed2020-04-08 16:39:05 +0100334 ext_modules=extensions_to_build
335 )