1#!/usr/bin/env python
2
3import sys, os
4from os import path
5from shutil import copyfile, rmtree
6from glob import glob
7
8from setuptools import setup, Extension
9from distutils.command.clean import clean as clean_cmd
10
11# a technique to build a shared library on windows
12from distutils.command.build_ext import build_ext
13
14build_ext.get_export_symbols = lambda x, y: []
15
16
17PACKAGE_DIR = "liblinear"
18PACKAGE_NAME = "liblinear-official"
19VERSION = "2.43.0"
20cpp_dir = "cpp-source"
21# should be consistent with dynamic_lib_name in liblinear/liblinear.py
22dynamic_lib_name = "clib"
23
24# sources to be included to build the shared library
25source_codes = [
26    path.join("blas", "daxpy.c"),
27    path.join("blas", "ddot.c"),
28    path.join("blas", "dnrm2.c"),
29    path.join("blas", "dscal.c"),
30    "linear.cpp",
31    "newton.cpp",
32]
33headers = [
34    path.join("blas", "blas.h"),
35    path.join("blas", "blasp.h"),
36    "newton.h",
37    "linear.h",
38    "linear.def",
39]
40
41kwargs_for_extension = {
42    "sources": [path.join(cpp_dir, f) for f in source_codes],
43    "depends": [path.join(cpp_dir, f) for f in headers],
44    "include_dirs": [cpp_dir],
45    "language": "c++",
46}
47
48# see ../Makefile.win
49if sys.platform == "win32":
50    kwargs_for_extension.update(
51        {
52            "define_macros": [("_WIN64", ""), ("_CRT_SECURE_NO_DEPRECATE", "")],
53            "extra_link_args": ["-DEF:{}\linear.def".format(cpp_dir)],
54        }
55    )
56
57
58def create_cpp_source():
59    for f in source_codes + headers:
60        src_file = path.join("..", f)
61        tgt_file = path.join(cpp_dir, f)
62        # ensure blas directory is created
63        os.makedirs(path.dirname(tgt_file), exist_ok=True)
64        copyfile(src_file, tgt_file)
65
66
67class CleanCommand(clean_cmd):
68    def run(self):
69        clean_cmd.run(self)
70        to_be_removed = ["build/", "dist/", "MANIFEST", cpp_dir, "{}.egg-info".format(PACKAGE_NAME)]
71        to_be_removed += glob("./{}/{}.*".format(PACKAGE_DIR, dynamic_lib_name))
72        for root, dirs, files in os.walk(os.curdir, topdown=False):
73            if "__pycache__" in dirs:
74                to_be_removed.append(path.join(root, "__pycache__"))
75            to_be_removed += [f for f in files if f.endswith(".pyc")]
76
77        for f in to_be_removed:
78            print("remove {}".format(f))
79            if f == ".":
80                continue
81            elif path.isfile(f):
82                os.remove(f)
83            elif path.isdir(f):
84                rmtree(f)
85
86def main():
87    if not path.exists(cpp_dir):
88        create_cpp_source()
89
90    with open("README") as f:
91        long_description = f.read()
92
93    setup(
94        name=PACKAGE_NAME,
95        packages=[PACKAGE_DIR],
96        version=VERSION,
97        description="Python binding of LIBLINEAR",
98        long_description=long_description,
99        long_description_content_type="text/plain",
100        author="ML group @ National Taiwan University",
101        author_email="cjlin@csie.ntu.edu.tw",
102        url="https://www.csie.ntu.edu.tw/~cjlin/liblinear",
103        install_requires=["scipy"],
104        ext_modules=[
105            Extension(
106                "{}.{}".format(PACKAGE_DIR, dynamic_lib_name), **kwargs_for_extension
107            )
108        ],
109        cmdclass={"clean": CleanCommand},
110    )
111
112
113main()
114
115