Skip to content

Commit 13d258e

Browse files
committed
Fix pymtracking build from setup.py
1 parent eaaac7a commit 13d258e

1 file changed

Lines changed: 159 additions & 106 deletions

File tree

setup.py

Lines changed: 159 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,42 @@
1-
import os
2-
import re
3-
import sys
4-
import platform
5-
import subprocess
6-
import shutil
7-
8-
from setuptools import setup, Extension
1+
import os, re, sys, shutil, platform, subprocess
2+
3+
from setuptools import setup, find_packages, Extension
94
from setuptools.command.build_ext import build_ext
105
from setuptools.command.install_lib import install_lib
6+
from setuptools.command.install_scripts import install_scripts
7+
from distutils.command.install_data import install_data
118
from distutils.version import LooseVersion
129

10+
PACKAGE_NAME = "pymtracking"
1311

1412
class CMakeExtension(Extension):
1513
def __init__(self, name, sourcedir=''):
1614
Extension.__init__(self, name, sources=[])
1715
self.sourcedir = os.path.abspath(sourcedir)
1816

1917

20-
class CMakeBuild(build_ext):
18+
class InstallCMakeLibsData(install_data):
19+
"""
20+
Just a wrapper to get the install data into the egg-info
21+
Listing the installed files in the egg-info guarantees that
22+
all of the package files will be uninstalled when the user
23+
uninstalls your package through pip
24+
"""
2125
def run(self):
22-
try:
23-
out = subprocess.check_output(['cmake', '--version'])
24-
except OSError:
25-
raise RuntimeError("CMake must be installed to build the following extensions: " +
26-
", ".join(e.name for e in self.extensions))
27-
28-
if platform.system() == "Windows":
29-
cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1))
30-
if cmake_version < '3.1.0':
31-
raise RuntimeError("CMake >= 3.1.0 is required on Windows")
32-
33-
for ext in self.extensions:
34-
self.build_extension(ext)
35-
36-
def build_extension(self, ext):
37-
extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
38-
# required for auto-detection of auxiliary "native" libs
39-
if not extdir.endswith(os.path.sep):
40-
extdir += os.path.sep
41-
42-
if platform.system() == "Windows":
43-
cmake_args = ['-DPYTHON_EXECUTABLE=' + sys.executable]
44-
else:
45-
cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, '-DPYTHON_EXECUTABLE=' + sys.executable]
46-
47-
cfg = 'Debug' if self.debug else 'Release'
48-
build_args = ['--config', cfg]
49-
50-
51-
if platform.system() == "Windows":
52-
cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)]
53-
if sys.maxsize > 2**32:
54-
cmake_args += ['-A', 'x64']
55-
build_args += ['--', '/m']
56-
else:
57-
cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]
58-
build_args += ['--', '-j4']
59-
60-
cmake_args += ['-DBUILD_YOLO_LIB=OFF']
61-
cmake_args += ['-DBUILD_YOLO_TENSORRT=OFF']
62-
63-
env = os.environ.copy()
64-
env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''),
65-
self.distribution.get_version())
66-
if not os.path.exists(self.build_temp):
67-
os.makedirs(self.build_temp)
68-
subprocess.check_call(['cmake', ext.sourcedir + os.path.sep + 'build'] + cmake_args, cwd='.', env=env)
69-
subprocess.check_call(['cmake', '--build', 'build'] + build_args, cwd='.')
70-
26+
"""
27+
Outfiles are the libraries that were built using cmake
28+
"""
29+
# There seems to be no other way to do this; I tried listing the
30+
# libraries during the execution of the InstallCMakeLibs.run() but
31+
# setuptools never tracked them, seems like setuptools wants to
32+
# track the libraries through package data more than anything...
33+
# help would be appriciated
34+
self.outfiles = self.distribution.data_files
7135

7236
__metaclass__ = type
7337
class InstallCMakeLibs(install_lib, object):
7438
"""
7539
Get the libraries from the parent distribution, use those as the outfiles
76-
7740
Skip building anything; everything is already built, forward libraries to
7841
the installation step
7942
"""
@@ -84,73 +47,163 @@ def run(self):
8447
self.announce("Moving library files", level=3)
8548
# We have already built the libraries in the previous build_ext step
8649
self.skip_build = True
87-
88-
if platform.system() == "Windows":
89-
if platform.system() == "Windows":
90-
bin_dir = os.path.join('build', 'Release')
91-
else:
92-
bin_dir = 'build'
93-
print(bin_dir)
94-
libs = [os.path.join(bin_dir, _lib) for _lib in
95-
os.listdir(bin_dir) if
96-
os.path.isfile(os.path.join(bin_dir, _lib)) and
97-
os.path.splitext(_lib)[1] in [".dll", ".so"]
98-
and not (_lib.startswith("python"))]
50+
if hasattr(self.distribution, 'bin_dir'):
51+
bin_dir = self.distribution.bin_dir
9952
else:
100-
if hasattr(self.distribution, 'bin_dir'):
101-
bin_dir = self.distribution.bin_dir
102-
else:
103-
bin_dir = os.path.join(self.build_dir)
104-
105-
# Depending on the files that are generated from your cmake
106-
# build chain, you may need to change the below code, such that
107-
# your files are moved to the appropriate location when the installation
108-
# is run
109-
libs = [os.path.join(bin_dir, _lib) for _lib in
110-
os.listdir(bin_dir) if
111-
os.path.isfile(os.path.join(bin_dir, _lib)) and
112-
os.path.splitext(_lib)[1] in [".dll", ".so"]
113-
and not (_lib.startswith("python"))]
114-
115-
print('Install libs', libs, 'from', bin_dir, 'to', self.build_dir)
53+
bin_dir = os.path.join(self.build_dir)
54+
# Depending on the files that are generated from your cmake
55+
# build chain, you may need to change the below code, such that
56+
# your files are moved to the appropriate location when the installation
57+
# is run
58+
libs = [os.path.join(bin_dir, _lib) for _lib in
59+
os.listdir(bin_dir) if
60+
os.path.isfile(os.path.join(bin_dir, _lib)) and
61+
os.path.splitext(_lib)[1] in [".dll", ".so"]
62+
and not (_lib.startswith("python") or _lib.startswith(PACKAGE_NAME))]
11663
for lib in libs:
117-
shutil.move(lib, os.path.join(self.build_dir, os.path.basename(lib)))
118-
# Mark the libs for installation, adding them to
119-
# distribution.data_files seems to ensure that setuptools' record
64+
shutil.move(lib, os.path.join(self.build_dir,
65+
os.path.basename(lib)))
66+
# Mark the libs for installation, adding them to
67+
# distribution.data_files seems to ensure that setuptools' record
12068
# writer appends them to installed-files.txt in the package's egg-info
12169
#
122-
# Also tried adding the libraries to the distribution.libraries list,
123-
# but that never seemed to add them to the installed-files.txt in the
124-
# egg-info, and the online recommendation seems to be adding libraries
125-
# into eager_resources in the call to setup(), which I think puts them
126-
# in data_files anyways.
127-
#
70+
# Also tried adding the libraries to the distribution.libraries list,
71+
# but that never seemed to add them to the installed-files.txt in the
72+
# egg-info, and the online recommendation seems to be adding libraries
73+
# into eager_resources in the call to setup(), which I think puts them
74+
# in data_files anyways.
75+
#
12876
# What is the best way?
12977
# These are the additional installation files that should be
13078
# included in the package, but are resultant of the cmake build
13179
# step; depending on the files that are generated from your cmake
13280
# build chain, you may need to modify the below code
133-
self.distribution.data_files = [os.path.join(self.install_dir,
81+
self.distribution.data_files = [os.path.join(self.install_dir,
13482
os.path.basename(lib))
13583
for lib in libs]
13684
# Must be forced to run after adding the libs to data_files
13785
self.distribution.run_command("install_data")
13886
super(InstallCMakeLibs, self).run()
13987

88+
__metaclass__ = type
89+
class InstallCMakeScripts(install_scripts, object):
90+
"""
91+
Install the scripts in the build dir
92+
"""
93+
def run(self):
94+
"""
95+
Copy the required directory to the build directory and super().run()
96+
"""
97+
self.announce("Moving scripts files", level=3)
98+
# Scripts were already built in a previous step
99+
self.skip_build = True
100+
bin_dir = self.distribution.bin_dir
101+
scripts_dirs = [os.path.join(bin_dir, _dir) for _dir in
102+
os.listdir(bin_dir) if
103+
os.path.isdir(os.path.join(bin_dir, _dir))]
104+
for scripts_dir in scripts_dirs:
105+
shutil.move(scripts_dir,
106+
os.path.join(self.build_dir,
107+
os.path.basename(scripts_dir)))
108+
# Mark the scripts for installation, adding them to
109+
# distribution.scripts seems to ensure that the setuptools' record
110+
# writer appends them to installed-files.txt in the package's egg-info
111+
self.distribution.scripts = scripts_dirs
112+
super(InstallCMakeScripts, self).run()
113+
114+
__metaclass__ = type
115+
class BuildCMakeExt(build_ext, object):
116+
"""
117+
Builds using cmake instead of the python setuptools implicit build
118+
"""
119+
def run(self):
120+
"""
121+
Perform build_cmake before doing the 'normal' stuff
122+
"""
123+
for extension in self.extensions:
124+
self.build_cmake(extension)
125+
super(BuildCMakeExt, self).run()
126+
127+
def build_cmake(self, extension):
128+
"""
129+
The steps required to build the extension
130+
"""
131+
self.announce("Preparing the build environment", level=3)
132+
build_dir = os.path.join(self.build_temp)
133+
extension_path = os.path.abspath(os.path.dirname(self.get_ext_fullpath(extension.name)))
134+
os.makedirs(build_dir)
135+
os.makedirs(extension_path)
136+
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
137+
138+
# Now that the necessary directories are created, build
139+
self.announce("Configuring cmake project", level=3)
140+
cmake_args = ['-DPYTHON_EXECUTABLE=' + sys.executable,
141+
'-DUSE_OCV_BGFG=ON',
142+
'-DUSE_OCV_KCF=ON',
143+
'-DSILENT_WORK=ON',
144+
'-DBUILD_EXAMPLES=OFF',
145+
'-DBUILD_ASYNC_DETECTOR=OFF',
146+
'-DBUILD_CARS_COUNTING=OFF',
147+
'-DBUILD_YOLO_LIB=OFF',
148+
'-DBUILD_YOLO_TENSORRT=OFF',
149+
'-DMTRACKER_PYTHON=ON']
150+
if not os.path.exists(self.build_temp):
151+
os.makedirs(self.build_temp)
152+
self.spawn(['cmake', '-H'+extension.sourcedir, '-B'+self.build_temp]+ cmake_args)
153+
154+
self.announce("Building binaries", level=3)
155+
self.spawn(["cmake", "--build", self.build_temp,
156+
"--config", "Release", '--', '-j8'])
157+
158+
# Build finished, now copy the files into the copy directory
159+
# The copy directory is the parent directory of the extension (.pyd)
160+
self.announce("Moving built python module", level=3)
161+
162+
bin_dir = "build" # self.build_temp
163+
self.distribution.bin_dir = bin_dir
164+
list_bin = os.listdir(bin_dir)
165+
print("bin_dir:", bin_dir, ", extension_path:", extension_path, ", list_bin:", list_bin)
166+
pyd_path = []
167+
for _pyd in list_bin:
168+
print("_pyd:", _pyd)
169+
if os.path.isfile(os.path.join(bin_dir, _pyd)) and os.path.splitext(_pyd)[0].startswith(PACKAGE_NAME) and os.path.splitext(_pyd)[1] in [".pyd", ".so"]:
170+
pyd_path.append(os.path.join(bin_dir, _pyd))
171+
print("pyd_path:", pyd_path)
172+
pyd_path = pyd_path[0]
173+
shutil.move(pyd_path, extension_path)
174+
175+
# After build_ext is run, the following commands will run:
176+
#
177+
# install_lib
178+
# install_scripts
179+
#
180+
# These commands are subclassed above to avoid pitfalls that
181+
# setuptools tries to impose when installing these, as it usually
182+
# wants to build those libs and scripts as well or move them to a
183+
# different place. See comments above for additional information
184+
185+
with open("README.md", "r") as fh:
186+
long_description = fh.read()
140187

141188
setup(
142-
name='pymtracking',
189+
name=PACKAGE_NAME,
143190
version='1.0.1',
144191
author='Nuzhny007',
145192
author_email='nuzhny@mail.ru',
146-
url='https://github.com/Nuzhny007',
147-
description='Multipe object tracking library',
148-
long_description='',
149-
ext_modules=[CMakeExtension('pymtracking')],
150-
#cmdclass=dict(build_ext=CMakeBuild, install_lib=InstallCMakeLibs),
193+
url='https://github.com/Smorodov/Multitarget-tracker',
194+
license='Apache 2.0',
195+
description='Official Python wrapper for Multitarget-tracker',
196+
long_description=long_description,
197+
long_description_content_type="text/markdown",
198+
ext_modules=[CMakeExtension(name=PACKAGE_NAME, sourcedir='.')],
151199
cmdclass={
152-
'build_ext': CMakeBuild,
153-
'install_lib': InstallCMakeLibs
154-
},
200+
'build_ext': BuildCMakeExt,
201+
'install_data': InstallCMakeLibsData,
202+
'install_lib': InstallCMakeLibs,
203+
#'install_scripts': InstallCMakeScripts
204+
},
155205
zip_safe=False,
206+
packages=find_packages(),
207+
keywords=['Multitarget-tracker', 'Multiple Object Tracking', 'Computer Vision', 'Machine Learning'],
156208
)
209+

0 commit comments

Comments
 (0)