"docs/advanced_features/hyperparameter_tuning.md" did not exist on "159cc741e47539897eb45cde0f328053c23f5bd2"
setup.py 9.24 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
18
To upload the source distribution to PyPi
    python setup.py sdist upload
19
20
To exclude/include certain options in the cmake config use --yes and --no:
    for example:
21
22
    --yes USE_AVX_INSTRUCTIONS: will set -DUSE_AVX_INSTRUCTIONS=yes
    --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
27
28
    --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.
29
30
"""
import os
31
import re
32
import sys
33
import shutil
34
import platform
35
import subprocess
36
import multiprocessing
37
from distutils import log
38
from math import ceil,floor
39

40
41
42
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
from distutils.version import LooseVersion
43

44

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

Ehsan Azarnasab's avatar
Ehsan Azarnasab committed
51
    opt_key = None
52

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

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

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

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

84
    return _cmake_extra_options, _clean_build_folder
85

86
cmake_extra_options,clean_build_folder = get_extra_cmake_options()
87
88


89
90
91
92
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
93

94
95
96
97
98
99
100
101
102
103
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
104

105
106
107
    if os.path.exists(name):
        log.info('Removing old directory {}'.format(name))
        shutil.rmtree(name, ignore_errors=False, onerror=remove_read_only)
108
109


110
class CMakeBuild(build_ext):
111

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

120
121
122
123
    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")
124

125
126
        for ext in self.extensions:
            self.build_extension(ext)
127

128
129
    def build_extension(self, ext):
        extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
130

131
132
        cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,
                      '-DPYTHON_EXECUTABLE=' + sys.executable]
133

134
        cmake_args += cmake_extra_options 
135

136
137
        cfg = 'Debug' if self.debug else 'Release'
        build_args = ['--config', cfg]
138

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

149
        build_folder = os.path.abspath(self.build_temp)
150

151
152
153
154
        if clean_build_folder:
            rmtree(build_folder)
        if not os.path.exists(build_folder):
            os.makedirs(build_folder)
155

Davis King's avatar
Davis King committed
156
157
158
159
160
161
162
        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)
163

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

181

182
183
184
from setuptools.command.test import test as TestCommand
class PyTest(TestCommand):
    user_options = [('pytest-args=', 'a', "Arguments to pass to pytest")]
185

186
187
    def initialize_options(self):
        TestCommand.initialize_options(self)
188
        self.pytest_args = '--ignore docs --ignore dlib'
189

190
191
192
193
194
195
    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)
196

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

205
206
207
208
def read_entire_file(fname):
    """Read text out of a file relative to setup.py.
    """
    return open(os.path.join(fname)).read()
209

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