"torchvision/vscode:/vscode.git/clone" did not exist on "12be107ba28a63564392d1aedefb91f7c48ead7a"
setup.py 9.6 KB
Newer Older
1
"""setup for the dlib project
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
2
3
 Copyright (C) 2015  Ehsan Azar (dashesy@linux.com)
 License: Boost Software License   See LICENSE.txt for the full license.
4

5
6
7
8
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.

9
10
11
12
To build the dlib:
    python setup.py build
To build and install:
    python setup.py install
13
To package the wheel (after pip installing twine and wheel):
14
    python setup.py bdist_wheel
Davis King's avatar
Davis King committed
15
To upload the binary wheel to PyPi
16
    twine upload dist/*.whl
Davis King's avatar
Davis King committed
17
To upload the source distribution to PyPi
Davis King's avatar
Davis King committed
18
19
    python setup.py sdist 
    twine upload dist/dlib-*.tar.gz
20
21
To exclude/include certain options in the cmake config use --yes and --no:
    for example:
22
23
    --yes USE_AVX_INSTRUCTIONS: will set -DUSE_AVX_INSTRUCTIONS=yes
    --no USE_AVX_INSTRUCTIONS: will set -DUSE_AVX_INSTRUCTIONS=no
24
Additional options:
25
26
    --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"
27
28
29
    --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.
30
    --set: set arbitrary options e.g. --set CUDA_HOST_COMPILER=/usr/bin/gcc-6.4.0
31
32
"""
import os
33
import re
34
import sys
35
import shutil
36
import platform
37
import subprocess
38
import multiprocessing
39
from distutils import log
40
from math import ceil,floor
41

42
43
44
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
from distutils.version import LooseVersion
45

46

47
def get_extra_cmake_options():
48
    """read --clean, --yes, --no, --set, --compiler-flags, and -G options from the command line and add them as cmake switches.
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
49
    """
50
51
    _cmake_extra_options = []
    _clean_build_folder = False
52

Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
53
    opt_key = None
54

55
    argv = [arg for arg in sys.argv]  # take a copy
56
    # parse command line options and consume those we care about
57
    for arg in argv:
58
59
        if opt_key == 'compiler-flags':
            _cmake_extra_options.append('-DCMAKE_CXX_FLAGS={arg}'.format(arg=arg.strip()))
60
61
        elif opt_key == 'G':
            _cmake_extra_options += ['-G', arg.strip()]
62
        elif opt_key == 'yes':
63
            _cmake_extra_options.append('-D{arg}=yes'.format(arg=arg.strip()))
64
        elif opt_key == 'no':
65
            _cmake_extra_options.append('-D{arg}=no'.format(arg=arg.strip()))
66
67
        elif opt_key == 'set':
            _cmake_extra_options.append('-D{arg}'.format(arg=arg.strip()))
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
68
69
70

        if opt_key:
            sys.argv.remove(arg)
71
            opt_key = None
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
72
            continue
73

74
75
        if arg == '--clean':
            _clean_build_folder = True
76
77
78
            sys.argv.remove(arg)
            continue

79
        if arg in ['--yes', '--no', '--set', '--compiler-flags']:
80
            opt_key = arg[2:].lower()
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
81
82
            sys.argv.remove(arg)
            continue
83
84
85
86
        if arg in ['-G']:
            opt_key = arg[1:]
            sys.argv.remove(arg)
            continue
87

88
    return _cmake_extra_options, _clean_build_folder
89

90
cmake_extra_options,clean_build_folder = get_extra_cmake_options()
91
92


93
94
95
96
class CMakeExtension(Extension):
    def __init__(self, name, sourcedir=''):
        Extension.__init__(self, name, sources=[])
        self.sourcedir = os.path.abspath(sourcedir)
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
97

98
99
100
101
102
103
104
105
106
107
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
108

109
110
111
    if os.path.exists(name):
        log.info('Removing old directory {}'.format(name))
        shutil.rmtree(name, ignore_errors=False, onerror=remove_read_only)
112
113


114
class CMakeBuild(build_ext):
115

116
    def get_cmake_version(self):
117
        try:
118
            out = subprocess.check_output(['cmake', '--version'])
119
        except OSError:
120
121
122
            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)
123

124
125
126
127
    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")
128

129
130
        for ext in self.extensions:
            self.build_extension(ext)
131

132
133
    def build_extension(self, ext):
        extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
134

135
136
        cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,
                      '-DPYTHON_EXECUTABLE=' + sys.executable]
137

138
        cmake_args += cmake_extra_options 
139

140
141
        cfg = 'Debug' if self.debug else 'Release'
        build_args = ['--config', cfg]
142

143
144
145
146
147
148
149
        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]
150
            # Do a parallel build
151
            build_args += ['--', '-j'+str(num_available_cpu_cores(2))]
152

153
        build_folder = os.path.abspath(self.build_temp)
154

155
156
157
158
        if clean_build_folder:
            rmtree(build_folder)
        if not os.path.exists(build_folder):
            os.makedirs(build_folder)
159

Davis King's avatar
Davis King committed
160
161
162
        cmake_setup = ['cmake', ext.sourcedir] + cmake_args
        cmake_build = ['cmake', '--build', '.'] + build_args

163
        print("Building extension for Python {}".format(sys.version.split('\n',1)[0]))
Davis King's avatar
Davis King committed
164
        print("Invoking CMake setup: '{}'".format(' '.join(cmake_setup)))
165
        sys.stdout.flush()
Davis King's avatar
Davis King committed
166
167
        subprocess.check_call(cmake_setup, cwd=build_folder)
        print("Invoking CMake build: '{}'".format(' '.join(cmake_build)))
168
        sys.stdout.flush()
Davis King's avatar
Davis King committed
169
        subprocess.check_call(cmake_build, cwd=build_folder)
170

171
def num_available_cpu_cores(ram_per_build_process_in_gb):
Davis King's avatar
Davis King committed
172
    if 'TRAVIS' in os.environ and os.environ['TRAVIS']=='true':
Davis King's avatar
Davis King committed
173
174
175
        # 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 
176
177
178
    try:
        mem_bytes = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES')  
        mem_gib = mem_bytes/(1024.**3)
179
180
        num_cores = multiprocessing.cpu_count() 
        # make sure we have enough ram for each build process.
181
182
183
184
185
186
187
        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.

188

189
190
191
from setuptools.command.test import test as TestCommand
class PyTest(TestCommand):
    user_options = [('pytest-args=', 'a', "Arguments to pass to pytest")]
192

193
194
    def initialize_options(self):
        TestCommand.initialize_options(self)
195
        self.pytest_args = '--ignore docs --ignore dlib'
196

197
198
199
200
201
202
    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)
203

204
205
206
207
208
209
210
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
211

212
213
214
215
def read_entire_file(fname):
    """Read text out of a file relative to setup.py.
    """
    return open(os.path.join(fname)).read()
216

217
218
setup(
    name='dlib',
219
    version=read_version_from_cmakelists('dlib/CMakeLists.txt'),
220
    description='A toolkit for making real world machine learning and data analysis applications',
221
    long_description=read_entire_file('README.md'),
222
223
224
225
    author='Davis King',
    author_email='davis@dlib.net',
    url='https://github.com/davisking/dlib',
    license='Boost Software License',
226
227
    ext_modules=[CMakeExtension('dlib','tools/python')],
    cmdclass=dict(build_ext=CMakeBuild, test=PyTest),
228
    zip_safe=False,
229
230
231
    tests_require=['pytest'],
    packages=['dlib'],
    keywords=['dlib', 'Computer Vision', 'Machine Learning'],
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
232
233
234
235
236
237
238
239
240
241
242
243
244
245
    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',
246
247
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
Davis King's avatar
Davis King committed
248
249
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
250
        'Topic :: Scientific/Engineering',
Davis King's avatar
Davis King committed
251
        'Topic :: Scientific/Engineering :: Artificial Intelligence',
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
252
253
254
        'Topic :: Scientific/Engineering :: Image Recognition',
        'Topic :: Software Development',
    ],
255
)