build-python.sh 13.7 KB
Newer Older
1
2
3
4
5
#!/bin/sh

# [description]
#
#     Prepare a source distribution (sdist) or built distribution (wheel)
6
#     of the Python-package, and optionally install it.
7
8
9
10
11
12
13
14
15
#
# [usage]
#
#     # build sdist and put it in dist/
#     sh ./build-python.sh sdist
#
#     # build wheel and put it in dist/
#     sh ./build-python.sh bdist_wheel [OPTIONS]
#
16
#     # compile lib_lightgbm and install the Python-package wrapping it
17
18
#     sh ./build-python.sh install [OPTIONS]
#
19
#     # install the Python-package using a pre-compiled lib_lightgbm
20
21
22
23
24
#     # (assumes lib_lightgbm.{dll,so} is located at the root of the repo)
#     sh ./build-python.sh install --precompile
#
# [options]
#
25
26
#     --boost-dir=FILEPATH
#                                   Directory with Boost package configuration file.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#     --boost-include-dir=FILEPATH
#                                   Directory containing Boost headers.
#     --boost-librarydir=FILEPATH
#                                   Preferred Boost library directory.
#     --boost-root=FILEPATH
#                                   Boost preferred installation prefix.
#     --opencl-include-dir=FILEPATH
#                                   OpenCL include directory.
#     --opencl-library=FILEPATH
#                                   Path to OpenCL library.
#     --bit32
#                                   Compile 32-bit version.
#     --cuda
#                                   Compile CUDA version.
#     --gpu
#                                   Compile GPU version.
#     --integrated-opencl
#                                   Compile integrated OpenCL version.
#     --mingw
#                                   Compile with MinGW.
#     --mpi
#                                   Compile MPI version.
49
50
51
#     --no-isolation
#                                   Assume all build and install dependencies are already installed,
#                                   don't go to the internet to get them.
52
53
54
55
56
#     --nomp
#                                   Compile version without OpenMP support.
#     --precompile
#                                   Use precompiled library.
#                                   Only used with 'install' command.
Jeff Daily's avatar
Jeff Daily committed
57
58
#     --rocm
#                                   Compile ROCm version.
59
#     --time-costs
60
#                                   Compile version that outputs time costs for different internal routines.
61
62
63
64
#     --user
#                                   Install into user-specific instead of global site-packages directory.
#                                   Only used with 'install' command.

65
set -e -u
66

67
echo "[INFO] building lightgbm"
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

# Default values of arguments
INSTALL="false"
BUILD_SDIST="false"
BUILD_WHEEL="false"

PIP_INSTALL_ARGS=""
BUILD_ARGS=""
PRECOMPILE="false"

while [ $# -gt 0 ]; do
  case "$1" in
    ############################
    # sub-commands of setup.py #
    ############################
    install)
      INSTALL="true"
      ;;
    sdist)
      BUILD_SDIST="true"
      ;;
    bdist_wheel)
      BUILD_WHEEL="true"
      ;;
    ############################
    # customized library paths #
    ############################
95
    --boost-dir|--boost-dir=*)
96
        if echo "$1" | grep -q '^*=*$';
97
98
99
            then shift;
        fi
        BOOST_DIR="${1#*=}"
100
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.Boost_DIR='${BOOST_DIR}'"
101
        ;;
102
    --boost-include-dir|--boost-include-dir=*)
103
        if echo "$1" | grep -q '^*=*$';
104
105
106
            then shift;
        fi
        BOOST_INCLUDE_DIR="${1#*=}"
107
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.Boost_INCLUDE_DIR='${BOOST_INCLUDE_DIR}'"
108
109
        ;;
    --boost-librarydir|--boost-librarydir=*)
110
        if echo "$1" | grep -q '^*=*$';
111
112
113
            then shift;
        fi
        BOOST_LIBRARY_DIR="${1#*=}"
114
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.BOOST_LIBRARYDIR='${BOOST_LIBRARY_DIR}'"
115
116
        ;;
    --boost-root|--boost-root=*)
117
        if echo "$1" | grep -q '^*=*$';
118
119
120
            then shift;
        fi
        BOOST_ROOT="${1#*=}"
121
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.Boost_ROOT='${BOOST_ROOT}'"
122
123
        ;;
    --opencl-include-dir|--opencl-include-dir=*)
124
        if echo "$1" | grep -q '^*=*$';
125
126
127
            then shift;
        fi
        OPENCL_INCLUDE_DIR="${1#*=}"
128
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.OpenCL_INCLUDE_DIR='${OPENCL_INCLUDE_DIR}'"
129
130
        ;;
    --opencl-library|--opencl-library=*)
131
        if echo "$1" | grep -q '^*=*$';
132
133
134
            then shift;
        fi
        OPENCL_LIBRARY="${1#*=}"
135
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.OpenCL_LIBRARY='${OPENCL_LIBRARY}'"
136
137
138
139
140
        ;;
    #########
    # flags #
    #########
    --bit32)
141
142
        echo "[INFO] Attempting to build 32-bit version of LightGBM, which is only supported on Windows with Visual Studio."
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.args=-AWin32"
143
144
        ;;
    --cuda)
145
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.USE_CUDA=ON"
146
        ;;
147
148
    --rocm)
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.USE_ROCM=ON"
tianlh's avatar
tianlh committed
149
150
	BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.CMAKE_HIP_COMPILER=dcc"
	#BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.GPU_MAX_THREADS_PER_BLOCK=256"
151
        ;;
152
    --gpu)
153
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.USE_GPU=ON"
154
155
        ;;
    --integrated-opencl)
156
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.__INTEGRATE_OPENCL=ON"
157
158
        ;;
    --mingw)
159
160
        # ref: https://stackoverflow.com/a/45104058/3986677
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.CMAKE_SH=CMAKE_SH-NOTFOUND"
161
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.args=-G'MinGW Makefiles'"
162
163
        ;;
    --mpi)
164
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.USE_MPI=ON"
165
        ;;
166
167
168
169
    --no-isolation)
        BUILD_ARGS="${BUILD_ARGS} --no-isolation"
        PIP_INSTALL_ARGS="${PIP_INSTALL_ARGS} --no-build-isolation"
        ;;
170
    --nomp)
171
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.USE_OPENMP=OFF"
172
173
174
175
176
        ;;
    --precompile)
        PRECOMPILE="true"
        ;;
    --time-costs)
177
        BUILD_ARGS="${BUILD_ARGS} --config-setting=cmake.define.USE_TIMETAG=ON"
178
179
180
181
182
        ;;
    --user)
        PIP_INSTALL_ARGS="${PIP_INSTALL_ARGS} --user"
        ;;
    *)
183
        echo "[ERROR] invalid argument '${1}'. Aborting"
184
        exit 1
185
186
187
188
189
        ;;
  esac
  shift
done

190
191
pip install --prefer-binary 'build>=0.10.0'

192
# create a new directory that just contains the files needed
193
# to build the Python-package
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
create_isolated_source_dir() {
    rm -rf \
        ./lightgbm-python \
        ./lightgbm \
        ./python-package/build \
        ./python-package/build_cpp \
        ./python-package/compile \
        ./python-package/dist \
        ./python-package/lightgbm.egg-info

    cp -R ./python-package ./lightgbm-python

    cp LICENSE ./lightgbm-python/
    cp VERSION.txt ./lightgbm-python/lightgbm/VERSION.txt

209
210
211
212
213
    cp -R ./cmake ./lightgbm-python
    cp CMakeLists.txt ./lightgbm-python
    cp -R ./include ./lightgbm-python
    cp -R ./src ./lightgbm-python
    cp -R ./swig ./lightgbm-python
214
215
216
217
218
219
220
221

    # include only specific files from external_libs, to keep the package
    # small and avoid redistributing code with licenses incompatible with
    # LightGBM's license

    ######################
    # fast_double_parser #
    ######################
222
    mkdir -p ./lightgbm-python/external_libs/fast_double_parser
223
224
    cp \
        external_libs/fast_double_parser/CMakeLists.txt \
225
        ./lightgbm-python/external_libs/fast_double_parser/CMakeLists.txt
226
227
    cp \
        external_libs/fast_double_parser/LICENSE* \
228
        ./lightgbm-python/external_libs/fast_double_parser/
229

230
    mkdir -p ./lightgbm-python/external_libs/fast_double_parser/include/
231
232
    cp \
        external_libs/fast_double_parser/include/fast_double_parser.h \
233
        ./lightgbm-python/external_libs/fast_double_parser/include/
234
235
236
237

    #######
    # fmt #
    #######
238
    mkdir -p ./lightgbm-python/external_libs/fmt
239
240
    cp \
        external_libs/fast_double_parser/CMakeLists.txt \
241
        ./lightgbm-python/external_libs/fmt/CMakeLists.txt
242
243
    cp \
        external_libs/fmt/LICENSE* \
244
        ./lightgbm-python/external_libs/fmt/
245

246
    mkdir -p ./lightgbm-python/external_libs/fmt/include/fmt
247
248
    cp \
        external_libs/fmt/include/fmt/*.h \
249
        ./lightgbm-python/external_libs/fmt/include/fmt/
250
251
252
253

    #########
    # Eigen #
    #########
254
    mkdir -p ./lightgbm-python/external_libs/eigen/Eigen
255
256
    cp \
        external_libs/eigen/CMakeLists.txt \
257
        ./lightgbm-python/external_libs/eigen/CMakeLists.txt
258
259
260
261

    modules="Cholesky Core Dense Eigenvalues Geometry Householder Jacobi LU QR SVD"
    for eigen_module in ${modules}; do
        cp \
262
263
264
265
            "external_libs/eigen/Eigen/${eigen_module}" \
            "./lightgbm-python/external_libs/eigen/Eigen/${eigen_module}"
        if [ "${eigen_module}" != "Dense" ]; then
            mkdir -p "./lightgbm-python/external_libs/eigen/Eigen/src/${eigen_module}/"
266
267
            cp \
                -R \
268
269
                "external_libs/eigen/Eigen/src/${eigen_module}"/* \
                "./lightgbm-python/external_libs/eigen/Eigen/src/${eigen_module}/"
270
271
272
        fi
    done

273
    mkdir -p ./lightgbm-python/external_libs/eigen/Eigen/misc
274
275
276
    cp \
        -R \
        external_libs/eigen/Eigen/src/misc \
277
        ./lightgbm-python/external_libs/eigen/Eigen/src/misc/
278

279
    mkdir -p ./lightgbm-python/external_libs/eigen/Eigen/plugins
280
281
282
    cp \
        -R \
        external_libs/eigen/Eigen/src/plugins \
283
        ./lightgbm-python/external_libs/eigen/Eigen/src/plugins/
284
285
286
287

    ###################
    # compute (Boost) #
    ###################
288
    mkdir -p ./lightgbm-python/external_libs/compute
289
290
291
    cp \
        -R \
        external_libs/compute/include \
292
        ./lightgbm-python/external_libs/compute/include/
293
294
295
296
297
298
299
300
301
}

create_isolated_source_dir

cd ./lightgbm-python

# installation involves building the wheel + `pip install`-ing it
if test "${INSTALL}" = true; then
    if test "${PRECOMPILE}" = true; then
302
303
304
305
306
307
308
309
310
        BUILD_SDIST=true
        BUILD_WHEEL=false
        BUILD_ARGS=""
        rm -rf \
            ./cmake \
            ./CMakeLists.txt \
            ./external_libs \
            ./include \
            ./src \
311
            ./swig
312
313
314
        # use regular-old setuptools for these builds, to avoid
        # trying to recompile the shared library
        sed -i.bak -e '/start:build-system/,/end:build-system/d' pyproject.toml
315
        # shellcheck disable=SC2129
316
317
318
319
        echo '[build-system]' >> ./pyproject.toml
        echo 'requires = ["setuptools"]' >> ./pyproject.toml
        echo 'build-backend = "setuptools.build_meta"' >> ./pyproject.toml
        echo "" >> ./pyproject.toml
320
        echo "recursive-include lightgbm *.dll *.dylib *.so" > ./MANIFEST.in
321
322
323
        echo "" >> ./MANIFEST.in
        mkdir -p ./lightgbm/lib
        if test -f ../lib_lightgbm.so; then
324
            echo "[INFO] found pre-compiled lib_lightgbm.so"
325
            cp ../lib_lightgbm.so ./lightgbm/lib/lib_lightgbm.so
326
        elif test -f ../lib_lightgbm.dylib; then
327
            echo "[INFO] found pre-compiled lib_lightgbm.dylib"
328
            cp ../lib_lightgbm.dylib ./lightgbm/lib/lib_lightgbm.dylib
329
330
331
        elif test -f ../lib_lightgbm.dll; then
            echo "[INFO] found pre-compiled lib_lightgbm.dll"
            cp ../lib_lightgbm.dll ./lightgbm/lib/lib_lightgbm.dll
332
        elif test -f ../Release/lib_lightgbm.dll; then
333
            echo "[INFO] found pre-compiled Release/lib_lightgbm.dll"
334
335
            cp ../Release/lib_lightgbm.dll ./lightgbm/lib/lib_lightgbm.dll
        elif test -f ../windows/x64/DLL/lib_lightgbm.dll; then
336
            echo "[INFO] found pre-compiled windows/x64/DLL/lib_lightgbm.dll"
337
338
            cp ../windows/x64/DLL/lib_lightgbm.dll ./lightgbm/lib/lib_lightgbm.dll
            cp ../windows/x64/DLL/lib_lightgbm.lib ./lightgbm/lib/lib_lightgbm.lib
339
340
341
342
343
344
345
        elif test -f ../windows/x64/Debug_DLL/lib_lightgbm.dll; then
            echo "[INFO] found pre-compiled windows/x64/Debug_DLL/lib_lightgbm.dll"
            cp ../windows/x64/Debug_DLL/lib_lightgbm.dll ./lightgbm/lib/lib_lightgbm.dll
            cp ../windows/x64/Debug_DLL/lib_lightgbm.lib ./lightgbm/lib/lib_lightgbm.lib
        else
            echo "[ERROR] cannot find pre-compiled library. Aborting"
            exit 1
346
347
        fi
        rm -f ./*.bak
348
349
350
351
352
353
354
    else
        BUILD_SDIST="false"
        BUILD_WHEEL="true"
    fi
fi

if test "${BUILD_SDIST}" = true; then
355
    echo "[INFO] --- building sdist ---"
356
    rm -f ../dist/*.tar.gz
357
358
359
360
361
    # use xargs to work with args that contain whitespaces
    # note that empty echo string leads to that xargs doesn't run the command
    # in some implementations of xargs
    # ref: https://stackoverflow.com/a/8296746
    echo "--sdist --outdir ../dist ${BUILD_ARGS} ." | xargs python -m build
362
363
364
fi

if test "${BUILD_WHEEL}" = true; then
365
    echo "[INFO] --- building wheel ---"
366
    rm -f ../dist/*.whl || true
367
368
369
370
371
    # use xargs to work with args that contain whitespaces
    # note that empty echo string leads to that xargs doesn't run the command
    # in some implementations of xargs
    # ref: https://stackoverflow.com/a/8296746
    echo "--wheel --outdir ../dist ${BUILD_ARGS} ." | xargs python -m build
372
373
374
fi

if test "${INSTALL}" = true; then
375
    echo "[INFO] --- installing lightgbm ---"
376
    cd ../dist
377
    if test "${BUILD_WHEEL}" = true; then
378
        PACKAGE_NAME="$(echo lightgbm*.whl)"
379
    else
380
        PACKAGE_NAME="$(echo lightgbm*.tar.gz)"
381
    fi
382
    # ref for use of '--find-links': https://stackoverflow.com/a/52481267/3986677
383
    # shellcheck disable=SC2086
384
385
    pip install \
        ${PIP_INSTALL_ARGS} \
386
        --force-reinstall \
387
        --no-cache-dir \
388
        --no-deps \
389
        --find-links=. \
390
        "${PACKAGE_NAME}"
391
392
393
    cd ../
fi

394
echo "[INFO] cleaning up"
395
rm -rf ./lightgbm-python