#!/usr/bin/env python # Copyright (c) 2013-2015, Kevin Greenan (kmgreen2@gmail.com) # Copyright (c) 2013-2015, Tushar Gohad (tusharsg@gmail.com) # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. THIS SOFTWARE IS # PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN # NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os import platform import re import subprocess import sys import ctypes from ctypes.util import find_library from distutils.command.build import build as _build from distutils.command.clean import clean as _clean from distutils.sysconfig import get_python_inc try: from setuptools import setup except ImportError: from distribute_setup import use_setuptools use_setuptools() from setuptools import setup from setuptools.command.install import install as _install from setuptools import Extension platform_str = platform.platform() default_python_incdir = get_python_inc() # this is to be used only for library existence/version checks, # not for rpath handling def _find_library(name): target_lib = find_library(name) if platform_str.find("Darwin") > -1: # If we didn't find it, try extending our search a bit if not target_lib: if 'DYLD_LIBRARY_PATH' in os.environ: os.environ['DYLD_LIBRARY_PATH'] += ':%s/lib' % sys.prefix else: os.environ['DYLD_LIBRARY_PATH'] = '%s/lib' % sys.prefix target_lib = find_library(name) # If we *still* don't find it, bail if not target_lib: return target_lib target_lib = os.path.abspath(target_lib) if os.path.islink(target_lib): p = os.readlink(target_lib) if os.path.isabs(p): target_lib = p else: target_lib = os.path.join(os.path.dirname(target_lib), p) elif not target_lib: # See https://bugs.python.org/issue9998 expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name) cmd = ['ld', '-t'] libpath = os.environ.get('LD_LIBRARY_PATH') if libpath: for d in libpath.split(':'): cmd.extend(['-L', d]) cmd.extend(['-o', os.devnull, '-l%s' % name]) try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) out, _ = p.communicate() if hasattr(os, 'fsdecode'): out = os.fsdecode(out) res = re.search(expr, out) if res: target_lib = res.group(0) except Exception: pass # result will be None # return absolute path to the library if found return target_lib class build(_build): def check_liberasure(self): library_basename = "liberasurecode" library_version = "1" library_url = "https://opendev.org/openstack/liberasurecode" found_path = _find_library("erasurecode") if found_path: libec = ctypes.CDLL(found_path) try: packed_version = libec.liberasurecode_get_version() except AttributeError: # If we don't have the version getter, we're probably # pre-1.4.0; fall back to some heuristics based on name version_parts = [x for x in found_path.split('.') if x.isdigit()] if not version_parts: # A bare .so? Well, we haven't released a 2.x yet... but # if we ever do, hopefully it'd still provide a # liberasurecode_get_version() return if version_parts[0] == library_version: return # else, seems to be an unknown version -- assume it won't work else: version = ( packed_version >> 16, (packed_version >> 8) & 0xff, packed_version & 0xff) if (1, 0, 0) <= version < (2, 0, 0): return if platform_str.find("Darwin") > -1: liberasure_file = \ library_basename + "." + library_version + ".dylib" else: liberasure_file = \ library_basename + ".so." + library_version print("**************************************************************") print("*** ") print("*** Can not locate %s" % (liberasure_file)) print("*** ") print("*** Install - ") print("*** Manual: %s" % library_url) print("*** Fedora/Red Hat variants: liberasurecode-devel") print("*** Debian/Ubuntu variants: liberasurecode-dev") print("*** ") print("**************************************************************") sys.exit(-1) def run(self): self.check_liberasure() _build.run(self) class clean(_clean): def run(self): _clean.run(self) class install(_install): def run(self): install_cmd = self.distribution.get_command_obj('install') install_lib = self.distribution.get_command_obj('install_lib') for cmd in (install_lib, install_cmd): cmd.ensure_finalized() # ensure that the paths are absolute so we don't get lost opts = {'exec_prefix': install_cmd.exec_prefix, 'root': install_cmd.root} for optname, value in list(opts.items()): if value is not None: opts[optname] = os.path.abspath(value) installroot = install_lib.install_dir _install.run(self) # Another Mac-ism... If the libraries are installed # in a strange place, DYLD_LIRBARY_PATH needs to be # updated. if platform_str.find("Darwin") > -1: ldpath_str = "DYLD_LIBRARY_PATH" else: ldpath_str = "LD_LIBRARY_PATH" print("***************************************************") print("** ") print("** PyECLib libraries have been installed to: ") print("** %s" % installroot) print("** ") print("** Any user using this library must update: ") print("** %s" % ldpath_str) print("** ") print("** Run 'ldconfig' or place this line: ") print("** export %s=%s" % (ldpath_str, "%s" % installroot)) print("** ") print("** into .bashrc, .profile, or the appropriate shell") print("** start-up script! Also look at ldconfig(8) man ") print("** page for a more static LD configuration ") print("** ") print("***************************************************") module = Extension('pyeclib_c', py_limited_api=True, define_macros=[('MAJOR VERSION', '1'), ('MINOR VERSION', '6')], include_dirs=[default_python_incdir, 'src/c/pyeclib_c', '/usr/include', '/usr/include/liberasurecode', '%s/include/liberasurecode' % sys.prefix, '%s/include' % sys.prefix], libraries=['erasurecode'], library_dirs=['%s/lib' % sys.prefix], runtime_library_dirs=['%s/lib' % sys.prefix], # The extra arguments are for debugging # extra_compile_args=['-g', '-O0'], sources=['src/c/pyeclib_c/pyeclib_c.c']) setup(name='pyeclib', version='1.6.1', author='Kevin Greenan', author_email='kmgreen2@gmail.com', maintainer='Kevin Greenan and Tushar Gohad', maintainer_email='kmgreen2@gmail.com, tusharsg@gmail.com', url='https://opendev.org/openstack/pyeclib', project_urls={ 'Bug Tracker': 'https://bugs.launchpad.net/pyeclib', }, description=('This library provides a simple Python interface for ' 'implementing erasure codes. To obtain the best possible ' 'performance, the underlying erasure code algorithms are ' 'written in C.'), classifiers=[ "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", ], long_description=open('README.rst', 'r').read(), long_description_content_type='text/x-rst', platforms='Linux', license='BSD', ext_modules=[module], packages=['pyeclib'], package_dir={'pyeclib': 'pyeclib'}, cmdclass={'build': build, 'install': install, 'clean': clean}, py_modules=['pyeclib.ec_iface', 'pyeclib.core'], command_options={ 'build_sphinx': { 'build_dir': ('setup.py', 'doc/build')}}, test_suite='test')