"docs/vscode:/vscode.git/clone" did not exist on "8db2dd335558f896880cbe268ab8970311e11ddb"
setup.py 10.3 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
To exclude certain options in the cmake config use --no:
21
    for example:
22
    --no USE_AVX_INSTRUCTIONS: will set -DUSE_AVX_INSTRUCTIONS=no
23
Additional options:
24
25
    --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"
26
    --clean: delete any previous build folders and rebuild.  You should do this if you change any build options
27
28
29
30
             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.
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, --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 == 'no':
63
            _cmake_extra_options.append('-D{arg}=no'.format(arg=arg.strip()))
64
65
        elif opt_key == 'set':
            _cmake_extra_options.append('-D{arg}'.format(arg=arg.strip()))
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
66
67
68

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

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

77
78
79
80
81
        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']:
82
            opt_key = arg[2:].lower()
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
83
84
            sys.argv.remove(arg)
            continue
85
86
87
88
        if arg in ['-G']:
            opt_key = arg[1:]
            sys.argv.remove(arg)
            continue
89

90
    return _cmake_extra_options, _clean_build_folder
91

92
cmake_extra_options,clean_build_folder = get_extra_cmake_options()
93
94


95
96
97
98
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
99

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

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


116
class CMakeBuild(build_ext):
117

118
    def get_cmake_version(self):
119
        try:
120
            out = subprocess.check_output(['cmake', '--version'])
121
        except OSError:
Davis King's avatar
Davis King committed
122
123
124
125
            raise RuntimeError("\n*******************************************************************\n" +
                                  " CMake must be installed to build the following extensions: " +
                               ", ".join(e.name for e in self.extensions) + 
                               "\n*******************************************************************\n")
126
        return re.search(r'version\s*([\d.]+)', out.decode()).group(1)
127

128
    def run(self):
Davis King's avatar
Davis King committed
129
        cmake_version = self.get_cmake_version()
130
        if platform.system() == "Windows":
Davis King's avatar
Davis King committed
131
            if LooseVersion(cmake_version) < '3.1.0':
132
                raise RuntimeError("CMake >= 3.1.0 is required on Windows")
133

134
135
        for ext in self.extensions:
            self.build_extension(ext)
136

137
138
    def build_extension(self, ext):
        extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
139

140
141
        cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,
                      '-DPYTHON_EXECUTABLE=' + sys.executable]
142

143
        cmake_args += cmake_extra_options 
144

145
146
        cfg = 'Debug' if self.debug else 'Release'
        build_args = ['--config', cfg]
147

148
149
150
151
        if platform.system() == "Windows":
            cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)]
            if sys.maxsize > 2**32:
                cmake_args += ['-A', 'x64']
Davis King's avatar
Davis King committed
152
153
            # Do a parallel build
            build_args += ['--', '/m'] 
154
155
        else:
            cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]
156
            # Do a parallel build
157
            build_args += ['--', '-j'+str(num_available_cpu_cores(2))]
158

159
        build_folder = os.path.abspath(self.build_temp)
160

161
162
163
164
        if clean_build_folder:
            rmtree(build_folder)
        if not os.path.exists(build_folder):
            os.makedirs(build_folder)
165

Davis King's avatar
Davis King committed
166
167
168
        cmake_setup = ['cmake', ext.sourcedir] + cmake_args
        cmake_build = ['cmake', '--build', '.'] + build_args

169
        print("Building extension for Python {}".format(sys.version.split('\n',1)[0]))
Davis King's avatar
Davis King committed
170
        print("Invoking CMake setup: '{}'".format(' '.join(cmake_setup)))
171
        sys.stdout.flush()
Davis King's avatar
Davis King committed
172
173
        subprocess.check_call(cmake_setup, cwd=build_folder)
        print("Invoking CMake build: '{}'".format(' '.join(cmake_build)))
174
        sys.stdout.flush()
Davis King's avatar
Davis King committed
175
        subprocess.check_call(cmake_build, cwd=build_folder)
176

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

194

195
196
197
from setuptools.command.test import test as TestCommand
class PyTest(TestCommand):
    user_options = [('pytest-args=', 'a', "Arguments to pass to pytest")]
198

199
200
    def initialize_options(self):
        TestCommand.initialize_options(self)
201
        self.pytest_args = '--ignore docs --ignore dlib'
202

203
204
205
206
207
208
    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)
209

210
211
212
213
214
215
216
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
217

218
219
220
221
def read_entire_file(fname):
    """Read text out of a file relative to setup.py.
    """
    return open(os.path.join(fname)).read()
222

223
224
setup(
    name='dlib',
225
    version=read_version_from_cmakelists('dlib/CMakeLists.txt'),
226
    description='A toolkit for making real world machine learning and data analysis applications',
227
    long_description='See http://dlib.net for documentation.',
228
229
230
231
    author='Davis King',
    author_email='davis@dlib.net',
    url='https://github.com/davisking/dlib',
    license='Boost Software License',
232
    ext_modules=[CMakeExtension('_dlib_pybind11','tools/python')],
233
    cmdclass=dict(build_ext=CMakeBuild, test=PyTest),
234
    zip_safe=False,
235
236
    # We need an older more-itertools version because v6 broke pytest (for everyone, not just dlib)
    tests_require=['pytest==3.8', 'more-itertools<6.0.0'],
237
    #install_requires=['cmake'], # removed because the pip cmake package is busted, maybe someday it will be usable.
238
    packages=['dlib'],
239
    package_dir={'': 'tools/python'},
240
    keywords=['dlib', 'Computer Vision', 'Machine Learning'],
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
241
242
243
244
245
246
247
248
249
250
251
252
253
254
    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',
255
256
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
Davis King's avatar
Davis King committed
257
258
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
259
        'Topic :: Scientific/Engineering',
Davis King's avatar
Davis King committed
260
        'Topic :: Scientific/Engineering :: Artificial Intelligence',
Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
261
262
263
        'Topic :: Scientific/Engineering :: Image Recognition',
        'Topic :: Software Development',
    ],
264
)