From a78cdff6eb7edea3e22690300c7eb53b93a245b8 Mon Sep 17 00:00:00 2001 From: Anirudha Bose Date: Sat, 16 Jul 2016 04:41:43 +0530 Subject: [PATCH] pybind: Rework cephfs/setup.py for PyPI Also use `set_include_dirs' in distutils.ccompiler instead of `add_include_dir' for supporting non-standard include directories. Signed-off-by: Anirudha Bose --- src/pybind/cephfs/MANIFEST.in | 1 + src/pybind/cephfs/setup.py | 222 +++++++++++++++++++++++++++------- src/pybind/rados/setup.py | 8 +- 3 files changed, 185 insertions(+), 46 deletions(-) create mode 100644 src/pybind/cephfs/MANIFEST.in diff --git a/src/pybind/cephfs/MANIFEST.in b/src/pybind/cephfs/MANIFEST.in new file mode 100644 index 0000000000000..abd9275e25d69 --- /dev/null +++ b/src/pybind/cephfs/MANIFEST.in @@ -0,0 +1 @@ +include cephfs.pyx diff --git a/src/pybind/cephfs/setup.py b/src/pybind/cephfs/setup.py index f95cb65a32b86..3b286af6628f3 100755 --- a/src/pybind/cephfs/setup.py +++ b/src/pybind/cephfs/setup.py @@ -1,56 +1,194 @@ -# Largely taken from -# https://blog.kevin-brown.com/programming/2014/09/24/combining-autotools-and-setuptools.html -import os, sys, os.path +from __future__ import print_function -from setuptools.command.egg_info import egg_info -from distutils.core import setup -from distutils.extension import Extension -from Cython.Build import cythonize -from Cython.Distutils import build_ext +import os +import pkgutil +import shutil +import subprocess +import sys +import tempfile +import textwrap +from distutils.ccompiler import new_compiler +from distutils.errors import CompileError, LinkError +from distutils.sysconfig import customize_compiler +if not pkgutil.find_loader('setuptools'): + from distutils.core import setup + from distutils.extension import Extension +else: + from setuptools import setup + from setuptools.extension import Extension -def get_version(): - try: - for line in open(os.path.join(os.path.dirname(__file__), "..", "ceph_ver.h")): - if "CEPH_GIT_NICE_VER" in line: - return line.split()[2][1:-1] +# PEP 440 versioning of the Ceph FS package on PyPI +# Bump this version, after every changeset + +__version__ = '2.0.0' + + +def get_python_flags(): + cflags = {'I': [], 'extras': []} + ldflags = {'l': [], 'L': [], 'extras': []} + + if os.environ.get('VIRTUAL_ENV', None): + python = "python" + else: + python = 'python' + str(sys.version_info.major) + '.' + str(sys.version_info.minor) + + python_config = python + '-config' + + for cflag in subprocess.check_output( + [python_config, "--cflags"] + ).strip().decode('utf-8').split(): + if cflag.startswith('-I'): + cflags['I'].append(cflag.replace('-I', '')) else: - return "0" - except IOError: - return "0" - -class EggInfoCommand(egg_info): - def finalize_options(self): - egg_info.finalize_options(self) - if "build" in self.distribution.command_obj: - build_command = self.distribution.command_obj["build"] - self.egg_base = build_command.build_base - self.egg_info = os.path.join(self.egg_base, os.path.basename(self.egg_info)) + cflags['extras'].append(cflag) + + for ldflag in subprocess.check_output( + [python_config, "--ldflags"] + ).strip().decode('utf-8').split(): + if ldflag.startswith('-l'): + ldflags['l'].append(ldflag.replace('-l', '')) + if ldflag.startswith('-L'): + ldflags['L'].append(ldflag.replace('-L', '')) + else: + ldflags['extras'].append(ldflag) + + return { + 'cflags': cflags, + 'ldflags': ldflags + } + + +def check_sanity(): + """ + Test if development headers and library for cephfs is available by compiling a dummy C program. + """ + + tmp_dir = tempfile.mkdtemp(dir=os.environ.get('TMPDIR', os.path.dirname(__file__))) + tmp_file = os.path.join(tmp_dir, 'cephfs_dummy.c') + + with open(tmp_file, 'w') as fp: + dummy_prog = textwrap.dedent(""" + #include + #include + + int main(void) { + struct ceph_mount_info *cmount = NULL; + ceph_init(cmount); + return 0; + } + """) + fp.write(dummy_prog) + + compiler = new_compiler() + customize_compiler(compiler) + + if {'MAKEFLAGS', 'MFLAGS', 'MAKELEVEL'}.issubset(set(os.environ.keys())): + # The setup.py has been invoked by a top-level Ceph make. + # Set the appropriate CFLAGS and LDFLAGS + CEPH_SRC_DIR = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + '..', + '..' + ) + + compiler.set_include_dirs(os.path.join(CEPH_SRC_DIR, 'include')) + compiler.add_library_dir(os.environ.get('CEPH_LIBDIR')) + + try: + compiler.define_macro('_FILE_OFFSET_BITS', '64') + + compiler.link_executable( + compiler.compile([tmp_file], tmp_dir), + os.path.join(tmp_dir, 'cephfs_dummy'), + libraries=['cephfs', 'rados'], + output_dir=tmp_dir, + ) + + except CompileError: + print('\nCompile Error: Ceph FS development headers not found', file=sys.stderr) + return False + except LinkError: + print('\nLink Error: Ceph FS library not found', file=sys.stderr) + return False + else: + return True + finally: + shutil.rmtree(tmp_dir) + + +if not check_sanity(): + sys.exit(1) + +cmdclass = {} +try: + from Cython.Build import cythonize + from Cython.Distutils import build_ext + + cmdclass = {'build_ext': build_ext} +except ImportError: + print("WARNING: Cython is not installed.") + + if not os.path.isfile('cephfs.c'): + print('ERROR: Cannot find Cythonized file cephfs.c', file=sys.stderr) + sys.exit(1) + else: + def cythonize(x, **kwargs): + return x + + source = "cephfs.c" +else: + source = "cephfs.pyx" # Disable cythonification if we're not really building anything if (len(sys.argv) >= 2 and - any(i in sys.argv[1:] for i in ('--help', 'clean', 'egg_info', '--version') - )): + any(i in sys.argv[1:] for i in ('--help', 'clean', 'egg_info', '--version') + )): def cythonize(x, **kwargs): return x +flags = get_python_flags() + setup( - name = 'cephfs', - version = get_version(), - description = "Python libraries for the Ceph libcephfs library", - long_description = ( - "This package contains Python libraries for interacting with Ceph's " - "cephfs library."), - ext_modules = cythonize([ - Extension("cephfs", - ["cephfs.pyx"], - libraries=["cephfs"], + name='cephfs', + version=__version__, + description="Python bindings for the Ceph FS library", + long_description=( + "This package contains Python bindings for interacting with the " + "Ceph Filesystem (Ceph FS) library. Ceph FS is a POSIX-compliant " + "filesystem that uses a Ceph Storage Cluster to store its data. The " + "Ceph filesystem uses the same Ceph Storage Cluster system as " + "Ceph Block Devices, Ceph Object Storage with its S3 and Swift APIs, " + "or native bindings (librados)." + ), + url='https://github.com/ceph/ceph/tree/master/src/pybind/cephfs', + license='LGPLv2+', + platforms='Linux', + ext_modules=cythonize( + [ + Extension( + "cephfs", + [source], + include_dirs=flags['cflags']['I'], + library_dirs=flags['ldflags']['L'], + libraries=['rados', 'cephfs'] + flags['ldflags']['l'], + extra_compile_args=flags['cflags']['extras'] + flags['ldflags']['extras'], ) - ], build_dir=os.environ.get("CYTHON_BUILD_DIR", None), include_path=[ - os.path.join(os.path.dirname(__file__), "..", "rados")] + ], + build_dir=os.environ.get("CYTHON_BUILD_DIR", None), + include_path=[ + os.path.join(os.path.dirname(__file__), "..", "rados") + ] ), - cmdclass={ - "build_ext": build_ext, - "egg_info": EggInfoCommand, - }, + classifiers=[ + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Cython', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5' + ], + cmdclass=cmdclass, ) diff --git a/src/pybind/rados/setup.py b/src/pybind/rados/setup.py index b3662b2f37ac7..d1d370a841fbd 100755 --- a/src/pybind/rados/setup.py +++ b/src/pybind/rados/setup.py @@ -95,7 +95,7 @@ def check_sanity(): compiler = new_compiler() customize_compiler(compiler) - if set(['MAKEFLAGS', 'MFLAGS', 'MAKELEVEL']).issubset(set(os.environ.keys())): + if {'MAKEFLAGS', 'MFLAGS', 'MAKELEVEL'}.issubset(set(os.environ.keys())): # The setup.py has been invoked by a top-level Ceph make. # Set the appropriate CFLAGS and LDFLAGS CEPH_SRC_DIR = os.path.join( @@ -104,7 +104,7 @@ def check_sanity(): '..' ) - compiler.add_include_dir(os.path.join(CEPH_SRC_DIR, 'include')) + compiler.set_include_dirs(os.path.join(CEPH_SRC_DIR, 'include')) compiler.add_library_dir(os.environ.get('CEPH_LIBDIR')) try: @@ -160,9 +160,9 @@ flags = get_python_flags() setup( name='rados', version=__version__, - description="Python libraries for the Ceph librados library", + description="Python bindings for the Ceph librados library", long_description=( - "This package contains Python libraries for interacting with Ceph's " + "This package contains Python bindings for interacting with Ceph's " "RADOS library. RADOS is a reliable, autonomic distributed object " "storage cluster developed as part of the Ceph distributed storage " "system. This is a shared library allowing applications to access " -- 2.39.5