diff options
Diffstat (limited to 'ml/dlib/setup.py')
-rw-r--r-- | ml/dlib/setup.py | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/ml/dlib/setup.py b/ml/dlib/setup.py new file mode 100644 index 00000000..9bc20b33 --- /dev/null +++ b/ml/dlib/setup.py @@ -0,0 +1,251 @@ +"""setup for the dlib project + Copyright (C) 2015 Ehsan Azar (dashesy@linux.com) + License: Boost Software License See LICENSE.txt for the full license. + +This file basically just uses CMake to compile the dlib python bindings project +located in the tools/python folder and then puts the outputs into standard +python packages. + +To build the dlib: + python setup.py build +To build and install: + python setup.py install +To package the wheel (after pip installing twine and wheel): + python setup.py bdist_wheel +To upload the binary wheel to PyPi + twine upload dist/*.whl +To upload the source distribution to PyPi + python setup.py sdist upload +To exclude/include certain options in the cmake config use --yes and --no: + for example: + --yes USE_AVX_INSTRUCTIONS: will set -DUSE_AVX_INSTRUCTIONS=yes + --no USE_AVX_INSTRUCTIONS: will set -DUSE_AVX_INSTRUCTIONS=no +Additional options: + --compiler-flags: pass flags onto the compiler, e.g. --compiler-flags "-Os -Wall" passes -Os -Wall onto GCC. + -G: Set the CMake generator. E.g. -G "Visual Studio 14 2015" + --clean: delete any previous build folders and rebuild. You should do this if you change any build options + by setting --compiler-flags or --yes or --no since last time you ran a build to make sure the changes + take effect. + --set: set arbitrary options e.g. --set CUDA_HOST_COMPILER=/usr/bin/gcc-6.4.0 +""" +import os +import re +import sys +import shutil +import platform +import subprocess +import multiprocessing +from distutils import log +from math import ceil,floor + +from setuptools import setup, Extension +from setuptools.command.build_ext import build_ext +from distutils.version import LooseVersion + + +def get_extra_cmake_options(): + """read --clean, --yes, --no, --set, --compiler-flags, and -G options from the command line and add them as cmake switches. + """ + _cmake_extra_options = [] + _clean_build_folder = False + + opt_key = None + + argv = [arg for arg in sys.argv] # take a copy + # parse command line options and consume those we care about + for arg in argv: + if opt_key == 'compiler-flags': + _cmake_extra_options.append('-DCMAKE_CXX_FLAGS={arg}'.format(arg=arg.strip())) + elif opt_key == 'G': + _cmake_extra_options += ['-G', arg.strip()] + elif opt_key == 'yes': + _cmake_extra_options.append('-D{arg}=yes'.format(arg=arg.strip())) + elif opt_key == 'no': + _cmake_extra_options.append('-D{arg}=no'.format(arg=arg.strip())) + elif opt_key == 'set': + _cmake_extra_options.append('-D{arg}'.format(arg=arg.strip())) + + if opt_key: + sys.argv.remove(arg) + opt_key = None + continue + + if arg == '--clean': + _clean_build_folder = True + sys.argv.remove(arg) + continue + + if arg in ['--yes', '--no', '--set', '--compiler-flags']: + opt_key = arg[2:].lower() + sys.argv.remove(arg) + continue + if arg in ['-G']: + opt_key = arg[1:] + sys.argv.remove(arg) + continue + + return _cmake_extra_options, _clean_build_folder + +cmake_extra_options,clean_build_folder = get_extra_cmake_options() + + +class CMakeExtension(Extension): + def __init__(self, name, sourcedir=''): + Extension.__init__(self, name, sources=[]) + self.sourcedir = os.path.abspath(sourcedir) + +def rmtree(name): + """remove a directory and its subdirectories. + """ + def remove_read_only(func, path, exc): + excvalue = exc[1] + if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES: + os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + func(path) + else: + raise + + if os.path.exists(name): + log.info('Removing old directory {}'.format(name)) + shutil.rmtree(name, ignore_errors=False, onerror=remove_read_only) + + +class CMakeBuild(build_ext): + + def get_cmake_version(self): + try: + out = subprocess.check_output(['cmake', '--version']) + except OSError: + raise RuntimeError("CMake must be installed to build the following extensions: " + + ", ".join(e.name for e in self.extensions)) + return re.search(r'version\s*([\d.]+)', out.decode()).group(1) + + def run(self): + if platform.system() == "Windows": + if LooseVersion(self.get_cmake_version()) < '3.1.0': + raise RuntimeError("CMake >= 3.1.0 is required on Windows") + + for ext in self.extensions: + self.build_extension(ext) + + def build_extension(self, ext): + extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) + + cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, + '-DPYTHON_EXECUTABLE=' + sys.executable] + + cmake_args += cmake_extra_options + + cfg = 'Debug' if self.debug else 'Release' + build_args = ['--config', cfg] + + if platform.system() == "Windows": + cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)] + if sys.maxsize > 2**32: + cmake_args += ['-A', 'x64'] + build_args += ['--', '/m'] + else: + cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] + # Do a parallel build + build_args += ['--', '-j'+str(num_available_cpu_cores(2))] + + build_folder = os.path.abspath(self.build_temp) + + if clean_build_folder: + rmtree(build_folder) + if not os.path.exists(build_folder): + os.makedirs(build_folder) + + cmake_setup = ['cmake', ext.sourcedir] + cmake_args + cmake_build = ['cmake', '--build', '.'] + build_args + + print("Invoking CMake setup: '{}'".format(' '.join(cmake_setup))) + subprocess.check_call(cmake_setup, cwd=build_folder) + print("Invoking CMake build: '{}'".format(' '.join(cmake_build))) + subprocess.check_call(cmake_build, cwd=build_folder) + +def num_available_cpu_cores(ram_per_build_process_in_gb): + if 'TRAVIS' in os.environ and os.environ['TRAVIS']=='true': + # When building on travis-ci, just use 2 cores since travis-ci limits + # you to that regardless of what the hardware might suggest. + return 2 + try: + mem_bytes = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') + mem_gib = mem_bytes/(1024.**3) + num_cores = multiprocessing.cpu_count() + # make sure we have enough ram for each build process. + mem_cores = int(floor(mem_gib/float(ram_per_build_process_in_gb)+0.5)); + # We are limited either by RAM or CPU cores. So pick the limiting amount + # and return that. + return max(min(num_cores, mem_cores), 1) + except ValueError: + return 2 # just assume 2 if we can't get the os to tell us the right answer. + + +from setuptools.command.test import test as TestCommand +class PyTest(TestCommand): + user_options = [('pytest-args=', 'a', "Arguments to pass to pytest")] + + def initialize_options(self): + TestCommand.initialize_options(self) + self.pytest_args = '--ignore docs --ignore dlib' + + def run_tests(self): + import shlex + #import here, cause outside the eggs aren't loaded + import pytest + errno = pytest.main(shlex.split(self.pytest_args)) + sys.exit(errno) + +def read_version_from_cmakelists(cmake_file): + """Read version information + """ + major = re.findall("set\(CPACK_PACKAGE_VERSION_MAJOR.*\"(.*)\"", open(cmake_file).read())[0] + minor = re.findall("set\(CPACK_PACKAGE_VERSION_MINOR.*\"(.*)\"", open(cmake_file).read())[0] + patch = re.findall("set\(CPACK_PACKAGE_VERSION_PATCH.*\"(.*)\"", open(cmake_file).read())[0] + return major + '.' + minor + '.' + patch + +def read_entire_file(fname): + """Read text out of a file relative to setup.py. + """ + return open(os.path.join(fname)).read() + +setup( + name='dlib', + version=read_version_from_cmakelists('dlib/CMakeLists.txt'), + description='A toolkit for making real world machine learning and data analysis applications', + long_description=read_entire_file('README.md'), + author='Davis King', + author_email='davis@dlib.net', + url='https://github.com/davisking/dlib', + license='Boost Software License', + ext_modules=[CMakeExtension('dlib','tools/python')], + cmdclass=dict(build_ext=CMakeBuild, test=PyTest), + zip_safe=False, + tests_require=['pytest'], + packages=['dlib'], + keywords=['dlib', 'Computer Vision', 'Machine Learning'], + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Science/Research', + 'Intended Audience :: Developers', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: POSIX', + 'Operating System :: POSIX :: Linux', + 'Operating System :: Microsoft', + 'Operating System :: Microsoft :: Windows', + 'Programming Language :: C++', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Scientific/Engineering', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Topic :: Scientific/Engineering :: Image Recognition', + 'Topic :: Software Development', + ], +) |