|
"""setup for the dlib project |
|
Copyright (C) 2015 Ehsan Azar ([email protected]) |
|
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 |
|
twine upload dist/dlib-*.tar.gz |
|
To exclude certain options in the cmake config use --no: |
|
for example: |
|
--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 --no since the last time you ran a build. This will |
|
ensure the changes take effect. |
|
--set: set arbitrary cmake options e.g. --set CUDA_HOST_COMPILER=/usr/bin/gcc-6.4.0 |
|
passes -DCUDA_HOST_COMPILER=/usr/bin/gcc-6.4.0 to CMake. |
|
""" |
|
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, --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] |
|
|
|
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 == '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 == '--yes': |
|
print("The --yes options to dlib's setup.py don't do anything since all these options ") |
|
print("are on by default. So --yes has been removed. Do not give it to setup.py.") |
|
sys.exit(1) |
|
if arg in ['--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: |
|
sys.stderr.write("\nERROR: CMake must be installed to build dlib\n\n") |
|
sys.exit(1) |
|
return re.search(r'version\s*([\d.]+)', out.decode()).group(1) |
|
|
|
def run(self): |
|
cmake_version = self.get_cmake_version() |
|
if platform.system() == "Windows": |
|
if LooseVersion(cmake_version) < '3.1.0': |
|
sys.stderr.write("\nERROR: CMake >= 3.1.0 is required on Windows\n\n") |
|
sys.exit(1) |
|
|
|
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] |
|
|
|
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("Building extension for Python {}".format(sys.version.split('\n',1)[0])) |
|
print("Invoking CMake setup: '{}'".format(' '.join(cmake_setup))) |
|
sys.stdout.flush() |
|
subprocess.check_call(cmake_setup, cwd=build_folder) |
|
print("Invoking CMake build: '{}'".format(' '.join(cmake_build))) |
|
sys.stdout.flush() |
|
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': |
|
|
|
|
|
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() |
|
|
|
mem_cores = int(floor(mem_gib/float(ram_per_build_process_in_gb)+0.5)); |
|
|
|
|
|
return max(min(num_cores, mem_cores), 1) |
|
except ValueError: |
|
return 2 |
|
|
|
|
|
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 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='See http://dlib.net for documentation.', |
|
author='Davis King', |
|
author_email='[email protected]', |
|
url='https://github.com/davisking/dlib', |
|
license='Boost Software License', |
|
ext_modules=[CMakeExtension('_dlib_pybind11','tools/python')], |
|
cmdclass=dict(build_ext=CMakeBuild, test=PyTest), |
|
zip_safe=False, |
|
|
|
tests_require=['pytest==3.8', 'more-itertools<6.0.0'], |
|
|
|
packages=['dlib'], |
|
package_dir={'': 'tools/python'}, |
|
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', |
|
], |
|
) |
|
|