"src/git@developer.sourcefind.cn:tianlh/lightgbm-dcu.git" did not exist on "bcad692e263e0317cab11032dd017c78f9e58e5f"
setup.py 15.8 KB
Newer Older
wxchan's avatar
wxchan committed
1
# coding: utf-8
2
# pylint: disable=invalid-name, exec-used, C0111
wxchan's avatar
wxchan committed
3
4
"""Setup lightgbm package."""
from __future__ import absolute_import
5

Guolin Ke's avatar
Guolin Ke committed
6
import distutils
7
import io
8
import logging
9
import os
Guolin Ke's avatar
Guolin Ke committed
10
import shutil
11
import struct
12
import subprocess
13
14
import sys

15
from platform import system
16
from setuptools import find_packages, setup
17
18
19
from setuptools.command.install import install
from setuptools.command.install_lib import install_lib
from setuptools.command.sdist import sdist
20

21
22

def find_lib():
23
    libpath_py = os.path.join(CURRENT_DIR, 'lightgbm', 'libpath.py')
24
25
26
27
    libpath = {'__file__': libpath_py}
    exec(compile(open(libpath_py, "rb").read(), libpath_py, 'exec'), libpath, libpath)

    LIB_PATH = [os.path.relpath(path, CURRENT_DIR) for path in libpath['find_lib_path']()]
28
    logger.info("Installing lib_lightgbm from: %s" % LIB_PATH)
29
30
31
32
33
34
    return LIB_PATH


def copy_files(use_gpu=False):

    def copy_files_helper(folder_name):
35
        src = os.path.join(CURRENT_DIR, os.path.pardir, folder_name)
36
        if os.path.exists(src):
37
            dst = os.path.join(CURRENT_DIR, 'compile', folder_name)
38
            shutil.rmtree(dst, ignore_errors=True)
39
            distutils.dir_util.copy_tree(src, dst, verbose=0)
40
        else:
41
            raise Exception('Cannot copy {0} folder'.format(src))
42

43
    if not os.path.isfile(os.path.join(CURRENT_DIR, '_IS_SOURCE_PACKAGE.txt')):
44
45
        copy_files_helper('include')
        copy_files_helper('src')
46
47
48
        if not os.path.exists(os.path.join(CURRENT_DIR, "compile", "windows")):
            os.makedirs(os.path.join(CURRENT_DIR, "compile", "windows"))
        distutils.file_util.copy_file(os.path.join(CURRENT_DIR, os.path.pardir, "windows", "LightGBM.sln"),
49
50
                                      os.path.join(CURRENT_DIR, "compile", "windows", "LightGBM.sln"),
                                      verbose=0)
51
        distutils.file_util.copy_file(os.path.join(CURRENT_DIR, os.path.pardir, "windows", "LightGBM.vcxproj"),
52
53
                                      os.path.join(CURRENT_DIR, "compile", "windows", "LightGBM.vcxproj"),
                                      verbose=0)
Guolin Ke's avatar
Guolin Ke committed
54
        if use_gpu:
55
            copy_files_helper('compute')
56
        distutils.file_util.copy_file(os.path.join(CURRENT_DIR, os.path.pardir, "CMakeLists.txt"),
57
58
                                      os.path.join(CURRENT_DIR, "compile", "CMakeLists.txt"),
                                      verbose=0)
59
        distutils.file_util.copy_file(os.path.join(CURRENT_DIR, os.path.pardir, "LICENSE"),
60
61
                                      os.path.join(CURRENT_DIR, "LICENSE"),
                                      verbose=0)
wxchan's avatar
wxchan committed
62
63


64
def clear_path(path):
65
66
67
68
69
70
71
72
73
74
75
76
    if os.path.isdir(path):
        contents = os.listdir(path)
        for file_name in contents:
            file_path = os.path.join(path, file_name)
            if os.path.isfile(file_path):
                os.remove(file_path)
            else:
                shutil.rmtree(file_path)


def silent_call(cmd, raise_error=False, error_msg=''):
    try:
77
        output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
78
        with open(LOG_PATH, "ab") as log:
79
80
81
82
            log.write(output)
        return 0
    except Exception as err:
        if isinstance(err, subprocess.CalledProcessError):
83
            with open(LOG_PATH, "ab") as log:
84
                log.write(err.output)
85
        if raise_error:
86
            raise Exception("\n".join((error_msg, LOG_NOTICE)))
87
        return 1
88
89


90
91
92
def compile_cpp(use_mingw=False, use_gpu=False, use_mpi=False, nomp=False,
                use_hdfs=False, boost_root=None, boost_dir=None,
                boost_include_dir=None, boost_librarydir=None,
93
94
                opencl_include_dir=None, opencl_library=None,
                openmp_include_dir=None, openmp_library=None):
95

96
97
98
99
    if os.path.exists(os.path.join(CURRENT_DIR, "build_cpp")):
        shutil.rmtree(os.path.join(CURRENT_DIR, "build_cpp"))
    os.makedirs(os.path.join(CURRENT_DIR, "build_cpp"))
    os.chdir(os.path.join(CURRENT_DIR, "build_cpp"))
100

101
102
    logger.info("Starting to compile the library.")

103
    cmake_cmd = ["cmake", "../compile/"]
104
    if use_gpu:
105
        cmake_cmd.append("-DUSE_GPU=ON")
106
107
108
109
110
111
112
113
114
115
116
117
118
119
        if boost_root:
            cmake_cmd.append("-DBOOST_ROOT={0}".format(boost_root))
        if boost_dir:
            cmake_cmd.append("-DBoost_DIR={0}".format(boost_dir))
        if boost_include_dir:
            cmake_cmd.append("-DBoost_INCLUDE_DIR={0}".format(boost_include_dir))
        if boost_librarydir:
            cmake_cmd.append("-DBOOST_LIBRARYDIR={0}".format(boost_librarydir))
        if opencl_include_dir:
            cmake_cmd.append("-DOpenCL_INCLUDE_DIR={0}".format(opencl_include_dir))
        if opencl_library:
            cmake_cmd.append("-DOpenCL_LIBRARY={0}".format(opencl_library))
    if use_mpi:
        cmake_cmd.append("-DUSE_MPI=ON")
120
121
    if nomp:
        cmake_cmd.append("-DUSE_OPENMP=OFF")
122
123
    if use_hdfs:
        cmake_cmd.append("-DUSE_HDFS=ON")
124

125
    if system() in ('Windows', 'Microsoft'):
126
        if use_mingw:
127
128
            if use_mpi:
                raise Exception('MPI version cannot be compiled by MinGW due to the miss of MPI library in it')
129
130
            logger.info("Starting to compile with CMake and MinGW.")
            silent_call(cmake_cmd + ["-G", "MinGW Makefiles"], raise_error=True,
131
                        error_msg='Please install CMake and all required dependencies first')
132
133
            silent_call(["mingw32-make.exe", "_lightgbm"], raise_error=True,
                        error_msg='Please install MinGW first')
134
        else:
135
            status = 1
136
            lib_path = os.path.join(CURRENT_DIR, "compile", "windows", "x64", "DLL", "lib_lightgbm.dll")
137
            if not any((use_gpu, use_mpi, use_hdfs)):
138
                logger.info("Starting to compile with MSBuild from existing solution file.")
139
                platform_toolsets = ("v142", "v141", "v140")
140
                for pt in platform_toolsets:
141
142
                    status = silent_call(["MSBuild",
                                          os.path.join(CURRENT_DIR, "compile", "windows", "LightGBM.sln"),
143
144
145
146
147
148
                                          "/p:Configuration=DLL",
                                          "/p:Platform=x64",
                                          "/p:PlatformToolset={0}".format(pt)])
                    if status == 0 and os.path.exists(lib_path):
                        break
                    else:
149
                        clear_path(os.path.join(CURRENT_DIR, "compile", "windows", "x64"))
150
151
152
                if status != 0 or not os.path.exists(lib_path):
                    logger.warning("Compilation with MSBuild from existing solution file failed.")
            if status != 0 or not os.path.exists(lib_path):
153
                vs_versions = ("Visual Studio 16 2019", "Visual Studio 15 2017", "Visual Studio 14 2015")
154
155
                for vs in vs_versions:
                    logger.info("Starting to compile with %s." % vs)
156
                    status = silent_call(cmake_cmd + ["-G", vs, "-A", "x64"])
157
158
159
                    if status == 0:
                        break
                    else:
160
                        clear_path(os.path.join(CURRENT_DIR, "build_cpp"))
161
                if status != 0:
162
                    raise Exception("\n".join(('Please install Visual Studio or MS Build and all required dependencies first',
163
                                    LOG_NOTICE)))
164
165
                silent_call(["cmake", "--build", ".", "--target", "_lightgbm", "--config", "Release"], raise_error=True,
                            error_msg='Please install CMake first')
166
    else:  # Linux, Darwin (macOS), etc.
167
        logger.info("Starting to compile with CMake.")
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
        # Apple Clang with OpenMP
        if system() == 'Darwin' and not nomp and not (os.environ.get('CC', '').startswith('gcc')
                                                      and os.environ.get('CXX', '').startswith('g++')):
            def get_cmake_opts(openmp_include_dir, openmp_library):
                if openmp_include_dir and openmp_library:
                    return ['-DOpenMP_C_FLAGS=-Xpreprocessor -fopenmp -I{0}'.format(openmp_include_dir),
                            '-DOpenMP_C_LIB_NAMES=omp',
                            '-DOpenMP_CXX_FLAGS=-Xpreprocessor -fopenmp -I{0}'.format(openmp_include_dir),
                            '-DOpenMP_CXX_LIB_NAMES=omp',
                            '-DOpenMP_omp_LIBRARY={0}'.format(openmp_library)]
                else:
                    return []

            status = silent_call(cmake_cmd + get_cmake_opts(openmp_include_dir, openmp_library))
            status += silent_call(["make", "_lightgbm", "-j4"])
            if status != 0:
                logger.warning("Compilation failed.")
                logger.info("Starting to compile with Homebrew OpenMP paths guesses.")
                clear_path(os.path.join(CURRENT_DIR, "build_cpp"))
                status = silent_call(cmake_cmd + get_cmake_opts('/usr/local/opt/libomp/include',
                                                                '/usr/local/opt/libomp/lib/libomp.dylib'))
                status += silent_call(["make", "_lightgbm", "-j4"])
            if status != 0:
                logger.warning("Compilation failed.")
                logger.info("Starting to compile with MacPorts OpenMP paths guesses.")
                clear_path(os.path.join(CURRENT_DIR, "build_cpp"))
                silent_call(cmake_cmd + get_cmake_opts('/opt/local/include/libomp',
                                                       '/opt/local/lib/libomp/libomp.dylib'),
                            raise_error=True, error_msg='Please install CMake and all required dependencies first')
                silent_call(["make", "_lightgbm", "-j4"], raise_error=True,
                            error_msg='An error has occurred while building lightgbm library file')
        else:
            silent_call(cmake_cmd, raise_error=True, error_msg='Please install CMake and all required dependencies first')
            silent_call(["make", "_lightgbm", "-j4"], raise_error=True,
                        error_msg='An error has occurred while building lightgbm library file')
203
    os.chdir(CURRENT_DIR)
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219


class CustomInstallLib(install_lib):

    def install(self):
        outfiles = install_lib.install(self)
        src = find_lib()[0]
        dst = os.path.join(self.install_dir, 'lightgbm')
        dst, _ = self.copy_file(src, dst)
        outfiles.append(dst)
        return outfiles


class CustomInstall(install):

    user_options = install.user_options + [
220
221
222
        ('mingw', 'm', 'Compile with MinGW'),
        ('gpu', 'g', 'Compile GPU version'),
        ('mpi', None, 'Compile MPI version'),
223
        ('nomp', None, 'Compile version without OpenMP support'),
224
        ('hdfs', 'h', 'Compile HDFS version'),
225
226
227
228
229
230
        ('precompile', 'p', 'Use precompiled library'),
        ('boost-root=', None, 'Boost preferred installation prefix'),
        ('boost-dir=', None, 'Directory with Boost package configuration file'),
        ('boost-include-dir=', None, 'Directory containing Boost headers'),
        ('boost-librarydir=', None, 'Preferred Boost library directory'),
        ('opencl-include-dir=', None, 'OpenCL include directory'),
231
232
233
        ('opencl-library=', None, 'Path to OpenCL library'),
        ('openmp-include-dir=', None, 'OpenMP include directory'),
        ('openmp-library=', None, 'Path to OpenMP library')
234
235
236
237
238
239
    ]

    def initialize_options(self):
        install.initialize_options(self)
        self.mingw = 0
        self.gpu = 0
240
241
242
243
244
245
        self.boost_root = None
        self.boost_dir = None
        self.boost_include_dir = None
        self.boost_librarydir = None
        self.opencl_include_dir = None
        self.opencl_library = None
246
247
        self.openmp_include_dir = None
        self.openmp_library = None
248
        self.mpi = 0
249
        self.hdfs = 0
250
        self.precompile = 0
251
        self.nomp = 0
252
253

    def run(self):
254
        open(LOG_PATH, 'wb').close()
255
256
        if not self.precompile:
            copy_files(use_gpu=self.gpu)
257
258
            compile_cpp(use_mingw=self.mingw, use_gpu=self.gpu, use_mpi=self.mpi, nomp=self.nomp,
                        use_hdfs=self.hdfs, boost_root=self.boost_root, boost_dir=self.boost_dir,
259
                        boost_include_dir=self.boost_include_dir, boost_librarydir=self.boost_librarydir,
260
261
                        opencl_include_dir=self.opencl_include_dir, opencl_library=self.opencl_library,
                        openmp_include_dir=self.openmp_include_dir, openmp_library=self.openmp_library)
262
        install.run(self)
263
264
        if os.path.isfile(LOG_PATH):
            os.remove(LOG_PATH)
265
266
267
268
269


class CustomSdist(sdist):

    def run(self):
Guolin Ke's avatar
Guolin Ke committed
270
        copy_files(use_gpu=True)
271
272
273
274
275
276
277
        open(os.path.join(CURRENT_DIR, '_IS_SOURCE_PACKAGE.txt'), 'w').close()
        if os.path.exists(os.path.join(CURRENT_DIR, 'lightgbm', 'Release')):
            shutil.rmtree(os.path.join(CURRENT_DIR, 'lightgbm', 'Release'))
        if os.path.exists(os.path.join(CURRENT_DIR, 'lightgbm', 'windows', 'x64')):
            shutil.rmtree(os.path.join(CURRENT_DIR, 'lightgbm', 'windows', 'x64'))
        if os.path.isfile(os.path.join(CURRENT_DIR, 'lightgbm', 'lib_lightgbm.so')):
            os.remove(os.path.join(CURRENT_DIR, 'lightgbm', 'lib_lightgbm.so'))
278
        sdist.run(self)
279
280
        if os.path.isfile(os.path.join(CURRENT_DIR, '_IS_SOURCE_PACKAGE.txt')):
            os.remove(os.path.join(CURRENT_DIR, '_IS_SOURCE_PACKAGE.txt'))
281
282
283
284


if __name__ == "__main__":
    if (8 * struct.calcsize("P")) != 64:
285
        raise Exception('Cannot install LightGBM in 32-bit Python, please use 64-bit Python instead.')
286

287
288
289
290
291
    CURRENT_DIR = os.path.abspath(os.path.dirname(__file__))
    LOG_PATH = os.path.join(os.path.expanduser('~'), 'LightGBM_compilation.log')
    LOG_NOTICE = "The full version of error log was saved into {0}".format(LOG_PATH)
    if os.path.isfile(os.path.join(CURRENT_DIR, os.path.pardir, 'VERSION.txt')):
        distutils.file_util.copy_file(os.path.join(CURRENT_DIR, os.path.pardir, 'VERSION.txt'),
292
293
                                      os.path.join(CURRENT_DIR, 'lightgbm', 'VERSION.txt'),
                                      verbose=0)
294
295
    version = io.open(os.path.join(CURRENT_DIR, 'lightgbm', 'VERSION.txt'), encoding='utf-8').read().strip()
    readme = io.open(os.path.join(CURRENT_DIR, 'README.rst'), encoding='utf-8').read()
296

297
    sys.path.insert(0, CURRENT_DIR)
298

299
300
301
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger('LightGBM')

Guolin Ke's avatar
Guolin Ke committed
302
303
304
    setup(name='lightgbm',
          version=version,
          description='LightGBM Python Package',
305
          long_description=readme,
Guolin Ke's avatar
Guolin Ke committed
306
307
308
309
310
311
312
313
          install_requires=[
              'numpy',
              'scipy',
              'scikit-learn'
          ],
          maintainer='Guolin Ke',
          maintainer_email='guolin.ke@microsoft.com',
          zip_safe=False,
314
315
316
317
318
          cmdclass={
              'install': CustomInstall,
              'install_lib': CustomInstallLib,
              'sdist': CustomSdist,
          },
Guolin Ke's avatar
Guolin Ke committed
319
320
          packages=find_packages(),
          include_package_data=True,
321
          license='The MIT License (Microsoft)',
322
          url='https://github.com/microsoft/LightGBM',
323
324
325
326
327
328
329
330
331
332
333
334
335
          classifiers=['Development Status :: 5 - Production/Stable',
                       'Intended Audience :: Science/Research',
                       'License :: OSI Approved :: MIT License',
                       'Natural Language :: English',
                       'Operating System :: MacOS',
                       'Operating System :: Microsoft :: Windows',
                       'Operating System :: POSIX',
                       'Operating System :: Unix',
                       'Programming Language :: Python :: 2',
                       'Programming Language :: Python :: 2.7',
                       'Programming Language :: Python :: 3',
                       'Programming Language :: Python :: 3.5',
                       'Programming Language :: Python :: 3.6',
336
                       'Programming Language :: Python :: 3.7',
337
                       'Topic :: Scientific/Engineering :: Artificial Intelligence'])