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
94from setuptools .command .build_ext import build_ext
105from 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
118from distutils .version import LooseVersion
129
10+ PACKAGE_NAME = "pymtracking"
1311
1412class 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
7337class 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
141188setup (
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