]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
pybind: Rework cephfs/setup.py for PyPI
authorAnirudha Bose <ani07nov@gmail.com>
Fri, 15 Jul 2016 23:11:43 +0000 (04:41 +0530)
committerAnirudha Bose <ani07nov@gmail.com>
Mon, 18 Jul 2016 20:53:41 +0000 (02:23 +0530)
Also use `set_include_dirs' in distutils.ccompiler instead of `add_include_dir'
for supporting non-standard include directories.

Signed-off-by: Anirudha Bose <ani07nov@gmail.com>
src/pybind/cephfs/MANIFEST.in [new file with mode: 0644]
src/pybind/cephfs/setup.py
src/pybind/rados/setup.py

diff --git a/src/pybind/cephfs/MANIFEST.in b/src/pybind/cephfs/MANIFEST.in
new file mode 100644 (file)
index 0000000..abd9275
--- /dev/null
@@ -0,0 +1 @@
+include cephfs.pyx
index f95cb65a32b865cfd0c90e4fa20f4c975990f32f..3b286af6628f32a259e6a3df1ae0a09bdb3ace4e 100755 (executable)
-# 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 <stddef.h>
+        #include <cephfs/libcephfs.h>
+
+        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,
 )
index b3662b2f37ac743584705bcef6e4c844b1f39cc7..d1d370a841fbd7181a6aad5a7c51de3f9e345c9a 100755 (executable)
@@ -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 "