blob: f90cd269988e7a471565ac888be4d71403266de5 [file] [log] [blame]
#
# SPDX-FileCopyrightText: Copyright 2021-2022 Arm Limited and/or its affiliates <open-source-office@arm.com>
# SPDX-License-Identifier: Apache-2.0
#
import logging
import os
from functools import lru_cache
from itertools import chain
from pathlib import Path
from setuptools import setup
from distutils.core import Extension
from setuptools.command.build_py import build_py
logger = logging.Logger(__name__)
def linux_gcc_lib_search():
"""
Calls the `gcc` to get linker default system paths.
Returns:
list of paths
"""
cmd = 'gcc --print-search-dirs | grep libraries'
cmd_res = os.popen(cmd).read()
cmd_res = cmd_res.split('=')
if len(cmd_res) > 1:
return tuple(cmd_res[1].split(':'))
return None
def find_includes(include_env: str = 'ETHOS_U_DRIVER_INCLUDE'):
include_path = os.getenv(include_env, '')
return [include_path] if include_path else ['/usr/local/include', '/usr/include']
@lru_cache(maxsize=1)
def find_driver(lib_name: str,
optional: bool = False,
libs_env: str = 'ETHOS_U_DRIVER_LIB',
default_lib_search: tuple = linux_gcc_lib_search()):
"""
Searches for library installation on the local machine.
Args:
lib_name: library name to find
optional: Do not fail if optional. Default is False - fail if library was not found.
libs_env: custom environment variable pointing to libraries location, default is 'ETHOS_U_DRIVER_LIB'
default_lib_search: list of paths to search for a library if not found within path provided by
'ETHOS_U_DRIVER_LIB' env variable
Returns:
tuple containing name of the driver libs, paths to the libs
"""
lib_path = os.getenv(libs_env, "")
lib_search = [lib_path] if lib_path else default_lib_search
libs = dict(map(lambda path: (':{}'.format(path.name), path),
chain.from_iterable(map(lambda lib_path: Path(lib_path).glob(lib_name),
lib_search))))
if not optional and len(libs) == 0:
raise RuntimeError("""Ethos-U driver library {} was not found in {}. Please install driver to one of the standard
locations or set correct ETHOS_U_DRIVER_INCLUDE and ETHOS_U_DRIVER_LIB env variables."""
.format(lib_name, lib_search))
# gives back tuple of names of the libs, set of unique libs locations and includes.
return list(libs.keys()), list(set(
map(lambda path: str(path.absolute().parent), libs.values())))
class LibFinderExtension(Extension):
"""
Derived from `Extension` this class adds libraries search on the user's machine.
SWIG options and compilation flags are updated with relevant libraries files locations (-L) and headers (-I).
Search for the library is executed only when attributes include_dirs, library_dirs, runtime_library_dirs, libraries or
swig_opts are queried.
"""
def __init__(self, name, sources, libs, include_dirs=None, define_macros=None, undef_macros=None,
library_dirs=None,
libraries=None, runtime_library_dirs=None, extra_objects=None, extra_compile_args=None,
extra_link_args=None, export_symbols=None, language=None, **kw):
self._include_dirs = None
self._library_dirs = None
self._runtime_library_dirs = None
self._libs = libs
super().__init__(name, sources, include_dirs, define_macros, undef_macros, library_dirs, libraries,
runtime_library_dirs, extra_objects, extra_compile_args, extra_link_args, export_symbols,
language, **kw)
@property
def include_dirs(self):
return self._include_dirs + find_includes()
@include_dirs.setter
def include_dirs(self, include_dirs):
self._include_dirs = include_dirs
@property
def library_dirs(self):
library_dirs = self._library_dirs
for lib in self._libs:
_, lib_path = find_driver(lib)
library_dirs = library_dirs + lib_path
return library_dirs
@library_dirs.setter
def library_dirs(self, library_dirs):
self._library_dirs = library_dirs
@property
def runtime_library_dirs(self):
library_dirs = self._runtime_library_dirs
for lib in self._libs:
_, lib_path = find_driver(lib)
library_dirs = library_dirs + lib_path
return library_dirs
@runtime_library_dirs.setter
def runtime_library_dirs(self, runtime_library_dirs):
self._runtime_library_dirs = runtime_library_dirs
@property
def libraries(self):
libraries = self._libraries
for lib in self._libs:
lib_names, _ = find_driver(lib)
libraries = libraries + lib_names
return libraries
@libraries.setter
def libraries(self, libraries):
self._libraries = libraries
def __eq__(self, other):
return self.__class__ == other.__class__ and self.name == other.name
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return self.name.__hash__()
class ExtensionPriorityBuilder(build_py):
"""
Runs extension builder before other stages. Otherwise generated files are not included to the distribution.
"""
def run(self):
self.run_command('build_ext')
return super().run()
if __name__ == '__main__':
# mandatory extensions
driver_module = LibFinderExtension('ethosu_driver._generated._driver',
sources=['src/ethosu_driver/_generated/driver_wrap.cpp'],
extra_compile_args=['-std=gnu++14'],
language='c++',
libs=['libethosu.a']
)
extensions_to_build = [driver_module]
setup(
name='ethosu_driver',
version='1.0.0',
author='Arm ltd',
author_email='support@arm.com',
description='Arm Ethos-U NPU Linux Driver Stack Python wrapper',
url='https://git.mlplatform.org/ml/ethos-u/ethos-u-linux-driver-stack.git/',
license='Apache License 2.0',
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Operating System :: POSIX :: Linux",
"Programming Language :: C",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Topic :: Scientific/Engineering :: Artificial Intelligence"
],
keywords=["ethos-u", "driver", "npu"],
package_dir={'': 'src'},
packages=[
'ethosu_driver',
'ethosu_driver._generated',
'ethosu_driver._utilities'
],
data_files=[('', ['LICENSE'])],
entry_points={"console_scripts": ["inference_runner = ethosu_driver.inference_runner:main"]},
python_requires='>=3.5',
extras_require={"numpy": ["numpy"]},
cmdclass={'build_py': ExtensionPriorityBuilder},
ext_modules=extensions_to_build
)