Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / ie_bridges / python / setup.py
1 import subprocess
2 from pathlib import Path
3 import platform
4 import sys
5 from itertools import chain
6 from distutils.command.build_py import build_py as _build_py
7 from distutils.command.clean import clean as _clean
8
9 import shutil
10 from setuptools import setup, Extension, find_packages
11 from setuptools.command.build_ext import build_ext as _build_ext
12 from setuptools.command.install import install as _install
13
14 IS_WINDOWS = (platform.system() == 'Windows')
15 IS_DARWIN = (platform.system() == 'Darwin')
16 IS_LINUX = (platform.system() == 'Linux')
17
18 REQUIREMENTS_FILE = 'requirements.txt'
19 PACKAGE_NAME = 'inference_engine'
20
21 PACKAGE = Path(PACKAGE_NAME)
22 C_LIB_NAME = '{}._C'.format(PACKAGE_NAME)
23
24 _build_cmd = ['cmake', '--build', '.']
25
26 INFERENCE_ENGINE_DIR = None
27 BUNDLE_INFERENCE_ENGINE = False
28
29
30 def parse_command_line_options(cls):
31     """Propagates command line options to sub-commands.
32     Allows to run install command with build_ext options"""
33
34     base_user_options = getattr(cls, 'user_options', [])
35     base_boolean_options = getattr(cls, 'boolean_options', [])
36     base_run = cls.run
37     base_init_options = cls.initialize_options
38
39     cls.user_options = base_user_options + [
40         ('copy-ie-libs', None, 'Copy Inference Engine Libraries to package directory'),
41         ('inference-engine-dir=', None, 'Path to Inference Engine directory')
42     ]
43
44     cls.boolean_options = base_boolean_options + [
45         'copy-ie-libs'
46     ]
47
48     def initialize_options(self):
49         self.copy_ie_libs = False
50         self.inference_engine_dir = None
51         base_init_options(self)
52
53     def run(self):
54         global INFERENCE_ENGINE_DIR
55         global BUNDLE_INFERENCE_ENGINE
56
57         if self.copy_ie_libs:
58             BUNDLE_INFERENCE_ENGINE = True
59
60         if self.inference_engine_dir:
61             INFERENCE_ENGINE_DIR = self.inference_engine_dir
62
63         base_run(self)
64
65     cls.initialize_options = initialize_options
66     cls.run = run
67     return cls
68
69
70 @parse_command_line_options
71 class install(_install):
72     pass
73
74
75 @parse_command_line_options
76 class build_py(_build_py):
77     pass
78
79
80 @parse_command_line_options
81 class build_ext(_build_ext):
82     def run(self):
83         if not self.extensions:
84             return
85
86         for i, ext in enumerate(self.extensions):
87             if ext.name == C_LIB_NAME:
88                 self._build_cmake()
89                 self.extensions.pop(i)
90                 break
91
92         super().run()
93
94     def _build_cmake(self):
95         print("Building C++ extension")
96         if Path.cwd().joinpath("Makefile").is_file():
97             # in build directory, run make only
98             subprocess.call(_build_cmd)
99         else:
100             # compile extension library and
101             self.build_cmake_lib()
102         print("Built C++ extension")
103
104     def build_cmake_lib(self):
105         def save_call(*args, error_msg=None, **kwargs):
106             if subprocess.call(*args, **kwargs) != 0:
107                 if error_msg:
108                     print(error_msg)
109                 shutil.rmtree(tmp_build_dir.as_posix(), ignore_errors=True)
110                 sys.exit(1)
111
112         tmp_build_dir = Path("tmp_build")
113         destination = Path(self.build_lib) / PACKAGE_NAME if not self.inplace else Path(PACKAGE_NAME)
114         tmp_build_dir.mkdir(exist_ok=False)
115
116         _python_executable_opt = ['-DPYTHON_EXECUTABLE={}'.format(sys.executable)]
117         _build_type_opt = ['-DCMAKE_BUILD_TYPE=Release']
118         _generator_opt = ['-G', 'NMake Makefiles' if IS_WINDOWS else "Unix Makefiles"]
119
120         _optional = []
121         if BUNDLE_INFERENCE_ENGINE:
122             _optional.append('-DCOPY_IE_LIBS=ON')
123
124         if INFERENCE_ENGINE_DIR:
125             _optional.append('-DInferenceEngine_DIR={}'.format(INFERENCE_ENGINE_DIR))
126
127         _cmake_cmd = list(chain(['cmake'], _generator_opt, _build_type_opt, _python_executable_opt, _optional, ['..']))
128
129         save_call(_cmake_cmd, cwd=tmp_build_dir.as_posix(), error_msg="Cmake generator failed")
130         save_call(_build_cmd, cwd=tmp_build_dir.as_posix(), error_msg="Build command failed")
131
132         build_ext.copy_compiled_libs(tmp_build_dir / PACKAGE_NAME, destination)
133         shutil.rmtree(tmp_build_dir.as_posix(), ignore_errors=False)
134
135     @staticmethod
136     def copy_compiled_libs(source_dir, destination):
137         extensions = ['so', 'dll', 'pyd']
138         for path in chain.from_iterable(source_dir.glob("*.%s" % ext) for ext in extensions):
139             shutil.copy(path.as_posix(), destination.as_posix())
140
141
142 class clean(_clean):
143     def run(self):
144         shutil.rmtree("tmp_build", ignore_errors=True)
145         extensions = ['so', 'dll', 'pyd']
146         for path in chain.from_iterable(PACKAGE.glob("*.%s" % ext) for ext in extensions):
147             path.unlink()
148         super().run()
149
150
151 def paths_to_str(paths):
152     return [p.as_posix() for p in paths]
153
154
155 with open(REQUIREMENTS_FILE) as reqs:
156     requirements = set(reqs.read().splitlines())
157
158 # do not spoil pre-installed opencv (in case it was built from source)
159 _opencv_package = "opencv-python"
160 try:
161     import cv2
162
163     if _opencv_package in requirements:
164         requirements.remove(_opencv_package)
165 except ImportError:
166     requirements.add(_opencv_package)
167
168
169 c_sources = [
170     PACKAGE / 'ie_api_impl.cpp',
171     PACKAGE / 'ie_api_impl.hpp',
172
173     PACKAGE / 'ie_api_impl_defs.pxd',
174     PACKAGE / 'ie_api.pyx',
175     PACKAGE / 'ie_api.pxd',
176 ]
177
178 extensions = [
179     Extension(C_LIB_NAME, paths_to_str(c_sources))
180 ]
181
182 cmdclass = {
183     'build_ext': build_ext,
184     'build_py': build_py,
185     'clean': clean,
186     'install': install,
187 }
188
189 setup(
190     name="src",
191     version='1.0',
192     description='Python inference for Inference Engine',
193     packages=find_packages(exclude=['tests']),
194     package_data={PACKAGE_NAME: ['*.so', '*.dll', '*dylib*', '*.pyd']},
195     include_package_data=True,
196     ext_modules=extensions,
197     cmdclass=cmdclass,
198     install_requires=list(requirements),
199     zip_safe=False,
200 )