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